From 6f7c904cca0f38a31aea8e25a10bd7ba34e28586 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 25 Jun 2026 10:10:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(wecom):=20=E6=8E=A8=E9=80=81=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E9=80=8F=E5=87=BA=E7=9C=9F=E5=AE=9E=20errcode/errmsg?= =?UTF-8?q?=20+=20=E5=8A=A0=20diag=5Fwecom=20=E8=AF=8A=E6=96=AD=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=20+=20bump=200.26.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前推送失败只回 error:RuntimeError,吞了企业微信的 errcode。改成 reason=str(e) (含 gettoken/message_send 失败的 errcode+errmsg);scripts/diag_wecom.py 分步查 gettoken vs send 的确切 errcode,服务器上直跑即可定位(可见范围/userid/凭据)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- core/__init__.py | 2 +- core/wechat/service.py | 6 ++--- scripts/diag_wecom.py | 59 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 scripts/diag_wecom.py diff --git a/core/__init__.py b/core/__init__.py index b197b56..1988beb 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.26.3" +__version__ = "0.26.4" diff --git a/core/wechat/service.py b/core/wechat/service.py index feb1f63..7501230 100644 --- a/core/wechat/service.py +++ b/core/wechat/service.py @@ -190,7 +190,7 @@ def push_clawbot( if file_path: client.send_file(snap.user_im_id, snap.context_token, file_path) except Exception as e: # noqa: BLE001 —— 调用方据 reason 决定兜底 - return PushResult(False, reason=f"error:{type(e).__name__}") + return PushResult(False, reason=f"error: {str(e)[:200]}") return PushResult(True, reason="sent") @@ -236,8 +236,8 @@ def push_wecom(user_id: UUID, text: str = "", file_path: Optional[str] = None) - wecom.send_text(wuid, text) if file_path: wecom.send_file(wuid, file_path) - except Exception as e: # noqa: BLE001 - return PushResult(False, channel="wecom", reason=f"error:{type(e).__name__}") + except Exception as e: # noqa: BLE001 —— 透出 errcode/errmsg 便于排错 + return PushResult(False, channel="wecom", reason=f"error: {str(e)[:200]}") return PushResult(True, channel="wecom", reason="sent") diff --git a/scripts/diag_wecom.py b/scripts/diag_wecom.py new file mode 100644 index 0000000..c161ae6 --- /dev/null +++ b/scripts/diag_wecom.py @@ -0,0 +1,59 @@ +"""企业微信推送诊断:分步查 gettoken / message_send 的确切 errcode/errmsg。 + +用法(服务器上,.env 同目录): + .venv/Scripts/python.exe scripts/diag_wecom.py +读 .env 的 WECOM_CORPID/AGENTID/SECRET。ASCII 输出,secret 不打印。 + +常见 errcode: + gettoken: 40013=corpid 错 / 40001|42001=secret 错 / 41002=缺 corpid + send: 60011=无权限(应用可见范围没包含该成员)/ 81013=UserID 不存在 + 40056=agentid 错 / 60020=IP 不在可信IP / 81014=该成员未关注/未激活 +""" +import os +import sys + +try: + from dotenv import find_dotenv, load_dotenv + load_dotenv(find_dotenv()) +except Exception: + pass + +from core.wechat import wecom + + +def main() -> int: + uid = sys.argv[1] if len(sys.argv) > 1 else None + print("[cfg] configured:", wecom.wecom_configured()) + print("[cfg] corpid:", (os.getenv("WECOM_CORPID", "") or "")[:8] + "...", + "| agentid:", os.getenv("WECOM_AGENTID", "")) + if not wecom.wecom_configured(): + print("[FAIL] WECOM_CORPID/AGENTID/SECRET 没读到(确认 .env 在当前目录、值已填)") + return 1 + + print("[step1] gettoken ...") + try: + tok = wecom.get_access_token(force=True) + print(f"[step1] OK (token len {len(tok)})") + except Exception as e: + print(f"[step1] FAIL: {e}") + print(" → corpid 或 secret 不对(secret 必须是这个自建应用的,不是通讯录密钥)") + return 2 + + if not uid: + print("[step2] 跳过(没给 userid 参数);用法: diag_wecom.py ") + return 0 + + print(f"[step2] message/send 到 userid={uid} ...") + try: + wecom.send_text(uid, "zcbot 企业微信诊断测试消息") + print(f"[step2] OK → 去企业微信查收。链路通了!") + except Exception as e: + print(f"[step2] FAIL: {e}") + print(" → 看 errcode:60011=应用可见范围没含该成员 / 81013=userid 写错" + "(大小写要和通讯录「账号」完全一致)/ 40056=agentid 错") + return 3 + return 0 + + +if __name__ == "__main__": + sys.exit(main())