From 5dd3499097860c79b89e8aa682ba8e413e7c9c97 Mon Sep 17 00:00:00 2001 From: Claude AI Date: Fri, 30 Jan 2026 20:48:56 +0000 Subject: [PATCH] feat: Major WhatsApp integration update with Odoo and pause/resume ## Frontend - Add media display (images, audio, video, docs) in Inbox - Add pause/resume functionality for WhatsApp accounts - Fix media URLs to use nginx proxy (relative URLs) ## API Gateway - Add /accounts/:id/pause and /accounts/:id/resume endpoints - Fix media URL handling for browser access ## WhatsApp Core - Add pauseSession() - disconnect without logout - Add resumeSession() - reconnect using saved credentials - Add media download and storage for incoming messages - Serve media files via /media/ static route ## Odoo Module (odoo_whatsapp_hub) - Add Chat Hub interface with DOLLARS theme (dark, 3-column layout) - Add WhatsApp/DRRR theme switcher for chat view - Add "ABRIR CHAT" button in conversation form - Add send_message_from_chat() method - Add security/ir.model.access.csv - Fix CSS scoping to avoid breaking Odoo UI - Update webhook to handle message events properly ## Documentation - Add docs/CONTEXTO_DESARROLLO.md with complete project context ## Infrastructure - Add whatsapp_media Docker volume - Configure nginx proxy for /media/ route - Update .gitignore to track src/sessions/ source files Co-Authored-By: Claude Opus 4.5 --- .gitignore | 5 +- docker-compose.yml | 5 + docs/CONTEXTO_DESARROLLO.md | 344 ++++++++ frontend/nginx.conf | 14 +- frontend/src/pages/Inbox.tsx | 60 +- frontend/src/pages/WhatsAppAccounts.tsx | 63 +- odoo_whatsapp_hub/__manifest__.py | 9 +- odoo_whatsapp_hub/controllers/webhook.py | 24 +- odoo_whatsapp_hub/models/whatsapp_account.py | 8 +- .../models/whatsapp_conversation.py | 73 ++ odoo_whatsapp_hub/models/whatsapp_message.py | 20 +- .../security/ir.model.access.csv | 8 + .../static/src/css/dollars_theme.css | 774 ++++++++++++++++++ odoo_whatsapp_hub/static/src/css/whatsapp.css | 321 ++++++-- .../static/src/js/chat_action.js | 232 ++++++ .../static/src/js/chat_widget.js | 2 +- .../static/src/js/dollars_chat.js | 254 ++++++ .../static/src/xml/chat_template.xml | 138 ++++ .../static/src/xml/chat_widget.xml | 32 +- .../static/src/xml/dollars_template.xml | 282 +++++++ odoo_whatsapp_hub/views/dollars_action.xml | 9 + .../views/whatsapp_conversation_views.xml | 42 +- odoo_whatsapp_hub/views/whatsapp_menu.xml | 13 +- qr-realtime.html | 220 +++++ qr-viewer.html | 83 ++ services/api-gateway/app/core/config.py | 4 + services/api-gateway/app/routers/whatsapp.py | 345 +++++++- services/api-gateway/requirements.txt | 3 +- services/whatsapp-core/package.json | 4 +- services/whatsapp-core/src/api/routes.ts | 22 + services/whatsapp-core/src/index.ts | 11 +- .../src/sessions/SessionManager.ts | 319 ++++++++ services/whatsapp-core/src/sessions/types.ts | 31 + 33 files changed, 3636 insertions(+), 138 deletions(-) create mode 100644 docs/CONTEXTO_DESARROLLO.md create mode 100644 odoo_whatsapp_hub/security/ir.model.access.csv create mode 100644 odoo_whatsapp_hub/static/src/css/dollars_theme.css create mode 100644 odoo_whatsapp_hub/static/src/js/chat_action.js create mode 100644 odoo_whatsapp_hub/static/src/js/dollars_chat.js create mode 100644 odoo_whatsapp_hub/static/src/xml/chat_template.xml create mode 100644 odoo_whatsapp_hub/static/src/xml/dollars_template.xml create mode 100644 odoo_whatsapp_hub/views/dollars_action.xml create mode 100644 qr-realtime.html create mode 100644 qr-viewer.html create mode 100644 services/whatsapp-core/src/sessions/SessionManager.ts create mode 100644 services/whatsapp-core/src/sessions/types.ts diff --git a/.gitignore b/.gitignore index a7b627c..efa98fe 100644 --- a/.gitignore +++ b/.gitignore @@ -105,12 +105,13 @@ docker-compose.override.yml # ----------------------------------------------------------------------------- # Sessions / Data # ----------------------------------------------------------------------------- -sessions/ +/sessions/ *.session *.session.json -# WhatsApp Baileys sessions +# WhatsApp Baileys sessions (data, not source code) services/whatsapp-core/sessions/ +!services/whatsapp-core/src/sessions/ auth_info*/ # ----------------------------------------------------------------------------- diff --git a/docker-compose.yml b/docker-compose.yml index e4bcd10..9f3633d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,6 +51,7 @@ services: WS_PORT: 3001 volumes: - whatsapp_sessions:/app/sessions + - whatsapp_media:/app/media ports: - "3001:3001" depends_on: @@ -70,6 +71,9 @@ services: REDIS_URL: redis://redis:6379 JWT_SECRET: ${JWT_SECRET:?JWT_SECRET required} WHATSAPP_CORE_URL: http://whatsapp-core:3001 + WHATSAPP_CORE_PUBLIC_URL: ${WHATSAPP_CORE_PUBLIC_URL:-http://localhost:3001} + FLOW_ENGINE_URL: http://flow-engine:8001 + ODOO_WEBHOOK_URL: ${ODOO_WEBHOOK_URL:-http://192.168.10.188:8069/whatsapp/webhook} CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000} ports: - "8000:8000" @@ -139,6 +143,7 @@ volumes: postgres_data: redis_data: whatsapp_sessions: + whatsapp_media: networks: wac_network: diff --git a/docs/CONTEXTO_DESARROLLO.md b/docs/CONTEXTO_DESARROLLO.md new file mode 100644 index 0000000..e0489a0 --- /dev/null +++ b/docs/CONTEXTO_DESARROLLO.md @@ -0,0 +1,344 @@ +# WhatsApp Centralizado - Contexto de Desarrollo + +> **Fecha de última actualización:** 2026-01-30 +> **Estado:** En desarrollo activo + +--- + +## Resumen del Proyecto + +Sistema centralizado de WhatsApp para gestión de múltiples números, integrado con Odoo y un frontend React. Permite: +- Conectar múltiples números de WhatsApp vía QR +- Recibir y enviar mensajes (texto, imágenes, audio, video, documentos) +- Gestionar conversaciones desde frontend web o Odoo +- Automatizar respuestas con flujos de bot +- Pausar/reanudar conexiones sin perder sesión + +--- + +## Arquitectura del Sistema + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Frontend │────▶│ API Gateway │────▶│ WhatsApp Core │ +│ React + Ant │ │ FastAPI │ │ Node + Baileys │ +│ Puerto: 3000 │ │ Puerto: 8000 │ │ Puerto: 3001 │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ + ▼ │ + ┌─────────────────┐ │ + │ PostgreSQL │◀────────────┘ + │ Puerto: 5432 │ + └─────────────────┘ + │ + ▼ + ┌─────────────────┐ + │ Odoo (CAS) │◀── Webhooks + │ 192.168.10.188 │ + │ Puerto: 8069 │ + └─────────────────┘ +``` + +--- + +## Servicios Docker + +| Servicio | Puerto | Descripción | +|----------|--------|-------------| +| `frontend` | 3000 | React + Ant Design | +| `api-gateway` | 8000 | FastAPI - API principal | +| `whatsapp-core` | 3001 | Node.js + Baileys - Conexión WhatsApp | +| `flow-engine` | 8001 | Motor de flujos de bot | +| `integrations` | 8002 | Integraciones externas | +| `postgres` | 5432 | Base de datos PostgreSQL | +| `redis` | 6379 | Cache y colas | + +### Comandos Docker útiles + +```bash +# Reconstruir servicios +docker compose build whatsapp-core frontend api-gateway + +# Reiniciar servicios +docker compose up -d + +# Ver logs +docker compose logs -f whatsapp-core +docker compose logs -f api-gateway + +# Entrar a un contenedor +docker exec -it wac_whatsapp sh +docker exec -it wac_api bash +``` + +--- + +## Servidor Odoo + +- **IP:** 192.168.10.188 +- **Puerto:** 8069 +- **Base de datos:** `cas` (NO usar "odoo") +- **Usuario SSH:** root / Aasi940812 +- **Ruta módulo:** `/opt/odoo/addons/odoo_whatsapp_hub/` + +### Comandos Odoo útiles + +```bash +# Conectar por SSH +sshpass -p 'Aasi940812' ssh root@192.168.10.188 + +# Actualizar módulo +systemctl stop odoo +/usr/bin/odoo -c /etc/odoo/odoo.conf -d cas -u odoo_whatsapp_hub --stop-after-init +systemctl start odoo + +# Instalar módulo +/usr/bin/odoo -c /etc/odoo/odoo.conf -d cas -i odoo_whatsapp_hub --stop-after-init + +# Ver logs +tail -f /var/log/odoo/odoo-server.log + +# Consultas a PostgreSQL +sudo -u postgres psql -d cas -c "SELECT * FROM whatsapp_conversation;" +``` + +### Configuración WhatsApp Account en Odoo + +```sql +-- Ver cuentas +SELECT id, name, external_id, api_url FROM whatsapp_account; + +-- Configurar external_id (debe coincidir con el UUID del frontend) +UPDATE whatsapp_account +SET external_id = '33ce868e-1aa0-4795-9d44-a389e8ade0de', + api_url = 'http://192.168.10.221:8000' +WHERE id = 1; +``` + +--- + +## Módulo Odoo (odoo_whatsapp_hub) + +### Estructura + +``` +odoo_whatsapp_hub/ +├── __manifest__.py +├── __init__.py +├── models/ +│ ├── whatsapp_account.py # Cuentas WhatsApp +│ ├── whatsapp_conversation.py # Conversaciones +│ └── whatsapp_message.py # Mensajes +├── controllers/ +│ └── webhook.py # Recibe eventos del sistema +├── views/ +│ ├── whatsapp_account_views.xml +│ ├── whatsapp_conversation_views.xml +│ ├── dollars_action.xml # Interfaz DOLLARS +│ └── whatsapp_menu.xml +├── wizards/ +│ └── send_whatsapp_wizard.xml +├── static/src/ +│ ├── css/ +│ │ ├── whatsapp.css # Estilos WhatsApp/DRRR +│ │ └── dollars_theme.css # Tema DOLLARS oscuro +│ ├── js/ +│ │ ├── chat_action.js # Chat con temas +│ │ └── dollars_chat.js # Chat Hub DOLLARS +│ └── xml/ +│ ├── chat_template.xml +│ └── dollars_template.xml +└── security/ + └── ir.model.access.csv +``` + +### Menú en Odoo + +- **WhatsApp Hub > Chat Hub** - Interfaz DOLLARS (oscura, 3 columnas) +- **WhatsApp Hub > Conversaciones (Lista)** - Vista clásica de Odoo +- **WhatsApp Hub > Cuentas** - Gestión de números +- **WhatsApp Hub > Configuración** - Ajustes + +### Webhook + +``` +POST http://192.168.10.188:8069/whatsapp/webhook +Header: X-Odoo-Database: cas +Header: Content-Type: application/json + +Body: +{ + "type": "message", // message, status_update, conversation_update, account_status + "account_id": "uuid-de-la-cuenta", + "data": { ... } +} +``` + +--- + +## Frontend React + +### Estructura principal + +``` +frontend/src/ +├── pages/ +│ ├── WhatsAppAccounts.tsx # Gestión de números (pausar/reanudar) +│ ├── Inbox.tsx # Bandeja de entrada +│ ├── Dashboard.tsx +│ └── ... +├── api/ +│ └── client.ts # Cliente API +└── App.tsx +``` + +### Funcionalidades implementadas + +1. **Gestión de cuentas WhatsApp** + - Crear cuenta y escanear QR + - Pausar conexión (mantiene sesión) + - Reanudar conexión (sin QR) + - Eliminar cuenta + +2. **Bandeja de entrada (Inbox)** + - Lista de conversaciones + - Visualización de mensajes + - Envío de mensajes + - Soporte para imágenes, audio, video, documentos + +### Proxy Nginx para media + +```nginx +location /media/ { + proxy_pass http://whatsapp-core:3001/media/; + proxy_set_header Host $host; +} +``` + +--- + +## API Endpoints Principales + +### WhatsApp Accounts + +``` +GET /api/whatsapp/accounts # Listar cuentas +POST /api/whatsapp/accounts # Crear cuenta +GET /api/whatsapp/accounts/:id # Obtener cuenta +DELETE /api/whatsapp/accounts/:id # Eliminar cuenta +POST /api/whatsapp/accounts/:id/pause # Pausar conexión +POST /api/whatsapp/accounts/:id/resume # Reanudar conexión +``` + +### Conversaciones y Mensajes + +``` +GET /api/whatsapp/conversations # Listar conversaciones +GET /api/whatsapp/conversations/:id # Detalle con mensajes +POST /api/whatsapp/conversations/:id/send # Enviar mensaje +``` + +### WhatsApp Core (interno) + +``` +POST /api/sessions # Crear sesión +GET /api/sessions/:id # Info de sesión +POST /api/sessions/:id/disconnect # Cerrar sesión (logout) +POST /api/sessions/:id/pause # Pausar (sin logout) +POST /api/sessions/:id/resume # Reanudar +POST /api/sessions/:id/messages # Enviar mensaje +``` + +--- + +## Temas de Chat en Odoo + +### Tema WhatsApp (clásico) +- Fondo beige con patrón +- Burbujas verdes (salientes) y blancas (entrantes) +- Estilo familiar de WhatsApp + +### Tema DRRR (Durarara/Dollars) +- Fondo negro (#0a0a0a) +- Texto verde neón (#00ff88) para salientes +- Texto cyan (#00ccff) para entrantes +- Nombres en corchetes [Usuario] +- Fuente monospace + +### Tema DOLLARS (Chat Hub) +- Interfaz oscura 3 columnas +- Sidebar con lista de conversaciones +- Chat central +- Panel de detalles a la derecha +- Acentos ámbar/naranja (#f59e0b) + +--- + +## Problemas Conocidos y Soluciones + +### 1. Imágenes no se ven en frontend +**Causa:** URLs absolutas con hostname Docker interno +**Solución:** Usar URLs relativas (`/media/uuid.jpg`) + proxy nginx + +### 2. Odoo no muestra cambios +**Causa:** Base de datos incorrecta (odoo vs cas) +**Solución:** Siempre usar `-d cas` y header `X-Odoo-Database: cas` + +### 3. CSS rompe todo Odoo +**Causa:** Estilos globales no encapsulados +**Solución:** Prefijar todo con `.o_dollars_chat` o `.o_whatsapp_chat_fullscreen` + +### 4. Webhook retorna 404 +**Causa:** Falta header de base de datos +**Solución:** Agregar `X-Odoo-Database: cas` a todas las peticiones + +### 5. Account not found en webhook +**Causa:** `external_id` no configurado en Odoo +**Solución:** Actualizar tabla whatsapp_account con el UUID correcto + +--- + +## Próximos Pasos Sugeridos + +1. **Testing de pausar/reanudar** - Verificar que funciona correctamente +2. **Notificaciones en tiempo real** - WebSocket para nuevos mensajes +3. **Mejoras al Chat Hub DOLLARS** - Indicador de typing, scroll automático +4. **Integración completa Odoo** - Sincronizar contactos con res.partner +5. **Panel de métricas** - Dashboard con estadísticas de uso + +--- + +## Credenciales y Accesos + +| Servicio | URL | Usuario | Contraseña | +|----------|-----|---------|------------| +| Frontend | http://192.168.10.221:3000 | admin | (ver BD) | +| API Gateway | http://192.168.10.221:8000 | - | - | +| Odoo | http://192.168.10.188:8069 | ialcarazsalazar@consultoria-as.com | (conocida) | +| SSH Odoo | 192.168.10.188:22 | root | Aasi940812 | +| Gitea | https://git.consultoria-as.com | consultoria-as | (token en remote) | + +--- + +## Comandos de Desarrollo + +```bash +# Clonar repositorio +git clone https://git.consultoria-as.com/consultoria-as/WhatsAppCentralizado.git + +# Levantar todo +cd WhatsAppCentralizado +docker compose up -d + +# Desplegar módulo Odoo +sshpass -p 'Aasi940812' scp -r odoo_whatsapp_hub root@192.168.10.188:/opt/odoo/addons/ +sshpass -p 'Aasi940812' ssh root@192.168.10.188 "systemctl stop odoo && /usr/bin/odoo -c /etc/odoo/odoo.conf -d cas -u odoo_whatsapp_hub --stop-after-init && systemctl start odoo" + +# Ver estado +docker compose ps +docker compose logs -f +``` + +--- + +*Documento generado automáticamente. Última sesión de trabajo: 2026-01-30* diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 53ca5fb..7c1293a 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -8,21 +8,23 @@ server { try_files $uri $uri/ /index.html; } - location /api { + location /api/ { + rewrite ^/api/(.*) /$1 break; proxy_pass http://api-gateway:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } - location /auth { - proxy_pass http://api-gateway:8000; - proxy_set_header Host $host; - } - location /ws { proxy_pass http://whatsapp-core:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } + + location /media/ { + proxy_pass http://whatsapp-core:3001/media/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } } diff --git a/frontend/src/pages/Inbox.tsx b/frontend/src/pages/Inbox.tsx index 47d4d89..80b316c 100644 --- a/frontend/src/pages/Inbox.tsx +++ b/frontend/src/pages/Inbox.tsx @@ -49,6 +49,7 @@ interface Message { direction: 'inbound' | 'outbound'; type: string; content: string | null; + media_url: string | null; created_at: string; is_internal_note: boolean; sent_by: string | null; @@ -368,6 +369,63 @@ export default function Inbox(): JSX.Element { ); } + function renderMessageContent(msg: Message): JSX.Element { + // Render media based on type + if (msg.media_url) { + const mediaType = msg.type?.toUpperCase(); + + if (mediaType === 'IMAGE') { + return ( + <> + Imagen window.open(msg.media_url!, '_blank')} + /> + {msg.content && msg.content !== '[Image]' && ( + {msg.content} + )} + + ); + } + + if (mediaType === 'AUDIO') { + return ( + + ); + } + + if (mediaType === 'VIDEO') { + return ( + + ); + } + + if (mediaType === 'DOCUMENT') { + return ( + + 📄 {msg.content || 'Documento'} + + ); + } + } + + // Default text content + return {msg.content}; + } + function renderMessage(msg: Message): JSX.Element { return (
Nota interna )} - {msg.content} + {renderMessageContent(msg)}
{ + await apiClient.post(`/api/whatsapp/accounts/${id}/pause`); + }, + onSuccess: () => { + message.success('Conexión pausada'); + queryClient.invalidateQueries({ queryKey: ['whatsapp-accounts'] }); + }, + onError: () => { + message.error('Error al pausar'); + }, + }); + + const resumeMutation = useMutation({ + mutationFn: async (id: string) => { + await apiClient.post(`/api/whatsapp/accounts/${id}/resume`); + }, + onSuccess: () => { + message.success('Reconectando...'); + queryClient.invalidateQueries({ queryKey: ['whatsapp-accounts'] }); + }, + onError: () => { + message.error('Error al reanudar'); + }, + }); + const handleShowQR = async (account: WhatsAppAccount) => { const data = await apiClient.get(`/api/whatsapp/accounts/${account.id}`); setQrModal(data); @@ -102,13 +130,15 @@ export default function WhatsAppAccounts() { connected: 'green', connecting: 'orange', disconnected: 'red', + paused: 'gold', }; const labels: Record = { connected: 'Conectado', connecting: 'Conectando', disconnected: 'Desconectado', + paused: 'Pausado', }; - return {labels[status]}; + return {labels[status] || status}; }, }, { @@ -116,7 +146,7 @@ export default function WhatsAppAccounts() { key: 'actions', render: (_: any, record: WhatsAppAccount) => ( - {record.status !== 'connected' && ( + {record.status !== 'connected' && record.status !== 'paused' && ( )} + {record.status === 'connected' && ( + + )} + {(record.status === 'paused' || record.status === 'disconnected') && ( + + )} +
+ + + +
+
+
+ +
+
+ +
+
+ + + + + +
+ +
+ +

+ + [SYSTEM] Conectando al servidor... + + + Cargando mensajes... + +

+
+
+ + +
+ +

[SYSTEM] No hay mensajes en este chat

+

[SYSTEM] Escribe algo para comenzar...

+
+ + +

No hay mensajes aún

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + Documento + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+ + +
+
+ + diff --git a/odoo_whatsapp_hub/static/src/xml/chat_widget.xml b/odoo_whatsapp_hub/static/src/xml/chat_widget.xml index f4518e2..ceec03f 100644 --- a/odoo_whatsapp_hub/static/src/xml/chat_widget.xml +++ b/odoo_whatsapp_hub/static/src/xml/chat_widget.xml @@ -23,7 +23,37 @@
-
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + +
diff --git a/odoo_whatsapp_hub/static/src/xml/dollars_template.xml b/odoo_whatsapp_hub/static/src/xml/dollars_template.xml new file mode 100644 index 0000000..89368bd --- /dev/null +++ b/odoo_whatsapp_hub/static/src/xml/dollars_template.xml @@ -0,0 +1,282 @@ + + + +
+ +
+ + +
+
+ + Conectado +
+
+ + Cifrado E2E +
+
+ + chats +
+
+ +
+ +
+
+ + +
+ +
+
+ +
+ +
+ +
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
+
+
+ + + + + + + + +
+
+
+ +
+
+ +
+
No hay conversaciones
+
+
+
+
+
+ + +
+ + +
+
+ +
+
+
+ +
+
+ + + + + +
+
+
+ + + +
+
+ + +
+ +
+
+ + + + + + +
+
+
+ + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + Documento + + + + + + + +
+ +
+ +
+
+
+
+
+
+
+ + +
+
+