import logging import firebase_admin from firebase_admin import credentials, messaging from odoo.tools import config # Fetch the credential file path safely from odoo.conf firebase_key_path = config.get("firebase_private_key_path") _logger = logging.getLogger(__name__) # Initialize the app safely to prevent "app already exists" runtime crashes try: firebase_app = firebase_admin.get_app() except ValueError: if firebase_key_path: firebase_cred = credentials.Certificate(firebase_key_path) firebase_app = firebase_admin.initialize_app(firebase_cred) else: # Fallback to default application credentials if config key is missing firebase_app = firebase_admin.initialize_app() def send_firebase_notifications(messages: list) -> int: """Sends list of notification messages using Firebase Cloud Messaging. Expecting input layout: [ {'token': '...', 'body': '...', 'title': '...'}, ] """ if not messages: return 0 # Build the payload using native Firebase messaging objects fcm_messages = [] for msg in messages: token = msg.get("token") if not token: continue notification = messaging.Notification( title=msg.get("title", ""), body=msg.get("body", ""), ) # include optional data payload data_payload = msg.get("data") fcm_messages.append( messaging.Message( notification=notification, token=token, data=data_payload if isinstance(data_payload, dict) else None, ) ) if not fcm_messages: return 0 # Execute the batch transmission using the Firebase Admin SDK try: # The SDK / FCM backend limits batch sizes; be conservative and chunk (500 is a safe upper bound). success_total = 0 chunk_size = 500 for i in range(0, len(fcm_messages), chunk_size): batch = fcm_messages[i : i + chunk_size] # Try newer API first (send_all), fall back to send_multicast for older SDK versions if hasattr(messaging, 'send_all'): response = messaging.send_all(batch) _logger.info( "Firebase Success Count: %s", response.success_count ) _logger.info( "Firebase Failure Count: %s", response.failure_count ) for idx, resp in enumerate(response.responses): if not resp.success: _logger.error( "Firebase Error %s: %s", idx, resp.exception ) else: # Older firebase-admin SDK: use send_multicast from firebase_admin.messaging import MulticastMessage multicast = MulticastMessage(tokens=[m.token for m in batch if m.token]) # send_multicast doesn't support per-token custom payloads; use individual sends as fallback success_count = 0 for msg in batch: try: messaging.send(msg) success_count += 1 except Exception as e: _logger.debug("Failed to send message to token %s: %s", msg.token, e) response = type('obj', (object,), {'success_count': success_count})() sent = getattr(response, "success_count", 0) success_total += sent _logger.info("Firebase: sent %s/%s messages in chunk", sent, len(batch)) return success_total except Exception as exc: # catch firebase exceptions and log them _logger.exception("Failed to send firebase notifications: %s", exc) return 0