Files
WhatsAppCentralizado/odoo_whatsapp_hub/controllers/webhook.py
Claude AI 5dd3499097 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 <noreply@anthropic.com>
2026-01-30 20:48:56 +00:00

145 lines
5.0 KiB
Python

from odoo import http
from odoo.http import request
import json
import logging
_logger = logging.getLogger(__name__)
class WhatsAppWebhookController(http.Controller):
@http.route('/whatsapp/webhook', type='http', auth='public', methods=['POST'], csrf=False)
def webhook(self, **kwargs):
"""
Receive webhooks from WhatsApp Central.
Events: message, status_update, conversation_update
"""
try:
# Parse JSON from request body
data = json.loads(request.httprequest.data.decode('utf-8'))
event_type = data.get('type')
_logger.info(f'WhatsApp webhook received: {event_type}')
handlers = {
'message': self._handle_message,
'status_update': self._handle_status_update,
'conversation_update': self._handle_conversation_update,
'account_status': self._handle_account_status,
}
handler = handlers.get(event_type)
if handler:
result = handler(data)
return request.make_json_response(result)
return request.make_json_response({'status': 'ignored', 'reason': f'Unknown event type: {event_type}'})
except Exception as e:
_logger.error(f'WhatsApp webhook error: {e}')
return request.make_json_response({'status': 'error', 'message': str(e)})
def _handle_message(self, data):
"""Handle incoming message"""
msg_data = data.get('data', {})
account_external_id = data.get('account_id')
conversation_external_id = msg_data.get('conversation_id')
account = request.env['whatsapp.account'].sudo().search([
('external_id', '=', account_external_id)
], limit=1)
if not account:
return {'status': 'ignored', 'reason': 'Account not found'}
conversation = request.env['whatsapp.conversation'].sudo().search([
('external_id', '=', conversation_external_id)
], limit=1)
if not conversation:
phone = msg_data.get('from', '').split('@')[0]
conversation = request.env['whatsapp.conversation'].sudo().create({
'external_id': conversation_external_id,
'account_id': account.id,
'phone_number': phone,
'contact_name': msg_data.get('contact_name'),
'status': 'bot',
})
# Try to find partner by phone
partner = request.env['res.partner'].sudo().search([
('phone', 'ilike', phone[-10:]),
], limit=1)
if partner:
conversation.partner_id = partner
# Get direction from webhook data (inbound or outbound)
direction = msg_data.get('direction', 'inbound')
request.env['whatsapp.message'].sudo().create({
'external_id': msg_data.get('id'),
'conversation_id': conversation.id,
'direction': direction,
'message_type': msg_data.get('type', 'text'),
'content': msg_data.get('content'),
'media_url': msg_data.get('media_url'),
'status': 'delivered' if direction == 'inbound' else 'sent',
})
return {'status': 'ok'}
def _handle_status_update(self, data):
"""Handle message status update"""
msg_data = data.get('data', {})
external_id = msg_data.get('message_id')
new_status = msg_data.get('status')
message = request.env['whatsapp.message'].sudo().search([
('external_id', '=', external_id)
], limit=1)
if message:
message.write({'status': new_status})
return {'status': 'ok'}
def _handle_conversation_update(self, data):
"""Handle conversation status update"""
conv_data = data.get('data', {})
external_id = conv_data.get('conversation_id')
new_status = conv_data.get('status')
conversation = request.env['whatsapp.conversation'].sudo().search([
('external_id', '=', external_id)
], limit=1)
if conversation:
conversation.write({'status': new_status})
return {'status': 'ok'}
def _handle_account_status(self, data):
"""Handle account status change"""
acc_data = data.get('data', {})
external_id = data.get('account_id')
new_status = acc_data.get('status')
account = request.env['whatsapp.account'].sudo().search([
('external_id', '=', external_id)
], limit=1)
if account:
account.write({
'status': new_status,
'phone_number': acc_data.get('phone_number'),
'qr_code': acc_data.get('qr_code'),
})
return {'status': 'ok'}
@http.route('/whatsapp/webhook/test', type='http', auth='public', methods=['GET'])
def webhook_test(self):
"""Test endpoint to verify webhook connectivity"""
return request.make_json_response({'status': 'ok', 'message': 'WhatsApp webhook is active'})