"""诊断微信对话里 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)))