""" APScheduler — 仅保留 wxid/数据库切换检测。 (不再运行任何 AI 分类轮询:AI 分析改为用户手动按时间窗口触发) """ import logging from apscheduler.schedulers.asyncio import AsyncIOScheduler from database import update_db_path log = logging.getLogger(__name__) scheduler = AsyncIOScheduler(timezone="Asia/Shanghai") _sync_failures = 0 def register_poll_job(group_id: int, poll_interval: int): """已废弃。保留空函数避免其他模块旧引用炸。""" log.debug(f"[scheduler] register_poll_job called (no-op now): group={group_id}") def _reschedule_sync(seconds: int): if scheduler.get_job("sync_jobs"): scheduler.remove_job("sync_jobs") scheduler.add_job(_sync_jobs, "interval", seconds=seconds, id="sync_jobs") async def _sync_jobs(): """定期触发 wxid 重新检测,让账号切换能自动切换数据库。""" global _sync_failures try: await update_db_path() if _sync_failures > 0: _sync_failures = 0 _reschedule_sync(10) except Exception as e: _sync_failures += 1 log.error(f"[scheduler] sync error (consecutive={_sync_failures}): {e}") if _sync_failures == 3: _reschedule_sync(60) log.warning("[scheduler] sync backoff to 60s after 3 failures") async def start_scheduler(): scheduler.add_job(_sync_jobs, "interval", seconds=10, id="sync_jobs") scheduler.start() # Do not block FastAPI startup on chatlog. Electron starts the backend # before chatlog, so the first account sync must happen in the background. scheduler.add_job(_sync_jobs, "date", id="sync_jobs_initial") log.info("[scheduler] started (db-path watcher only, no poll jobs)")