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'})