|  |  | # Plan de Sincronización de Miembros (Grupos de WhatsApp)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Este documento define el plan para implementar una sincronización robusta de miembros por grupo, usando Evolution API como fuente de verdad y una réplica local en SQLite para consultas rápidas y lógica de negocio.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Objetivos y alcance
 | 
						
						
						
							|  |  | - Mantener una lista actualizada de miembros por grupo (JID @g.us), con:
 | 
						
						
						
							|  |  |   - Estado: activo/inactivo (soft delete).
 | 
						
						
						
							|  |  |   - Rol: admin/miembro.
 | 
						
						
						
							|  |  |   - Trazabilidad temporal: first_seen_at, last_seen_at, last_role_change_at.
 | 
						
						
						
							|  |  | - Fuente: Evolution API (full sync + eventos incrementales por webhook).
 | 
						
						
						
							|  |  | - Consistencia: idempotencia, tolerancia a webhooks perdidos (reconciliación periódica).
 | 
						
						
						
							|  |  | - Privacidad: almacenar solo IDs normalizados y metadatos mínimos.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Modelo de datos (migración inicial)
 | 
						
						
						
							|  |  | - Tabla groups
 | 
						
						
						
							|  |  |   - group_id TEXT PK (JID completo, p.ej. 123@g.us)
 | 
						
						
						
							|  |  |   - name TEXT
 | 
						
						
						
							|  |  |   - active BOOLEAN
 | 
						
						
						
							|  |  |   - last_verified TEXT (ISO) opcional
 | 
						
						
						
							|  |  |   - updated_at TEXT
 | 
						
						
						
							|  |  | - Tabla group_members
 | 
						
						
						
							|  |  |   - group_id TEXT NOT NULL
 | 
						
						
						
							|  |  |   - user_id TEXT NOT NULL (teléfono normalizado; dígitos)
 | 
						
						
						
							|  |  |   - is_admin BOOLEAN NOT NULL DEFAULT 0
 | 
						
						
						
							|  |  |   - is_active BOOLEAN NOT NULL DEFAULT 1
 | 
						
						
						
							|  |  |   - first_seen_at TEXT NOT NULL
 | 
						
						
						
							|  |  |   - last_seen_at TEXT NOT NULL
 | 
						
						
						
							|  |  |   - last_role_change_at TEXT
 | 
						
						
						
							|  |  |   - PK compuesta (group_id, user_id)
 | 
						
						
						
							|  |  |   - Índices: 
 | 
						
						
						
							|  |  |     - group_members(group_id, is_active)
 | 
						
						
						
							|  |  |     - group_members(user_id, is_active)
 | 
						
						
						
							|  |  | - Normalización:
 | 
						
						
						
							|  |  |   - group_id: mantener sufijo @g.us
 | 
						
						
						
							|  |  |   - user_id: usar normalizeWhatsAppId existente
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Estrategia de sincronización
 | 
						
						
						
							|  |  | 1) Full sync (reconciliación periódica)
 | 
						
						
						
							|  |  | - Listar grupos bajo WHATSAPP_COMMUNITY_ID.
 | 
						
						
						
							|  |  | - Para cada grupo activo:
 | 
						
						
						
							|  |  |   - Obtener miembros (paginado si aplica).
 | 
						
						
						
							|  |  |   - Upsert de cada miembro presente:
 | 
						
						
						
							|  |  |     - is_active=true, last_seen_at=now.
 | 
						
						
						
							|  |  |     - is_admin si el rol viene en la respuesta; actualizar last_role_change_at cuando cambie.
 | 
						
						
						
							|  |  |   - Marcar como inactivos los que ya no aparezcan (is_active=false, last_seen_at=now).
 | 
						
						
						
							|  |  | - Intervalo configurable (e.g., cada 6–24h); en test/desarrollo puede ser menor.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 2) Incremental por webhooks (near-real-time)
 | 
						
						
						
							|  |  | - Suscripción a eventos de Evolution API:
 | 
						
						
						
							|  |  |   - Alta de participante (join/add).
 | 
						
						
						
							|  |  |   - Baja de participante (leave/remove).
 | 
						
						
						
							|  |  |   - Cambio de rol (promote/demote).
 | 
						
						
						
							|  |  |   - Renombrado/archivado de grupo.
 | 
						
						
						
							|  |  | - Handler idempotente:
 | 
						
						
						
							|  |  |   - Upsert por (group_id, user_id).
 | 
						
						
						
							|  |  |   - Cambios de estado/rol atómicos (transacción si se tocan varias filas).
 | 
						
						
						
							|  |  |   - Si el payload es parcial (sin rol), no pisar is_admin.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Consistencia y fallos
 | 
						
						
						
							|  |  | - La app consulta siempre is_active=true.
 | 
						
						
						
							|  |  | - Full sync corrige drift por webhooks perdidos o desordenados.
 | 
						
						
						
							|  |  | - Backoff y rate limiting al hablar con Evolution API.
 | 
						
						
						
							|  |  | - Logs con contexto: group_id, user_id, evento/tipo, resultado.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Uso en la aplicación (consumidores)
 | 
						
						
						
							|  |  | - “/t ver todo” y recordatorios:
 | 
						
						
						
							|  |  |   - Incluir “sin responsable” únicamente de grupos donde el usuario sea miembro activo.
 | 
						
						
						
							|  |  |   - Fallback: si aún no hay snapshot de membresías, usar heurística (grupos con tareas del usuario).
 | 
						
						
						
							|  |  | - Validación (opcional por fase):
 | 
						
						
						
							|  |  |   - Antes de operar sobre una tarea de grupo, comprobar membresía activa.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Privacidad y seguridad
 | 
						
						
						
							|  |  | - Solo almacenar user_id normalizado y rol/fechas.
 | 
						
						
						
							|  |  | - No persistir nombres salvo que ya exista caché controlada.
 | 
						
						
						
							|  |  | - Webhook y full sync preferiblemente en red interna; si no, proteger con allowlist/proxy.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Plan por etapas
 | 
						
						
						
							|  |  | Etapa 1 — Esquema + servicio — COMPLETADA
 | 
						
						
						
							|  |  | - Migración: crear groups y group_members + índices.
 | 
						
						
						
							|  |  | - Servicio GroupSync:
 | 
						
						
						
							|  |  |   - syncGroups(): lista grupos + reconcilia miembros.
 | 
						
						
						
							|  |  |   - reconcileGroup(groupId, membersSnapshot).
 | 
						
						
						
							|  |  |   - Utilidades: timestamps ISO, normalización de IDs.
 | 
						
						
						
							|  |  | - Tests:
 | 
						
						
						
							|  |  |   - Migración up-only.
 | 
						
						
						
							|  |  |   - Reconciliación: de [A,B,C] a [A,C,D] → B inactivo, D añadido; tiempos/roles actualizados.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Etapa 2 — Integración con Evolution API — COMPLETADA
 | 
						
						
						
							|  |  | - Full sync: llamadas reales a endpoints (respetando paginación y límites).
 | 
						
						
						
							|  |  | - Webhooks: registro y handlers idempotentes (alta/baja/rol/rename).
 | 
						
						
						
							|  |  | - Tests:
 | 
						
						
						
							|  |  |   - Fixtures de payloads típicos (duplicados, orden alterado).
 | 
						
						
						
							|  |  |   - Reintentos/backoff en errores de red/5xx.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Etapa 3 — Consumidores (comandos y recordatorios) — COMPLETADA
 | 
						
						
						
							|  |  | - “/t ver todo” y RemindersService usan membership real.
 | 
						
						
						
							|  |  | - Fallback heurístico si no hay snapshot aún.
 | 
						
						
						
							|  |  | - Validaciones opcionales de pertenencia.
 | 
						
						
						
							|  |  | - Tests:
 | 
						
						
						
							|  |  |   - Cobertura de ambos caminos (con y sin membership).
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Etapa 4 — Observabilidad y mantenimiento
 | 
						
						
						
							|  |  | - Métricas:
 | 
						
						
						
							|  |  |   - grupos sincronizados, miembros activos, difs por sync, errores de webhook.
 | 
						
						
						
							|  |  | - Estado de salud:
 | 
						
						
						
							|  |  |   - último sync OK, edad de la snapshot.
 | 
						
						
						
							|  |  | - Mantenimiento:
 | 
						
						
						
							|  |  |   - Re-sync diario.
 | 
						
						
						
							|  |  |   - Purgado opcional de inactivos antiguos.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Criterios de aceptación
 | 
						
						
						
							|  |  | - Tras una full sync, group_members refleja fielmente miembros activos por grupo.
 | 
						
						
						
							|  |  | - Webhooks de alta/baja actualizan el estado en <1s y son idempotentes.
 | 
						
						
						
							|  |  | - “/t ver todo” y recordatorios respetan la membresía y no rompen UX preexistente.
 | 
						
						
						
							|  |  | - 100% de tests existentes siguen pasando; nuevos tests cubren sync/reconciliación/handlers.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Métricas y trazas sugeridas
 | 
						
						
						
							|  |  | - Contadores: sync_runs_total, sync_errors_total, webhook_events_total, webhook_errors_total.
 | 
						
						
						
							|  |  | - Gauges: active_groups, active_members.
 | 
						
						
						
							|  |  | - Logs estructurados: action, group_id, user_id, outcome, diff_counts.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Riesgos y mitigaciones
 | 
						
						
						
							|  |  | - Webhooks perdidos: reconciliación periódica.
 | 
						
						
						
							|  |  | - Cambios de API o límites: backoff + degradación controlada.
 | 
						
						
						
							|  |  | - Coste de consultas: índices adecuados; joins simples.
 | 
						
						
						
							|  |  | - Privacidad: minimización de datos, documentación en README/STATUS.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Estimación
 | 
						
						
						
							|  |  | - Etapa 1: 0.5–1 día.
 | 
						
						
						
							|  |  | - Etapa 2: 1–1.5 días.
 | 
						
						
						
							|  |  | - Etapa 3: 0.5 día.
 | 
						
						
						
							|  |  | - Etapa 4 (opcional): 0.5 día.
 | 
						
						
						
							|  |  | Total: 2–3.5 días netos.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Dependencias y archivos a tocar (cuando implementemos)
 | 
						
						
						
							|  |  | - src/db.ts (esquema base para nuevas instalaciones).
 | 
						
						
						
							|  |  | - src/db/migrations/index.ts (migración up-only: tablas e índices).
 | 
						
						
						
							|  |  | - src/services/webhook-manager.ts (registro y handlers de eventos).
 | 
						
						
						
							|  |  | - src/services/group-sync.ts (full sync y reconciliación).
 | 
						
						
						
							|  |  | - src/services/command.ts (consumo en “/t ver todo”; validaciones opcionales).
 | 
						
						
						
							|  |  | - src/services/reminders.ts y src/tasks/service.ts (consultas usando membresía).
 | 
						
						
						
							|  |  | - tests/unit/services/* (sync, reconciliación, webhooks, consumidores).
 |