from __future__ import annotations import asyncio import json import logging import urllib.error import urllib.parse import urllib.request from datetime import date logger = logging.getLogger(__name__) _NOTIFY_PREFIX = { "d0": "๐Ÿ”” ์˜ค๋Š˜ ์ผ์ •", "d1": "๐Ÿ“… ๋‚ด์ผ ์ผ์ •", "d7": "๐Ÿ“† 7์ผ ํ›„ ์ผ์ •", } class SchedulerService: """asyncio ํƒœ์Šคํฌ ๊ธฐ๋ฐ˜ ์•Œ๋ฆผ ์Šค์ผ€์ค„๋Ÿฌ. ๋งค 60์ดˆ๋งˆ๋‹ค td_reminders๋ฅผ ํ™•์ธํ•ด D-7/D-1/D-0 Telegram ์•Œ๋ฆผ์„ ๋ฐœ์†กํ•œ๋‹ค. TELEGRAM_BOT_TOKEN์ด ๋น„์–ด ์žˆ์œผ๋ฉด ๋ฐœ์†ก ์—†์ด ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅํ•œ๋‹ค. """ def __init__(self, reminder_repo, bot_token: str, user_map_json: str): self._repo = reminder_repo self._token = bot_token try: self._user_map: dict[str, str] = json.loads(user_map_json) if user_map_json else {} except Exception: self._user_map = {} self._task: asyncio.Task | None = None def start(self) -> None: self._task = asyncio.create_task(self._loop()) logger.info("[Scheduler] ์•Œ๋ฆผ ์Šค์ผ€์ค„๋Ÿฌ ์‹œ์ž‘ (60์ดˆ ๊ฐ„๊ฒฉ)") def shutdown(self) -> None: if self._task: self._task.cancel() logger.info("[Scheduler] ์•Œ๋ฆผ ์Šค์ผ€์ค„๋Ÿฌ ์ข…๋ฃŒ") async def _loop(self) -> None: while True: try: await self._check_reminders() except Exception as e: logger.error(f"[Scheduler] ์•Œ๋ฆผ ํ™•์ธ ์ค‘ ์˜ค๋ฅ˜: {e}") await asyncio.sleep(60) async def _check_reminders(self) -> None: today = date.today() reminders = self._repo.get_due(today) for r in reminders: notify_type = r.get("notify_type") if not notify_type: continue text = self._format_message(r["message"], notify_type, r["remind_date"]) chat_id = self._user_map.get(r["user_id"]) if self._token and chat_id: try: self._send_telegram(chat_id, text) except Exception as e: logger.error(f"[Scheduler] Telegram ๋ฐœ์†ก ์‹คํŒจ (user={r['user_id']}): {e}") continue else: logger.info(f"[Scheduler] ์•Œ๋ฆผ(Telegram ๋ฏธ์„ค์ •): {text}") try: self._repo.mark_sent(r["id"], notify_type) except Exception as e: logger.error(f"[Scheduler] mark_sent ์‹คํŒจ: {e}") def _format_message(self, message: str, notify_type: str, remind_date) -> str: prefix = _NOTIFY_PREFIX.get(notify_type, "์•Œ๋ฆผ") return f"[์œจ๋ด‡ ์•Œ๋ฆผ] {prefix}\n๋‚ ์งœ: {remind_date}\n{message}" def _send_telegram(self, chat_id: str, text: str) -> None: url = f"https://api.telegram.org/bot{self._token}/sendMessage" data = urllib.parse.urlencode({"chat_id": chat_id, "text": text}).encode() req = urllib.request.Request(url, data=data, method="POST") with urllib.request.urlopen(req, timeout=10): pass