feat: aplicar codeId y monoespacio en listados, ayudas y mensajes

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
pull/1/head
borja 2 months ago
parent 60ed92970b
commit fe5a8d29cb

@ -170,7 +170,7 @@ export class CommandService {
' · Tomar: tomar, claim', ' · Tomar: tomar, claim',
' · Soltar: soltar, unassign', ' · Soltar: soltar, unassign',
'- Preferencias:', '- 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:', '- Notas:',
' · En grupos, el bot responde por DM al autor (no publica en el grupo).', ' · 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.', ' · 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 = [ const help = [
'Guía rápida:', 'Guía rápida:',
'- Crear: /t n Descripción mañana @Ana', '- Crear: `/t n Descripción mañana @Ana`',
'- Ver grupo: /t ver grupo', '- Ver grupo: `/t ver grupo`',
'- Ver mis: /t ver mis', '- Ver mis: `/t ver mis`',
'- Ver todos: /t ver todos', '- Ver todos: `/t ver todos`',
'- Completar: /t x 123', '- Completar: `/t x 123`',
'- Configurar recordatorios: /t configurar daily|weekly|off' '- Configurar recordatorios: `/t configurar daily|weekly|off`'
].join('\n'); ].join('\n');
return [{ return [{
recipient: context.sender, recipient: context.sender,
@ -217,7 +217,7 @@ export class CommandService {
if (!isGroupId(context.groupId)) { if (!isGroupId(context.groupId)) {
return [{ return [{
recipient: context.sender, 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)) { if (!GroupSyncService.isGroupActive(context.groupId)) {
@ -239,7 +239,7 @@ export class CommandService {
const rendered = items.map((t) => { const rendered = items.map((t) => {
const isOverdue = t.due_date ? t.due_date < todayYMD : false; const isOverdue = t.due_date ? t.due_date < todayYMD : false;
const datePart = t.due_date ? `${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; 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); const total = TaskService.countGroupUnassigned(context.groupId);
@ -288,7 +288,7 @@ export class CommandService {
: `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`;
const isOverdue = t.due_date ? t.due_date < todayYMD : false; const isOverdue = t.due_date ? t.due_date < todayYMD : false;
const datePart = t.due_date ? `${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; 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); sections.push(...rendered);
} }
@ -313,7 +313,7 @@ export class CommandService {
const renderedUnassigned = unassigned.map((t) => { const renderedUnassigned = unassigned.map((t) => {
const isOverdue = t.due_date ? t.due_date < todayYMD : false; const isOverdue = t.due_date ? t.due_date < todayYMD : false;
const datePart = t.due_date ? `${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; 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); sections.push(...renderedUnassigned);
@ -341,7 +341,7 @@ export class CommandService {
const renderedUnassigned = unassigned.map((t) => { const renderedUnassigned = unassigned.map((t) => {
const isOverdue = t.due_date ? t.due_date < todayYMD : false; const isOverdue = t.due_date ? t.due_date < todayYMD : false;
const datePart = t.due_date ? `${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; 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); sections.push(...renderedUnassigned);
@ -353,7 +353,7 @@ export class CommandService {
} }
} else { } else {
// Si no hay snapshot fresca de membresía, mantenemos una nota instructiva mínima // 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)) { if (!isGroupId(context.groupId)) {
return [{ return [{
recipient: context.sender, 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)) { if (!GroupSyncService.isGroupActive(context.groupId)) {
@ -407,7 +407,7 @@ export class CommandService {
: `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`;
const isOverdue = t.due_date ? t.due_date < todayYMD : false; const isOverdue = t.due_date ? t.due_date < todayYMD : false;
const datePart = t.due_date ? `${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; 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); const total = TaskService.countGroupPending(context.groupId);
@ -458,7 +458,7 @@ export class CommandService {
: `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`;
const isOverdue = t.due_date ? t.due_date < todayYMD : false; const isOverdue = t.due_date ? t.due_date < todayYMD : false;
const datePart = t.due_date ? `${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; 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); sections.push(...rendered);
} }
@ -480,7 +480,7 @@ export class CommandService {
if (!id || Number.isNaN(id)) { if (!id || Number.isNaN(id)) {
return [{ return [{
recipient: context.sender, 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)}` : ''; const due = res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '';
return [{ return [{
recipient: context.sender, 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)) { if (!id || Number.isNaN(id)) {
return [{ return [{
recipient: context.sender, 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)) { if (!id || Number.isNaN(id)) {
return [{ return [{
recipient: context.sender, recipient: context.sender,
message: ' Uso: /t soltar 26' message: ' Uso: `/t soltar 26`'
}]; }];
} }
@ -669,7 +669,7 @@ export class CommandService {
if (!freq) { if (!freq) {
return [{ return [{
recipient: context.sender, recipient: context.sender,
message: 'Uso: /t configurar daily|weekly|off' message: 'Uso: `/t configurar daily|weekly|off`'
}]; }];
} }
const ensured = ensureUserExists(context.sender, this.dbInstance); const ensured = ensureUserExists(context.sender, this.dbInstance);
@ -809,7 +809,8 @@ export class CommandService {
`${description || '(sin descripción)'}`, `${description || '(sin descripción)'}`,
formatDDMM(dueDate) ? `${ICONS.date} ${formatDDMM(dueDate)}` : null, formatDDMM(dueDate) ? `${ICONS.date} ${formatDDMM(dueDate)}` : null,
groupName ? `Grupo: ${groupName}` : 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'), ].filter(Boolean).join('\n'),
mentions: [creatorJid] mentions: [creatorJid]
}); });

@ -47,7 +47,7 @@ describe('CommandService - /t tomar y /t soltar', () => {
const res = await CommandService.handle(ctx('111', '/t tomar')); const res = await CommandService.handle(ctx('111', '/t tomar'));
expect(res).toHaveLength(1); expect(res).toHaveLength(1);
expect(res[0].recipient).toBe('111'); 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 () => { it('tomar: not_found', async () => {
@ -78,7 +78,7 @@ describe('CommandService - /t tomar y /t soltar', () => {
it('soltar: uso inválido (sin id)', async () => { it('soltar: uso inválido (sin id)', async () => {
const res = await CommandService.handle(ctx('111', '/t soltar')); 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 () => { it('soltar: not_found', async () => {

@ -73,7 +73,7 @@ describe('CommandService - configurar recordatorios', () => {
it('configurar con opción inválida devuelve uso correcto y no escribe en DB', async () => { it('configurar con opción inválida devuelve uso correcto y no escribe en DB', async () => {
const res = await runCmd('/t configurar foo'); const res = await runCmd('/t configurar foo');
expect(res).toHaveLength(1); 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(); const pref = getPref();
expect(pref).toBeNull(); expect(pref).toBeNull();

@ -93,8 +93,8 @@ test('listar “mis” por defecto en DM con /t ver', async () => {
const msg = responses[0].message; const msg = responses[0].message;
expect(msg).toContain('Test Group'); expect(msg).toContain('Test Group');
expect(msg).toContain('Group 2'); expect(msg).toContain('Group 2');
expect(msg).toMatch(/- \d+\) _G1 Task_/); expect(msg).toMatch(/- `\d{4}` _G1 Task_/);
expect(msg).toMatch(/- \d+\) _G2 Task_/); expect(msg).toMatch(/- `\d{4}` _G2 Task_/);
}); });
test('completar tarea: camino feliz, ya completada y no encontrada', async () => { 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.length).toBe(1);
expect(responses[0].recipient).toBe('1234567890'); expect(responses[0].recipient).toBe('1234567890');
expect(responses[0].message).toContain(`${taskId} completada`); expect(responses[0].message).toMatch(/^✅ `\d{4}` completada/);
// 2) Ya completada // 2) Ya completada
responses = await CommandService.handle({ responses = await CommandService.handle({

Loading…
Cancel
Save