/** * Match inbound *text* messages to keyword commands (case-insensitive). */ export type CommandHandler = (ctx: Ctx) => void | Promise; interface Command { aliases: string[]; handler: CommandHandler; } export class CommandRouter { private commands: Command[] = []; private _fallback: CommandHandler | null = null; command(aliases: string | string[], handler: CommandHandler): this { const list = Array.isArray(aliases) ? aliases : [aliases]; this.commands.push({ aliases: list.map((a) => a.toLowerCase()), handler }); return this; } fallback(handler: CommandHandler): this { this._fallback = handler; return this; } async dispatch(text: string | undefined | null, ctx: Ctx): Promise { const norm = (text || '').trim().toLowerCase(); for (const c of this.commands) { if (c.aliases.includes(norm)) { await c.handler(ctx); return true; } } if (this._fallback) { await this._fallback(ctx); return true; } return false; } }