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