本次提交包含多项优化与新增功能: 1. 清理多个文件中多余的空行与导入顺序 2. 修复voice.py中的多行字符串格式化问题 3. 新增微信公众号被动回复构建函数与配置项 4. 新增企业微信markdown消息发送支持 5. 新增消息去重TTL与最大条目配置 6. 新增markdown文本截断工具函数 7. 新增微信授权与OAuth相关工具方法 8. 重构消息去重逻辑,使用DedupPolicy替代本地字典实现 9. 新增子账号多租户支持功能 10. 新增消息动作处理适配器,支持send/reply等操作 11. 修复token持久化逻辑,新增状态存储支持
132 lines
5.6 KiB
Python
132 lines
5.6 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,
|
||
},
|
||
"passive_reply_mode": {
|
||
"type": "string",
|
||
"enum": ["auto", "transfer_cs", "disabled"],
|
||
"default": "auto",
|
||
"description": (
|
||
"MP 模式被动回复策略:"
|
||
"auto - 自动尝试被动回复(优先缓存 → 空回复 + 客服消息兜底);"
|
||
"transfer_cs - 始终转发到客服;"
|
||
"disabled - 仅使用客服消息(当前行为)"
|
||
),
|
||
},
|
||
},
|
||
}
|
||
|
||
|
||
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)
|