新增企业微信、微博、WhatsApp、Workplace 四个渠道扩展。 企业微信渠道扩展主要模块:config, gateway, webhook, webhook_bot, outbound, streaming, pairing, security, crypto, dedupe, persistent_dedupe, card, directory, events, externalcontact, media, mentions, menu, message, oauth, status 微博渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, passive_reply, broadcast, message, menu, media, subscription, template, status WhatsApp 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, monitor, status Workplace 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, challenge, groups, media, mentions, menu, monitor, persona, quick_reply, signature, subscriptions, template, threading, users, status
145 lines
5.9 KiB
Python
145 lines
5.9 KiB
Python
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))
|