from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass @dataclass class BaseChannelAccount: """渠道账户基类(所有可多账户的适配器共用)""" account_id: str = "default" enabled: bool = True dm_policy: str = "allowlist" group_policy: str = "allowlist" name: str = "" upgrade_url: str = "" upgrade_mode: str = "" class BaseAccountManager(ABC): """多账户管理器基类 定义四个标准接口: - resolve_account(cfg, account_id) → BaseChannelAccount - is_configured(account_id) → bool - default_account_id(cfg) → str - list_account_ids(cfg) → list[str] """ @abstractmethod def resolve_account(self, cfg: dict, account_id: str = "default") -> BaseChannelAccount: ... @abstractmethod def is_configured(self, account_id: str = "default") -> bool: ... @abstractmethod def default_account_id(self, cfg: dict) -> str: ... @abstractmethod def list_account_ids(self, cfg: dict) -> list[str]: ... def resolve_account_from_config_section( config: dict, account_id: str, section_key: str = "accounts", ) -> dict: """从配置中提取指定账户的配置段 兼容两种配置结构: 1. 多账户(推荐): { accounts: { default: {...}, second: {...} }, defaultAccount: 'default' } 2. 单账户(兼容旧格式): { app_id: "...", client_secret: "..." } — 平铺格式 返回:账户配置 dict(可能为空) """ accounts = config.get(section_key, {}) if not accounts: return config account_cfg = accounts.get(account_id, {}) if not account_cfg and account_id == "default": first = next(iter(accounts.values()), {}) return first return account_cfg def resolve_account_ids( config: dict, section_key: str = "accounts", ) -> list[str]: """从配置中提取所有账户 ID""" accounts = config.get(section_key, {}) if not accounts: return ["default"] return list(accounts.keys()) def resolve_default_account_id( config: dict, section_key: str = "accounts", ) -> str: """解析默认账户 ID 优先级:defaultAccount > 'default' > 第一个账户 """ explicit = config.get("defaultAccount", config.get("default_account", "")) if explicit: return explicit accounts = config.get(section_key, {}) if not accounts: return "default" if "default" in accounts: return "default" return next(iter(accounts.keys()), "default") def resolve_effective_dm_scope(config: dict) -> str: """解析会话隔离策略(多账户场景的关键配置) - 'merged': 所有账户的 DM 合并到同一个会话 - 'per-account-channel-peer': 按 账户/渠道/发送者 独立隔离会话(推荐默认值) """ return config.get("dmScope", config.get("dm_scope", "per-account-channel-peer"))