该提交实现了支持企业微信、微信公众号、个人微信桥接三种模式的完整微信渠道适配器,包含以下核心模块: 1. 基础认证与配置相关:auth_adapter、config_reload、setup_contract等 2. 消息处理与格式转换:format、attachment_adapter、outbound_adapter等 3. 多模式客户端支持:wecom/mp子模块,包含加解密、消息收发能力 4. 辅助能力:限速器、防抖、会话绑定、事件映射、模板渲染等 5. 扩展能力:二维码登录、消息读取、特权用户、心跳监控等 实现了完整的微信生态对接能力,支持消息收发、事件处理、API调用限流、配置热重载等功能。
161 lines
5.6 KiB
Python
161 lines
5.6 KiB
Python
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
|
||
def check_dm_policy(config: dict[str, Any], open_id: str) -> bool:
|
||
dm_policy = config.get("dm_policy", "open")
|
||
|
||
if dm_policy == "open":
|
||
return True
|
||
elif dm_policy == "disabled":
|
||
return False
|
||
elif dm_policy == "allowlist":
|
||
allow_from = config.get("allow_from", [])
|
||
return f"wx:{open_id}" in allow_from
|
||
|
||
return False
|
||
|
||
|
||
def check_group_policy(config: dict[str, Any], chat_id: str, open_id: str) -> bool:
|
||
group_policy = config.get("group_policy", "allowlist")
|
||
|
||
if group_policy == "open":
|
||
return True
|
||
elif group_policy == "disabled":
|
||
return False
|
||
elif group_policy == "allowlist":
|
||
global_allow = config.get("group_allow_from", [])
|
||
if f"wx:{open_id}" in global_allow:
|
||
return True
|
||
|
||
groups_config = config.get("groups", {})
|
||
chat_config = groups_config.get(chat_id, {})
|
||
per_group_allow = chat_config.get("allow_from", [])
|
||
return f"wx:{open_id}" in per_group_allow
|
||
|
||
return False
|
||
|
||
|
||
def is_bot_mentioned(config: dict[str, Any], payload: dict, mode: str) -> bool:
|
||
if mode == "wecom":
|
||
content = payload.get("Content", "")
|
||
agent_name = config.get("agent_name", "")
|
||
if agent_name and ("@" in content and agent_name in content):
|
||
return True
|
||
return "@" in content
|
||
|
||
if mode == "mp":
|
||
content = payload.get("Content", "")
|
||
return "@" in content
|
||
|
||
if mode == "personal":
|
||
at_list = payload.get("at_list", [])
|
||
return len(at_list) > 0
|
||
|
||
return False
|
||
|
||
|
||
def should_require_mention(config: dict[str, Any], chat_id: str) -> bool:
|
||
groups_config = config.get("groups", {})
|
||
chat_config = groups_config.get(chat_id, {})
|
||
require_mention = chat_config.get("require_mention", True)
|
||
return require_mention
|
||
|
||
|
||
WECHAT_SAFETY_STRATEGIES: dict[str, dict[str, Any]] = {
|
||
"rateLimit": {
|
||
"description": "API 调用频率控制",
|
||
"implementation": "TokenBucketRateLimiter(rate=20, per=60)",
|
||
},
|
||
"banned": {
|
||
"description": "封禁检测与处理",
|
||
"implementation": "检测微信错误码 48001(API未授权)/ 违规记录 → 自动禁用适配器",
|
||
},
|
||
"autoJoin": {
|
||
"description": "自动加入群组策略",
|
||
"implementation": "config.auto_join_groups 白名单 → connect() 时自动加入",
|
||
},
|
||
"autoDerive": {
|
||
"description": "自动派生会话策略",
|
||
"implementation": "群聊消息按 sender 自动派生子会话(可配置开关)",
|
||
},
|
||
}
|
||
|
||
|
||
class WeChatSecurityAdapter:
|
||
@staticmethod
|
||
def resolve_dm_policy(config: dict[str, Any], account_id: str = "default") -> str:
|
||
accounts = config.get("accounts", {})
|
||
if account_id in accounts:
|
||
return accounts[account_id].get("dm_policy", "pairing")
|
||
return config.get("dm_policy", "pairing")
|
||
|
||
@staticmethod
|
||
def resolve_dm_allow_from(config: dict[str, Any], account_id: str = "default") -> list[str]:
|
||
accounts = config.get("accounts", {})
|
||
if account_id in accounts:
|
||
return accounts[account_id].get("allow_from", [])
|
||
return config.get("allow_from", [])
|
||
|
||
@staticmethod
|
||
def resolve_group_policy(config: dict[str, Any], account_id: str = "default") -> str:
|
||
accounts = config.get("accounts", {})
|
||
if account_id in accounts:
|
||
return accounts[account_id].get("group_policy", "allowlist")
|
||
return config.get("group_policy", "allowlist")
|
||
|
||
@staticmethod
|
||
def resolve_group_allow_from(config: dict[str, Any], chat_id: str) -> list[str]:
|
||
global_allow = config.get("group_allow_from", [])
|
||
groups_config = config.get("groups", {})
|
||
chat_config = groups_config.get(chat_id, {})
|
||
per_group_allow = chat_config.get("allow_from", [])
|
||
result = list(global_allow)
|
||
for entry in per_group_allow:
|
||
if entry not in result:
|
||
result.append(entry)
|
||
return result
|
||
|
||
@staticmethod
|
||
def collect_warnings(config: dict[str, Any]) -> list[str]:
|
||
warnings: list[str] = []
|
||
dm_policy = config.get("dm_policy", "pairing")
|
||
|
||
if dm_policy == "open":
|
||
warnings.append("dm_policy='open' 存在高危安全风险,建议启用配对机制")
|
||
|
||
allow_from = config.get("allow_from", [])
|
||
group_allow = config.get("group_allow_from", [])
|
||
|
||
if dm_policy == "allowlist" and not allow_from:
|
||
warnings.append("allowlist 为空,无人可访问")
|
||
|
||
if not group_allow:
|
||
groups_config = config.get("groups", {})
|
||
has_per_group = any(chat_config.get("allow_from") for chat_config in groups_config.values())
|
||
if not has_per_group and config.get("group_policy") == "allowlist":
|
||
warnings.append("群组 allowlist 为空,所有群组将被拒绝访问")
|
||
|
||
has_pairing = any(acc.get("dm_policy") == "pairing" for acc in config.get("accounts", {}).values())
|
||
if dm_policy != "pairing" and not has_pairing:
|
||
warnings.append("未启用配对机制 (pairing),建议开启以增强安全性")
|
||
|
||
return warnings
|
||
|
||
@staticmethod
|
||
def collect_audit_findings(config: dict[str, Any]) -> list[dict[str, Any]]:
|
||
findings: list[dict[str, Any]] = []
|
||
allow_from = config.get("allow_from", [])
|
||
|
||
if len(allow_from) > 1000:
|
||
findings.append(
|
||
{
|
||
"severity": "warning",
|
||
"category": "allowlist_size",
|
||
"message": f"allowlist 规模异常({len(allow_from)} 条目 > 1000)",
|
||
}
|
||
)
|
||
|
||
return findings
|