Files
Veskslop-t/poc/src/communication.ts
T

84 lines
2.4 KiB
TypeScript

/*
* Veskslop communication backend — pure logic (no Discord imports).
*
* Keeps a small ring buffer of trigger-qualifying messages: the last 100 from
* trusted users and the last 30 from untrusted ones. Also parses the
* `whitelist` commands and normalizes @mentions.
*/
export interface PuppetMessage {
id: string;
channelId: string;
guildId?: string;
authorId: string;
authorName: string;
/** Raw message content as received. */
content: string;
timestamp: string;
trusted: boolean;
/** Parsed command text when the message is one. */
command?: string;
}
export const TRUSTED_LOG_LIMIT = 100;
export const UNTRUSTED_LOG_LIMIT = 30;
export class MessageLog {
trusted: PuppetMessage[] = [];
untrusted: PuppetMessage[] = [];
add(message: PuppetMessage) {
const bucket = message.trusted ? this.trusted : this.untrusted;
const limit = message.trusted ? TRUSTED_LOG_LIMIT : UNTRUSTED_LOG_LIMIT;
bucket.push(message);
if (bucket.length > limit) bucket.splice(0, bucket.length - limit);
}
snapshot() {
return { trusted: [...this.trusted], untrusted: [...this.untrusted] };
}
}
const USER_MENTION = /<@!?(\d+)>/g;
export function extractMentionIds(content: string, mentions?: Array<{ id: string }>): Set<string> {
const ids = new Set<string>();
for (const mention of mentions ?? []) ids.add(mention.id);
for (const match of content.matchAll(USER_MENTION)) ids.add(match[1]);
return ids;
}
export function stripMentions(content: string, ids: ReadonlySet<string>): string {
return content
.replace(USER_MENTION, (full, id) => (ids.has(id) ? "" : full))
.replace(/\s+/g, " ")
.trim();
}
export interface ParsedCommand {
name: string;
args: string[];
}
export function parseCommand(text: string): ParsedCommand | undefined {
const trimmed = text.trim();
if (!trimmed) return undefined;
const [name, ...args] = trimmed.split(/\s+/);
return { name: name.toLowerCase(), args };
}
export function isTrusted(
userId: string,
ownerId: string | undefined,
whitelist: readonly string[] | undefined
): boolean {
return userId === ownerId || !!whitelist?.includes(userId);
}
/** Same 500-2800 ms pacing used by the control bridge, for autonomous replies. */
export function randomActionDelay(): number {
return 500 + Math.floor(Math.random() * 2301);
}