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