diff --git a/src/services/command.ts b/src/services/command.ts index 9d9318e..ba76978 100644 --- a/src/services/command.ts +++ b/src/services/command.ts @@ -11,7 +11,7 @@ import { IdentityService } from './identity'; import { AllowedGroups } from './allowed-groups'; import { Metrics } from './metrics'; import { ResponseQueue } from './response-queue'; -import { randomTokenBase64Url, sha256Hex } from '../utils/crypto'; +import { randomTokenBase64Url } from '../utils/crypto'; import { route as routeCommand } from './commands'; import { ACTION_ALIASES, SCOPE_ALIASES, todayYMD as todayYMD_TZ, resolveTaskIdFromInput, parseMultipleIds, enforceMembership } from './commands/shared'; import { parseNueva } from './commands/parsers/nueva'; @@ -657,129 +657,7 @@ export class CommandService { }]; } - if (action === 'configurar') { - const optRaw = (tokens[2] || '').toLowerCase(); - const map: Record = { - 'daily': 'daily', - 'diario': 'daily', - 'diaria': 'daily', - 'l-v': 'weekdays', - 'lv': 'weekdays', - 'laborables': 'weekdays', - 'weekdays': 'weekdays', - 'semanal': 'weekly', - 'weekly': 'weekly', - 'off': 'off', - 'apagar': 'off', - 'ninguno': 'off' - }; - const freq = map[optRaw]; - - // Hora opcional HH:MM - const timeRaw = tokens[3] || ''; - let timeNorm: string | null = null; - if (timeRaw) { - const m = /^(\d{1,2}):([0-5]\d)$/.exec(timeRaw); - if (!m) { - return [{ - recipient: context.sender, - message: 'ℹ️ Uso: `/t configurar diario|l-v|semanal|off [HH:MM]`' - }]; - } - const hh = Math.max(0, Math.min(23, parseInt(m[1], 10))); - timeNorm = `${String(hh).padStart(2, '0')}:${m[2]}`; - } - - if (!freq) { - return [{ - recipient: context.sender, - message: 'ℹ️ Uso: `/t configurar diario|l-v|semanal|off [HH:MM]`' - }]; - } - const ensured = ensureUserExists(context.sender, this.dbInstance); - if (!ensured) { - throw new Error('No se pudo asegurar el usuario'); - } - this.dbInstance.prepare(` - INSERT INTO user_preferences (user_id, reminder_freq, reminder_time, last_reminded_on, updated_at) - VALUES (?, ?, COALESCE(?, COALESCE((SELECT reminder_time FROM user_preferences WHERE user_id = ?), '08:30')), NULL, strftime('%Y-%m-%d %H:%M:%f', 'now')) - ON CONFLICT(user_id) DO UPDATE SET - reminder_freq = excluded.reminder_freq, - reminder_time = CASE WHEN ? IS NOT NULL THEN excluded.reminder_time ELSE reminder_time END, - updated_at = excluded.updated_at - `).run(ensured, freq, timeNorm, ensured, timeNorm); - - let label: string; - if (freq === 'daily') { - label = timeNorm ? `diario (${timeNorm})` : 'diario'; - } else if (freq === 'weekdays') { - label = timeNorm ? `laborables (lunes a viernes ${timeNorm})` : 'laborables (lunes a viernes)'; - } else if (freq === 'weekly') { - label = timeNorm ? `semanal (lunes ${timeNorm})` : 'semanal (lunes 08:30)'; - } else { - label = 'apagado'; - } - return [{ - recipient: context.sender, - message: `✅ Recordatorios: ${label}` - }]; - } - - // Enlace de acceso a la web (/t web) - if (action === 'web') { - // Solo por DM - if (isGroupId(context.groupId)) { - return [{ - recipient: context.sender, - message: 'ℹ️ Este comando se usa por privado. Envíame `/t web` por DM.' - }]; - } - - const base = (process.env.WEB_BASE_URL || '').trim(); - if (!base) { - return [{ - recipient: context.sender, - message: '⚠️ La web no está configurada todavía. Contacta con el administrador (falta WEB_BASE_URL).' - }]; - } - - const ensured = ensureUserExists(context.sender, this.dbInstance); - if (!ensured) { - throw new Error('No se pudo asegurar el usuario'); - } - - const toIso = (d: Date) => d.toISOString().replace('T', ' ').replace('Z', ''); - const now = new Date(); - const nowIso = toIso(now); - const expiresIso = toIso(new Date(now.getTime() + 10 * 60 * 1000)); // 10 minutos - - // Invalidar tokens vigentes (uso único) - this.dbInstance.prepare(` - UPDATE web_tokens - SET used_at = ? - WHERE user_id = ? - AND used_at IS NULL - AND expires_at > ? - `).run(nowIso, ensured, nowIso); - - // Generar nuevo token y guardar solo el hash - const token = randomTokenBase64Url(32); - const tokenHash = await sha256Hex(token); - - this.dbInstance.prepare(` - INSERT INTO web_tokens (user_id, token_hash, expires_at, metadata) - VALUES (?, ?, ?, NULL) - `).run(ensured, tokenHash, expiresIso); - - try { Metrics.inc('web_tokens_issued_total'); } catch { } - - const url = new URL(`/login?token=${encodeURIComponent(token)}`, base).toString(); - return [{ - recipient: context.sender, - message: `Acceso web: ${url}\nVálido durante 10 minutos. Si caduca, vuelve a enviar "/t web".` - }]; - } if (action !== 'nueva') { const feature = String(process.env.FEATURE_HELP_V2 ?? 'true').toLowerCase();