feat: permitir múltiples IDs en /t x y /t tomar (espacios o comas; máx 10)

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
pull/1/head
brobert 1 month ago
parent ac0c5ff064
commit 9b57662a6b

@ -167,8 +167,8 @@ export class CommandService {
'- Comandos y alias:', '- Comandos y alias:',
' · Crear: n, nueva, crear, +', ' · Crear: n, nueva, crear, +',
' · Ver: ver, mostrar, listar, ls (scopes: grupo | mis | todos | sin)', ' · Ver: ver, mostrar, listar, ls (scopes: grupo | mis | todos | sin)',
' · Completar: x, hecho, completar, done', ' · Completar: x, hecho, completar, done (acepta varios IDs: "x 14 19 24" o "x 14,19,24"; máximo 10)',
' · Tomar: tomar, claim', ' · Tomar: tomar, claim (acepta varios IDs: "tomar 12 19 50" o "tomar 12,19,50"; máximo 10)',
' · Soltar: soltar, unassign', ' · Soltar: soltar, unassign',
'- Preferencias:', '- Preferencias:',
' · `/t configurar daily|l-v|weekly|off [HH:MM]` (por defecto 08:30; semanal: lunes; l-v: lunes a viernes)', ' · `/t configurar daily|l-v|weekly|off [HH:MM]` (por defecto 08:30; semanal: lunes; l-v: lunes a viernes)',
@ -189,7 +189,8 @@ export class CommandService {
'- Ver grupo: `/t ver` (en el grupo)', '- Ver grupo: `/t ver` (en el grupo)',
'- Ver mis tareas: `/t ver mis` (por DM)', '- Ver mis tareas: `/t ver mis` (por DM)',
'- Ver todos: `/t ver todos`', '- Ver todos: `/t ver todos`',
'- Completar: `/t x 123`', '- Completar: `/t x 123` (también varias: `/t x 14 19 24` o `/t x 14,19,24` — máx. 10)',
'- Tomar varias: `/t tomar 12 19 50` o `/t tomar 12,19,50` — máx. 10',
'- Configurar recordatorios: `/t configurar daily|l-v|weekly|off [HH:MM]`' '- Configurar recordatorios: `/t configurar daily|l-v|weekly|off [HH:MM]`'
].join('\n'); ].join('\n');
return [{ return [{
@ -490,15 +491,32 @@ export class CommandService {
// Completar tarea (con validación opcional de membresía) // Completar tarea (con validación opcional de membresía)
if (action === 'completar') { if (action === 'completar') {
const idToken = tokens[2]; // Soportar múltiples IDs separados por espacios y/o comas
const id = idToken ? parseInt(idToken, 10) : NaN; const rawIds = (tokens.slice(2).join(' ') || '').trim();
if (!id || Number.isNaN(id)) { const parsedList = Array.from(new Set(
rawIds
.split(/[,\s]+/)
.map(t => t.trim())
.filter(Boolean)
.map(t => parseInt(t, 10))
.filter(n => Number.isFinite(n) && n > 0)
));
const MAX_IDS = 10;
const truncated = parsedList.length > MAX_IDS;
const ids = parsedList.slice(0, MAX_IDS);
// Sin IDs: ayuda de uso
if (ids.length === 0) {
return [{ return [{
recipient: context.sender, recipient: context.sender,
message: ' Uso: `/t x 26`' message: ' Uso: `/t x 26` o múltiples: `/t x 14 19 24` o `/t x 14,19,24` (máx. 10)'
}]; }];
} }
// Caso de 1 ID: mantener comportamiento actual
if (ids.length === 1) {
const id = ids[0];
const task = TaskService.getTaskById(id); const task = TaskService.getTaskById(id);
if (!task) { if (!task) {
return [{ return [{
@ -537,17 +555,88 @@ export class CommandService {
}]; }];
} }
// Modo múltiple
const enforce = String(process.env.GROUP_MEMBERS_ENFORCE || '').toLowerCase() === 'true';
let cntUpdated = 0, cntAlready = 0, cntNotFound = 0, cntBlocked = 0;
const lines: string[] = [];
if (truncated) {
lines.push('⚠️ Se procesarán solo los primeros 10 IDs.');
}
for (const id of ids) {
const task = TaskService.getTaskById(id);
if (!task) {
lines.push(`⚠️ ${codeId(id)} no encontrada.`);
cntNotFound++;
continue;
}
if (task.group_id && GroupSyncService.isSnapshotFresh(task.group_id) && enforce && !GroupSyncService.isUserActiveInGroup(context.sender, task.group_id)) {
lines.push(`🚫 ${codeId(id)} — no permitido (no eres miembro activo).`);
cntBlocked++;
continue;
}
const res = TaskService.completeTask(id, context.sender);
const due = res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '';
if (res.status === 'already') {
lines.push(` ${codeId(id)} ya estaba completada — ${res.task?.description || '(sin descripción)'}${due}`);
cntAlready++;
} else if (res.status === 'updated') {
lines.push(`${ICONS.complete} ${codeId(id)} completada — ${res.task?.description || '(sin descripción)'}${due}`);
cntUpdated++;
} else if (res.status === 'not_found') {
lines.push(`⚠️ ${codeId(id)} no encontrada.`);
cntNotFound++;
}
}
// Resumen final
const summary: string[] = [];
if (cntUpdated) summary.push(`completadas ${cntUpdated}`);
if (cntAlready) summary.push(`ya estaban ${cntAlready}`);
if (cntNotFound) summary.push(`no encontradas ${cntNotFound}`);
if (cntBlocked) summary.push(`bloqueadas ${cntBlocked}`);
if (summary.length) {
lines.push('');
lines.push(`Resumen: ${summary.join(', ')}.`);
}
return [{
recipient: context.sender,
message: lines.join('\n')
}];
}
// Tomar tarea (con validación opcional de membresía) // Tomar tarea (con validación opcional de membresía)
if (action === 'tomar') { if (action === 'tomar') {
const idToken = tokens[2]; // Soportar múltiples IDs separados por espacios y/o comas
const id = idToken ? parseInt(idToken, 10) : NaN; const rawIds = (tokens.slice(2).join(' ') || '').trim();
if (!id || Number.isNaN(id)) { const parsedList = Array.from(new Set(
rawIds
.split(/[,\s]+/)
.map(t => t.trim())
.filter(Boolean)
.map(t => parseInt(t, 10))
.filter(n => Number.isFinite(n) && n > 0)
));
const MAX_IDS = 10;
const truncated = parsedList.length > MAX_IDS;
const ids = parsedList.slice(0, MAX_IDS);
// Sin IDs: ayuda de uso
if (ids.length === 0) {
return [{ return [{
recipient: context.sender, recipient: context.sender,
message: ' Uso: `/t tomar 26`' message: ' Uso: `/t tomar 26` o múltiples: `/t tomar 12 19 50` o `/t tomar 12,19,50` (máx. 10)'
}]; }];
} }
// Caso de 1 ID: mantener comportamiento actual
if (ids.length === 1) {
const id = ids[0];
const task = TaskService.getTaskById(id); const task = TaskService.getTaskById(id);
if (!task) { if (!task) {
return [{ return [{
@ -596,6 +685,64 @@ export class CommandService {
}]; }];
} }
// Modo múltiple
const enforce = String(process.env.GROUP_MEMBERS_ENFORCE || '').toLowerCase() === 'true';
let cntClaimed = 0, cntAlready = 0, cntCompleted = 0, cntNotFound = 0, cntBlocked = 0;
const lines: string[] = [];
if (truncated) {
lines.push('⚠️ Se procesarán solo los primeros 10 IDs.');
}
for (const id of ids) {
const task = TaskService.getTaskById(id);
if (!task) {
lines.push(`⚠️ ${codeId(id)} no encontrada.`);
cntNotFound++;
continue;
}
if (task.group_id && GroupSyncService.isSnapshotFresh(task.group_id) && enforce && !GroupSyncService.isUserActiveInGroup(context.sender, task.group_id)) {
lines.push(`🚫 ${codeId(id)} — no permitido (no eres miembro activo).`);
cntBlocked++;
continue;
}
const res = TaskService.claimTask(id, context.sender);
const due = res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '';
if (res.status === 'already') {
lines.push(` ${codeId(id)} ya la tenías — ${res.task?.description || '(sin descripción)'}${due}`);
cntAlready++;
} else if (res.status === 'claimed') {
lines.push(`${ICONS.take} ${codeId(id)} tomada — ${res.task?.description || '(sin descripción)'}${due}`);
cntClaimed++;
} else if (res.status === 'completed') {
lines.push(` ${codeId(id)} ya estaba completada — ${res.task?.description || '(sin descripción)'}${due}`);
cntCompleted++;
} else if (res.status === 'not_found') {
lines.push(`⚠️ ${codeId(id)} no encontrada.`);
cntNotFound++;
}
}
// Resumen final
const summary: string[] = [];
if (cntClaimed) summary.push(`tomadas ${cntClaimed}`);
if (cntAlready) summary.push(`ya las tenías ${cntAlready}`);
if (cntCompleted) summary.push(`ya completadas ${cntCompleted}`);
if (cntNotFound) summary.push(`no encontradas ${cntNotFound}`);
if (cntBlocked) summary.push(`bloqueadas ${cntBlocked}`);
if (summary.length) {
lines.push('');
lines.push(`Resumen: ${summary.join(', ')}.`);
}
return [{
recipient: context.sender,
message: lines.join('\n')
}];
}
// Soltar tarea (con validación opcional de membresía) // Soltar tarea (con validación opcional de membresía)
if (action === 'soltar') { if (action === 'soltar') {
const idToken = tokens[2]; const idToken = tokens[2];

Loading…
Cancel
Save