personaltaskmanagementapi-l.../app/services/push_notification_service.py
Automated Action 41bbd8c182 Build comprehensive real-time chat API with advanced features
Complete rewrite from task management to full-featured chat system:

Core Features:
- Real-time WebSocket messaging with connection management
- Direct messages and group chats with admin controls
- Message types: text, images, videos, audio, documents
- Message status tracking: sent, delivered, read receipts
- Typing indicators and user presence (online/offline)
- Message replies, editing, and deletion

Security & Encryption:
- End-to-end encryption with RSA + AES hybrid approach
- JWT authentication for API and WebSocket connections
- Secure file storage with access control
- Automatic RSA key pair generation per user

Media & File Sharing:
- Multi-format file upload (images, videos, audio, documents)
- Automatic thumbnail generation for images/videos
- File size validation and MIME type checking
- Secure download endpoints with permission checks

Notifications & Alerts:
- Real-time WebSocket notifications
- Push notifications via Firebase integration
- @username mention alerts with notification history
- Unread message and mention counting
- Custom notification types (message, mention, group invite)

Advanced Features:
- Group chat management with roles (member, admin, owner)
- User search and chat member management
- Message pagination and chat history
- Last seen timestamps and activity tracking
- Comprehensive API documentation with WebSocket events

Architecture:
- Clean layered architecture with services, models, schemas
- WebSocket connection manager for real-time features
- Modular notification system with multiple channels
- Comprehensive error handling and validation
- Production-ready with Docker support

Technologies: FastAPI, WebSocket, SQLAlchemy, SQLite, Cryptography, Firebase, Pillow
2025-06-21 16:49:25 +00:00

255 lines
8.3 KiB
Python

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()