zcbot/scripts/diag_wechat_push.py

88 lines
3.2 KiB
Python

"""诊断微信对话里 wechat_push 发文件失败:dump 绑定状态 + 微信 task 里 wechat_push 工具调用与返回。
ASCII 标签(Windows GBK 安全)。用法:.venv/Scripts/python.exe scripts/diag_wechat_push.py [email]
"""
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
env = Path(__file__).resolve().parent.parent / ".env"
for line in env.read_text(encoding="utf-8").splitlines():
if line.strip().startswith("ZCBOT_DB_URL="):
os.environ["ZCBOT_DB_URL"] = line.split("=", 1)[1].strip()
from sqlalchemy import create_engine, text # noqa: E402
import builtins # noqa: E402
_out = open(Path(__file__).resolve().parent / "_wechat_push_dump.txt", "w", encoding="utf-8")
def print(*a, **k): # noqa: A001
builtins.print(*a, **k, file=_out)
engine = create_engine(os.environ["ZCBOT_DB_URL"])
email = sys.argv[1] if len(sys.argv) > 1 else "caoqianming@foxmail.com"
def s(x, n=2000):
t = str(x or "")
return t if len(t) <= n else t[:n] + f"...[+{len(t)-n}]"
with engine.connect() as conn:
row = conn.execute(text("select user_id from users where email=:e"), {"e": email}).fetchone()
if not row:
print("[NO USER]", email); sys.exit(1)
uid = row[0]
print("[USER]", uid)
b = conn.execute(text(
"select user_im_id, base_url, status, context_token_at, "
"(latest_context_token is not null) as has_ctx, chat_task_id "
"from wechat_bot_bindings where user_id=:u"), {"u": uid}).fetchone()
if not b:
print("[NO BINDING]"); sys.exit(1)
print("[BINDING] status=%s user_im_id=%s has_ctx=%s ctx_at=%s base=%s" % (
b.status, b.user_im_id, b.has_ctx, b.context_token_at, b.base_url))
print("[BINDING] chat_task_id=%s" % b.chat_task_id)
if b.context_token_at:
at = b.context_token_at
if at.tzinfo is None:
at = at.replace(tzinfo=timezone.utc)
age = datetime.now(timezone.utc) - at
print("[BINDING] ctx age = %s (fresh if <24h)" % age)
tid = b.chat_task_id
if not tid:
print("[NO CHAT TASK]"); sys.exit(0)
# dump messages, focus on wechat_push tool calls/results
rows = conn.execute(text(
"select idx, payload from messages where task_id=:t order by idx desc limit 60"),
{"t": tid}).fetchall()
print("\n[MESSAGES] last %d (newest first):" % len(rows))
for idx, payload in rows:
if isinstance(payload, str):
try:
payload = json.loads(payload)
except Exception:
pass
if not isinstance(payload, dict):
continue
role = payload.get("role")
# assistant tool_calls
tcs = payload.get("tool_calls") or []
for tc in tcs:
fn = (tc.get("function") or {})
if fn.get("name") == "wechat_push":
print(" #%s [CALL wechat_push] args=%s" % (idx, s(fn.get("arguments"), 800)))
# tool result
if role == "tool":
name = payload.get("name", "")
content = payload.get("content")
if name == "wechat_push" or "微信" in s(content, 200) or "wechat" in s(name):
print(" #%s [TOOL RESULT %s] %s" % (idx, name, s(content, 800)))