该提交实现了支持企业微信、微信公众号、个人微信桥接三种模式的完整微信渠道适配器,包含以下核心模块: 1. 基础认证与配置相关:auth_adapter、config_reload、setup_contract等 2. 消息处理与格式转换:format、attachment_adapter、outbound_adapter等 3. 多模式客户端支持:wecom/mp子模块,包含加解密、消息收发能力 4. 辅助能力:限速器、防抖、会话绑定、事件映射、模板渲染等 5. 扩展能力:二维码登录、消息读取、特权用户、心跳监控等 实现了完整的微信生态对接能力,支持消息收发、事件处理、API调用限流、配置热重载等功能。
121 lines
5.2 KiB
Python
121 lines
5.2 KiB
Python
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
WECHAT_CONFIG_SCHEMA: dict[str, Any] = {
|
||
"type": "object",
|
||
"additionalProperties": False,
|
||
"properties": {
|
||
"enabled": {"type": "boolean"},
|
||
"accounts": {
|
||
"type": "object",
|
||
"patternProperties": {
|
||
"^[a-zA-Z0-9_-]+$": {
|
||
"type": "object",
|
||
"oneOf": [
|
||
{
|
||
"required": ["corp_id", "corp_secret", "agent_id"],
|
||
"properties": {
|
||
"corp_id": {"type": "string", "minLength": 1},
|
||
"corp_secret": {"type": "string", "minLength": 1},
|
||
"agent_id": {"type": "string", "minLength": 1},
|
||
},
|
||
},
|
||
{
|
||
"required": ["app_id", "app_secret"],
|
||
"properties": {
|
||
"app_id": {"type": "string", "minLength": 1},
|
||
"app_secret": {"type": "string", "minLength": 1},
|
||
},
|
||
},
|
||
{
|
||
"required": ["bridge_url"],
|
||
"properties": {
|
||
"bridge_url": {"type": "string", "format": "uri"},
|
||
},
|
||
},
|
||
],
|
||
},
|
||
},
|
||
},
|
||
"dmPolicy": {"enum": ["open", "pairing", "allowlist", "disabled"]},
|
||
"groupPolicy": {"enum": ["open", "allowlist", "disabled"]},
|
||
"retry_attempts": {"type": "integer", "minimum": 1, "maximum": 10},
|
||
"retry_min_delay": {"type": "number", "minimum": 0.1, "maximum": 60.0},
|
||
"retry_max_delay": {"type": "number", "minimum": 1.0, "maximum": 120.0},
|
||
"outbound_priority_enabled": {"type": "boolean"},
|
||
"exec_approval_timeout": {"type": "integer", "minimum": 1, "maximum": 30},
|
||
"ban_backoff_intervals": {
|
||
"type": "array",
|
||
"items": {"type": "integer", "minimum": 1},
|
||
"minItems": 1,
|
||
"maxItems": 5,
|
||
},
|
||
},
|
||
}
|
||
|
||
|
||
def validate_wechat_config(config: dict[str, Any]) -> list[str]:
|
||
errors: list[str] = []
|
||
|
||
if not isinstance(config, dict):
|
||
return ["配置必须是对象(dict)"]
|
||
|
||
enabled = config.get("enabled")
|
||
if enabled is not None and not isinstance(enabled, bool):
|
||
errors.append("enabled 必须是布尔值")
|
||
|
||
dm_policy = config.get("dm_policy", "pairing")
|
||
if dm_policy not in ("open", "pairing", "allowlist", "disabled"):
|
||
errors.append(f"dm_policy 值无效: {dm_policy}(可选: open/pairing/allowlist/disabled)")
|
||
|
||
group_policy = config.get("group_policy", "allowlist")
|
||
if group_policy not in ("open", "allowlist", "disabled"):
|
||
errors.append(f"group_policy 值无效: {group_policy}(可选: open/allowlist/disabled)")
|
||
|
||
retry_attempts = config.get("retry_attempts")
|
||
if retry_attempts is not None and (not isinstance(retry_attempts, int) or retry_attempts < 0):
|
||
errors.append("retry_attempts 必须是非负整数")
|
||
|
||
retry_min_delay = config.get("retry_min_delay")
|
||
if retry_min_delay is not None and (not isinstance(retry_min_delay, (int, float)) or retry_min_delay < 0):
|
||
errors.append("retry_min_delay 必须是非负数")
|
||
|
||
retry_max_delay = config.get("retry_max_delay")
|
||
if retry_max_delay is not None and (not isinstance(retry_max_delay, (int, float)) or retry_max_delay < 0):
|
||
errors.append("retry_max_delay 必须是非负数")
|
||
|
||
outbound_priority_enabled = config.get("outbound_priority_enabled")
|
||
if outbound_priority_enabled is not None and not isinstance(outbound_priority_enabled, bool):
|
||
errors.append("outbound_priority_enabled 必须是布尔值")
|
||
|
||
exec_approval_timeout = config.get("exec_approval_timeout")
|
||
if exec_approval_timeout is not None and (not isinstance(exec_approval_timeout, int) or exec_approval_timeout < 0):
|
||
errors.append("exec_approval_timeout 必须是非负整数")
|
||
|
||
ban_backoff_intervals = config.get("ban_backoff_intervals")
|
||
if ban_backoff_intervals is not None:
|
||
if not isinstance(ban_backoff_intervals, list) or not all(
|
||
isinstance(x, int) and x > 0 for x in ban_backoff_intervals
|
||
):
|
||
errors.append("ban_backoff_intervals 必须是正整数列表")
|
||
|
||
has_corp = all(config.get(k) for k in ("corp_id", "corp_secret", "agent_id"))
|
||
has_mp = all(config.get(k) for k in ("app_id", "app_secret"))
|
||
has_bridge = bool(config.get("bridge_url"))
|
||
|
||
if not (has_corp or has_mp or has_bridge):
|
||
errors.append("缺少有效的微信配置 (wecom/mp/bridge)")
|
||
|
||
accounts = config.get("accounts", {})
|
||
if accounts and isinstance(accounts, dict):
|
||
for account_id, account_cfg in accounts.items():
|
||
if not isinstance(account_id, str) or not account_id.replace("-", "").replace("_", "").isalnum():
|
||
errors.append(f"账户 ID 格式无效: {account_id}")
|
||
|
||
return errors
|
||
|
||
|
||
def get_schema() -> dict[str, Any]:
|
||
return dict(WECHAT_CONFIG_SCHEMA)
|