/** * Router de comandos (Etapa 3) * Maneja 'configurar' y 'web', y delega el resto al código actual (null → fallback). * Nota: No importar CommandService aquí para evitar ciclos de import. */ import type { Database } from 'bun:sqlite'; import { ACTION_ALIASES } from './shared'; import { handleConfigurar } from './handlers/configurar'; import { handleWeb } from './handlers/web'; import { handleVer } from './handlers/ver'; import { handleCompletar } from './handlers/completar'; import { handleTomar } from './handlers/tomar'; import { handleSoltar } from './handlers/soltar'; import { handleNueva } from './handlers/nueva'; type NuevaCtx = Parameters[0]; type VerCtx = Parameters[0]; type CompletarCtx = Parameters[0]; type TomarCtx = Parameters[0]; type SoltarCtx = Parameters[0]; type ConfigurarCtx = Parameters[0]; type WebCtx = Parameters[0]; import { ResponseQueue } from '../response-queue'; import { isGroupId } from '../../utils/whatsapp'; import { Metrics } from '../metrics'; function getQuickHelp(): string { return [ 'Guía rápida:', '- Ver tus tareas: `t mias`', '- Ver todas: `t todas`', '- Crear: `t n Descripción 2028-11-26 @Ana`', '- Completar: `t x 123`', '- Tomar: `t tomar 12`', '- Configurar recordatorios: `t configurar diario|l-v|semanal|off [HH:MM]`', '- Web: `t web`' ].join('\n'); } function getFullHelp(): string { return [ 'Ayuda avanzada:', 'Comandos y alias:', ' · Crear: `n`, `nueva`, `crear`, `+`', ' · Ver: `ver`, `listar`, `mostrar`, `ls` (scopes: `mis` | `todas`)', ' · Completar: `x`, `hecho`, `completar`, `done`', ' · Tomar: `tomar`, `claim`', ' · Soltar: `soltar`, `unassign`', 'Preferencias:', ' · `t configurar diario|l-v|semanal|off [HH:MM]`', 'Fechas:', ' · `YYYY-MM-DD` o `YY-MM-DD` → `20YY-MM-DD` (ej.: 27-09-04)', ' · Palabras: `hoy`, `mañana`', 'Acceso web:', ' · `t web`', 'Atajos:', ' · `t mias`', ' · `t todas`' ].join('\n'); } function buildUnknownHelp(): string { const header = '❓ COMANDO NO RECONOCIDO'; const cta = 'Prueba `t info`'; return [header, cta, '', getQuickHelp()].join('\n'); } export type RoutedMessage = { recipient: string; message: string; mentions?: string[]; }; export type RouteContext = { sender: string; groupId: string; message: string; mentions: string[]; messageId?: string; participant?: string; fromMe?: boolean; }; // --------------------------------------------------------------------------- // Route helpers // --------------------------------------------------------------------------- function trackOnboarding(): void { try { ResponseQueue.setOnboardingAggregatesMetrics(); } catch {} } function trackAlias(metric: string): void { try { Metrics.inc('commands_alias_used_total', 1, { action: metric }); } catch {} } function trackUknown(): void { try { Metrics.inc('commands_unknown_total'); } catch {} } // --------------------------------------------------------------------------- // Route dispatcher // --------------------------------------------------------------------------- export async function route(context: RouteContext, deps?: { db: Database }): Promise { const trimmed = (context.message || '').trim(); const tokens = trimmed.split(/\s+/); const rawAction = (tokens[1] || '').toLowerCase(); const action = ACTION_ALIASES[rawAction] || rawAction; // --- ayuda (no requiere DB) --- if (action === 'ayuda') { if (rawAction === 'info' || rawAction === '?') trackAlias('info'); trackOnboarding(); const isAdvanced = (tokens[2] || '').toLowerCase() === 'avanzada'; const message = isAdvanced ? getFullHelp() : [getQuickHelp(), '', 'Ayuda avanzada: `t ayuda avanzada`'].join('\n'); return [{ recipient: context.sender, message }]; } // --- resto de comandos requieren DB --- const database = deps?.db; if (!database) return null; // --- nueva --- if (action === 'nueva') { trackOnboarding(); return await handleNueva(context as unknown as NuevaCtx, { db: database }); } // --- ver --- if (action === 'ver') { if (rawAction === 'mias' || rawAction === 'mías') trackAlias('mias'); else if (rawAction === 'todas' || rawAction === 'todos') trackAlias('todas'); trackOnboarding(); // En grupo: transición a DM if (isGroupId(context.groupId)) { try { Metrics.inc('ver_dm_transition_total'); } catch {} return [{ recipient: context.sender, message: 'No respondo en grupos. Tus tareas: t mias · Todas: t todas · Info: t info · Web: t web', }]; } return await handleVer(context as unknown as VerCtx); } // --- completar / tomar / soltar --- if (action === 'completar') { trackOnboarding(); return await handleCompletar(context as unknown as CompletarCtx); } if (action === 'tomar') { trackOnboarding(); return await handleTomar(context as unknown as TomarCtx); } if (action === 'soltar') { trackOnboarding(); return await handleSoltar(context as unknown as SoltarCtx); } // --- configurar / web --- if (action === 'configurar') { trackOnboarding(); return handleConfigurar(context as unknown as ConfigurarCtx, { db: database }); } if (action === 'web') { trackOnboarding(); return await handleWeb(context as unknown as WebCtx, { db: database }); } // --- desconocido --- trackUknown(); return [{ recipient: context.sender, message: buildUnknownHelp() }]; }