refactor(xxm): consolidate 4 bot implementations into unified xmpp_agent_core.py

- Merge bot_base.py, gateway/scripts/xmpp_bot.py, bots/*, xmpp_bot_rest.py
  into single xmpp_agent_core.py with --agent flag (xxm|mohe|zhiwei|xiaoguo)
- Add xxm_bot.py wrapper (encoding=utf-8 for Windows exec)
- Fix slixmpp connect() API: use host=/port= keyword args (was tuple)
- Clean up orphans: bots/, scripts/, hermes_state.py, xmpp_bot.py, xmpp_bot_rest.py
- Add docs/CLEANUP_PLAN.md documenting the migration
- Update README.md project structure
- Also: fix WeChat agent path resolution (relative paths)
This commit is contained in:
hmo
2026-06-21 16:13:57 +08:00
parent b9df510f31
commit babbc46801
22 changed files with 1273 additions and 5442 deletions
+34 -20
View File
@@ -66,9 +66,9 @@ def get_db_handle():
dbs = r.get("data") or []
for db in dbs:
dbname = db.get("databaseName", "")
if "MSG" in dbname or "Msg" in dbname:
db_handle_cache = db.get("handle")
return db_handle_cache
if dbname.startswith("MSG") and "Media" not in dbname:
db_handle_cache = db.get("handle")
return db_handle_cache
return None
@@ -94,24 +94,42 @@ def query_history(wxid, limit=10):
return None
limit_val = min(int(limit), 200)
sql = (
f"SELECT CreateTime, IsSender, Type, SubType, StrContent, DisplayContent "
f"FROM MSG WHERE StrTalker='{wxid}' AND Type IN (1,49) "
f"SELECT CreateTime, IsSender, Type, SubType, StrContent, DisplayContent, CompressContent, BytesExtra "
f"FROM MSG WHERE StrTalker='{wxid}' AND Type IN ('1','49') "
f"ORDER BY CreateTime DESC LIMIT {limit_val}"
)
r = wxpost("/api/execSql", {"dbHandle": h, "sql": sql}, timeout=15)
data = r.get("data") or []
if not data or len(data) < 2:
return None
# Skip header row, reverse to chronological order
rows = data[1:]
# wxhelper returns [{value: [cols]}, {value: [row1]}, ...]
rows = [item.get("value", item) if isinstance(item, dict) else item for item in data]
rows = rows[1:] # skip header
rows.reverse()
results = []
for row in rows:
content = (row[4] or "").strip() if len(row) > 4 else ""
if not content and len(row) > 5:
content = (row[5] or "").strip()
# Type 49 (article link): extract URL from CompressContent or BytesExtra
if not content and str(row[2]) == "49":
try:
import re
# Try BytesExtra first (row[7])
for idx in [7, 6]:
if idx < len(row) and row[idx]:
text = str(row[idx])
urls = re.findall(r'https?://[^\s\x00-\x1f<>\"\']{10,}', text)
if urls:
content = urls[0]
break
except:
pass
if not content:
continue
if str(row[2]) == "49":
content = "[文章链接]"
else:
continue
results.append({
"CreateTime": row[0],
"IsSender": row[1],
@@ -181,29 +199,25 @@ def get_recent_chats(limit=20):
if not h:
return []
sql = (
f"SELECT StrTalker, MAX(CreateTime) as last_time, COUNT(*) as msg_count "
f"FROM MSG WHERE Type IN (1,49) "
f"GROUP BY StrTalker ORDER BY last_time DESC LIMIT {min(limit, 50)}"
f"SELECT DISTINCT StrTalker FROM MSG WHERE Type IN ('1','49') "
f"LIMIT {min(limit, 50)}"
)
r = wxpost("/api/execSql", {"dbHandle": h, "sql": sql}, timeout=15)
data = r.get("data") or []
if not data or len(data) < 2:
return []
rows = [item.get("value", item) if isinstance(item, dict) else item for item in data]
results = []
for row in data[1:]:
for row in rows[1:]:
wxid = (row[0] or "").strip()
if not wxid or wxid in ("fmessage", "weixin", "wechat", "filehelper"):
if not wxid or wxid in ("fmessage", "weixin", "wechat", "filehelper", "medianote", "floatbottle", "qmessage"):
continue
if wxid.startswith("gh_"):
continue
ts = int(row[1]) if row[1] else 0
count = int(row[2]) if len(row) > 2 and row[2] else 0
results.append({
"wxid": wxid,
"nickname": get_nickname(wxid),
"last_message_time": datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") if ts else None,
"last_message_ts": ts,
"message_count": count,
"last_message_time": None,
"last_message_ts": 0,
"message_count": 0,
})
return results