/* * 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 { const ids = new Set(); 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 { 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); }