from __future__ import annotations import asyncio import logging from yuxi.channel.extensions.line.bot import LineBotClient from yuxi.channel.extensions.line.webhook import LineWebhookHandler, set_webhook_config logger = logging.getLogger(__name__) class LineGatewayAdapter: def __init__(self): self._webhook = LineWebhookHandler() self._running = False async def start(self, ctx) -> object: account_id = getattr(ctx, "account_id", "default") config = getattr(ctx, "config", {}) or {} account = self._resolve_account(ctx) token = account.get("channel_access_token", "") secret = account.get("channel_secret", "") if not token or not secret: logger.warning("LINE gateway start: token or secret not configured for account %s", account_id) self._running = True queue = getattr(ctx, "queue", asyncio.Queue()) return queue bot = LineBotClient(channel_access_token=token) profile = await bot.get_bot_info() if profile: logger.info( "LINE bot connected: display_name=%s user_id=%s", profile.display_name, profile.user_id, ) else: logger.warning("LINE bot probe failed for account %s", account_id) self._webhook.start() self._running = True set_webhook_config(config) queue = getattr(ctx, "queue", asyncio.Queue()) return queue @staticmethod def _resolve_account(ctx) -> dict: account_id = getattr(ctx, "account_id", "default") config = getattr(ctx, "config", {}) or {} line_cfg = config.get("channels", {}).get("line", {}) if not isinstance(line_cfg, dict): line_cfg = {} accounts = line_cfg.get("accounts", {}) if not isinstance(accounts, dict): accounts = {} account_raw = accounts.get(account_id, {}) if not isinstance(account_raw, dict): account_raw = {} token = ( account_raw.get("channel_access_token") or line_cfg.get("channel_access_token", "") ) secret = ( account_raw.get("channel_secret") or line_cfg.get("channel_secret", "") ) return { "account_id": account_id, "channel_access_token": token, "channel_secret": secret, "name": account_raw.get("name", account_id), "dm_policy": account_raw.get("dm_policy", line_cfg.get("dm_policy", "pairing")), "group_policy": account_raw.get("group_policy", line_cfg.get("group_policy", "allowlist")), "allow_from": account_raw.get("allow_from", line_cfg.get("allow_from", [])), "group_allow_from": account_raw.get("group_allow_from", line_cfg.get("group_allow_from", [])), "webhook_path": account_raw.get("webhook_path", line_cfg.get("webhook_path", "/line/webhook")), "text_chunk_limit": account_raw.get("text_chunk_limit", line_cfg.get("text_chunk_limit", 5000)), "media_max_mb": account_raw.get("media_max_mb", line_cfg.get("media_max_mb", 10)), } async def stop(self, ctx) -> None: self._running = False self._webhook.stop() logger.info("LINE gateway stopped") async def probe_bot(self, account: dict) -> dict | None: token = account.get("channel_access_token", "") if not token: return None bot = LineBotClient(channel_access_token=token) profile = await bot.get_bot_info() if profile: return { "user_id": profile.user_id, "display_name": profile.display_name, "picture_url": profile.picture_url, "chat_mode": profile.chat_mode, } return None