"""Ntfy notification helpers — send meal reminders with action buttons.""" from __future__ import annotations import json import urllib.request import urllib.error from datetime import date def _ntfy_headers(token: str) -> dict[str, str]: """Build Authorization header if token is provided.""" headers = {"Content-Type": "application/json"} if token.strip(): headers["Authorization"] = f"Bearer {token.strip()}" return headers def send_meal_reminder( ntfy_url: str, ntfy_token: str, callback_token: str, meal_type: str, for_date: date, app_base_url: str, ) -> bool: """Send a meal reminder notification with Home/Out action buttons. Args: ntfy_url: Full topic URL (e.g. https://ntfy.sh/mytopic). ntfy_token: Bearer token for auth (empty for public topics). callback_token: Per-user token for the webhook callback. meal_type: 'lunch' or 'dinner'. for_date: The meal date. app_base_url: Base URL of this app (e.g. https://meals.ct.cozytren.ch). Returns: True if the notification was sent successfully. """ date_str = for_date.isoformat() meal_label = "Lunch" if meal_type == "lunch" else "Dinner" emoji = "🌤" if meal_type == "lunch" else "🌙" callback_url = f"{app_base_url.rstrip('/')}/api/ntfy-callback" payload = { "topic": ntfy_url.rstrip("/").rsplit("/", 1)[-1], "title": f"{emoji} {meal_label} — Meal Reminder", "message": f"Will you be home for {meal_label.lower()} on {date_str}?", "priority": 4, "tags": ["plate_with_cutlery"], "actions": [ { "action": "http", "label": "🏠 Home", "method": "POST", "url": callback_url, "headers": {"Content-Type": "application/json"}, "body": json.dumps({ "token": callback_token, "meal_type": meal_type, "date": date_str, "status": "yes", }), }, { "action": "http", "label": "🍽 Out", "method": "POST", "url": callback_url, "headers": {"Content-Type": "application/json"}, "body": json.dumps({ "token": callback_token, "meal_type": meal_type, "date": date_str, "status": "no", }), }, ], "click": f"{app_base_url.rstrip('/')}/dashboard?date={date_str}", } data = json.dumps(payload).encode("utf-8") req = urllib.request.Request(ntfy_url, data=data, headers=_ntfy_headers(ntfy_token)) try: with urllib.request.urlopen(req, timeout=10) as resp: return 200 <= resp.status < 300 except urllib.error.URLError as e: print(f"[ntfy] Failed to send to {ntfy_url}: {e}") return False def send_test_notification(ntfy_url: str, ntfy_token: str) -> bool: """Send a simple test notification to verify ntfy configuration.""" payload = { "topic": ntfy_url.rstrip("/").rsplit("/", 1)[-1], "title": "✅ Meal Tracker — Test", "message": "Your ntfy integration is working! You'll receive meal reminders here.", "priority": 3, "tags": ["white_check_mark"], } data = json.dumps(payload).encode("utf-8") req = urllib.request.Request(ntfy_url, data=data, headers=_ntfy_headers(ntfy_token)) try: with urllib.request.urlopen(req, timeout=10) as resp: return 200 <= resp.status < 300 except urllib.error.URLError as e: print(f"[ntfy] Test failed for {ntfy_url}: {e}") return False