ForcePilot/backend/package/yuxi/channels/adapters/wechat/diagnose.py
Kris 05abecc02b feat(wechat): 新增完整微信渠道适配器实现
该提交实现了支持企业微信、微信公众号、个人微信桥接三种模式的完整微信渠道适配器,包含以下核心模块:
1. 基础认证与配置相关:auth_adapter、config_reload、setup_contract等
2. 消息处理与格式转换:format、attachment_adapter、outbound_adapter等
3. 多模式客户端支持:wecom/mp子模块,包含加解密、消息收发能力
4. 辅助能力:限速器、防抖、会话绑定、事件映射、模板渲染等
5. 扩展能力:二维码登录、消息读取、特权用户、心跳监控等

实现了完整的微信生态对接能力,支持消息收发、事件处理、API调用限流、配置热重载等功能。
2026-05-12 00:51:04 +08:00

149 lines
5.8 KiB
Python

from __future__ import annotations
from typing import Any, TYPE_CHECKING
import httpx
if TYPE_CHECKING:
from .adapter import WeChatAdapter
async def diagnose_bridge(bridge_client) -> dict[str, Any]:
findings: list[dict[str, Any]] = []
try:
status = await bridge_client.check_login_status()
if not status.get("logged_in"):
findings.append({"severity": "error", "message": "Bridge not logged in"})
else:
findings.append({"severity": "ok", "message": "Bridge connected and logged in"})
except Exception as e:
findings.append({"severity": "error", "message": f"Bridge unreachable: {e}"})
return {
"health": "degraded" if any(f["severity"] == "error" for f in findings) else "healthy",
"findings": findings,
}
async def repair_bridge(bridge_client) -> dict[str, Any]:
actions: list[dict[str, Any]] = []
try:
status = await bridge_client.check_login_status()
if not status.get("logged_in"):
actions.append({"action": "relogin", "status": "pending"})
except Exception as e:
actions.append({"action": "restart_bridge", "status": "error", "error": str(e)})
return {"repaired": len([a for a in actions if a.get("status") == "ok"]) > 0, "actions": actions}
async def diagnose_wecom(client, http_client, config: dict[str, Any]) -> dict[str, Any]:
findings: list[dict[str, Any]] = []
try:
token = await client.get_access_token()
if token:
findings.append({"severity": "ok", "message": "WeCom access token valid"})
else:
findings.append({"severity": "error", "message": "WeCom access token invalid"})
except Exception as e:
findings.append({"severity": "error", "message": f"WeCom auth failed: {e}"})
try:
if http_client:
resp = await http_client.get("https://qyapi.weixin.qq.com", timeout=httpx.Timeout(5.0))
findings.append({"severity": "ok", "message": f"WeCom API reachable (HTTP {resp.status_code})"})
except Exception as e:
findings.append({"severity": "error", "message": f"WeCom API unreachable: {e}"})
webhook_url = config.get("webhook_url", "")
if not webhook_url:
findings.append({"severity": "warning", "message": "Webhook URL not configured"})
token_cfg = config.get("token", "")
if not token_cfg:
findings.append(
{"severity": "warning", "message": "Webhook token not configured, signature verification disabled"}
)
return {"health": "healthy" if all(f["severity"] == "ok" for f in findings) else "degraded", "findings": findings}
async def diagnose_mp(client, http_client, config: dict[str, Any]) -> dict[str, Any]:
findings: list[dict[str, Any]] = []
try:
token = await client.get_access_token()
if token:
findings.append({"severity": "ok", "message": "MP access token valid"})
else:
findings.append({"severity": "error", "message": "MP access token invalid"})
except Exception as e:
findings.append({"severity": "error", "message": f"MP auth failed: {e}"})
try:
if http_client:
resp = await http_client.get("https://api.weixin.qq.com", timeout=httpx.Timeout(5.0))
findings.append({"severity": "ok", "message": f"MP API reachable (HTTP {resp.status_code})"})
except Exception as e:
findings.append({"severity": "error", "message": f"MP API unreachable: {e}"})
webhook_url = config.get("webhook_url", "")
if not webhook_url:
findings.append({"severity": "warning", "message": "Webhook URL not configured"})
return {"health": "healthy" if all(f["severity"] == "ok" for f in findings) else "degraded", "findings": findings}
async def diagnose_channel(adapter: WeChatAdapter) -> dict[str, Any]:
findings: list[dict[str, Any]] = []
mode = adapter._mode
if adapter._banned:
findings.append(
{"severity": "error", "message": f"Channel is banned: {adapter._banned_reason or 'API unauthorized'}"}
)
return {"health": "unhealthy", "findings": findings}
if not adapter._http_client:
findings.append({"severity": "error", "message": "HTTP client not initialized"})
return {"health": "unhealthy", "findings": findings}
mode_result: dict[str, Any] = {}
if mode == "wecom" and adapter._wecom_client:
mode_result = await diagnose_wecom(adapter._wecom_client, adapter._http_client, adapter.config)
elif mode == "mp" and adapter._mp_client:
mode_result = await diagnose_mp(adapter._mp_client, adapter._http_client, adapter.config)
elif mode == "personal" and adapter._bridge_client:
mode_result = await diagnose_bridge(adapter._bridge_client)
else:
findings.append({"severity": "error", "message": "No active mode"})
return {"health": "unknown", "findings": findings}
findings.extend(mode_result.get("findings", []))
return {
"health": mode_result.get("health", "unknown"),
"mode": mode,
"findings": findings,
}
async def repair_channel(adapter: WeChatAdapter) -> dict[str, Any]:
mode = adapter._mode
actions: list[dict[str, Any]] = []
if adapter._banned:
actions.append(
{"action": "unban", "status": "error", "message": "Channel is banned, requires manual intervention"}
)
if mode == "personal" and adapter._bridge_client:
result = await repair_bridge(adapter._bridge_client)
actions.extend(result.get("actions", []))
return {"repaired": result.get("repaired", False), "actions": actions}
if mode in ("wecom", "mp"):
actions.append(
{"action": "token_refresh", "status": "ok", "message": "Token refresh will be attempted on next connect"}
)
return {"repaired": False, "actions": actions}