from __future__ import annotations import asyncio from yuxi.channels.adapters.line.probe import probe_line_bot from yuxi.utils.logging_config import logger class LINEGateway: """Per-account Gateway 启动管理器,支持独立 connect/monitor/Webhook 注册。""" def __init__(self, adapter): self._adapter = adapter self._account_states: dict[str, dict] = {} self._monitor_tasks: dict[str, asyncio.Task] = {} async def start_account(self, account_id: str, token: str, secret: str) -> bool: from yuxi.channels.adapters.line.send import LINESender state = self._account_states.setdefault(account_id, {"status": "starting", "sender": None, "error": None}) try: sender = LINESender(token) await sender.__aenter__() info = await sender.get_bot_info() if not info: await sender.__aexit__() state["status"] = "error" state["error"] = "Failed to get bot info" return False state["sender"] = sender state["status"] = "connected" state["info"] = { "display_name": info.get("displayName", ""), "user_id": info.get("userId", ""), "picture_url": info.get("pictureUrl", ""), } logger.info(f"[LINE Gateway] account '{account_id}' started: {state['info']['display_name']}") return True except Exception as e: state["status"] = "error" state["error"] = str(e) logger.error(f"[LINE Gateway] failed to start account '{account_id}': {e}") return False async def stop_account(self, account_id: str) -> None: if account_id in self._monitor_tasks: self._monitor_tasks[account_id].cancel() del self._monitor_tasks[account_id] state = self._account_states.pop(account_id, None) if state and state.get("sender"): try: await state["sender"].__aexit__() except Exception: pass logger.info(f"[LINE Gateway] account '{account_id}' stopped") async def probe_account(self, token: str) -> dict: return await probe_line_bot(token) def get_account_state(self, account_id: str) -> dict | None: return self._account_states.get(account_id) def list_accounts(self) -> list[str]: return list(self._account_states.keys()) async def start_monitor(self, account_id: str, interval: float = 60.0) -> None: if account_id in self._monitor_tasks: return async def _monitor_loop(): while True: try: state = self._account_states.get(account_id) if not state or state["status"] != "connected": break sender = state.get("sender") if sender: info = await sender.get_bot_info() if not info: state["status"] = "error" state["error"] = "Bot info fetch failed during monitor" break except asyncio.CancelledError: break except Exception as e: logger.warning(f"[LINE Gateway] monitor error for '{account_id}': {e}") await asyncio.sleep(interval) task = asyncio.ensure_future(_monitor_loop()) self._monitor_tasks[account_id] = task async def stop_all(self) -> None: for account_id in list(self._monitor_tasks.keys()): await self.stop_account(account_id)