73 lines
2.3 KiB
Python
73 lines
2.3 KiB
Python
#!/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 = "/home/hmo/.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()
|