backend-push-notifications/src/send_notification.py
Florian b272e069d4 Unified logging behaviour
- Logger doesn't start with log level DEBUG by default, instead reads a environment variable
- Added extensive debug logging
- Wrote a readme
- Changed database healthcheck loop to only catch pymsql errors
2025-10-17 10:58:37 +02:00

73 lines
2.4 KiB
Python

import aiohttp
import aiodns
import asyncio
from logger_handler import setup_logger
API_ENDPOINT="https://exp.host/--/api/v2/push/send"
logger = setup_logger(__name__)
retryable_exceptions = (
aiohttp.ClientConnectionError,
aiohttp.ServerDisconnectedError,
aiohttp.ClientOSError,
aiohttp.ServerTimeoutError,
asyncio.TimeoutError,
)
async def send_notification(message: dict, push_tokens, max_retries: int = 5, timeout: int = 5):
logger.debug(f"[Notification] Sending to {len(push_tokens)} push tokens")
results = {}
for uuid, token in push_tokens.items():
logger.debug(f"[Notification] Preparing to send to uuid={uuid}")
results[uuid] = await _send_to_token(token, uuid, message, max_retries, timeout)
return results
async def _send_to_token(token: str, uuid:str , message: dict, max_retries: int, timeout: int):
payload = create_payload(token, message)
logger.debug(f"[Notification] Payload for uuid={uuid}: {payload}")
resolver = aiohttp.AsyncResolver()
connector = aiohttp.TCPConnector(resolver=resolver)
for attempt in range(1, max_retries + 1):
try:
async with aiohttp.ClientSession(connector=connector) as session:
async with session.post(
url=API_ENDPOINT,
json=payload,
headers={"Content-Type": "application/json"},
timeout=timeout
) as response:
response.raise_for_status()
data = await response.json()
logger.info(f"Notification sent successfully to uuid {uuid}")
return {"status":"ok","data":data}
except retryable_exceptions as e:
logger.warning(f"[Notification] Attempt {attempt}/{max_retries} failed for uuid={uuid}: {type(e).__name__}: {e}", exc_info=True)
await asyncio.sleep(2 ** (attempt - 1))
logger.debug(f"[Notification] Retrying uuid={uuid}, attempt {attempt + 1}")
except Exception as e:
logger.error(f"[Notification] Unexpected failure for uuid={uuid}: {e}", exc_info=True)
return {"status": "error", "exception": str(e)}
logger.error(f"Failed to send notification to uuid {uuid} after {max_retries} attempts")
return {"status": "failure"}
def create_payload(push_token: str, message: dict) -> dict:
payload = {
"to": push_token,
"title": message.get("title"),
"body": message.get("body"),
"data": {
"link": message.get("link"),
"category": message.get("category"),
"timestamp": message.get("timestamp")
},
"sound": "default",
"priority": "high"
}
logger.debug(f"[Notification] Created payload: {payload}")
return payload