import asyncio import logging import os from typing import Dict import httpx from .reminders import close_redis, pop_due_reminders logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") logger = logging.getLogger("daarion-reminder-worker") POLL_SECONDS = float(os.getenv("DAARION_REMINDER_POLL_SECONDS", "2")) TELEGRAM_TIMEOUT = float(os.getenv("DAARION_REMINDER_TELEGRAM_TIMEOUT", "20")) AGENT_TOKEN_ENV: Dict[str, str] = { "daarwizz": "DAARWIZZ_TELEGRAM_BOT_TOKEN", "helion": "HELION_TELEGRAM_BOT_TOKEN", "greenfood": "GREENFOOD_TELEGRAM_BOT_TOKEN", "agromatrix": "AGROMATRIX_TELEGRAM_BOT_TOKEN", "alateya": "ALATEYA_TELEGRAM_BOT_TOKEN", "nutra": "NUTRA_TELEGRAM_BOT_TOKEN", "druid": "DRUID_TELEGRAM_BOT_TOKEN", "clan": "CLAN_TELEGRAM_BOT_TOKEN", "eonarch": "EONARCH_TELEGRAM_BOT_TOKEN", "senpai": "SENPAI_TELEGRAM_BOT_TOKEN", "oneok": "ONEOK_TELEGRAM_BOT_TOKEN", "soul": "SOUL_TELEGRAM_BOT_TOKEN", "yaromir": "YAROMIR_TELEGRAM_BOT_TOKEN", "sofiia": "SOFIIA_TELEGRAM_BOT_TOKEN", } def _token_for_agent(agent_id: str) -> str: env = AGENT_TOKEN_ENV.get((agent_id or "").lower(), "") return os.getenv(env, "") if env else "" async def _send_reminder(item: Dict[str, str]) -> bool: agent_id = str(item.get("agent_id", "")) chat_id = str(item.get("chat_id", "")) reminder_text = str(item.get("text", "")).strip() due_at = str(item.get("due_at", "")) token = _token_for_agent(agent_id) if not token: logger.warning("reminder_skip_no_token agent=%s reminder_id=%s", agent_id, item.get("reminder_id")) return False if not chat_id or not reminder_text: logger.warning("reminder_skip_invalid_payload reminder_id=%s", item.get("reminder_id")) return False body = { "chat_id": chat_id, "text": f"⏰ Нагадування ({agent_id})\n\n{reminder_text}\n\n🕒 {due_at}", } url = f"https://api.telegram.org/bot{token}/sendMessage" async with httpx.AsyncClient(timeout=TELEGRAM_TIMEOUT) as client: resp = await client.post(url, json=body) if resp.status_code != 200: logger.warning( "reminder_send_failed reminder_id=%s status=%s body=%s", item.get("reminder_id"), resp.status_code, resp.text[:300], ) return False logger.info("reminder_sent reminder_id=%s agent=%s chat=%s", item.get("reminder_id"), agent_id, chat_id) return True async def worker_loop() -> None: logger.info("reminder_worker_started poll_seconds=%s", POLL_SECONDS) while True: try: items = await pop_due_reminders(limit=20) if items: for item in items: try: await _send_reminder(item) except Exception: logger.exception("reminder_send_exception reminder_id=%s", item.get("reminder_id")) except asyncio.CancelledError: raise except Exception: logger.exception("reminder_worker_cycle_failed") await asyncio.sleep(POLL_SECONDS) if __name__ == "__main__": try: asyncio.run(worker_loop()) finally: try: asyncio.run(close_redis()) except Exception: pass