|
|
|
@ -5,6 +5,7 @@ import { TaskService } from '../tasks/service';
|
|
|
|
import { GroupSyncService } from './group-sync';
|
|
|
|
import { GroupSyncService } from './group-sync';
|
|
|
|
import { ContactsService } from './contacts';
|
|
|
|
import { ContactsService } from './contacts';
|
|
|
|
import { ICONS } from '../utils/icons';
|
|
|
|
import { ICONS } from '../utils/icons';
|
|
|
|
|
|
|
|
import { padTaskId, codeId, formatDDMM, bold, italic } from '../utils/formatting';
|
|
|
|
|
|
|
|
|
|
|
|
type CommandContext = {
|
|
|
|
type CommandContext = {
|
|
|
|
sender: string; // normalized user id (digits only), but accept raw too
|
|
|
|
sender: string; // normalized user id (digits only), but accept raw too
|
|
|
|
@ -141,16 +142,7 @@ export class CommandService {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const action = ACTION_ALIASES[rawAction] || rawAction;
|
|
|
|
const action = ACTION_ALIASES[rawAction] || rawAction;
|
|
|
|
|
|
|
|
|
|
|
|
// Helper para fechas dd/MM
|
|
|
|
// Usar formatDDMM desde utils/formatting
|
|
|
|
const formatDDMM = (ymd?: string | null): string | null => {
|
|
|
|
|
|
|
|
if (!ymd) return null;
|
|
|
|
|
|
|
|
const parts = String(ymd).split('-');
|
|
|
|
|
|
|
|
if (parts.length >= 3) {
|
|
|
|
|
|
|
|
const [Y, M, D] = parts;
|
|
|
|
|
|
|
|
if (D && M) return `${D}/${M}`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return String(ymd);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TZ y "hoy" en TZ para marcar vencidas en listados
|
|
|
|
// TZ y "hoy" en TZ para marcar vencidas en listados
|
|
|
|
const TZ = process.env.TZ && process.env.TZ.trim() ? process.env.TZ : 'Europe/Madrid';
|
|
|
|
const TZ = process.env.TZ && process.env.TZ.trim() ? process.env.TZ : 'Europe/Madrid';
|
|
|
|
@ -167,14 +159,36 @@ export class CommandService {
|
|
|
|
const todayYMD = ymdInTZ(new Date());
|
|
|
|
const todayYMD = ymdInTZ(new Date());
|
|
|
|
|
|
|
|
|
|
|
|
if (!action || action === 'ayuda') {
|
|
|
|
if (!action || action === 'ayuda') {
|
|
|
|
|
|
|
|
const isAdvanced = (tokens[2] || '').toLowerCase() === 'avanzada';
|
|
|
|
|
|
|
|
if (isAdvanced) {
|
|
|
|
|
|
|
|
const adv = [
|
|
|
|
|
|
|
|
'Ayuda avanzada:',
|
|
|
|
|
|
|
|
'- Comandos y alias:',
|
|
|
|
|
|
|
|
' · Crear: n, nueva, crear, +',
|
|
|
|
|
|
|
|
' · Ver: ver, mostrar, listar, ls (scopes: grupo | mis | todos | sin)',
|
|
|
|
|
|
|
|
' · Completar: x, hecho, completar, done',
|
|
|
|
|
|
|
|
' · Tomar: tomar, claim',
|
|
|
|
|
|
|
|
' · Soltar: soltar, unassign',
|
|
|
|
|
|
|
|
'- Preferencias:',
|
|
|
|
|
|
|
|
' · /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.',
|
|
|
|
|
|
|
|
' · Fechas dd/MM con ⚠️ si está vencida.',
|
|
|
|
|
|
|
|
' · Mostramos los IDs con 4 dígitos, pero puedes escribirlos sin ceros (p. ej., 26).',
|
|
|
|
|
|
|
|
].join('\n');
|
|
|
|
|
|
|
|
return [{
|
|
|
|
|
|
|
|
recipient: context.sender,
|
|
|
|
|
|
|
|
message: adv
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
}
|
|
|
|
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 @600123456',
|
|
|
|
'- Ver grupo: /t ver grupo',
|
|
|
|
'- Ver grupo: /t ver grupo | tus tareas: /t ver mis',
|
|
|
|
'- Ver mis: /t ver mis',
|
|
|
|
'- Completar: /t x 26 | Tomar: /t tomar 26 | Soltar: /t soltar 26',
|
|
|
|
'- Ver todos: /t ver todos',
|
|
|
|
'- Recordatorios: /t configurar daily|weekly|off',
|
|
|
|
'- Completar: /t x 123',
|
|
|
|
'- Más: /t ayuda avanzada'
|
|
|
|
'- Configurar recordatorios: /t configurar daily|weekly|off'
|
|
|
|
|
|
|
|
].join('\n');
|
|
|
|
].join('\n');
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
@ -224,17 +238,17 @@ 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);
|
|
|
|
if (total > items.length) {
|
|
|
|
if (total > items.length) {
|
|
|
|
rendered.push(`… y ${total - items.length} más`);
|
|
|
|
rendered.push(italic(`… y ${total - items.length} más`));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: [`${groupName} — Sin responsable`, ...rendered].join('\n')
|
|
|
|
message: [bold(`${groupName} — Sin responsable`), ...rendered].join('\n')
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -243,7 +257,7 @@ export class CommandService {
|
|
|
|
const sections: string[] = [];
|
|
|
|
const sections: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
// Encabezado fijo para la sección de tareas del usuario
|
|
|
|
// Encabezado fijo para la sección de tareas del usuario
|
|
|
|
sections.push('Tus tareas');
|
|
|
|
sections.push(bold('Tus tareas'));
|
|
|
|
|
|
|
|
|
|
|
|
// Tus tareas (mis)
|
|
|
|
// Tus tareas (mis)
|
|
|
|
const myItems = TaskService.listUserPending(context.sender, LIMIT);
|
|
|
|
const myItems = TaskService.listUserPending(context.sender, LIMIT);
|
|
|
|
@ -262,7 +276,7 @@ export class CommandService {
|
|
|
|
(groupId && GroupSyncService.activeGroupsCache.get(groupId)) ||
|
|
|
|
(groupId && GroupSyncService.activeGroupsCache.get(groupId)) ||
|
|
|
|
(groupId && groupId !== '(sin grupo)' ? groupId : 'Sin grupo');
|
|
|
|
(groupId && groupId !== '(sin grupo)' ? groupId : 'Sin grupo');
|
|
|
|
|
|
|
|
|
|
|
|
sections.push(groupName);
|
|
|
|
sections.push(bold(groupName));
|
|
|
|
const rendered = await Promise.all(arr.map(async (t) => {
|
|
|
|
const rendered = await Promise.all(arr.map(async (t) => {
|
|
|
|
const names = await Promise.all(
|
|
|
|
const names = await Promise.all(
|
|
|
|
(t.assignees || []).map(async (uid) => (await ContactsService.getDisplayName(uid)) || uid)
|
|
|
|
(t.assignees || []).map(async (uid) => (await ContactsService.getDisplayName(uid)) || uid)
|
|
|
|
@ -273,14 +287,14 @@ 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const totalMy = TaskService.countUserPending(context.sender);
|
|
|
|
const totalMy = TaskService.countUserPending(context.sender);
|
|
|
|
if (totalMy > myItems.length) {
|
|
|
|
if (totalMy > myItems.length) {
|
|
|
|
sections.push(`… y ${totalMy - myItems.length} más`);
|
|
|
|
sections.push(italic(`… y ${totalMy - myItems.length} más`));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
sections.push('No tienes tareas pendientes.');
|
|
|
|
sections.push('No tienes tareas pendientes.');
|
|
|
|
@ -294,17 +308,17 @@ export class CommandService {
|
|
|
|
const groupName = GroupSyncService.activeGroupsCache.get(context.groupId) || context.groupId;
|
|
|
|
const groupName = GroupSyncService.activeGroupsCache.get(context.groupId) || context.groupId;
|
|
|
|
const unassigned = TaskService.listGroupUnassigned(context.groupId, LIMIT);
|
|
|
|
const unassigned = TaskService.listGroupUnassigned(context.groupId, LIMIT);
|
|
|
|
if (unassigned.length > 0) {
|
|
|
|
if (unassigned.length > 0) {
|
|
|
|
sections.push(`${groupName} — Sin responsable`);
|
|
|
|
sections.push(bold(`${groupName} — Sin responsable`));
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
const totalUnassigned = TaskService.countGroupUnassigned(context.groupId);
|
|
|
|
const totalUnassigned = TaskService.countGroupUnassigned(context.groupId);
|
|
|
|
if (totalUnassigned > unassigned.length) {
|
|
|
|
if (totalUnassigned > unassigned.length) {
|
|
|
|
sections.push(`… y ${totalUnassigned - unassigned.length} más`);
|
|
|
|
sections.push(italic(`… y ${totalUnassigned - unassigned.length} más`));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
sections.push(`${groupName} — Sin responsable\n(no hay tareas sin responsable)`);
|
|
|
|
sections.push(`${groupName} — Sin responsable\n(no hay tareas sin responsable)`);
|
|
|
|
@ -322,17 +336,17 @@ export class CommandService {
|
|
|
|
gid;
|
|
|
|
gid;
|
|
|
|
|
|
|
|
|
|
|
|
if (unassigned.length > 0) {
|
|
|
|
if (unassigned.length > 0) {
|
|
|
|
sections.push(`${groupName} — Sin responsable`);
|
|
|
|
sections.push(bold(`${groupName} — Sin responsable`));
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
const totalUnassigned = TaskService.countGroupUnassigned(gid);
|
|
|
|
const totalUnassigned = TaskService.countGroupUnassigned(gid);
|
|
|
|
if (totalUnassigned > unassigned.length) {
|
|
|
|
if (totalUnassigned > unassigned.length) {
|
|
|
|
sections.push(`… y ${totalUnassigned - unassigned.length} más`);
|
|
|
|
sections.push(italic(`… y ${totalUnassigned - unassigned.length} más`));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -392,7 +406,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);
|
|
|
|
@ -402,7 +416,7 @@ export class CommandService {
|
|
|
|
|
|
|
|
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: [groupName, ...rendered].join('\n')
|
|
|
|
message: [bold(groupName), ...rendered].join('\n')
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -432,7 +446,7 @@ export class CommandService {
|
|
|
|
(groupId && GroupSyncService.activeGroupsCache.get(groupId)) ||
|
|
|
|
(groupId && GroupSyncService.activeGroupsCache.get(groupId)) ||
|
|
|
|
(groupId && groupId !== '(sin grupo)' ? groupId : 'Sin grupo');
|
|
|
|
(groupId && groupId !== '(sin grupo)' ? groupId : 'Sin grupo');
|
|
|
|
|
|
|
|
|
|
|
|
sections.push(groupName);
|
|
|
|
sections.push(bold(groupName));
|
|
|
|
const rendered = await Promise.all(arr.map(async (t) => {
|
|
|
|
const rendered = await Promise.all(arr.map(async (t) => {
|
|
|
|
const names = await Promise.all(
|
|
|
|
const names = await Promise.all(
|
|
|
|
(t.assignees || []).map(async (uid) => (await ContactsService.getDisplayName(uid)) || uid)
|
|
|
|
(t.assignees || []).map(async (uid) => (await ContactsService.getDisplayName(uid)) || uid)
|
|
|
|
@ -443,13 +457,13 @@ 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (total > items.length) {
|
|
|
|
if (total > items.length) {
|
|
|
|
sections.push(`… y ${total - items.length} más`);
|
|
|
|
sections.push(italic(`… y ${total - items.length} más`));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
@ -465,7 +479,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 <id>'
|
|
|
|
message: 'ℹ️ Uso: /t x 26'
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -473,7 +487,7 @@ export class CommandService {
|
|
|
|
if (!task) {
|
|
|
|
if (!task) {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `⚠️ Tarea ${id} no encontrada.`
|
|
|
|
message: `⚠️ Tarea ${codeId(id)} no encontrada.`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const enforce = String(process.env.GROUP_MEMBERS_ENFORCE || '').toLowerCase() === 'true';
|
|
|
|
const enforce = String(process.env.GROUP_MEMBERS_ENFORCE || '').toLowerCase() === 'true';
|
|
|
|
@ -489,21 +503,27 @@ export class CommandService {
|
|
|
|
if (res.status === 'not_found') {
|
|
|
|
if (res.status === 'not_found') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `⚠️ Tarea ${id} no encontrada.`
|
|
|
|
message: `⚠️ Tarea ${codeId(id)} no encontrada.`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (res.status === 'already') {
|
|
|
|
if (res.status === 'already') {
|
|
|
|
const due = res.task?.due_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: `ℹ️ ${id} ya estaba completada — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: `ℹ️ ${codeId(id)} ya estaba completada — ${res.task?.description || '(sin descripción)'}${due}`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const due = res.task?.due_date ? ` — 📅 ${formatDDMM(res.task?.due_date)}` : '';
|
|
|
|
const dueLine = res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '';
|
|
|
|
|
|
|
|
const lines = [
|
|
|
|
|
|
|
|
`${ICONS.complete} ${codeId(id)}`,
|
|
|
|
|
|
|
|
`${res.task?.description || '(sin descripción)'}`,
|
|
|
|
|
|
|
|
dueLine,
|
|
|
|
|
|
|
|
italic(`Gracias, ${who}.`)
|
|
|
|
|
|
|
|
].filter(Boolean);
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `${ICONS.complete} ${id} completada — _${res.task?.description || '(sin descripción)'}_${due}\nGracias, ${who}.`
|
|
|
|
message: lines.join('\n')
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -514,7 +534,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 <id>'
|
|
|
|
message: 'ℹ️ Uso: /t tomar 26'
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -522,7 +542,7 @@ export class CommandService {
|
|
|
|
if (!task) {
|
|
|
|
if (!task) {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `⚠️ Tarea ${id} no encontrada.`
|
|
|
|
message: `⚠️ Tarea ${codeId(id)} no encontrada.`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const enforce = String(process.env.GROUP_MEMBERS_ENFORCE || '').toLowerCase() === 'true';
|
|
|
|
const enforce = String(process.env.GROUP_MEMBERS_ENFORCE || '').toLowerCase() === 'true';
|
|
|
|
@ -534,7 +554,7 @@ export class CommandService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const res = TaskService.claimTask(id, context.sender);
|
|
|
|
const res = TaskService.claimTask(id, context.sender);
|
|
|
|
const due = res.task?.due_date ? ` — 📅 ${formatDDMM(res.task?.due_date)}` : '';
|
|
|
|
const due = res.task?.due_date ? ` — ${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '';
|
|
|
|
|
|
|
|
|
|
|
|
if (res.status === 'not_found') {
|
|
|
|
if (res.status === 'not_found') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
@ -545,19 +565,24 @@ export class CommandService {
|
|
|
|
if (res.status === 'completed') {
|
|
|
|
if (res.status === 'completed') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `ℹ️ ${id} ya estaba completada — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: `ℹ️ ${codeId(id)} ya estaba completada — ${res.task?.description || '(sin descripción)'}${due}`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (res.status === 'already') {
|
|
|
|
if (res.status === 'already') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `ℹ️ ${id} ya la tenías — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: `ℹ️ ${codeId(id)} ya la tenías — ${res.task?.description || '(sin descripción)'}${due}`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lines = [
|
|
|
|
|
|
|
|
`${ICONS.take} ${codeId(id)}`,
|
|
|
|
|
|
|
|
`${res.task?.description || '(sin descripción)'}`,
|
|
|
|
|
|
|
|
res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : ''
|
|
|
|
|
|
|
|
].filter(Boolean);
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `${ICONS.take} Has tomado ${id} — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: lines.join('\n')
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -568,7 +593,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 <id>'
|
|
|
|
message: 'ℹ️ Uso: /t soltar 26'
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -588,7 +613,7 @@ export class CommandService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const res = TaskService.unassignTask(id, context.sender);
|
|
|
|
const res = TaskService.unassignTask(id, context.sender);
|
|
|
|
const due = res.task?.due_date ? ` — 📅 ${formatDDMM(res.task?.due_date)}` : '';
|
|
|
|
const due = res.task?.due_date ? ` — ${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '';
|
|
|
|
|
|
|
|
|
|
|
|
if (res.status === 'not_found') {
|
|
|
|
if (res.status === 'not_found') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
@ -599,26 +624,37 @@ export class CommandService {
|
|
|
|
if (res.status === 'completed') {
|
|
|
|
if (res.status === 'completed') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `ℹ️ ${id} ya estaba completada — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: `ℹ️ ${codeId(id)} ya estaba completada — ${res.task?.description || '(sin descripción)'}${due}`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (res.status === 'not_assigned') {
|
|
|
|
if (res.status === 'not_assigned') {
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `ℹ️ ${id} no la tenías asignada — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: `ℹ️ ${codeId(id)} no la tenías asignada — ${res.task?.description || '(sin descripción)'}${due}`
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (res.now_unassigned) {
|
|
|
|
if (res.now_unassigned) {
|
|
|
|
|
|
|
|
const lines = [
|
|
|
|
|
|
|
|
`${ICONS.unassigned} ${codeId(id)}`,
|
|
|
|
|
|
|
|
`${res.task?.description || '(sin descripción)'}`,
|
|
|
|
|
|
|
|
res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : '',
|
|
|
|
|
|
|
|
italic('Queda sin responsable.')
|
|
|
|
|
|
|
|
].filter(Boolean);
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `${ICONS.unassigned} ${id} queda sin responsable — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: lines.join('\n')
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lines = [
|
|
|
|
|
|
|
|
`${ICONS.unassign} ${codeId(id)}`,
|
|
|
|
|
|
|
|
`${res.task?.description || '(sin descripción)'}`,
|
|
|
|
|
|
|
|
res.task?.due_date ? `${ICONS.date} ${formatDDMM(res.task?.due_date)}` : ''
|
|
|
|
|
|
|
|
].filter(Boolean);
|
|
|
|
return [{
|
|
|
|
return [{
|
|
|
|
recipient: context.sender,
|
|
|
|
recipient: context.sender,
|
|
|
|
message: `${ICONS.unassign} Has soltado ${id} — _${res.task?.description || '(sin descripción)'}_${due}`
|
|
|
|
message: lines.join('\n')
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -753,8 +789,8 @@ export class CommandService {
|
|
|
|
const responses: CommandResponse[] = [];
|
|
|
|
const responses: CommandResponse[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
// 1) Ack al creador con formato compacto
|
|
|
|
// 1) Ack al creador con formato compacto
|
|
|
|
const ackHeader = `${ICONS.create} ${taskId} _${description || '(sin descripción)'}_`;
|
|
|
|
const ackHeader = `${ICONS.create} ${codeId(taskId)}`;
|
|
|
|
const ackLines: string[] = [ackHeader];
|
|
|
|
const ackLines: string[] = [ackHeader, `${description || '(sin descripción)'}`];
|
|
|
|
const dueFmt = formatDDMM(dueDate);
|
|
|
|
const dueFmt = formatDDMM(dueDate);
|
|
|
|
if (dueFmt) ackLines.push(`${ICONS.date} ${dueFmt}`);
|
|
|
|
if (dueFmt) ackLines.push(`${ICONS.date} ${dueFmt}`);
|
|
|
|
if (assignmentUserIds.length === 0) {
|
|
|
|
if (assignmentUserIds.length === 0) {
|
|
|
|
@ -775,10 +811,11 @@ export class CommandService {
|
|
|
|
responses.push({
|
|
|
|
responses.push({
|
|
|
|
recipient: uid,
|
|
|
|
recipient: uid,
|
|
|
|
message: [
|
|
|
|
message: [
|
|
|
|
`${ICONS.assignNotice} Tarea ${taskId}${formatDDMM(dueDate) ? ` — ${ICONS.date} ${formatDDMM(dueDate)}` : ''}`,
|
|
|
|
`${ICONS.assignNotice} ${codeId(taskId)}`,
|
|
|
|
`_${description || '(sin descripción)'}_`,
|
|
|
|
`${description || '(sin descripción)'}`,
|
|
|
|
|
|
|
|
formatDDMM(dueDate) ? `${ICONS.date} ${formatDDMM(dueDate)}` : null,
|
|
|
|
groupName ? `Grupo: ${groupName}` : null,
|
|
|
|
groupName ? `Grupo: ${groupName}` : null,
|
|
|
|
`Completar: /t x ${taskId}`
|
|
|
|
italic(`Acciones: Completar → /t x ${taskId} · Soltar → /t soltar ${taskId}`)
|
|
|
|
].filter(Boolean).join('\n'),
|
|
|
|
].filter(Boolean).join('\n'),
|
|
|
|
mentions: [creatorJid]
|
|
|
|
mentions: [creatorJid]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|