import logging from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.interval import IntervalTrigger from glitchup_bot.services.digest_builder import build_digest from glitchup_bot.services.runtime_settings import get_runtime_settings from glitchup_bot.services.sync_service import sync_issues from glitchup_bot.services.telegram_sender import send_digest_message logger = logging.getLogger(__name__) scheduler: AsyncIOScheduler | None = None async def weekly_digest_job() -> None: logger.info("Running weekly digest job") try: await send_digest_message(await build_digest(refresh=False)) logger.info("Weekly digest sent successfully") except Exception: logger.exception("Failed to send weekly digest") async def sync_job() -> None: logger.info("Running scheduled issue sync") try: summary = await sync_issues() logger.info( "Issue sync finished: %s projects, %s issues, %s resolved", summary.project_count, summary.issue_count, summary.resolved_count, ) except Exception: logger.exception("Scheduled issue sync failed") async def setup_scheduler() -> AsyncIOScheduler: global scheduler if scheduler is not None and scheduler.running: return scheduler runtime = await get_runtime_settings() scheduler = AsyncIOScheduler() if runtime.sync_enabled: scheduler.add_job( sync_job, IntervalTrigger(minutes=runtime.sync_interval_minutes), id="issue_sync", replace_existing=True, ) scheduler.add_job( weekly_digest_job, CronTrigger( day_of_week=runtime.digest_cron_day, hour=runtime.digest_cron_hour, minute=runtime.digest_cron_minute, timezone=runtime.digest_timezone, ), id="weekly_digest", replace_existing=True, ) scheduler.start() logger.info( "Scheduler started: sync %s, digest at %s %02d:%02d %s", ( f"every {runtime.sync_interval_minutes} min" if runtime.sync_enabled else "disabled" ), runtime.digest_cron_day, runtime.digest_cron_hour, runtime.digest_cron_minute, runtime.digest_timezone, ) return scheduler async def reload_scheduler() -> AsyncIOScheduler: await shutdown_scheduler() return await setup_scheduler() async def shutdown_scheduler() -> None: global scheduler if scheduler is not None: scheduler.shutdown(wait=False) scheduler = None