diff --git a/docs/plan-onboarding-comandos.md b/docs/plan-onboarding-comandos.md index 9623613..e6118bf 100644 --- a/docs/plan-onboarding-comandos.md +++ b/docs/plan-onboarding-comandos.md @@ -20,7 +20,7 @@ Estado actual relevante (resumen) Principios - Nunca escribimos mensajes en grupos (solo reacciones). - Onboarding solo se dispara cuando se crea una tarea en un grupo (evento con “excusa” clara). -- Onboarding por usuario: máx. 2 DMs, separados al menos 14 días; si no interactúa, no insistimos más. +- Onboarding por usuario: máx. 2 paquetes (cada paquete = 2 DMs consecutivos con breve retraso), separados al menos 14 días y solo si no hubo interacción desde el primer paquete. - Alias de comandos más cortos y claros para fomentar uso por DM. --- @@ -77,26 +77,29 @@ Impacto en tests --- -## Fase 2 — Infra de Onboarding por DM (sin migraciones) +## Fase 2 — Infra de Onboarding por DM en paquetes (2 DMs, migración mínima para interacción) Objetivos -- Implementar un onboarding DM que se dispare al crear una tarea en un grupo, con 2 variantes: - - Mensaje 1 (initial): CTA “/t tomar {CÓDIGO}” + “/t info”. - - Mensaje 2 (reminder): mini‑chuleta (“/t mias”, “/t todas”, “/t configurar …”, “/t web”). -- Cumplir reglas: máx. 2 DMs por usuario, separados ≥ 14 días; cap por evento; sin mensajes en grupos. +- Enviar un paquete de 2 DMs (Mensaje 1 + Mensaje 2) por usuario cuando se crea una tarea en un grupo. + - Mensaje 1: CTA “/t tomar {CÓDIGO}” + “/t info”. + - Mensaje 2: mini‑chuleta (“/t mias”, “/t todas”, “/t configurar …”, “/t web”), 3–8 s después del Mensaje 1. +- Repetir el mismo paquete una única vez más si pasan ≥ 14 días sin interacción del usuario (si hubo interacción, no se envía el segundo paquete). +- Cap por evento; sin mensajes en grupos. Archivos a modificar - src/services/group-sync.ts (añadir listActiveMemberIds(groupId): string[]) -- src/services/response-queue.ts (añadir helpers para onboarding: enqueueOnboarding y getOnboardingStats; reutilizar metadata JSON con kind='onboarding') -- src/services/command.ts (desencadenar onboarding tras crear tarea en grupo, respetando gating y caps) +- src/services/response-queue.ts (añadir helpers para onboarding con metadata { kind='onboarding', variant, part, bundle_id } y soporte de retraso para el segundo DM del paquete; getOnboardingStats) +- src/services/command.ts (desencadenar el paquete tras crear tarea en grupo, respetando gating y caps; actualizar users.last_command_at al recibir cualquier comando) - src/services/allowed-groups.ts (usado para gating en modo enforce) +- src/db/migrations/* (añadir columna users.last_command_at) y wiring en src/db/migrator.ts - src/services/identity.ts y src/services/contacts.ts (solo consumo; no se cambian) Overview de cambios - GroupSyncService: nuevo helper para obtener IDs de miembros activos del grupo (solo dígitos, grupos activos, no comunidad/archivados). - ResponseQueue: - - enqueueOnboarding(recipient, message, metadata) con metadata canónica: { kind: 'onboarding', variant: 'initial'|'reminder', group_id, task_id, display_code }. - - getOnboardingStats(recipient): { total, lastSentAt } consultando response_queue por metadata.kind='onboarding'. + - enqueueOnboarding(recipient, message, metadata) con metadata canónica: { kind: 'onboarding', variant: 'initial'|'reminder', part: 1|2, bundle_id, group_id, task_id, display_code }. + - getOnboardingStats(recipient): { total, lastSentAt, lastVariant?: 'initial'|'reminder' } consultando response_queue por metadata.kind='onboarding'. + - Soportar programar el segundo DM del paquete con next_attempt_at = now + ONBOARDING_BUNDLE_DELAY_MS (por defecto 4000–8000ms). - CommandService (en /t nueva): - Tras crear la tarea en grupo, construir candidatos: - miembros activos del grupo (GroupSync.listActiveMemberIds), @@ -105,31 +108,33 @@ Overview de cambios - si GROUP_GATING_MODE=enforce y el grupo no está allowed → no enviar. - Cap por evento (ONBOARDING_EVENT_CAP, p. ej. 30). - Para cada destinatario: - - stats.total=0 → enviar Mensaje 1. - - stats.total=1 y lastSentAt <= now-14 días → enviar Mensaje 2. + - Si no hay paquetes previos → encolar paquete 'initial' con 2 DMs (part=1 ahora; part=2 con retraso). + - Si hay paquete 'initial' y han pasado ≥14 días SIN interacción (users.last_command_at ≤ primer paquete) → encolar paquete 'reminder' (2 DMs). - En cualquier otro caso → no enviar. -- Envío inmediato (next_attempt_at = now). Ventanas horarias opcionales (ver Fase 4). +- Envío del primer DM del paquete inmediato (next_attempt_at = now) y el segundo con pequeño retraso. Ventanas horarias opcionales (ver Fase 4). Copys de onboarding (exactos) -- Mensaje 1: +- Mensaje 1 (en ambos disparos): - “Hola, soy el bot de tareas. En ‘{Grupo}’ acaban de crear una tarea: #{CÓDIGO} {descripción corta} - Encárgate: envíame /t tomar {CÓDIGO} por privado· Más info: /t info (también por privado) - Nota: Solo respondo por privado. (Este mensaje solo lo envío una vez)” -- Mensaje 2 (mini‑chuleta, solo si no interactuó aún): + Encárgate: /t tomar {CÓDIGO} · Más info: /t info + Nota: nunca respondo en grupos; solo por privado.” +- Mensaje 2 (mini‑chuleta; se envía tras 3–8 s, en ambos disparos): - “Guía rápida (este es un mensaje único): · Tus tareas: /t mias · Todas: /t todas - · Recordatorios: /t configurar diario | l‑v | semanal + · Recordatorios: /t configurar diario | l‑v | semanal | off · Web: /t web” Impacto en tests - Tests unitarios para: - Construcción de destinatarios (exclusiones, cap). - - Idempotencia por usuario (0 → initial; 1 y >14d → reminder; resto skip). + - Paquetes: por disparo se encolan 2 DMs/usuario (part=1 y part=2 con retraso). + - Recordatorio a los ≥14 días solo si no hubo interacción desde el primer paquete; si la hubo, skip. - Gating enforce (grupo no allowed → no enviar). - - Metadata de enqueue (kind='onboarding', variant correcto). + - Metadata de enqueue (kind='onboarding', variant initial|reminder, part 1|2, bundle_id). Notas sobre migraciones -- No se requiere migración: se usa response_queue como fuente de verdad para conteo/fecha de onboarding por usuario (consultando metadata.kind='onboarding'). +- Migración mínima recomendada: añadir users.last_command_at para registrar la última interacción del usuario y así decidir si enviar el segundo paquete tras ≥14 días. Actualizar este campo al procesar cualquier comando entrante. +- Si no se implementa, el recordatorio se puede degradar a “si han pasado ≥14 días desde el primer paquete”, sin comprobar interacción (menos preciso). --- @@ -171,9 +176,10 @@ Archivos a modificar - src/services/metrics.ts (no requiere cambios de API) Métricas propuestas -- onboarding_dm_sent_total (labels: variant=initial|reminder, group_id) +- onboarding_dm_sent_total (labels: variant=initial|reminder, part=1|2, group_id) +- onboarding_bundle_sent_total (labels: variant=initial|reminder, group_id) — opcional - onboarding_dm_skipped_total (labels: reason, group_id) - - reasons: 'cooldown_active', 'capped_event', 'not_allowed', 'disabled', 'no_members', 'no_group', 'not_group_context', 'no_display_code' + - reasons: 'cooldown_active', 'capped_event', 'not_allowed', 'disabled', 'no_members', 'no_group', 'not_group_context', 'no_display_code', 'had_interaction' - onboarding_recipients_capped_total (labels: group_id) - commands_alias_used_total (labels: action=info|mias|todas) - commands_blocked_total (ya existe para gating; mantener) @@ -182,6 +188,7 @@ Flags/env sugeridas - ONBOARDING_DM_ENABLED=true - ONBOARDING_DM_COOLDOWN_DAYS=14 - ONBOARDING_EVENT_CAP=30 +- ONBOARDING_BUNDLE_DELAY_MS=4000 - ONBOARDING_ENABLE_IN_TEST=false (o true si se van a probar envíos en tests) - GROUP_GATING_MODE=enforce|off (ya existente) - Opcional (si se diferencia horario amable): ONBOARDING_SILENCE_HOURS=22-08 (futuro) @@ -207,10 +214,10 @@ Objetivos - Contexto grupo: - Invocar listados desde un grupo responde por DM con transición (no lista en el grupo). - Onboarding: - - Evento de creación en grupo dispara destinatarios esperados (excluye creador/asignados/bot; cap). - - 0 → initial, 1 y >14 días → reminder, resto → skip. + - Por evento de creación en grupo se encolan 2 DMs/usuario (part=1 inmediato y part=2 con retraso). + - Tras ≥14 días sin interacción desde el primer paquete, se encola un segundo paquete idéntico; si hubo interacción, no se encola. - Gating enforce: grupos no permitidos → no enviar. - - Metadata de response_queue correcta (kind, variant). + - Metadata de response_queue correcta (kind, variant, part, bundle_id). - Help v2 actualizado (snapshots). - CTAs añadidos al final de DMs operativos.