feat: añadir métricas de onboarding y alias; recalcular tras comandos

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
main
brobert 4 days ago
parent 2d05b6f3e1
commit bd4f0cc364

@ -202,6 +202,18 @@ export class CommandService {
'configurar': 'configurar' 'configurar': 'configurar'
}; };
const action = ACTION_ALIASES[rawAction] || rawAction; const action = ACTION_ALIASES[rawAction] || rawAction;
// Métrica: uso de alias (info/mias/todas)
try {
if (rawAction === 'info') {
Metrics.inc('commands_alias_used_total', 1, { action: 'info' });
} else if (rawAction === 'mias' || rawAction === 'mías') {
Metrics.inc('commands_alias_used_total', 1, { action: 'mias' });
} else if (rawAction === 'todas' || rawAction === 'todos') {
Metrics.inc('commands_alias_used_total', 1, { action: 'todas' });
}
} catch {}
// Refrescar métricas agregadas de onboarding tras cualquier comando (para conversión)
try { ResponseQueue.setOnboardingAggregatesMetrics(); } catch {}
// Usar formatDDMM desde utils/formatting // Usar formatDDMM desde utils/formatting
@ -300,6 +312,7 @@ export class CommandService {
// En grupos: no listamos; responder por DM con transición // En grupos: no listamos; responder por DM con transición
if (isGroupId(context.groupId)) { if (isGroupId(context.groupId)) {
try { Metrics.inc('ver_dm_transition_total'); } catch {}
return [{ return [{
recipient: context.sender, recipient: context.sender,
message: 'No respondo en grupos. Tus tareas: /t mias · Todas: /t todas · Info: /t info · Web: /t web' message: 'No respondo en grupos. Tus tareas: /t mias · Todas: /t todas · Info: /t info · Web: /t web'
@ -1295,7 +1308,8 @@ export class CommandService {
const cooldownRaw = Number(process.env.ONBOARDING_DM_COOLDOWN_DAYS); const cooldownRaw = Number(process.env.ONBOARDING_DM_COOLDOWN_DAYS);
const cooldownDays = Number.isFinite(cooldownRaw) && cooldownRaw >= 0 ? Math.floor(cooldownRaw) : 14; const cooldownDays = Number.isFinite(cooldownRaw) && cooldownRaw >= 0 ? Math.floor(cooldownRaw) : 14;
const delay2 = 5000 + Math.floor(Math.random() * 5001); // 510s const delayEnv = Number(process.env.ONBOARDING_BUNDLE_DELAY_MS);
const delay2 = Number.isFinite(delayEnv) && delayEnv >= 0 ? Math.floor(delayEnv) : 5000 + Math.floor(Math.random() * 5001); // 510s por defecto
const groupLabel = GroupSyncService.activeGroupsCache.get(gid) || gid; const groupLabel = GroupSyncService.activeGroupsCache.get(gid) || gid;
const codeStr = String(displayCode); const codeStr = String(displayCode);

@ -415,6 +415,15 @@ export const ResponseQueue = {
updated_at = strftime('%Y-%m-%d %H:%M:%f', 'now') updated_at = strftime('%Y-%m-%d %H:%M:%f', 'now')
WHERE id = ? WHERE id = ?
`).run(statusCode ?? null, id); `).run(statusCode ?? null, id);
// Recalcular métricas agregadas de onboarding si aplica
try {
const row = this.dbInstance.prepare(`SELECT metadata FROM response_queue WHERE id = ?`).get(id) as any;
let meta: any = null;
try { meta = row?.metadata ? JSON.parse(String(row.metadata)) : null; } catch {}
if (meta && meta.kind === 'onboarding') {
this.setOnboardingAggregatesMetrics();
}
} catch {}
}, },
markFailed(id: number, errorMsg: string, statusCode?: number, attempts?: number) { markFailed(id: number, errorMsg: string, statusCode?: number, attempts?: number) {
@ -444,6 +453,50 @@ export const ResponseQueue = {
`).run(nextAttempts, nextAttemptAt, msg, statusCode ?? null, id); `).run(nextAttempts, nextAttemptAt, msg, statusCode ?? null, id);
}, },
setOnboardingAggregatesMetrics(): void {
try {
// Total de mensajes de onboarding enviados
const sentRow = this.dbInstance.prepare(`
SELECT COUNT(*) AS c
FROM response_queue
WHERE status = 'sent' AND metadata LIKE '%"kind":"onboarding"%'
`).get() as any;
const sentAbs = Number(sentRow?.c || 0);
// Destinatarios únicos con al menos 1 onboarding enviado
const rcptRow = this.dbInstance.prepare(`
SELECT COUNT(DISTINCT recipient) AS c
FROM response_queue
WHERE status = 'sent' AND metadata LIKE '%"kind":"onboarding"%'
`).get() as any;
const recipientsAbs = Number(rcptRow?.c || 0);
// Usuarios convertidos: last_command_at > primer onboarding enviado
const convRow = this.dbInstance.prepare(`
SELECT COUNT(*) AS c
FROM users u
JOIN (
SELECT recipient, MIN(created_at) AS first_at
FROM response_queue
WHERE status = 'sent' AND metadata LIKE '%"kind":"onboarding"%'
GROUP BY recipient
) f ON f.recipient = u.id
WHERE u.last_command_at IS NOT NULL
AND u.last_command_at > f.first_at
`).get() as any;
const convertedAbs = Number(convRow?.c || 0);
const rate = recipientsAbs > 0 ? Math.max(0, Math.min(1, convertedAbs / recipientsAbs)) : 0;
try { Metrics.set('onboarding_dm_sent_abs', sentAbs); } catch {}
try { Metrics.set('onboarding_recipients_abs', recipientsAbs); } catch {}
try { Metrics.set('onboarding_converted_users_abs', convertedAbs); } catch {}
try { Metrics.set('onboarding_conversion_rate', rate); } catch {}
} catch {
// no-op
}
},
async workerLoop(workerId: number) { async workerLoop(workerId: number) {
while (this._running) { while (this._running) {
try { try {

Loading…
Cancel
Save