Files
AgentsMeeting/gateway/scripts/specs/xmpp_bot.json
T

119 lines
10 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"module": "xmpp_bot",
"version": "3.0",
"purpose": "XMPP Bot — 笑笑(xxm@yoin.fun)。统一 Bot 核心 xmpp_agent_core.py--agent xxm)连接 ejabberd via slixmpp,桥接 XMPP 消息到 OpenCode serve session,提供 HTTP bridge (:5807) 给 dashboard 代理 EasyTier/RDP/health 查询。支持 MUC 群聊、私有聊天、消息去重、协调者协议、MAM 恢复守护。",
"ui_location": "Agents tab → xxm Agent 卡片 + Services 标签",
"human_help": {
"title": "XMPP Bot — xxm 消息收发",
"description": [
"xxm 消息机器人(JID: xxm@yoin.fun)运行在 Windows 开发机,由统一 Bot 核心 xmpp_agent_core.py 以 --agent xxm 启动(兼容入口 xxm_bot.py)。它连接 ejabberd XMPP 服务器,把群聊和私聊消息桥接到 OpenCode serve session,让 AI 能参与 XMPP 群聊对话。",
"核心功能:XMPP 消息收发(MUC 群聊 + 私聊)、HTTP bridge 服务(:5807,提供 /health /muc /easytier /rdp /usage /send 端点)、消息去重(100条缓存)、协调者协议(coordinator/GRANT/REVOKE in-band 信令)、5分钟沉默 cooldown。",
"去重机制:通过 XMPP stanza message ID 去重,100 条缓存用完自动清空。防止 MAM 恢复和实时消息重复处理。",
"协调者协议:mohe 是默认协调者。hmo 可切换 coordinator={name}。支持 GRANT(授权一次发言)和 REVOKE(禁言5分钟)。全部通过 XMPP in-band 消息信令,无需外部 DB。",
"HTTP bridge 安全:/easytier /rdp /usage 等端点需要 X-Api-Key 认证(与 dashboard 的 _BRIDGE_KEY 匹配)。/health 免认证供监控系统使用。"
],
"participants": [
{"name": "小小莫", "device": "Windows 192.168.1.16", "role": "xmpp bot 运行者 + OpenCode serve session 所有者", "note": "bot 跑在本地 Python 进程中(xmpp_agent_core.py --agent xxm"},
{"name": "ejabberd", "device": "246 Docker", "role": "XMPP 服务器 — 消息路由", "note": "192.168.1.246:5222"},
{"name": "莫荷", "device": "Linux 192.168.1.246", "role": "默认协调者 + dashboard 使用者", "note": "dashboard 通过 _bridge_post 代理到 xmpp bot HTTP bridge (:5807)"}
],
"usage": [
"1. 启动 bot: python xmpp_agent_core.py --agent xxm(自动获取 PID 锁,防重复)",
"2. bot 自动连接 ejabberd 并加入 MUC 群聊: coregroup + jujidina",
"3. 私聊消息: 直接发送到 OpenCode session → AI 处理 → 通过 XMPP 回复",
"4. 群聊消息: 通过 MUC 接收 → 去重 + 协调者检查 → AI 处理 → MUC 回复",
"5. 协调者控制: hmo 在群里发 '[GRANT:xxm]' 授权/取消禁言,'[REVOKE:xxm]' 禁言5分钟",
"6. 沉默命令: 对 bot 说 '闭嘴'/'别说话'/'安静' → 5分钟静默",
"7. HTTP bridge: GET :5807/health 查连接状态,POST :5807/easytier 控制 VPN"
],
"troubleshooting": [
"如果 bot 不回复消息: 检查 OpenCode serve session 是否在线 (localhost:4096) — session_attach 超时5min",
"如果群聊收不到: 检查 MUC join 是否成功 — 看日志是否有 'joined MUC' 或 MAM recovery 是否卡住",
"如果 HTTP bridge 不可达: 检查 5807 端口 — netstat -ano | findstr 5807",
"如果 dashboard EasyTier/RDP 无响应: 检查 _BRIDGE_KEY 是否匹配 — dashboard 和 bot 必须一致;再确认 dashboard 的 XMPP_BRIDGE_URL 指向 :5807",
"如果消息重复: 去重缓存100条上限后自动清空 — 极少数情况下可能漏过去,通常无害",
"如果 bot 启动失败: 检查 proc_guard PID 锁 — 删除 temp/.xmpp_bot.pid 后重试"
],
"related": "依赖 chat_bridge.py (SessionBridge) + session_router.py (SessionRouter) + proc_guard.py (PID 锁)"
},
"ai_spec": {
"apis": [
{"method": "GET", "path": "/health", "port": 5807, "returns": "{ok, xmpp_connected, ejabberd_alive, bot_jid, uptime_seconds}", "note": "XMPP 连接状态 + bot 自检 — 免认证(供监控系统使用)"},
{"method": "GET", "path": "/muc", "port": 5807, "returns": "{rooms: {room_name: {participants: [{jid, nick}]}}}", "note": "MUC 群聊参与者列表(R01 已知不稳定)— 需 X-Api-Key"},
{"method": "POST", "path": "/easytier", "port": 5807, "body": "{\"action\":\"status|start|stop\"}", "returns": "{ok, running, ...}", "note": "EasyTier VPN 控制 — 需 X-Api-Key"},
{"method": "POST", "path": "/rdp", "port": 5807, "body": "{\"action\":\"status|start|stop\"}", "returns": "{ok, rdp_enabled, tunnel_running}", "note": "RDP 远程桌面控制 — 需 X-Api-Key"},
{"method": "POST", "path": "/usage", "port": 5807, "body": "{\"action\":\"status|collect_now\"}", "returns": "{ok, accounts, ...}", "note": "OpenCode Go 用量查询/采集 — 需 X-Api-Key"},
{"method": "POST", "path": "/send", "port": 5807, "body": "{\"message\":\"text\"}", "returns": "{ok}", "note": "发送群聊消息 — 需 X-Api-Key"}
],
"dependencies": [
"slixmpp — XMPP 客户端库 (slixmpp.ClientXMPP)",
"chat_bridge.py — SessionBridge: 连接 OpenCode serve session (ses_xxm_xmpp)",
"session_router.py — SessionRouter: 消息路由 + 命令分发 + 协调者逻辑",
"proc_guard.py — PID 锁 (guard('xmpp_bot'))",
"ejabberd 192.168.1.246:5222 — XMPP 服务器",
"OpenCode serve session (localhost:4096) — AI 处理后端",
"MUC rooms: coregroup@conference.yoin.fun + jujidina@conference.yoin.fun",
"HTTP bridge 由 xmpp_agent_core.py 内嵌 (_BridgeHandler),非独立进程"
],
"architecture": {
"entrypoint": "xmpp_agent_core.py --agent xxm(兼容入口 xxm_bot.py → xmpp_agent_core.py --agent xxm",
"flow": "XMPP消息 → slixmpp → 去重 (_is_duplicate) → MAM恢复守护 → 协调者信令解析 → SessionRouter → SessionBridge → OpenCode serve → AI回复 → XMPP发送",
"http_bridge": "HTTP server on :5807_BridgeHandler 线程)— /health /muc /easytier /rdp /usage /send。历史端口 5802 已被 wechat-hermes-gateway 占用(见 xmpp_agent_core.py L79-82 注释)",
"bridge_auth": "_bridge_auth() 校验 X-Api-Key header/health 免认证,L957-958",
"dedup": "threading.Lock 保护的 set — 100条缓存,满了清空",
"mam_recovery": "启动后30s内收到的群消息被丢弃 (MAM历史回放保护) — 超时后强制禁用",
"coordinator": "in-band XMPP 信令 — hmo切换coordinator / GRANT临时授权 / REVOKE禁言5min",
"shutup": "关键词匹配 '闭嘴'/'别说话'/'安静' → 5分钟不回复"
},
"constraints": [
"PID 锁必须获取成功才能启动 — guard('xmpp_bot')",
"HTTP bridge 的 /muc /easytier /rdp /usage /send 需要 X-Api-Key (与 dashboard _BRIDGE_KEY 一致)/health 免认证",
"端口 5807 — 5802 已被 wechat-hermes-gateway 的 xmpp_bot.py 占用,不要改回 5802(曾导致 ~50% /easytier /rdp 请求 400",
"dashboard 侧必须设 XMPP_BRIDGE_URL=http://192.168.1.16:5807246 systemd 已配置)",
"MUC join 时需处理 DNS 解析超时 (conference.yoin.fun) — 用 try/except 包裹",
"去重缓存上限 100 — 足够处理正常流量,极端情况下清空后可能漏1-2条",
"MAM 恢复超时 30s — 如果 ejabberd 没发 MAM history,不会永远卡住",
"不要用 threading 处理消息 — slixmpp 是 asyncio 驱动的"
],
"must_not": [
"不要把 _BRIDGE_KEY 硬编码到 dashboard.py 和 bot 之外的任何文件",
"不要在 MAM 恢复期间处理群消息 — 会导致 AI 收到重复/过时上下文",
"不要修改去重逻辑的锁结构 — threading.Lock 必须保持一致",
"不要把端口改回 5802 — 与 wechat-hermes-gateway 冲突"
],
"related_modules": [
{"module": "dashboard", "relation": "dashboard 通过 _bridge_post 代理 EasyTier/RDP/usage 到 bot HTTP bridge (:5807)"},
{"module": "easytier", "relation": "bot 提供 /easytier 端点给 dashboard 代理 — bot 是 EasyTier 控制的实际执行者"},
{"module": "rdp", "relation": "bot 提供 /rdp 端点 — bot 是 RDP 控制的实际执行者"},
{"module": "usage_monitor", "relation": "bot 提供 /usage 端点 — 旧版用法,新版 dashboard 直接读本地 usage_stats.json"},
{"module": "agents", "relation": "xxm agent 的核心通信层 — dashboard 通过 ejabberdctl 查询 bot 的 JID 在线状态"}
],
"tests": [
{"id": "XB01", "name": "GET /health 返回 xmpp_connected + ejabberd_alive", "endpoint": "GET :5807/health"},
{"id": "XB02", "name": "GET /muc 返回群聊参与者列表", "endpoint": "GET :5807/muc(带 X-Api-Key"},
{"id": "XB03", "name": "POST /easytier status 需认证", "endpoint": "POST :5807/easytier → 无 key 应 401"},
{"id": "XB04", "name": "POST /send 发送消息成功", "endpoint": "POST :5807/send(带 X-Api-Key)→ ok=true"},
{"id": "XB05", "name": "去重: 相同 message ID 第二次被跳过", "endpoint": "注入重复 msg_id → _is_duplicate 返回 True"}
],
"known_issues": [
"R01: MUC join 超时 — conference.yoin.fun DNS 偶尔不可达,需 raw presence 双保险",
"slixmpp 依赖 Python asyncio — 与 Flask (同步 threading) 混跑时偶尔死锁",
"HTTP bridge 无 HTTPS — 仅局域网内使用,不暴露公网",
"去重清空时极少数重复消息可能漏过 — 100条缓存未命中时当做新消息处理",
"历史端口 5802 与 wechat-hermes-gateway 冲突导致 400(已迁移 5807,勿回退)"
],
"related_files": [
"xmpp_agent_core.py — 统一 Bot 核心(HTTP bridge 实现在此,xxm http_port=5807 L82",
"xxm_bot.py — 兼容入口 → xmpp_agent_core.py --agent xxm",
"gateway/scripts/chat_bridge.py — SessionBridge",
"gateway/scripts/session_router.py — SessionRouter + 协调者逻辑",
"gateway/scripts/proc_guard.py — PID 锁",
"gateway/scripts/specs/xmpp_bot.json — 本 spec",
"gateway/logs/xmpp_bot.log — 运行日志",
"gateway/temp/.xmpp_bot.pid — PID 锁文件"
]
}
}