From fe5a8d29cba44550214de2d0146a237e4afcb863 Mon Sep 17 00:00:00 2001 From: borja Date: Mon, 8 Sep 2025 21:06:24 +0200 Subject: [PATCH] feat: aplicar codeId y monoespacio en listados, ayudas y mensajes Co-authored-by: aider (openrouter/openai/gpt-5) --- src/services/command.ts | 45 ++++++++++--------- .../services/command.claim-unassign.test.ts | 4 +- .../services/command.reminders-config.test.ts | 2 +- tests/unit/services/command.test.ts | 6 +-- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/services/command.ts b/src/services/command.ts index 3e48a7e..10c544c 100644 --- a/src/services/command.ts +++ b/src/services/command.ts @@ -170,7 +170,7 @@ export class CommandService { ' · Tomar: tomar, claim', ' · Soltar: soltar, unassign', '- Preferencias:', - ' · /t configurar daily|weekly|off (hora por defecto 08:30; semanal: lunes 08:30)', + ' · `/t configurar daily|weekly|off` (hora por defecto 08:30; semanal: lunes 08:30)', '- Notas:', ' · En grupos, el bot responde por DM al autor (no publica en el grupo).', ' · Si creas en grupo y no mencionas a nadie → “sin responsable”; en DM → se asigna al creador.', @@ -184,12 +184,12 @@ export class CommandService { } const help = [ 'Guía rápida:', - '- Crear: /t n Descripción mañana @Ana', - '- Ver grupo: /t ver grupo', - '- Ver mis: /t ver mis', - '- Ver todos: /t ver todos', - '- Completar: /t x 123', - '- Configurar recordatorios: /t configurar daily|weekly|off' + '- Crear: `/t n Descripción mañana @Ana`', + '- Ver grupo: `/t ver grupo`', + '- Ver mis: `/t ver mis`', + '- Ver todos: `/t ver todos`', + '- Completar: `/t x 123`', + '- Configurar recordatorios: `/t configurar daily|weekly|off`' ].join('\n'); return [{ recipient: context.sender, @@ -217,7 +217,7 @@ export class CommandService { if (!isGroupId(context.groupId)) { return [{ recipient: context.sender, - message: 'Este comando se usa en grupos. Prueba: /t ver mis' + message: 'Este comando se usa en grupos. Prueba: `/t ver mis`' }]; } if (!GroupSyncService.isGroupActive(context.groupId)) { @@ -239,7 +239,7 @@ export class CommandService { const rendered = items.map((t) => { const isOverdue = t.due_date ? t.due_date < todayYMD : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; - return `- ${t.id}) _${t.description || '(sin descripción)'}_${datePart} — ${ICONS.unassigned} sin responsable`; + return `- ${codeId(t.id)} _${t.description || '(sin descripción)'}_${datePart} — ${ICONS.unassigned} sin responsable`; }); const total = TaskService.countGroupUnassigned(context.groupId); @@ -288,7 +288,7 @@ export class CommandService { : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; const isOverdue = t.due_date ? t.due_date < todayYMD : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; - return `- ${t.id}) _${t.description || '(sin descripción)'}_${datePart} — ${owner}`; + return `- ${codeId(t.id)} _${t.description || '(sin descripción)'}_${datePart} — ${owner}`; })); sections.push(...rendered); } @@ -313,7 +313,7 @@ export class CommandService { const renderedUnassigned = unassigned.map((t) => { const isOverdue = t.due_date ? t.due_date < todayYMD : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; - return `- ${t.id}) _${t.description || '(sin descripción)'}_${datePart} — ${ICONS.unassigned} sin responsable`; + return `- ${codeId(t.id)} _${t.description || '(sin descripción)'}_${datePart} — ${ICONS.unassigned} sin responsable`; }); sections.push(...renderedUnassigned); @@ -341,7 +341,7 @@ export class CommandService { const renderedUnassigned = unassigned.map((t) => { const isOverdue = t.due_date ? t.due_date < todayYMD : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; - return `- ${t.id}) _${t.description || '(sin descripción)'}_${datePart} — ${ICONS.unassigned} sin responsable`; + return `- ${codeId(t.id)} _${t.description || '(sin descripción)'}_${datePart} — ${ICONS.unassigned} sin responsable`; }); sections.push(...renderedUnassigned); @@ -353,7 +353,7 @@ export class CommandService { } } else { // Si no hay snapshot fresca de membresía, mantenemos una nota instructiva mínima - sections.push('ℹ️ Para ver tareas sin responsable de un grupo, usa “/t ver sin” desde ese grupo.'); + sections.push('ℹ️ Para ver tareas sin responsable de un grupo, usa `/t ver sin` desde ese grupo.'); } } @@ -368,7 +368,7 @@ export class CommandService { if (!isGroupId(context.groupId)) { return [{ recipient: context.sender, - message: 'Este comando se usa en grupos. Prueba: /t ver mis' + message: 'Este comando se usa en grupos. Prueba: `/t ver mis`' }]; } if (!GroupSyncService.isGroupActive(context.groupId)) { @@ -407,7 +407,7 @@ export class CommandService { : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; const isOverdue = t.due_date ? t.due_date < todayYMD : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; - return `- ${t.id}) _${t.description || '(sin descripción)'}_${datePart} — ${owner}`; + return `- ${codeId(t.id)} _${t.description || '(sin descripción)'}_${datePart} — ${owner}`; })); const total = TaskService.countGroupPending(context.groupId); @@ -458,7 +458,7 @@ export class CommandService { : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; const isOverdue = t.due_date ? t.due_date < todayYMD : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; - return `- ${t.id}) _${t.description || '(sin descripción)'}_${datePart} — ${owner}`; + return `- ${codeId(t.id)} _${t.description || '(sin descripción)'}_${datePart} — ${owner}`; })); sections.push(...rendered); } @@ -480,7 +480,7 @@ export class CommandService { if (!id || Number.isNaN(id)) { return [{ recipient: context.sender, - message: 'ℹ️ Uso: /t x 26' + message: 'ℹ️ Uso: `/t x 26`' }]; } @@ -518,7 +518,7 @@ export class CommandService { const due = res.task?.due_date ? ` — ${ICONS.date} ${formatDDMM(res.task?.due_date)}` : ''; return [{ recipient: context.sender, - message: `${ICONS.complete} ${id} completada — _${res.task?.description || '(sin descripción)'}_${due}` + message: `${ICONS.complete} ${codeId(id)} completada — _${res.task?.description || '(sin descripción)'}_${due}` }]; } @@ -529,7 +529,7 @@ export class CommandService { if (!id || Number.isNaN(id)) { return [{ recipient: context.sender, - message: 'ℹ️ Uso: /t tomar 26' + message: 'ℹ️ Uso: `/t tomar 26`' }]; } @@ -588,7 +588,7 @@ export class CommandService { if (!id || Number.isNaN(id)) { return [{ recipient: context.sender, - message: 'ℹ️ Uso: /t soltar 26' + message: 'ℹ️ Uso: `/t soltar 26`' }]; } @@ -669,7 +669,7 @@ export class CommandService { if (!freq) { return [{ recipient: context.sender, - message: 'Uso: /t configurar daily|weekly|off' + message: 'Uso: `/t configurar daily|weekly|off`' }]; } const ensured = ensureUserExists(context.sender, this.dbInstance); @@ -809,7 +809,8 @@ export class CommandService { `${description || '(sin descripción)'}`, formatDDMM(dueDate) ? `${ICONS.date} ${formatDDMM(dueDate)}` : null, groupName ? `Grupo: ${groupName}` : null, - italic(`Acciones: Completar → /t x ${taskId} · Soltar → /t soltar ${taskId}`) + `- Completar: \`/t x ${taskId}\``, + `- Soltar: \`/t soltar ${taskId}\`` ].filter(Boolean).join('\n'), mentions: [creatorJid] }); diff --git a/tests/unit/services/command.claim-unassign.test.ts b/tests/unit/services/command.claim-unassign.test.ts index 833c019..cbf729c 100644 --- a/tests/unit/services/command.claim-unassign.test.ts +++ b/tests/unit/services/command.claim-unassign.test.ts @@ -47,7 +47,7 @@ describe('CommandService - /t tomar y /t soltar', () => { const res = await CommandService.handle(ctx('111', '/t tomar')); expect(res).toHaveLength(1); expect(res[0].recipient).toBe('111'); - expect(res[0].message).toContain('Uso: /t tomar 26'); + expect(res[0].message).toContain('Uso: `/t tomar 26`'); }); it('tomar: not_found', async () => { @@ -78,7 +78,7 @@ describe('CommandService - /t tomar y /t soltar', () => { it('soltar: uso inválido (sin id)', async () => { const res = await CommandService.handle(ctx('111', '/t soltar')); - expect(res[0].message).toContain('Uso: /t soltar 26'); + expect(res[0].message).toContain('Uso: `/t soltar 26`'); }); it('soltar: not_found', async () => { diff --git a/tests/unit/services/command.reminders-config.test.ts b/tests/unit/services/command.reminders-config.test.ts index 31be5b0..62f25af 100644 --- a/tests/unit/services/command.reminders-config.test.ts +++ b/tests/unit/services/command.reminders-config.test.ts @@ -73,7 +73,7 @@ describe('CommandService - configurar recordatorios', () => { it('configurar con opción inválida devuelve uso correcto y no escribe en DB', async () => { const res = await runCmd('/t configurar foo'); expect(res).toHaveLength(1); - expect(res[0].message).toContain('Uso: /t configurar daily|weekly|off'); + expect(res[0].message).toContain('Uso: `/t configurar daily|weekly|off`'); const pref = getPref(); expect(pref).toBeNull(); diff --git a/tests/unit/services/command.test.ts b/tests/unit/services/command.test.ts index 4eb9c82..fac0895 100644 --- a/tests/unit/services/command.test.ts +++ b/tests/unit/services/command.test.ts @@ -93,8 +93,8 @@ test('listar “mis” por defecto en DM con /t ver', async () => { const msg = responses[0].message; expect(msg).toContain('Test Group'); expect(msg).toContain('Group 2'); - expect(msg).toMatch(/- \d+\) _G1 Task_/); - expect(msg).toMatch(/- \d+\) _G2 Task_/); + expect(msg).toMatch(/- `\d{4}` _G1 Task_/); + expect(msg).toMatch(/- `\d{4}` _G2 Task_/); }); test('completar tarea: camino feliz, ya completada y no encontrada', async () => { @@ -122,7 +122,7 @@ test('completar tarea: camino feliz, ya completada y no encontrada', async () => }); expect(responses.length).toBe(1); expect(responses[0].recipient).toBe('1234567890'); - expect(responses[0].message).toContain(`✅ ${taskId} completada`); + expect(responses[0].message).toMatch(/^✅ `\d{4}` completada/); // 2) Ya completada responses = await CommandService.handle({