import os import logging logger = logging.getLogger(__name__) class WhatsAppConfigAdapter: def list_account_ids(self, config: dict) -> list[str]: accounts = config.get("accounts", {}) if not accounts: return ["default"] if self._env_token_exists("default") else [] return list(accounts.keys()) async def resolve_account(self, account_id: str) -> dict: raw = self._load_config(account_id) return self._build_account(account_id, raw) def is_configured(self, account: dict) -> bool: return bool(account.get("phone_number_id") and account.get("access_token")) def is_enabled(self, account: dict) -> bool: return account.get("enabled", True) def describe_account(self, account: dict) -> dict: return { "account_id": account.get("account_id", ""), "phone_number_id": account.get("phone_number_id", ""), "display_phone_number": account.get("display_phone_number", ""), "dm_policy": account.get("dm_policy", "pairing"), "configured": self.is_configured(account), } 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 config_schema(self) -> dict: return { "$schema": "https://json-schema.org/draft-07/schema#", "type": "object", "title": "WhatsApp 渠道配置", "properties": { "phone_number_id": { "type": "string", "title": "电话号码 ID", "description": "WhatsApp Business 电话号码 ID(从 Meta Business Suite 获取)", }, "business_account_id": { "type": "string", "title": "Business Account ID", "description": "WhatsApp Business Account ID", }, "access_token": { "type": "string", "title": "Access Token", "description": "Meta Permanent Access Token(也可通过环境变量 WHATSAPP_ACCESS_TOKEN 注入)", "x-ui-password": True, }, "app_secret": { "type": "string", "title": "App Secret", "description": "Webhook 签名验证密钥(也可通过环境变量 WHATSAPP_APP_SECRET 注入)", "x-ui-password": True, }, "verify_token": { "type": "string", "title": "Webhook 验证 Token", "description": "Meta Webhook 配置时的自定义验证 Token", }, "display_phone_number": { "type": "string", "title": "展示号码", "description": "用于前端展示的电话号码", }, "dm_policy": { "type": "string", "title": "DM 策略", "enum": ["pairing", "allowlist", "open", "disabled"], "default": "pairing", }, "group_policy": { "type": "string", "title": "群组策略", "enum": ["open", "disabled", "allowlist"], "default": "open", }, "text_chunk_limit": { "type": "integer", "title": "文本分块上限", "default": 2000, "minimum": 100, "maximum": 4096, }, "media_max_mb": { "type": "integer", "title": "媒体大小上限 (MB)", "default": 50, "minimum": 1, "maximum": 100, }, }, "required": ["phone_number_id", "access_token", "app_secret"], } def _load_config(self, account_id: str) -> dict: return {} def _build_account(self, account_id: str, raw: dict) -> dict: env_token = self._env_access_token(account_id) env_secret = os.environ.get("WHATSAPP_APP_SECRET", "") env_verify = os.environ.get("WHATSAPP_VERIFY_TOKEN", "") return { "account_id": account_id, "phone_number_id": raw.get("phone_number_id", os.environ.get("WHATSAPP_PHONE_NUMBER_ID", "")), "business_account_id": raw.get("business_account_id", os.environ.get("WHATSAPP_BUSINESS_ACCOUNT_ID", "")), "access_token": env_token or raw.get("access_token", ""), "app_secret": env_secret or raw.get("app_secret", ""), "verify_token": env_verify or raw.get("verify_token", ""), "display_phone_number": raw.get("display_phone_number", ""), "name": raw.get("name", account_id), "enabled": raw.get("enabled", True), "dm_policy": raw.get("dm_policy", "pairing"), "group_policy": raw.get("group_policy", "open"), "allow_from": raw.get("allow_from", []), "text_chunk_limit": raw.get("text_chunk_limit", 2000), "media_max_mb": raw.get("media_max_mb", 50), "reaction_level": raw.get("reaction_level", "minimal"), } @staticmethod def _env_access_token(account_id: str) -> str: key = f"WHATSAPP_ACCESS_TOKEN_{account_id.upper()}" return os.environ.get(key, "") or os.environ.get("WHATSAPP_ACCESS_TOKEN", "") @staticmethod def _env_token_exists(account_id: str) -> bool: return bool(WhatsAppConfigAdapter._env_access_token(account_id))