diff --git a/STATUS.md b/STATUS.md index ba9d54b..ecc3838 100644 --- a/STATUS.md +++ b/STATUS.md @@ -64,7 +64,7 @@ Siguiente paso de desarrollo: ejecutar el plan mínimo de CI/CD, healthcheck y b - [ ] Logs accesibles; worker de cola activo. - Funcional - [ ] Bot añadido a los grupos; grupos activos sincronizados. - - [ ] Comandos base verificados en un grupo real (crear/ver/x/tomar/soltar/configurar). + - [ ] Comandos base verificados en un grupo real (crear/ver/x/tomar/soltar/alarma). - Comunicación/privacidad - [ ] Aviso a la junta: cómo usar, qué se guarda, cómo desactivar recordatorios. diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 0953216..fa1d9d8 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -41,12 +41,12 @@ Comandos y alias - Ejemplo: t soltar 26 - Idempotente: si no estabas asignado, lo indica sin error. La tarea puede quedar “sin responsable” si no quedan asignados. - Configurar recordatorios - - Aliases: configurar, config + - Aliases: alarma, recordatorio, configurar, config - Opciones: daily | weekly | off - Ejemplos: - - t configurar daily - - t configurar weekly - - t configurar off + - t alarma daily + - t alarma weekly + - t alarma off - Notas: resumen diario/weekly por DM; weekly los lunes a la hora configurada (por defecto 08:30 si aplica); se evita duplicar en el mismo día y no se envía si no hay tareas. - Ayuda - Aliases: ayuda, help, ? @@ -80,7 +80,7 @@ Ejemplos prácticos - t tomar 42 - t soltar 42 - Configurar recordatorios: - - t configurar weekly + - t alarma weekly Limitaciones y notas - El bot no publica en grupos por diseño. diff --git a/docs/commands-inventory.md b/docs/commands-inventory.md index 67fc85c..5ba5c8f 100644 --- a/docs/commands-inventory.md +++ b/docs/commands-inventory.md @@ -116,10 +116,10 @@ Ejemplos --- -## t configurar (recordatorios) +## t alarma / t recordatorio (recordatorios) -Alias: `config`, `configurar` -Sintaxis: `t configurar diario|l-v|semanal|off [HH:MM]` +Alias: `alarma`, `recordatorio`, `configurar`, `config` +Sintaxis: `t alarma diario|l-v|semanal|off [HH:MM]` Valores admitidos y alias - `diario`/`diaria` → recordatorio diario (se guarda como `daily`). @@ -135,10 +135,10 @@ Nota de localización - Internamente se almacenan claves en inglés (`daily`, `weekdays`, `weekly`, `off`), pero el copy al usuario es en español. Pendiente de revisión futura para evitar fugas como “weekly” en mensajes. Ejemplos -- `t configurar diaria 09:00` -- `t configurar l-v 08:30` -- `t configurar semanal` (→ lunes 08:30) -- `t configurar off` +- `t alarma diaria 09:00` +- `t alarma l-v 08:30` +- `t alarma semanal` (→ lunes 08:30) +- `t alarma off` --- diff --git a/docs/golden/command.texts.json b/docs/golden/command.texts.json index a6b2686..ed5c3da 100644 --- a/docs/golden/command.texts.json +++ b/docs/golden/command.texts.json @@ -20,7 +20,7 @@ "ℹ️ Uso: `t x 26` o múltiples: `t x 14 19 24` o `t x 14,19,24` (máx. 10)", "ℹ️ Uso: `t tomar 26` o múltiples: `t tomar 12 19 50` o `t tomar 12,19,50` (máx. 10)", "ℹ️ Uso: `t soltar 26`", - "ℹ️ Uso: `t configurar diario|l-v|semanal|off [HH:MM]`" + "ℹ️ Uso: `t alarma diario|l-v|semanal|off [HH:MM]`" ], "errors": [ "⚠️ Tarea {id} no encontrada.", diff --git a/docs/plan-ayuda-bot.md b/docs/plan-ayuda-bot.md index dfcb7f2..57d2ddc 100644 --- a/docs/plan-ayuda-bot.md +++ b/docs/plan-ayuda-bot.md @@ -42,7 +42,7 @@ Objetivo: hacer la ayuda consistente, útil ante comandos desconocidos, visible - Comandos: `t soltar`, `t unassign`, `t dejar`, `t liberar`, `t renunciar` - Un solo ID - Configurar recordatorios - - Comandos: `t configurar diario|l-v|semanal|off [HH:MM]` + - Comandos: `t alarma diario|l-v|semanal|off [HH:MM]` - Mapea alias a `daily`, `weekdays`, `weekly`, `off`; hora opcional con normalización - Ayuda - Comandos: `t ayuda`, `t help`, `t ?`, y variante “ayuda avanzada” @@ -96,7 +96,7 @@ Objetivo: hacer la ayuda consistente, útil ante comandos desconocidos, visible - Contenido sugerido (resumen): - Ayuda rápida: - Secciones: “COMANDOS BÁSICOS”, “LISTADOS”, “ACCESO WEB” - - Bullets con: crear (`t n ...`), ver (`t ver mis|grupo|todos|sin`), completar/tomar/soltar, configurar recordatorios, y `t web` + - Bullets con: crear (`t n ...`), ver (`t ver mis|grupo|todos|sin`), completar/tomar/soltar, alarmas/recordatorios, y `t web` - Nota: _El bot responde por DM, incluso si escribes desde un grupo._ - Ayuda extendida: - Además: formatos de fecha (`YYYY-MM-DD`, `YY-MM-DD`→`20YY-MM-DD`, `hoy|mañana`), límites (máx. 10 IDs), reglas de asignación por contexto, gating de grupos, detalles de “ver todos”. diff --git a/docs/plan-onboarding-comandos.md b/docs/plan-onboarding-comandos.md index 6ca917f..33e0eae 100644 --- a/docs/plan-onboarding-comandos.md +++ b/docs/plan-onboarding-comandos.md @@ -82,7 +82,7 @@ Impacto en tests Objetivos - Enviar un paquete de 2 DMs (Mensaje 1 + Mensaje 2) por usuario cuando se crea una tarea en un grupo. - Mensaje 1: CTA “t tomar {CÓDIGO}” + “t info”. - - Mensaje 2: mini‑chuleta (“t mias”, “t todas”, “t configurar …”, “t web”), 5–10 s después del Mensaje 1. + - Mensaje 2: mini‑chuleta (“t mias”, “t todas”, “t alarma …”, “t web”), 5–10 s después del Mensaje 1. - Repetir el mismo paquete una única vez más si pasan ≥ 14 días sin interacción del usuario (si hubo interacción, no se envía el segundo paquete). - Cap por evento; sin mensajes en grupos. @@ -121,7 +121,7 @@ Copys de onboarding (exactos) - Mensaje 2 (mini‑chuleta; se envía tras 5–10 s, en ambos disparos): - “Guía rápida (este es un mensaje único): · Tus tareas: t mias · Todas: t todas - · Recordatorios: t configurar diario | l‑v | semanal | off + · Recordatorios: t alarma diario | l‑v | semanal | off · Web: t web” Impacto en tests @@ -246,7 +246,7 @@ Ideas a evaluar después - “Bienvenida al primer DM inbound” (mensaje corto de bienvenida una única vez cuando el usuario inicia chat). - Ventanas horarias: programar next_attempt_at si se detecta horario nocturno (requiere mínima lógica extra). - Tabla explícita de onboarding (si se quiere persistir fuera de response_queue), p. ej. user_onboarding con timestamps y contadores. -- Resumen semanal opt‑in (ya soportado con “t configurar …”): medir retención y satisfacción. +- Resumen semanal opt‑in (ya soportado con “t alarma …”): medir retención y satisfacción. --- @@ -291,7 +291,7 @@ Ideas a evaluar después 2) Onboarding — Mensaje 2 (reminder, único) - “Guía rápida (este es un mensaje único): · Tus tareas: t mias · Todas: t todas - · Recordatorios: t configurar diario | l‑v | semanal | off + · Recordatorios: t alarma diario | l‑v | semanal | off · Web: t web” 3) Transición cuando se intenta listar desde grupo (responder por DM) diff --git a/docs/refactor-command-service.md b/docs/refactor-command-service.md index e4a4672..f18312d 100644 --- a/docs/refactor-command-service.md +++ b/docs/refactor-command-service.md @@ -28,7 +28,7 @@ Este documento describe un plan incremental y sin cambios de UX para refactoriza - completar.ts - tomar.ts - soltar.ts - - configurar.ts + - alarma.ts - web.ts - src/services/onboarding.ts ← JIT y bundle de 2 DMs (disparado desde “nueva”) - (opcional) src/services/web-access.ts ← gestión de tokens web (invalidate, generate, hash, URL) @@ -65,7 +65,7 @@ Etapa 2 — Parser de “nueva” - Tests unitarios: hoy/mañana (con y sin acento), YYYY-MM-DD y YY-MM-DD→20YY, @yo, limpieza de puntuación, excluir @menciones de la descripción. Etapa 3 — Handlers pequeños -- configurar.ts: upsert en `user_preferences`, validación `HH:MM`, etiquetas de respuesta. +- alarma.ts: upsert en `user_preferences`, validación `HH:MM`, etiquetas de respuesta. - web.ts: invalidar tokens vigentes, generar token, hash (sha256), insertar y construir URL. Métrica `web_tokens_issued_total`. - Conectar ambos en el router. @@ -110,7 +110,7 @@ Etapa 8 — Hardening y observabilidad ## Detalles técnicos por handler -- configurar: +- configurar (alarma): - Validar mapa de alias: diario/daily, l‑v/weekdays, semanal/weekly, off/apagar/ninguno. - Hora opcional `HH:MM` (clamp 00–23; minutos 00–59). - Upsert con `last_reminded_on` a NULL al cambiar frecuencia. @@ -129,7 +129,7 @@ Etapa 8 — Hardening y observabilidad ## Utilidades compartidas (shared.ts) -- ACTION_ALIASES y SCOPE_ALIASES (incluyendo: n/+/crear/nueva, ver/ls/listar/mostrar, x/hecho/completar/done, tomar/claim, soltar/unassign, ayuda/help/info/?, configurar/config, web). +- ACTION_ALIASES y SCOPE_ALIASES (incluyendo: n/+/crear/nueva, ver/ls/listar/mostrar, x/hecho/completar/done, tomar/claim, soltar/unassign, ayuda/help/info/?, alarma/recordatorio/configurar/config, web). - ymdInTZ(d, TZ) y todayYMD. - formatters: reutilizar `formatDDMM`, `codeId`, `padTaskId`, `bold`, `italic`, `code`, `section`. - parseMultipleIds(tokens: string[], max=10): retorna números válidos > 0, dedup, truncado. diff --git a/docs/whatsapp-style-guide.md b/docs/whatsapp-style-guide.md index 7d617c8..33f79e0 100644 --- a/docs/whatsapp-style-guide.md +++ b/docs/whatsapp-style-guide.md @@ -46,7 +46,7 @@ Patrones comunes - Sufijo “... y N más” si aplica - Ayuda rápida: - Secciones: “COMANDOS BÁSICOS”, “LISTADOS”, “ACCESO WEB” - - Bullets con ejemplos: `` `t n ...` ``, `` `t ver mis|grupo|todos|sin` ``, `` `t x 26` ``, `` `t tomar 12` ``, `` `t configurar ...` ``, `` `t web` `` + - Bullets con ejemplos: `` `t n ...` ``, `` `t ver mis|grupo|todos|sin` ``, `` `t x 26` ``, `` `t tomar 12` ``, `` `t alarma ...` ``, `` `t web` `` Localización - Todo copy en español. Evitar fugas de claves internas en inglés (ej. “weekly”). @@ -77,7 +77,7 @@ Ayuda rápida - `t n Descripción 2025-11-05 @Ana` - `t ver` (en grupo) · `t ver mis` (DM) · `t ver todos` - `t x 26` · `t tomar 12` -- `t configurar diario|l-v|semanal|off [HH:MM]` +- `t alarma diario|l-v|semanal|off [HH:MM]` - `t web` _El bot responde por DM, incluso si escribes desde un grupo._ ``` diff --git a/src/services/commands/handlers/configurar.ts b/src/services/commands/handlers/alarma.ts similarity index 91% rename from src/services/commands/handlers/configurar.ts rename to src/services/commands/handlers/alarma.ts index b9ee90b..c469278 100644 --- a/src/services/commands/handlers/configurar.ts +++ b/src/services/commands/handlers/alarma.ts @@ -13,7 +13,7 @@ type Msg = { mentions?: string[]; }; -export function handleConfigurar(context: Ctx, deps: { db: Database }): Msg[] { +export function handleAlarma(context: Ctx, deps: { db: Database }): Msg[] { const tokens = (context.message || '').trim().split(/\s+/); const optRaw = (tokens[2] || '').toLowerCase(); @@ -41,7 +41,7 @@ export function handleConfigurar(context: Ctx, deps: { db: Database }): Msg[] { if (!m) { return [{ recipient: context.sender, - message: 'ℹ️ Uso: `t configurar diario|l-v|semanal|off [HH:MM]`' + message: 'ℹ️ Uso: `t alarma diario|l-v|semanal|off [HH:MM]`' }]; } const hh = Math.max(0, Math.min(23, parseInt(m[1], 10))); @@ -51,7 +51,7 @@ export function handleConfigurar(context: Ctx, deps: { db: Database }): Msg[] { if (!freq) { return [{ recipient: context.sender, - message: 'ℹ️ Uso: `t configurar diario|l-v|semanal|off [HH:MM]`' + message: 'ℹ️ Uso: `t alarma diario|l-v|semanal|off [HH:MM]`' }]; } diff --git a/src/services/commands/index.ts b/src/services/commands/index.ts index 68d4e6e..8a34e26 100644 --- a/src/services/commands/index.ts +++ b/src/services/commands/index.ts @@ -1,11 +1,11 @@ /** * Router de comandos (Etapa 3) - * Maneja 'configurar' y 'web', y delega el resto al código actual (null → fallback). + * Maneja 'alarma' 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 { handleAlarma } from './handlers/alarma'; import { handleWeb } from './handlers/web'; import { handleVer } from './handlers/ver'; import { handleCompletar } from './handlers/completar'; @@ -17,7 +17,7 @@ type VerCtx = Parameters[0]; type CompletarCtx = Parameters[0]; type TomarCtx = Parameters[0]; type SoltarCtx = Parameters[0]; -type ConfigurarCtx = Parameters[0]; +type ConfigurarCtx = Parameters[0]; type WebCtx = Parameters[0]; import { ResponseQueue } from '../response-queue'; import { isGroupId } from '../../utils/whatsapp'; @@ -31,7 +31,7 @@ function getQuickHelp(): string { '- 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]`', + '- Alarmas/recordatorios: `t alarma diario|l-v|semanal|off [HH:MM]`', '- Web: `t web`' ].join('\n'); } @@ -46,7 +46,7 @@ function getFullHelp(): string { ' · Tomar: `tomar`, `claim`', ' · Soltar: `soltar`, `unassign`', 'Preferencias:', - ' · `t configurar diario|l-v|semanal|off [HH:MM]`', + ' · `t alarma 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`', @@ -151,8 +151,8 @@ export async function route(context: RouteContext, deps?: { db: Database }): Pro 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 }); } + // --- alarma / web --- + if (action === 'alarma') { trackOnboarding(); return handleAlarma(context as unknown as ConfigurarCtx, { db: database }); } if (action === 'web') { trackOnboarding(); return await handleWeb(context as unknown as WebCtx, { db: database }); } // --- desconocido --- diff --git a/src/services/commands/shared.ts b/src/services/commands/shared.ts index a25f598..be9e732 100644 --- a/src/services/commands/shared.ts +++ b/src/services/commands/shared.ts @@ -38,8 +38,10 @@ export const ACTION_ALIASES: Record = { 'help': 'ayuda', 'info': 'ayuda', '?': 'ayuda', - 'config': 'configurar', - 'configurar': 'configurar', + 'config': 'alarma', + 'configurar': 'alarma', + 'alarma': 'alarma', + 'recordatorio': 'alarma', 'web': 'web' }; diff --git a/src/services/onboarding.ts b/src/services/onboarding.ts index 6f32eef..710aea8 100644 --- a/src/services/onboarding.ts +++ b/src/services/onboarding.ts @@ -213,7 +213,7 @@ function buildOnboardingMessage2(): string { Puedes interactuar con el bot escribiéndome por privado: - Ver todas las tareas: ${code('t todas')} - Ver solo tus tareas: ${code('t mias')} -- ¿Quieres recordatorios?: ${code('t configurar diario|l-v|semanal|off')} +- ¿Quieres recordatorios?: ${code('t alarma diario|l-v|semanal|off')} - Web: ${code('t web')}`; } diff --git a/tests/unit/services/command.reminders-config.test.ts b/tests/unit/services/command.reminders-config.test.ts index 69c22bc..c193d7b 100644 --- a/tests/unit/services/command.reminders-config.test.ts +++ b/tests/unit/services/command.reminders-config.test.ts @@ -4,7 +4,7 @@ import { initializeDatabase } from '../../../src/db'; import { CommandService } from '../../../src/services/command'; import { setDb } from '../../../src/db/locator'; -describe('CommandService - configurar recordatorios', () => { +describe('CommandService - alarma recordatorios', () => { let memdb: Database; const SENDER = '34600123456'; @@ -39,8 +39,8 @@ describe('CommandService - configurar recordatorios', () => { return { freq: String(row.freq), time: row.time ? String(row.time) : null }; } - it('configurar daily guarda preferencia y responde confirmación', async () => { - const res = await runCmd('t configurar daily'); + it('alarma daily guarda preferencia y responde confirmación', async () => { + const res = await runCmd('t alarma daily'); expect(res).toHaveLength(1); expect(res[0].recipient).toBe(SENDER); expect(res[0].message).toContain('✅ Recordatorios: diario'); @@ -51,8 +51,8 @@ describe('CommandService - configurar recordatorios', () => { expect(pref!.time).toBe('08:30'); // default }); - it('configurar weekly guarda preferencia y responde confirmación', async () => { - const res = await runCmd('t configurar weekly'); + it('alarma weekly guarda preferencia y responde confirmación', async () => { + const res = await runCmd('t alarma weekly'); expect(res).toHaveLength(1); expect(res[0].message).toContain('semanal (lunes 08:30)'); @@ -61,8 +61,8 @@ describe('CommandService - configurar recordatorios', () => { expect(pref!.freq).toBe('weekly'); }); - it('configurar off guarda preferencia y responde confirmación', async () => { - const res = await runCmd('t configurar off'); + it('alarma off guarda preferencia y responde confirmación', async () => { + const res = await runCmd('t alarma off'); expect(res).toHaveLength(1); expect(res[0].message).toContain('apagado'); @@ -71,27 +71,27 @@ describe('CommandService - configurar recordatorios', () => { expect(pref!.freq).toBe('off'); }); - it('configurar con opción inválida devuelve uso correcto y no escribe en DB', async () => { - const res = await runCmd('t configurar foo'); + it('alarma con opción inválida devuelve uso correcto y no escribe en DB', async () => { + const res = await runCmd('t alarma foo'); expect(res).toHaveLength(1); - expect(res[0].message).toContain('Uso: `t configurar diario|l-v|semanal|off [HH:MM]`'); + expect(res[0].message).toContain('Uso: `t alarma diario|l-v|semanal|off [HH:MM]`'); const pref = getPref(); expect(pref).toBeNull(); }); it('upsert idempotente: cambiar de daily a off actualiza la fila existente', async () => { - await runCmd('t configurar daily'); + await runCmd('t alarma daily'); let pref = getPref(); expect(pref!.freq).toBe('daily'); - await runCmd('t configurar off'); + await runCmd('t alarma off'); pref = getPref(); expect(pref!.freq).toBe('off'); }); - it('configurar l-v con hora guarda weekdays y respeta hora', async () => { - const res = await runCmd('t configurar l-v 8:00'); + it('alarma l-v con hora guarda weekdays y respeta hora', async () => { + const res = await runCmd('t alarma l-v 8:00'); expect(res).toHaveLength(1); expect(res[0].recipient).toBe(SENDER); expect(res[0].message).toContain('laborables'); diff --git a/tests/unit/services/command.unknown-help.test.ts b/tests/unit/services/command.unknown-help.test.ts index 9d8e387..8f7d18f 100644 --- a/tests/unit/services/command.unknown-help.test.ts +++ b/tests/unit/services/command.unknown-help.test.ts @@ -34,6 +34,6 @@ describe('CommandService - comando desconocido devuelve ayuda rápida', () => { expect(msg).toContain('t info'); expect(msg).toContain('t mias'); expect(msg).toContain('t web'); - expect(msg).toContain('t configurar'); + expect(msg).toContain('t alarma'); }); });