You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
23 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Task WhatsApp Chatbot
A WhatsApp chatbot for task management, designed to work with Evolution API in a secure internal network environment.
## 📌 Overview
This service provides a WhatsApp interface for task management within WhatsApp groups. It:
- Listens for `/tarea` commands in WhatsApp groups via Evolution API webhooks.
- Stores tasks, users, and groups in a SQLite database.
- Synchronizes group information periodically from the Evolution API.
- Manages user permissions and group membership (partially implemented).
- Integrates with Evolution API for WhatsApp connectivity.
- Sends direct messages to acknowledge the creator and notify each assignee; includes mentions with phone numbers for quick action.
- DM-only responses: the bot does not post messages in groups; all outputs are sent via DM to the author (except the optional creation summary below).
- Advanced listings: "/t ver" supports scopes "grupo", "mis", "sin" and "todos" with pagination ("… y X más").
- Complete tasks: "/t x <id>" (aliases: hecho, completar, done) with compact DM feedback.
- Claim/unassign tasks: "/t tomar <id>" and "/t soltar <id>" with compact DM feedback.
- Natural date parsing: supports "hoy"/"mañana" with TZ (env TZ; default Europe/Madrid).
- Unified date display: all dates are rendered as dd/MM; help available via DM with "/t" or "ayuda".
- Optional: group notification on task creation controlled by NOTIFY_GROUP_ON_CREATE (default false), including proper mentions for visibility.
## 🔐 Security Model
- **Internal Networking**: The webhook should ideally only accept connections from Evolution API via internal Docker networking (configuration dependent).
- **Environment Variables**: Sensitive configuration (API keys, URLs) is managed through environment variables.
- **Group Restrictions**: Designed to operate within pre-approved WhatsApp groups (validation logic pending integration).
- **Input Validation**: Basic validation exists for webhook structure; needs enhancement for command arguments and user/group IDs.
## 🧱 Architecture
```mermaid
graph TD
A[Webhook Received] --> B{Valid Payload?}
B -->|No| C[Ignore]
B -->|Yes| D{Normalize IDs & Check Group Active?}
D -->|No| C[Ignore/Log]
D -->|Yes| E[Ensure User Exists in DB]
E --> G{/tarea Command?}
G -->|No| C
G -->|Yes| J[Process Command Logic]
J -- Success/Error --> K[Queue Response(s)]
K --> L[Process Queue & Send Response via API]
subgraph Database Interaction
E --> DB[(SQLite DB)]
J --> DB
end
subgraph Evolution API
L --> EA((Evolution API))
EA -- Webhook --> A
end
```
*(Diagram updated for planned flow)*
## Decisiones de diseño de la cola de respuestas (estado actual)
1) Persistencia: 100% persistente (tabla única).
2) Worker: continuo en background.
3) Locking: status=processing (sin lease).
4) Orden: sin orden estricto por chat.
5) Reintentos: activados con backoff exponencial + jitter (configurables).
6) Errores: 4xx = fallo definitivo; 5xx/red = reintento hasta RQ_MAX_ATTEMPTS con `next_attempt_at`.
7) Idempotencia: no.
8) Esquema DB: tabla única response_queue.
9) Transporte: envío desde ResponseQueue (fetch directo a Evolution API).
10) Concurrencia: N workers globales.
11) Integración: encolar y worker continuo (arranca con el servidor).
12) Configuración: defaults fijos (sin nuevas env vars por ahora).
13) Limpieza: retención periódica de historiales aplicada (sent y failed antiguos).
14) Seguridad: no enviar al número del bot (CHATBOT_PHONE_NUMBER).
15) Pruebas: unitarias de cola con mocks de fetch.
Actualización Phase 4 — Etapa 1 (Completada):
- Reintentos activados con backoff exponencial + jitter.
- Nuevos campos: `next_attempt_at`, `last_status_code`.
- Selección de pendientes filtrando por `(status='queued' AND next_attempt_at <= now)`.
- Config por entorno: `RQ_MAX_ATTEMPTS`, `RQ_BASE_BACKOFF_MS`, `RQ_MAX_BACKOFF_MS`.
## Arquitectura de la cola persistente (MVP)
- Estados: queued | processing | sent | failed.
- Campos actuales por mensaje: id (PK), recipient, message, status, attempts (0), last_error (nullable), metadata (nullable), created_at, updated_at.
- Índices recomendados: (status, created_at) para seleccionar pendientes rápidamente.
- Sin orden estricto por chat; el envío puede intercalarse entre destinatarios.
- Concurrencia: N workers globales operando en bucle, cada uno toma mensajes en estado queued y los marca processing.
Estado: la tabla response_queue ya está creada e incluida en los tests de DB.
## Flujo del worker continuo (MVP)
- Se inicia al arrancar el servidor (desactivado en tests).
- Ciclo: seleccionar hasta un pequeño batch de mensajes queued, marcar processing, enviar a Evolution API, marcar sent o failed según respuesta.
- Sin reintentos; logs mínimos y no sensibles.
## Limitaciones actuales
- Sin orden garantizado por chat (pendiente serialización por destinatario).
- Sin idempotencia (pendiente `idempotency_key` e índice único).
- Métricas/observabilidad básicas pendientes (endpoint /metrics).
- Sin garantía de orden cross-chat; fair scheduling pendiente.
## Plan incremental posterior
- Añadir reintentos con backoff exponencial y jitter. (Completado)
- Garantizar orden por chat (serialización por recipient).
- Introducir lease (lease_until) para tolerancia a fallos y recuperación.
- Limpieza/retención y métricas/observabilidad.
- Opcional: idempotencia e índices adicionales.
## ✅ Current Status (as of latest commit)
### Implemented
- Webhook server setup (`src/server.ts`) receiving Evolution API events.
- Corrección: extracción robusta de remitente en DMs vs grupos para evitar 'Invalid sender ID'.
- Sistema de migraciones up-only para SQLite (`src/db/migrator.ts`, `src/db/migrations/`), ejecutado al arranque; backup automático con VACUUM INTO y baseline cuando existe esquema previo.
- Group synchronization service (`src/services/group-sync.ts`) to fetch/store/cache groups. Includes active group caching and `isGroupActive` checks in server.
- Webhook registration and verification with Evolution API (`src/services/webhook-manager.ts`).
- WhatsApp ID normalization utility (`src/utils/whatsapp.ts`).
- User creation/update function (`src/db.ts::ensureUserExists`) integrated into the main flow.
- Command handling for `/tarea nueva` end-to-end (`src/services/command.ts`):
- Parses description and selects the last future date (YYYY-MM-DD) as due date.
- Extracts assignees from explicit mentions and plain-text @tokens; defaults to creator only when none found.
- Cleans description to remove @mentions tokens.
- Persists task and assignments atomically via `TaskService`.
- Builds response with assignment list and includes Evolution API “mentioned” JIDs via `ResponseQueue`.
- Command handling for listings, completion and assignment (tomar/soltar):
- `/t ver` with scopes `grupo`, `mis`, `sin` and `todos` with top-N and “… y X más”; DM-only policy enforced.
- `/t x <id>` to complete tasks (aliases: `hecho`, `completar`, `done`) with compact feedback.
- Task persistence service (`src/tasks/service.ts`) with `created_by` and assignment inserts in a transaction; supports DB injection for tests.
- Response queue persistente con workers en background y envío vía Evolution API (`src/services/response-queue.ts`), persistiendo metadata `{ mentioned: [...] }` y enviándola como `mentioned` en el payload, con reintentos (backoff exponencial + jitter), recuperación de `processing` y limpieza/retención.
- Contacts service and friendly names: `ContactsService` resolves display names via webhooks (CONTACTS_UPDATE/CHATS_UPDATE) and Evolution API fallback; used to render names in outgoing texts (falls back to numbers). Skips network calls under NODE_ENV=test for fast and isolated unit tests.
- Notification UX: Always send DM acknowledgment to the creator in a single line (format: ✅ Tarea <id> creada: "description"), DM to each assignee (excluding the creator); optional group notification controlled by `NOTIFY_GROUP_ON_CREATE` (default false) with proper mentions.
- Natural date tokens support (“hoy”/“mañana”) with TZ configurable (env TZ; default Europe/Madrid) in task creation.
- Unified dd/MM date formatting across outgoing texts.
- Help by DM for "/t" and "/t ayuda" with a concise guide and examples.
- Environment variable validation (`src/server.ts`, `src/services/webhook-manager.ts`).
- Health check endpoint (`/health`).
- **Database isolation in unit tests**: Using in-memory instances to avoid conflicts.
### Incomplete / Missing Core Functionality
- ResponseQueue reliability: reintentos con backoff, recuperación de `processing`, métricas y limpieza/retención.
- ContactsService improvements (optional): refine caching policy and endpoints; basic friendly-name resolution is already implemented and used in outgoing texts.
- More robust error handling and observability around API/DB operations.
### Known Issues
- Mentions UX: In group chats, mentions are highlighted and each client resolves the chip to their local contact name; we include friendly names in text when available. In direct messages, WhatsApp does not render mention chips for third parties, so we include the number as @digits for quick action; no per-recipient name rewriting occurs.
- Test suite: all unit tests passing.
## 🛠️ Setup
### Environment Variables
*(Ensure these are set correctly)*
```env
# Evolution API Connection
EVOLUTION_API_URL=http://evolution-api:3000 # Or your API URL
EVOLUTION_API_KEY=your-api-key
EVOLUTION_API_INSTANCE=main # Your instance name
# WhatsApp Specific
WHATSAPP_COMMUNITY_ID=your-community-id # ID of the main community to sync groups from
CHATBOT_PHONE_NUMBER=1234567890 # Bot's normalized phone number (e.g., for assigning tasks)
# Webhook Configuration
WEBHOOK_URL=http://your-service-internal-url:3007 # URL Evolution API calls *back* to this service
PORT=3007 # Port this service listens on
# Runtime Environment
NODE_ENV=production # Or development
TZ=Europe/Madrid # Default timezone used for "hoy/mañana" parsing and date displays
# Optional
# GROUP_SYNC_INTERVAL_MS=3600000 # Sync interval in ms (default: 24h)
# NOTIFY_GROUP_ON_CREATE=false # If 'true', also post a brief summary with mentions to the group
```
### Development Setup
```bash
# Install dependencies
bun install
# Copy .env.example to .env and fill in values
cp .env.example .env
# Start development server (watches for changes)
bun run dev
# Run tests
bun test
```
## 📅 Roadmap & Priorities (Updated Plan)
### MVP — Próximos pasos (prioridad actual)
1) Rate limiting básico: bucket en memoria por remitente (p. ej., 10/min), respuesta amable al exceder, desactivado en NODE_ENV=test.
2) Recordatorios diarios por DM: preferencia por usuario (daily|off, por defecto off), hora fija local (p. ej., 08:30 TZ), un DM con resumen compacto de “tus tareas”.
3) Refinar ContactsService: mejorar heurística de nombre, invalidación temprana en CHATS_UPDATE/CONTACTS_UPDATE, TTL configurable, robustez ante fallos, sin llamadas de red en tests.
4) Sincronización mínima de miembros: obtener y cachear miembros de grupos activos con TTL, uso no bloqueante; base para futuras validaciones/UX.
5) Ampliar test suite: cubrir recordatorios, rate limiting, actualización de nombres, sync de miembros y casos extremos de ResponseQueue.
### Phase 1: User & Group Foundation (Highest Priority - In Progress)
- [x] **Create WhatsApp ID Normalization Utility:** (`src/utils/whatsapp.ts`) Handle different ID formats.
- [x] **Implement `ensureUserExists`:** (`src/db.ts`) Add users to DB on first interaction.
- [x] **Implement `isGroupActive` Check:** (`src/services/group-sync.ts`, `src/server.ts`) Cache exists in `group-sync.ts`. **Needs integration** into `src/server.ts`.
- [x] **Integrate Validation in Server:** (`src/server.ts`) Use normalization, `ensureUserExists`, and active group check before processing commands.
- [x] **Implement DB isolation in tests:** (`tests/unit/services/group-sync.test.ts`) Use in-memory instances to avoid conflicts.
### Phase 2: Refinar `/tarea nueva` (Alta prioridad)
- [x] Completar `TaskService.createTask` con `created_by` y asignaciones (incluyendo `ensureUserExists` para asignados) y transacción atómica.
- [x] Mejorar `CommandService` para parsear menciones y devolver el id de la tarea creada y resumen de asignados.
### Phase 3: Comandos adicionales y refinamientos (Alta prioridad)
- [x] Implementar `/tarea mostrar [group|mine]` para listar pendientes.
- [x] Implementar `/tarea completar <task_id>` con validaciones básicas.
- [x] Soportar mensajes de texto extendido y captions de media (además de conversation).
- [x] Implementar tomar/soltar (claim/unassign) con feedback por DM.
### Phase 4: Fiabilidad de la cola (Media; observabilidad pospuesta post-MVP)
- [x] Añadir reintentos con backoff exponencial y jitter.
- [x] Recuperar ítems en estado `processing` tras reinicios (lease o expiración y requeue).
- [x] Limpieza/retención de historiales.
### Phase 4 — Desglose y estado
- Etapa 1 — Reintentos con backoff exponencial + jitter (Completada)
- Parámetros: RQ_MAX_ATTEMPTS (6), RQ_BASE_BACKOFF_MS (5000), RQ_MAX_BACKOFF_MS (3600000).
- Lógica: 2xx → sent; 4xx → failed definitivo; 5xx/red → reintento con `next_attempt_at` hasta MAX_ATTEMPTS.
- Etapa 2 — Recuperación de items en `processing` mediante lease/expiración (Completada)
- Etapa 3 — Métricas y observabilidad (Pospuesta post-MVP)
- Etapa 4 — Limpieza/retención (Completada)
### Phase 5: Advanced Features (Low Priority)
- [ ] Add task reminders system.
- [ ] Implement user permissions system.
- [ ] Add rate limiting.
- [ ] Create task history tracking.
### Etapas siguientes (priorización propuesta)
1) Phase 4 — Etapa 4: Limpieza y retención
- Objetivo: evitar crecimiento indefinido y mantener la BD sana.
- Implica: retención/archivado de response_queue, posible VACUUM, config RQ_RETENTION_DAYS y job periódico.
2) Fiabilidad avanzada de ResponseQueue
- Objetivo: mejorar entrega y evitar duplicados/ruido.
- Implica: orden por destinatario, idempotency_key con índice único, límites de inflight/backpressure.
3) UX Iteración A (MVP centrado en valor)
- Objetivo: fluidez de uso y silencio en grupos.
- Implica: alias y sinónimos, “solo DM”, soporte “hoy/mañana”, comandos tomar/soltar, ayuda breve.
4) UX Iteración B (listas y completar)
- Objetivo: visibilidad y cierre rápido.
- Implica: “ver grupo” y “ver mis” con tope y “y X más…”, completar (x/hecho/done) robusto, formatos compactos.
5) Recordatorios (Phase 5)
- Objetivo: nudge suave por DM.
- Implica: tabla user_preferences, comando “configurar”, job diario de resumen.
6) Permisos y pertenencia a grupos
- Objetivo: control de quién puede qué, y pertenencia válida.
- Implica: roles y/o verificación de pertenencia; posibles migraciones y sincronización de miembros.
7) Historial de tareas (auditoría ligera)
- Objetivo: trazabilidad de cambios.
- Implica: tabla task_events; eventos en crear/asignar/tomar/soltar/completar; consulta “historial”.
8) Rate limiting (operación segura)
- Objetivo: proteger ante abuso o loops.
- Implica: token bucket por usuario/grupo; configuración de límites.
9) Sincronización de miembros (opcional)
- Objetivo: conocer miembros activos por grupo para features avanzadas.
- Implica: endpoints Evolution API para miembros; cache/migraciones.
10) Métricas y observabilidad (deferido del MVP)
- Objetivo: visibilidad con bajo coste.
- Implica: counters/gauges/histograms y endpoint /metrics; logging estructurado. Se pospone al final de esta lista.
## 🔑 Key Considerations & Caveats
* **WhatsApp ID Normalization:** Crucial for consistently identifying users and groups. Needs careful implementation to handle edge cases. (Utility function exists).
* **Response Latency:** Sending responses requires an API call back to Evolution. Ensure the `ResponseQueue` processing is efficient.
* **Cola de respuestas:** Persistente en DB; pendiente añadir reintentos/backoff y limpieza/retención.
* **Group Sync:** The current full sync might be slow or rate-limited with many groups. Delta updates are recommended long-term.
* **Error Handling:** Failures in command processing or response sending should be logged clearly and potentially reported back to the user. Database operations should use transactions for atomicity (especially task+assignment creation).
* **State Management:** The current design is stateless. Complex interactions might require state persistence later.
* **Security:** Ensure group/user validation logic is robust once integrated.
## 🧪 Testing
### Running Tests
```bash
bun test
```
### Test Coverage
- Database initialization and basic operations (`db.test.ts`).
- Webhook validation (basic) (`webhook-manager.test.ts`).
- Command parsing (basic structure) (`command.test.ts`).
- Environment checks (`server.test.ts`).
- Basic error handling (`server.test.ts`).
- WhatsApp ID normalization (`whatsapp.test.ts`).
- Group sync operations (`group-sync.test.ts`).
- **Needed:** Tests for `ensureUserExists` integration, `isGroupActive` integration, `CommandService` logic, `ResponseQueue` processing (mocking API), `TaskService` operations.
- All unit tests passing. Added unit tests for CommandService (date parsing "hoy/mañana", DM help, dd/MM formatting, default assignment rules).
## 🧑‍💻 Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/implement-user-validation`)
3. Add/update tests for new functionality
4. Ensure tests pass (`bun test`)
5. Submit a pull request
## 📚 Documentation
For detailed API documentation and architecture decisions, see the [docs/](docs/) directory (if created).
---
## 📐 Diseño UX acordado (MVP y siguientes iteraciones)
Este apartado documenta las decisiones de UX aprobadas para el MVP y su evolución inmediata. Objetivo: mínima fricción, cero ruido en grupos y mensajes compactos.
### Principios
- Silencio en grupos: el bot NO publica mensajes en grupos. Cuando alguien usa un comando en un grupo, el bot responde solo por DM al autor, sin dejar mensaje en el grupo.
- Homogeneidad: mismos comandos y comportamiento tanto en la comunidad “Casa” como en la del AMPA.
- Mensajes compactos: máximo 23 líneas, usando emojis y formato WhatsApp (negritas, monoespacio) para legibilidad.
- Aprendizaje progresivo: alias cortos y ayuda accesible por DM.
### Comando base y alias
- Prefijo admitido: “/t” y “/tarea”.
- Subcomandos y sinónimos (aceptar cualquiera, mapear a una acción canónica):
- Crear: n, nueva, crear, +
- Ver: ver, mostrar, listar, ls
- Completar: x, hecho, completar, done
- Tomar: tomar, claim
- Soltar: soltar, unassign
- Ayuda: ayuda, help, ?
- Configurar: configurar, config
### Gramática de “crear tarea”
- Texto libre: descripción.
- Fecha: soportar tokens “hoy” y “mañana” (MVP). Futuro: +2d, +1w, lun/mar/…
- Menciones: “@…” y menciones reales del cliente.
- Asignación por defecto:
- En grupos: si no hay menciones → tarea queda “sin dueño”.
- En DM: si no hay menciones → asignada al creador.
- Comandos de gestión de asignación:
- /t tomar <id> → el usuario se asigna la tarea.
- /t soltar <id> → elimina su asignación, devolviendo la tarea a “sin dueño” si no quedan asignados.
### Listados
- /t ver grupo → devuelve por DM las pendientes del grupo desde el que se invoca (incluye sección “sin dueño”).
- /t ver mis → devuelve por DM las pendientes del usuario agregadas de todos sus grupos.
- Listas extensas: mostrar top N (p. ej., 10) y resumen “y X más…”.
### Completar
- /t x <id> (alias: /t hecho <id>, /t completar <id>)
- Registro de quién completó. Por ahora no se restringe a asignados (permite fluidez); política configurable en el futuro.
- Confirmación solo por DM.
### Ayuda y onboarding
- “/t” sin parámetros o “ayuda” → siempre por DM, con guía corta y 23 ejemplos.
- En grupos: no se escribe nada en el grupo; únicamente el DM al autor.
### Mensajes: plantillas compactas
- Confirmación al crear (DM al creador):
- ✅ 26 “*Acta de la reunión*”
- 📅 12/09
- 👥 sin dueño (Junta AMPA) — o — 👤 @Juan
- DM a asignados:
- 🔔 Tarea 26 — 📅 12/09
- “*Acta de la reunión*”
- Grupo: Junta AMPA
- Completar: /t x 26
- Listado (enviado por DM):
- Junta AMPA
- 26) “*Acta…*” — 📅 12/09 — 👤 @Juan
- 27) “*Carteles fiesta*” — 📅 10/09 — 👥 sin dueño
- … y 3 más
- Completar (feedback por DM):
- ✔️ 26 completada — “*Acta…*”
- Gracias, Juan.
### Preferencias (MVP)
- Única preferencia: frecuencia de recordatorios por DM: daily | off.
- MVP sin web: el usuario escribe “configurar” por DM y el bot le ofrece elegir “diario” u “off”.
- Por defecto: off (evitar spam). Futuro: hora y zona horaria configurables; magic link a web de configuración.
### Recordatorios
- Resumen diario por DM (si el usuario eligió “diario”):
- Buenos días, Ana — hoy tienes 3 tareas:
- 26) “*Acta…*” — 12/09 — Junta AMPA
- 31) “*Pagar comedor*” — hoy — Casa
- 33) “*…*” — 15/09 — Casa
- Completar: /t x <id>
- Un solo DM con secciones por comunidad para evitar múltiples mensajes.
### No objetivos del MVP
- No asignar por defecto a “todo el grupo” (evita DMs masivos y responsabilidad difusa).
- No canal “Tareas” compartido por defecto (riesgo de ruido). Se considerará en el futuro solo si hay demanda y optin.
### Plan de implementación (iteraciones)
- Iteración A — UX base y silencios
- Alias de comandos y sinónimos en CommandService.
- Respuestas de todos los comandos únicamente por DM (incluido cuando se invocan en grupos).
- Mensajes compactos con plantillas.
- Soporte de “hoy/mañana”.
- Default sin dueño en grupos; asignar al creador en DMs.
- Nuevos comandos: tomar y soltar.
- Ayuda por DM.
- Iteración B — Listados y completar
- /t ver grupo, /t ver mis.
- /t x <id> con registro de quién completa.
- Tests para alias, hoy/mañana, ver y x.
- Iteración C — Recordatorios
- Preferencia reminder_freq (daily|off) por usuario via “configurar” por DM.
- Job diario que envía el resumen (solo “tus tareas” en MVP).
- Iteración D — (Opcional) Miembros de grupo
- Sincronizar miembros si se necesita incluir “sin dueño” por grupo en recordatorios.
### Cambios técnicos asociados (resumen)
- src/services/command.ts
- Mapeo de sinónimos a acciones canónicas.
- Parser de “hoy/mañana”.
- Subcomandos: ver grupo|mis, x, tomar, soltar.
- Render de mensajes compactos.
- src/server.ts
- Detección de contexto grupo vs DM; nunca responder en grupo (solo DM al autor).
- src/tasks/service.ts
- Permitir tareas sin asignaciones.
- Métodos: claimTask(user_id), unassignTask(user_id), completeTask(id, completed_by).
- src/services/response-queue.ts
- Envío de DMs para ayuda, confirmaciones, listados y recordatorios.
- (Futuro) Preferencias
- Tabla user_preferences(user_id PK, reminder_freq TEXT, updated_at).
- “configurar” por DM en MVP; más adelante, web con magic link (user_tokens).
### Testing sugerido
- Alias de comandos y “/t” sin parámetros → DM de ayuda.
- Crear en grupo sin menciones → sin dueño; no hay mensaje en el grupo; DM al autor.
- Crear en DM sin menciones → asignada al creador.
- “hoy/mañana” en fechas.
- ver grupo y ver mis → DM con paginación/resumen.
- completar, tomar y soltar → reglas y feedback por DM.