import os import json from typing import Dict, List, Optional import firebase_admin from firebase_admin import credentials, messaging from app.models.user import User from app.db.session import SessionLocal class PushNotificationService: def __init__(self): self.firebase_app = None self._initialize_firebase() def _initialize_firebase(self): """Initialize Firebase Admin SDK""" try: # Check if Firebase credentials are available firebase_cred_path = os.getenv("FIREBASE_CREDENTIALS_PATH") firebase_cred_json = os.getenv("FIREBASE_CREDENTIALS_JSON") if firebase_cred_path and os.path.exists(firebase_cred_path): cred = credentials.Certificate(firebase_cred_path) self.firebase_app = firebase_admin.initialize_app(cred) elif firebase_cred_json: cred_dict = json.loads(firebase_cred_json) cred = credentials.Certificate(cred_dict) self.firebase_app = firebase_admin.initialize_app(cred) else: print("Firebase credentials not found. Push notifications will be disabled.") self.firebase_app = None except Exception as e: print(f"Failed to initialize Firebase: {e}") self.firebase_app = None async def send_push_notification( self, device_token: str, title: str, body: str, data: Optional[Dict[str, str]] = None ) -> bool: """Send push notification to a device""" if not self.firebase_app or not device_token: return False try: # Create message message = messaging.Message( notification=messaging.Notification( title=title, body=body ), data=data or {}, token=device_token, android=messaging.AndroidConfig( notification=messaging.AndroidNotification( icon="ic_notification", color="#2196F3", sound="default", channel_id="chat_messages" ), priority="high" ), apns=messaging.APNSConfig( payload=messaging.APNSPayload( aps=messaging.Aps( alert=messaging.ApsAlert( title=title, body=body ), badge=1, sound="default" ) ) ) ) # Send message response = messaging.send(message) print(f"Successfully sent message: {response}") return True except Exception as e: print(f"Failed to send push notification: {e}") return False async def send_message_notification( self, recipient_user_id: int, sender_username: str, message_content: str, chat_id: int, chat_name: Optional[str] = None ): """Send push notification for new message""" db = SessionLocal() try: user = db.query(User).filter(User.id == recipient_user_id).first() if not user or not user.device_token: return title = f"New message from {sender_username}" if chat_name: title = f"New message in {chat_name}" body = message_content[:100] if len(message_content) > 100: body += "..." data = { "type": "message", "chat_id": str(chat_id), "sender_username": sender_username, "chat_name": chat_name or "" } await self.send_push_notification( device_token=user.device_token, title=title, body=body, data=data ) finally: db.close() async def send_mention_notification( self, mentioned_user_id: int, sender_username: str, message_content: str, chat_id: int, chat_name: Optional[str] = None ): """Send push notification for mention""" db = SessionLocal() try: user = db.query(User).filter(User.id == mentioned_user_id).first() if not user or not user.device_token: return title = f"You were mentioned by {sender_username}" if chat_name: title = f"Mentioned in {chat_name}" body = message_content[:100] if len(message_content) > 100: body += "..." data = { "type": "mention", "chat_id": str(chat_id), "sender_username": sender_username, "chat_name": chat_name or "" } await self.send_push_notification( device_token=user.device_token, title=title, body=body, data=data ) finally: db.close() async def send_group_invite_notification( self, invited_user_id: int, inviter_username: str, group_name: str, chat_id: int ): """Send push notification for group invitation""" db = SessionLocal() try: user = db.query(User).filter(User.id == invited_user_id).first() if not user or not user.device_token: return title = f"Invited to {group_name}" body = f"{inviter_username} invited you to join {group_name}" data = { "type": "group_invite", "chat_id": str(chat_id), "inviter_username": inviter_username, "group_name": group_name } await self.send_push_notification( device_token=user.device_token, title=title, body=body, data=data ) finally: db.close() async def send_bulk_notifications( self, tokens_and_messages: List[Dict] ) -> Dict[str, int]: """Send multiple notifications at once""" if not self.firebase_app: return {"success": 0, "failure": len(tokens_and_messages)} messages = [] for item in tokens_and_messages: message = messaging.Message( notification=messaging.Notification( title=item["title"], body=item["body"] ), data=item.get("data", {}), token=item["token"] ) messages.append(message) try: response = messaging.send_all(messages) return { "success": response.success_count, "failure": response.failure_count } except Exception as e: print(f"Failed to send bulk notifications: {e}") return {"success": 0, "failure": len(tokens_and_messages)} def validate_device_token(self, device_token: str) -> bool: """Validate if device token is valid""" if not self.firebase_app or not device_token: return False try: # Try to send a test message (dry run) message = messaging.Message( notification=messaging.Notification( title="Test", body="Test" ), token=device_token ) # Dry run - doesn't actually send messaging.send(message, dry_run=True) return True except Exception: return False # Global push notification service instance push_notification_service = PushNotificationService()