From 9fad0cfa14299173145fcd90ca19967e2934b019 Mon Sep 17 00:00:00 2001 From: hmo Date: Thu, 23 Jul 2026 15:53:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20kanban=E2=86=92XMPP=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E6=A1=A5=EF=BC=88assignee=3Dxiaoxiao/xxm=E7=9A=84=E5=8D=A1?= =?UTF-8?q?=E7=89=87DM=E7=BB=99xxm@yoin.fun=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - hermes gateway dispatcher只派给246本地hermes profile, 小小莫在Windows够不着 - 每2分钟扫kanban.db, [Kanban] card.assigned格式DM, 去重状态文件 --- deploy/profile-scripts/kanban_xmpp_bridge.py | 72 ++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 deploy/profile-scripts/kanban_xmpp_bridge.py diff --git a/deploy/profile-scripts/kanban_xmpp_bridge.py b/deploy/profile-scripts/kanban_xmpp_bridge.py new file mode 100644 index 00000000..786a4d18 --- /dev/null +++ b/deploy/profile-scripts/kanban_xmpp_bridge.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""kanban_xmpp_bridge.py — kanban→XMPP 通知桥(小小莫专用) + +背景:hermes gateway 内置 kanban dispatcher 把卡片派给 246 上的 hermes profile +(zhiwei/mohe),但小小莫在 Windows(xxm@yoin.fun),dispatcher 够不着。 +本脚本每 2 分钟扫一次 kanban.db,把 assignee=xiaoxiao/xxm 的新卡片以 +`[Kanban] card.assigned t_xxx 标题` 格式 DM 给 xxm@yoin.fun(kanban-handler 协议)。 + +只桥接 xiaoxiao/xxm —— 其他 assignee 由 gateway dispatcher 负责,避免重复通知。 +""" +import json, os, sqlite3, time, urllib.request + +KANBAN_DB = os.path.expanduser("~/.hermes/kanban.db") +STATE = "/home/hmo/MoFin/gateway/logs/kanban_bridge_state.json" +XMPP_SEND = "http://127.0.0.1:5805/" +TARGET_JID = "xxm@yoin.fun" +MY_ASSIGNEES = ("xiaoxiao", "xxm", "小小莫") + + +def _load_state(): + try: + return set(json.load(open(STATE))) + except Exception: + return set() + + +def _save_state(notified): + keep = sorted(notified)[-200:] + os.makedirs(os.path.dirname(STATE), exist_ok=True) + with open(STATE, "w") as f: + json.dump(keep, f) + + +def _send_xmpp(body, to=TARGET_JID): + req = urllib.request.Request( + XMPP_SEND, + data=json.dumps({"body": body, "to": to, "type": "chat"}).encode(), + headers={"Content-Type": "application/json"}) + urllib.request.urlopen(req, timeout=5) + + +def main(): + if not os.path.exists(KANBAN_DB): + return + notified = _load_state() + db = sqlite3.connect(KANBAN_DB) + rows = db.execute( + "SELECT id, title, assignee, created_by FROM tasks " + "WHERE status='ready' AND assignee IN (%s) ORDER BY created_at" + % ",".join("?" * len(MY_ASSIGNEES)), MY_ASSIGNEES).fetchall() + db.close() + + sent = 0 + for tid, title, assignee, creator in rows: + if tid in notified: + continue + body = f"[Kanban] card.assigned {tid} {title}(来自 {creator or '?'})" + try: + _send_xmpp(body) + notified.add(tid) + sent += 1 + print(f"已通知 {tid}: {title[:40]}", flush=True) + time.sleep(1) # 防XMPP突发 + except Exception as e: + print(f"通知失败 {tid}: {e}", flush=True) + if sent: + _save_state(notified) + print(f"scan done: {len(rows)} ready cards, {sent} notified", flush=True) + + +if __name__ == "__main__": + main()