ForcePilot/backend/package/yuxi/channel/extensions/whatsapp/config.py
Kris 7215418610 feat(channel): 添加企业微信、微博、WhatsApp 和 Workplace 渠道扩展
新增企业微信、微博、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
2026-05-21 12:01:56 +08:00

145 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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))