# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) import json import logging from odoo import fields, http from odoo.http import request, Response _logger = logging.getLogger(__name__) class RcWebhookController(http.Controller): @http.route('/ringcentral/webhook', type='json', auth='none', csrf=False, methods=['POST']) def webhook_handler(self, **kw): """Receive webhook events from RingCentral.""" headers = request.httprequest.headers validation_token = headers.get('Validation-Token') if validation_token: return Response( status=200, headers={'Validation-Token': validation_token}, ) try: body = json.loads(request.httprequest.get_data(as_text=True)) except (json.JSONDecodeError, TypeError): _logger.warning("RingCentral webhook: invalid JSON body") return {'status': 'error', 'message': 'Invalid JSON'} event = body.get('event', '') _logger.info("RingCentral webhook event: %s", event) if 'telephony/sessions' in event: self._handle_telephony_event(body) return {'status': 'ok'} def _handle_telephony_event(self, body): """Process a telephony session event.""" try: event_body = body.get('body', {}) parties = event_body.get('parties', []) for party in parties: status_code = party.get('status', {}).get('code', '') if status_code != 'Disconnected': continue direction_raw = party.get('direction', '').lower() direction = 'inbound' if direction_raw == 'inbound' else 'outbound' from_info = party.get('from', {}) to_info = party.get('to', {}) from_number = from_info.get('phoneNumber', '') to_number = to_info.get('phoneNumber', '') session_id = event_body.get('sessionId', '') if not session_id: continue env = request.env(su=True) existing = env['rc.call.history'].search_count([ ('rc_session_id', '=', str(session_id)), ]) if existing: continue duration = party.get('duration', 0) result = party.get('status', {}).get('reason', 'Unknown') status_map = { 'Answered': 'answered', 'CallConnected': 'answered', 'HangUp': 'answered', 'Voicemail': 'voicemail', 'Missed': 'missed', 'NoAnswer': 'no_answer', 'Busy': 'busy', 'Rejected': 'rejected', } status = status_map.get(result, 'answered' if duration > 0 else 'missed') env['rc.call.history'].create({ 'rc_session_id': str(session_id), 'direction': direction, 'from_number': from_number, 'to_number': to_number, 'start_time': fields.Datetime.now(), 'duration': duration, 'status': status, }) except Exception: _logger.exception("Error processing telephony webhook event")