import os import logging logger = logging.getLogger(__name__) ENV_PREFIX = "ZOOMCHAT" class ZoomConfigAdapter: def __init__(self, config: dict | None = None): self._config = config or {} def list_account_ids(self, config: dict) -> list[str]: accounts = config.get("accounts", {}) if not accounts: return ["default"] if self._env_client_id() else [] return list(accounts.keys()) async def resolve_account(self, account_id: str = "default") -> dict: accounts = self._get_raw_accounts() raw = accounts.get(account_id, {}) if account_id == "default": client_id = raw.get("client_id") or self._env_client_id() client_secret = raw.get("client_secret") or os.environ.get(f"{ENV_PREFIX}_CLIENT_SECRET", "") account_id_zoom = raw.get("account_id") or os.environ.get(f"{ENV_PREFIX}_ACCOUNT_ID", "") webhook_secret = raw.get("webhook_secret") or os.environ.get(f"{ENV_PREFIX}_WEBHOOK_SECRET", "") else: upper = account_id.upper() client_id = raw.get("client_id") or os.environ.get(f"{ENV_PREFIX}_CLIENT_ID_{upper}", "") client_secret = raw.get("client_secret") or os.environ.get(f"{ENV_PREFIX}_CLIENT_SECRET_{upper}", "") account_id_zoom = raw.get("account_id") or os.environ.get(f"{ENV_PREFIX}_ACCOUNT_ID_{upper}", "") webhook_secret = raw.get("webhook_secret") or os.environ.get(f"{ENV_PREFIX}_WEBHOOK_SECRET_{upper}", "") return { "account_id": account_id_zoom, "client_id": client_id, "client_secret": client_secret, "webhook_secret": webhook_secret, "bot_user_id": raw.get("bot_user_id", ""), "name": raw.get("name", account_id), "dm_policy": raw.get("dm_policy", "open"), "group_policy": raw.get("group_policy", "mentioned"), "allow_from": raw.get("allow_from", []), "api_base_url": raw.get("api_base_url", "https://api.zoom.us/v2"), "text_chunk_limit": raw.get("text_chunk_limit", 3800), "chunk_mode": raw.get("chunk_mode", "length"), "webhook_host": raw.get("webhook_host", "0.0.0.0"), "webhook_port": raw.get("webhook_port", 0), "enabled": raw.get("enabled", True), } def is_configured(self, account: dict) -> bool: return bool( account.get("account_id") and account.get("client_id") and account.get("client_secret") and account.get("webhook_secret") ) def is_enabled(self, account: dict) -> bool: return account.get("enabled", True) def disabled_reason(self, account: dict) -> str: if not self.is_configured(account): return "Missing Zoom API credentials (account_id / client_id / client_secret / webhook_secret)" if not self.is_enabled(account): return "Account disabled in config" return "" def describe_account(self, account: dict) -> dict: return { "label": account.get("name", account.get("account_id", "Unknown")), "bot_user_id": account.get("bot_user_id", "") or "(auto)", "dm_policy": account.get("dm_policy", "open"), "api_base_url": account.get("api_base_url", "https://api.zoom.us/v2"), } def config_schema(self) -> dict: return { "type": "object", "title": "Zoom Team Chat 渠道配置", "properties": { "accounts": { "type": "object", "description": "Zoom account configurations", "additionalProperties": { "type": "object", "properties": { "client_id": { "type": "string", "title": "Client ID", "description": "Zoom Marketplace App Client ID", }, "client_secret": { "type": "string", "title": "Client Secret", "description": "Zoom Marketplace App Client Secret", "x-ui-password": True, }, "account_id": { "type": "string", "title": "Account ID", "description": "Zoom Account ID for S2S OAuth", }, "webhook_secret": { "type": "string", "title": "Webhook Secret Token", "description": "Zoom Webhook Secret Token", "x-ui-password": True, }, "bot_user_id": { "type": "string", "title": "Bot User ID", "description": "Bot Zoom user ID (auto-resolved if empty)", }, "dm_policy": { "type": "string", "title": "DM 策略", "enum": ["open", "pairing", "allowlist", "disabled"], }, "group_policy": { "type": "string", "title": "群组策略", "enum": ["mentioned", "activated", "allowlist"], }, "allow_from": {"type": "array", "items": {"type": "string"}, "title": "允许列表"}, "api_base_url": { "type": "string", "title": "API Base URL", "description": "Custom API base URL for data residency", }, "text_chunk_limit": {"type": "integer", "title": "文本分块上限", "default": 3800}, }, }, } }, } def default_account_id(self, config: dict) -> str: return config.get("default_account", "default") def resolve_allow_from(self, config: dict, account_id: str | None = None) -> list[str]: aid = account_id or self.default_account_id(config) accounts = config.get("accounts", {}) account = accounts.get(aid, {}) return account.get("allow_from", []) def set_config(self, config: dict) -> None: self._config = config def _get_raw_accounts(self) -> dict: return self._config.get("accounts", {}) @staticmethod def _env_client_id() -> str: return os.environ.get(f"{ENV_PREFIX}_CLIENT_ID", "")