#!/usr/bin/env python3 """XMPP Bot zhiwei@yoin.fun - Hermes API 版(稳定重连版)""" import asyncio, logging, ssl, json, urllib.request, os, subprocess, time from xml.sax.saxutils import escape logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s') GATEWAY = "http://localhost:8643/v1/chat/completions" API_KEY = "hermes123" _opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) def send(from_jid, to_jid, body): safe = escape(body) subprocess.run(["docker","exec","ejabberd","ejabberdctl","send_stanza", from_jid, to_jid, f"{safe}" ], capture_output=True, timeout=10) class ZhiweiBot: def __init__(self): import slixmpp self.xmpp = slixmpp.ClientXMPP('zhiwei@yoin.fun', 'hermes123') self.xmpp.add_event_handler('session_bind', self.on_bind) self.xmpp.add_event_handler('message', self.on_msg) self.xmpp.add_event_handler('disconnected', self.on_disconnect) self.xmpp.add_event_handler('connected', self.on_connected) # 启用slixmpp内置自动重连(已禁用—与手动重连冲突) # self.xmpp.auto_reconnect = True ctx = ssl.create_default_context(); ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE self.xmpp.ssl_context = ctx self.ready = asyncio.Event() self._call_seq = 0 async def on_connected(self, event): logging.info("🔗 知微TCP连接已建立") async def on_bind(self, event): self.xmpp.send_presence(); self.xmpp.get_roster(); self.ready.set() logging.info("✅ 知微上线") async def on_disconnect(self, event): self.ready.clear() logging.warning("⚠️ 知微断线") # 不要在这里调用 self.xmpp.disconnect(),让 auto_reconnect 处理 async def on_msg(self, msg): body = msg['body']; sender = str(msg['from']) if not body or msg['type'] != 'chat': return if 'hmo@yoin.fun' in sender: self._call_seq += 1 logging.info(f"📩 老爸(#{self._call_seq}): {body}") try: payload = json.dumps({ "model":"hermes-agent", "messages":[{"role":"user","content":f"[zhiwei] {body}"}] }).encode() req = urllib.request.Request(GATEWAY, data=payload, method="POST") req.add_header("Content-Type","application/json") req.add_header("Authorization",f"Bearer {API_KEY}") req.add_header("X-Hermes-Session-Id","xmpp-zhiwei") loop = asyncio.get_event_loop() result = await loop.run_in_executor(None, lambda: _opener.open(req, timeout=600)) data = json.loads(result.read()) reply = data.get("choices",[{}])[0].get("message",{}).get("content","") finish = data.get("choices",[{}])[0].get("finish_reason","") if reply.strip() and finish != "silent": send("zhiwei@yoin.fun", sender, reply) logging.info(f"✅ 知微回复: {reply[:80]}") except Exception as e: logging.error(f"❌ 知微错误: {e}") async def main(): retry_delay = 1 max_delay = 60 while True: try: z = ZhiweiBot() z.xmpp.register_plugin('xep_0030'); z.xmpp.register_plugin('xep_0199') z.xmpp.connect(host='127.0.0.1', port=5222) await asyncio.wait_for(z.ready.wait(), timeout=30); logging.info("知微就绪") retry_delay = 1 # 保持运行,断线时自动重连 while True: await asyncio.sleep(15) if not z.xmpp.is_connected(): logging.warning("知微连接丢失,准备重连...") break except Exception as e: logging.error(f"知微main错误: {e}") # 指数退避重连 logging.info(f"⏳ 知微等待 {retry_delay} 秒后重连...") await asyncio.sleep(retry_delay) retry_delay = min(retry_delay * 2, max_delay) if __name__ == '__main__': asyncio.run(main())