本次提交包含多项优化与新增功能: 1. 清理多个文件中多余的空行与导入顺序 2. 修复voice.py中的多行字符串格式化问题 3. 新增微信公众号被动回复构建函数与配置项 4. 新增企业微信markdown消息发送支持 5. 新增消息去重TTL与最大条目配置 6. 新增markdown文本截断工具函数 7. 新增微信授权与OAuth相关工具方法 8. 重构消息去重逻辑,使用DedupPolicy替代本地字典实现 9. 新增子账号多租户支持功能 10. 新增消息动作处理适配器,支持send/reply等操作 11. 修复token持久化逻辑,新增状态存储支持
147 lines
5.8 KiB
Python
147 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
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}
|