odoo18/addons_extensions/firebase_integration/tools/firebase.py

110 lines
3.8 KiB
Python

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