from __future__ import annotations import json import logging import httpx from server.settings import settings logger = logging.getLogger(__name__) def _publish_url() -> str | None: if not settings.ntfy_url or not settings.ntfy_topic: return None return f"{settings.ntfy_url.rstrip('/')}/{settings.ntfy_topic}" def _click_base() -> str: return settings.ntfy_click_url.rstrip("/") async def send_slot_reminder( slot_id: str, title: str, body: str, *, silent: bool = True, ) -> int: url = _publish_url() if not url: logger.warning( "Notify skipped slot=%s reason=ntfy_not_configured (NTFY_URL + NTFY_TOPIC)", slot_id, ) return 0 click_base = _click_base() headers: dict[str, str] = { "Title": title, "Click": f"{click_base}/?slot={slot_id}", "Priority": "2" if silent else "3", "Tags": "pill", "Actions": json.dumps( [ { "action": "view", "label": "Genommen ✓", "url": f"{click_base}/?action=take&slot={slot_id}", }, { "action": "view", "label": "+15 Min", "url": f"{click_base}/?action=snooze&slot={slot_id}&minutes=15", }, { "action": "view", "label": "+30 Min", "url": f"{click_base}/?action=snooze&slot={slot_id}&minutes=30", }, ] ), } if settings.ntfy_token: headers["Authorization"] = f"Bearer {settings.ntfy_token}" logger.info("Notify start slot=%s title=%r topic=%s", slot_id, title, settings.ntfy_topic) try: async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.post(url, content=body.encode("utf-8"), headers=headers) resp.raise_for_status() except httpx.HTTPError as exc: logger.warning("Notify failed slot=%s error=%s", slot_id, exc) return 0 logger.info("Notify ok slot=%s", slot_id) return 1