Files
hmo 1b2b935832 Initial: multi-agent XMPP communication system with dashboard
- Platform-based architecture (Windows/Linux/Mac)
- Agent instance registry (agents.yaml)
- Management dashboard with cross-platform monitoring
- xmpp_bot with HTTP bridge + health endpoints
- wechat_agent with WeChat-Hermes bridging
- Platform services: ProcessGuardian, HealthProbe, APIRouter, ChannelBridge
- Deployment: systemd (Linux) + PowerShell (Windows)
- Monitoring: SSH+ejabberdctl for cross-platform presence
2026-06-12 21:51:36 +08:00

117 lines
4.5 KiB
Python

"""
VoceChat Webhook → Session Bridge.
Receives VoceChat webhook events, forwards to opencode serve session,
captures AI reply, and sends it back to the VC group.
"""
import os, sys, json, time, threading, urllib.request
from http.server import HTTPServer, BaseHTTPRequestHandler
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from chat_bridge import SessionBridge
from session_router import SessionRouter
from proc_guard import guard as _proc_guard
# ── PID lock — prevent duplicate instances ──
_lock = _proc_guard("vc_webhook")
if not _lock.ok:
print(_lock.message, flush=True)
sys.exit(1)
# ── Config ────────────────────────────────────────────────
SERVE_URL = "http://127.0.0.1:4096"
ATTACH_SESSION = "ses_1d95d15c4ffehQaZ6hrbIbak5k"
VC_API = "http://192.168.1.246:3009"
VC_BOT_KEY = os.environ.get(
"VC_BOT_KEY",
"5b2bd4ce2e0395503b4849a69a47a4e2a3f7aa81af242d2666b31e7519589c477b22756964223a362c226e6f6e6365223a2252576a744643384947476f41414141417a4c6a6e355a7a484731723839494b59227d")
VC_SELF_UID = 6
SPEAKERS = {1: "老爸", 5: "莫荷", VC_SELF_UID: "莫笑笑"}
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "logs")
LOG_FILE = os.path.join(LOG_DIR, "vc_webhook.log")
os.makedirs(LOG_DIR, exist_ok=True)
# ── Logging ───────────────────────────────────────────────
def log(msg: str):
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"{time.strftime('%H:%M:%S')} {msg}\n")
# ── Router ────────────────────────────────────────────────
_router = SessionRouter(
bridge=SessionBridge(session_id=ATTACH_SESSION, serve_url=SERVE_URL),
default_session=ATTACH_SESSION,
)
def _speaker(uid: int) -> str:
return SPEAKERS.get(uid, f"用户{uid}")
def _send_to_vc_group(gid: int, text: str):
"""Post reply to a VoceChat group via Bot API."""
url = f"{VC_API}/api/bot/send_to_group/{gid}"
headers = {"X-API-Key": VC_BOT_KEY, "Content-Type": "text/plain"}
urllib.request.urlopen(
urllib.request.Request(url, data=text.encode("utf-8"), headers=headers),
timeout=10)
def _process_message(content: str, sender: int, data: dict):
"""VC message → router → reply → VC."""
if sender == VC_SELF_UID:
return
log(f"router.route: sender={sender} content={content[:50]}...")
reply = _router.route("vc", str(sender), content)
if reply:
log(f"reply[:80]={reply[:80]}")
gid = data.get("target", {}).get("gid", 0)
if gid:
try:
_send_to_vc_group(gid, reply)
log(f"Replied to VC group {gid}")
except Exception as e:
log(f"VC reply ERR: {e}")
else:
log("no text reply in time")
# ── HTTP Handler ──────────────────────────────────────────
class WebhookHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b"ok")
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
log(f"RAW: {body.decode('utf-8', errors='replace')[:300]}")
try:
data = json.loads(body)
if data.get("type") in ("new_message", "chat"):
detail = data.get("detail", {})
content = detail.get("content", "") or data.get("content", "")
sender = data.get("from_uid", 0)
log(f"MSG uid={sender}: {str(content)[:80]}")
threading.Thread(
target=_process_message,
args=(str(content), sender, data),
daemon=True,
).start()
except Exception as e:
log(f"ERR: {e}")
self.send_response(200)
self.end_headers()
def log_message(self, *args):
pass
# ── Main ──────────────────────────────────────────────────
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8010), WebhookHandler)
log("VC webhook listening on :8010")
server.serve_forever()