From c98bd91fb990a242acabcfa80cd6105bbc18ce07 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 31 Mar 2026 10:56:15 -0500 Subject: [PATCH] 's' --- __pycache__/config.cpython-312.pyc | Bin 289 -> 1099 bytes __pycache__/crypto_utils.cpython-312.pyc | Bin 3419 -> 4386 bytes __pycache__/main.cpython-312.pyc | Bin 22860 -> 50720 bytes config.py | 18 +- main.py | 247 ++++++++++++++++++----- 5 files changed, 209 insertions(+), 56 deletions(-) diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc index a9ec32de7267dde4f65741a304fee1e1a81aa6b0..4305abd45c8a54492a1ab4b398172c399152c804 100644 GIT binary patch literal 1099 zcmYLIJ#X7a7^XfWC8A}wP64NB8*~U_w3aDLvZbI1Op%mDMWntcQ@Vwqct?r)A}vyq z#i5Y9w4Iu*Yu6$`|45dk8LDs!kf|sN7|52XrzEcLfV=m3pXZ)?aWDCdBySRU?EU-9 z(1Qf=W(}@J;A8Oi4;=hTKmriZ2Yk>E{4fB6!vF}t5D3CB2>A&V=*O-OUhnY6Q{M-4 z?z8Lv2z15ayC;Azco(c`E>V3({b<!}f?NTCkX{&$%CT=g=g|%mda6HxSCg*^|D8J{;}ikoExfX_onK& zn?MsoJMNBDW0=s)ksE7aDt&mE%Tk$RZ+nikp6V#p#F(iLQl>VV*zP}QQR;P0G^1ja z_@>JzMT58baXq6k{2@QETBl4Ym%yThD%XrkpUVg?mlXP%&5v?(AZIMF7LKx+(*~6m zIkh<7+ z*n`+pkhGM@4qU;lQ6e)pK&v>w zuZUP7D2=wrD0EXQeyDHfD#daIx>=SKxs|U-N}gwbC*H2Vvv*O=9P36mVJ+T8z8jfv zIz*2qUtnhaZ`t4DRC{di?PHe3UJyHz&v6O=0T6va@&Et; delta 176 zcmX@jv5?8;G%qg~0}v#XoX+e3(vLwL7+{1lJ_`UD(-~42q8L&bqZm_|qL?a~HCZO! zievWEWS)G5NilgP!)K7n&Ma40RDY=<>F)pda*(Lb}G07Q;B}w@uG0FLP zX_@JI1(m-zY;yBcN^?@}inxIqK~@!u0*MdIjEsyoIHelcZg6Ncu-*`MxX2(-#0iuE E0B=n!%>V!Z diff --git a/__pycache__/crypto_utils.cpython-312.pyc b/__pycache__/crypto_utils.cpython-312.pyc index cc4d9d81adb32d4087fdc9b25882acb577513095..aa20361a916d11cef82bfedd9e8c59062b76d6ca 100644 GIT binary patch delta 1967 zcmZ`)U2GIp6rR7G-<@f@+b-?WUka3Mi%@|U3N=Kq6)PmwNOfOuyLbAt>~8CwX@q9i zE^17x#+D3(ZA7RTwMkV>h=iCJ@IfWPhc5d-Gi{6yw9@#}gg1ly2eP+5!U2vMInrYmPiP^V~vf!|15$K=fir|A_( zl1p%iF{L#am-8slm?6kA1R;i0ib`^(l0!w@R)^?S*04#J^_*CtGN!hM@DPMmENXO- zBQ=Ze;SD)&Lda!F3hQ#OSrK=Vp(w1Fq`%HVuH22c>4boIMER+tXl~5;Bc(a%F-Jl| zB(K*`O+z$_8m1^Dl88wOu}L{0C!`^wDG6m=5n?XU$-_^SAmuzzf(W-2D8Zd@M=D70 ze;*|0urE^q<(N(3Vv>J}1K3g_kU>)UgD*c@xcc70_(zM^M;6aqT{?er;rlO^KDo4b z?K~!RK4PklU{d;E)&HRvzP_;d&36y3zyHVi4yn9*}0s>f}K2!Eq|3zl6&6jjJJBSE$gjMy*RJtk9CfAj(1)RUye>hr=HEKJ5sIIPN8`C zz95h6d*`h*b64=sYsE7qjajW}Mr@kZHl&#}I}(RzTH#o1G?qR*Su?e1s$sG&t35UK zMn-FZ67maDM+megqg8<_Xy?4+omS~xq09+(Agi^^h%K{n!FV9uHY0CN9hz0NvF6d{ z@t&+wKEv%S$9bgT(m*uS2W>nBJr4~GoajU0^&2Yy##!h~R!S&m*(7IuDpb|E9vf0c?Hf%tS!yT0>C1JJ>haF6Jf1>v6LRNRNk)&X2I$uD&nlJ z%9{CWmO`?^dEE4By-6`4MmB)0R7*;acQoQBFlB8`B4v=Ed^o@z!&@PgGt!t0Lm+Qu zLU}z-M$TsX4Of}m7T_X2Jz!!#(WI`%-7Ass z4$uQKZYD?{A^WL4K+A|&4#Z}0=uJ%eX!8Rhgmi?TCW^EgP)7#&qEuE|inQtVRuK2Y zV_XLEpW&UAz4v5)M)s$hvT}9m(0xU9-g?&u&s=Q1+&8Q zl$KlkFY1r#?Vjwx;~C}nwbvw$IY4?7;B28cWb{V+x?qja9^3*(Tk6$Mp}rp7Fatbp z0}FnhkRyb=KuEhaq13oKKy0;sQjU5&PDAif;(LaW?UvuYcO%|LWb&tg$v$H8yKEL_ z5qDTIcUk!>pp)-`@hgyJmZs@BN}Z#$IZ6V-y5jEG@1-jycP&%EZd5z$R`o-oE!T^5 a(R@h;;!w-pZ*=N5* zl?j=JCN3oIjIO{1JK3{=V1gEYf-#NkOpKl>mPgz;$$aO0-*@iG%$fVP`=G_U?eQ1_ ztPjq1Hma?9o0ivsy%dN*P+o{1M-)fE#k^RM;u7MTl$Q%iT!CDUW1`q9!M2)~iTYHI zYiGCyTqnA1fG^o9)QHW{A1V!SOk#p_>>6oI>OdQ*Bc5}j7eo*7f#$cpq#j}bwgns8 zM!JqPfbUyGk;c^@uRHt3M-vYmn>vXkDP(68H*9Cdohv4$Gf6i!=O)+-6h5K z#=3Y}L>JjtxnL@AC<*q_H-Swmop)SLsw*egveQ@+-lJo-B)e)&&uO*nG?qjcvp2>i z_7IzjQc`jr_FHLd#jdYbQ^F^`a0%z?SyHu{QB9i?-ih~qD)Vmo>TpD-9Zc8y+sl~P z({sd*sI(K>&BnaDi1h66;20}aG!M(uHliMp3&+i{v z&HGkx*9xxP+OzuElzwgGi`lSmw(gp(uO{})(2g9U{H$fYm``SBU30pc z2Pc<=oW!rda)vP?yWFm>l3cselJegp^C1tL!5yp}x3d*f7jqVGy?kamv>Uod4B7#q zjO|%I7TO4e@@JzV4q@)8;L*jROItY8Q$x|e#XZ0y8tvtk=LbnKFU7?qAvU3r>UX@0 z0c~vJ@YiO~>biReAmN`oGXVcg`209GbdM}v=kF1b%W5X+Wb)ZmSt^i0+6Q7;&)5qj z``C8e2&Pd`=pct74lxeH?53|r8H03?&HL_Z{_2J3DEsUiZY@iWORKY>{Mji#Sy_Tt zqhqYc-_|q^od3AaCxD8C5IPV%2ZEQS{F70P!fOK+0m5dm8n%K*oK*rC#n!4)Y}4XS OY|G+KtP)_~{n0;-QR@o; diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index 55bbde4a90d1f8194be994673e2c9dcb98a60313..c7300736969239fcdc4e8f7cda01a08e4d8688e5 100644 GIT binary patch literal 50720 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 Vap`sJ!`W4aKw8}uiV(Xge*hqq=n4P; delta 5526 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 D8YJrm diff --git a/config.py b/config.py index 23f72ca..a9ea42d 100644 --- a/config.py +++ b/config.py @@ -2,8 +2,22 @@ APP_ID = "fXYWvGfq04uMScP" TOKEN = "EWRotyMHfTLAlYmCHpdH8AZcuZPBHn" ENCODING_AES_KEY = "1wJZiJm8URfRjkHu1QfIVDeNFc2jbzzeCZqvhQecsCx" -# PocketBase(默认直连本机;与 payjsapi 项目保持同风格) -PB_URL = "http://127.0.0.1:8090" +import os + +# PocketBase 自动切换策略: +# - 本地调试(Windows)优先使用数据库域名 +# - 线上(非 Windows)优先使用内网 IP+端口 +# 你也可以通过环境变量 PB_URL 强制覆盖。 +PB_DOMAIN_URL = "https://pocketbase.hackrobot.cn" +PB_INTERNAL_URL = "http://127.0.0.1:8090" +_pb_force = (os.getenv("PB_URL") or "").strip() +if _pb_force: + PB_URL = _pb_force +elif os.name == "nt": + PB_URL = PB_DOMAIN_URL +else: + PB_URL = PB_INTERNAL_URL + PB_ADMIN_EMAIL = "xiaoshuang.eric@gmail.com" PB_ADMIN_PASSWORD = "Xiao4669805@" PB_COLLECTION = "wechat_private_users" diff --git a/main.py b/main.py index 0ea0396..e34e391 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,8 @@ import json import logging import base64 import time +from html import escape +import xml.etree.ElementTree as ET from datetime import datetime, timezone from typing import Optional @@ -31,16 +33,12 @@ app = FastAPI(title="WeChat Dialog ThirdAPI Demo") _processed_callback_cache: dict[str, int] = {} -def _build_keyword_h5_reply(query: str, user_id: str) -> Optional[str]: - """ - 按关键词返回 H5 卡片(answer/short_answer 需为 JSON 字符串)。 - """ +def _build_keyword_h5_obj(query: str, user_id: str) -> Optional[dict]: q = (query or "").strip() if not q: return None - if "电子书" in q: - msg = { + return { "news": { "articles": [ { @@ -53,10 +51,8 @@ def _build_keyword_h5_reply(query: str, user_id: str) -> Optional[str]: ] } } - return json.dumps(msg, ensure_ascii=False) - if "群" in q: - msg = { + return { "news": { "articles": [ { @@ -69,11 +65,128 @@ def _build_keyword_h5_reply(query: str, user_id: str) -> Optional[str]: ] } } - return json.dumps(msg, ensure_ascii=False) - return None +def _parse_decrypted_payload(decrypted_text: str) -> dict: + """ + 同时兼容 JSON 与 XML 的回调明文,统一成业务侧可读字段。 + """ + text = (decrypted_text or "").strip() + if not text: + raise ValueError("empty decrypted payload") + + # 1) 优先 JSON + if text.startswith("{"): + obj = json.loads(text) + if not isinstance(obj, dict): + raise ValueError("invalid json payload") + obj["_payload_format"] = "json" + return obj + + # 2) XML(第三方客服回调常见) + if text.startswith("<"): + root = ET.fromstring(text) + + def get_text(tag: str, default: str = "") -> str: + node = root.find(tag) + return (node.text or "").strip() if node is not None and node.text is not None else default + + content_node = root.find("content") + query = "" + msg_type = get_text("msgtype", "") + if content_node is not None: + msg_node = content_node.find("msg") + type_node = content_node.find("msgtype") + if msg_node is not None and msg_node.text: + query = msg_node.text.strip() + if type_node is not None and type_node.text: + msg_type = type_node.text.strip() + + return { + # 统一成原业务逻辑使用的字段 + "UserId": get_text("userid", ""), + "Query": query, + "Timestamp": get_text("createtime", ""), + "MsgId": get_text("msgid", ""), + "ChannelId": get_text("appid", ""), + "MessageType": msg_type, + # 保留原始字段便于日志与后续扩展 + "appid": get_text("appid", ""), + "channel": get_text("channel", ""), + "from": get_text("from", ""), + "status": get_text("status", ""), + "kfstate": get_text("kfstate", ""), + "_payload_format": "xml", + } + + raise ValueError("unsupported decrypted payload format") + + +def _build_keyword_h5_reply(query: str, user_id: str) -> Optional[str]: + """ + 按关键词返回 H5 卡片(answer/short_answer 需为 JSON 字符串)。 + """ + msg = _build_keyword_h5_obj(query, user_id) + return json.dumps(msg, ensure_ascii=False) if msg else None + + +async def _send_kefu_msg_for_xml_callback(data: dict) -> None: + """ + XML 回调渠道下,主动调用 sendmsg 下发消息(回调响应体不会直接展示给用户)。 + """ + query = str(data.get("Query", "")).strip() + user_id = str(data.get("UserId", "")).strip() + appid = str(data.get("ChannelId", "") or data.get("appid", "")).strip() + channel = int(str(data.get("channel", "0") or "0")) + msg = _build_keyword_h5_obj(query, user_id) + if not msg: + return + + url = f"https://chatbot.weixin.qq.com/openapi/sendmsg/{TOKEN}" + body = { + "appid": appid, + "openid": user_id, + "msg": msg, + "channel": channel, + } + async with httpx.AsyncClient(timeout=10, verify=False) as client: + # 1) 先尝试明文 JSON 发送(多数场景可直接工作) + resp = await client.post(url, json=body, headers={"content-type": "application/json"}) + resp.raise_for_status() + payload = resp.json() if resp.content else {} + errcode = int(payload.get("errcode", -1)) if isinstance(payload, dict) else -1 + if errcode == 0: + logger.info("关键词命中,sendmsg 明文发送成功") + return + + logger.warning("sendmsg 明文返回非成功,准备加密重试:%s", payload) + + # 2) 按文档加密格式重试:body = {"encrypt": "..."} + msg_text = json.dumps(msg, ensure_ascii=False) + xml_data = ( + "" + f"" + f"" + f"" + f"{channel}" + "" + ) + encrypted = aes_encrypt_base64(xml_data, ENCODING_AES_KEY, APP_ID) + enc_resp = await client.post( + url, + json={"encrypt": encrypted}, + headers={"content-type": "application/json"}, + ) + enc_resp.raise_for_status() + enc_payload = enc_resp.json() if enc_resp.content else {} + enc_errcode = int(enc_payload.get("errcode", -1)) if isinstance(enc_payload, dict) else -1 + if enc_errcode == 0: + logger.info("关键词命中,sendmsg 加密发送成功") + return + logger.error("sendmsg 加密发送失败:%s", enc_payload) + + def _safe_filter_value(value: str) -> str: return (value or "").replace("\\", "\\\\").replace('"', '\\"') @@ -183,46 +296,55 @@ async def _pb_upsert_user_profile(record: dict) -> None: if not userid: return - async with httpx.AsyncClient(timeout=10, verify=False) as client: - auth_resp = await client.post( - f"{pb_url}/api/admins/auth-with-password", - json={"identity": pb_admin_email, "password": pb_admin_password}, - ) - auth_resp.raise_for_status() - token = (auth_resp.json() or {}).get("token", "") - if not token: - logger.warning("PocketBase 登录无 token,跳过资料同步") - return - - headers = {"Authorization": f"Bearer {token}"} - user_expr = _safe_filter_value(userid) - chan_expr = _safe_filter_value(channel_id) - filter_exp = f'userid="{user_expr}" && channel_id="{chan_expr}"' if channel_id else f'userid="{user_expr}"' - list_resp = await client.get( - f"{pb_url}/api/collections/{pb_collection}/records", - params={"page": 1, "perPage": 1, "filter": filter_exp, "sort": "-updated"}, - headers=headers, - ) - list_resp.raise_for_status() - items = ((list_resp.json() or {}).get("items")) or [] - - if items: - rid = items[0].get("id") - if rid: - patch_resp = await client.patch( - f"{pb_url}/api/collections/{pb_collection}/records/{rid}", - json=record, - headers=headers, + try: + async with httpx.AsyncClient(timeout=10, verify=False) as client: + auth_resp = await client.post( + f"{pb_url}/api/admins/auth-with-password", + json={"identity": pb_admin_email, "password": pb_admin_password}, + ) + # 兼容新旧 PocketBase:旧版本 admins,新版本 _superusers + if auth_resp.status_code == 404: + auth_resp = await client.post( + f"{pb_url}/api/collections/_superusers/auth-with-password", + json={"identity": pb_admin_email, "password": pb_admin_password}, ) - patch_resp.raise_for_status() + auth_resp.raise_for_status() + token = (auth_resp.json() or {}).get("token", "") + if not token: + logger.warning("PocketBase 登录无 token,跳过资料同步") return - create_resp = await client.post( - f"{pb_url}/api/collections/{pb_collection}/records", - json=record, - headers=headers, - ) - create_resp.raise_for_status() + headers = {"Authorization": f"Bearer {token}"} + user_expr = _safe_filter_value(userid) + chan_expr = _safe_filter_value(channel_id) + filter_exp = f'userid="{user_expr}" && channel_id="{chan_expr}"' if channel_id else f'userid="{user_expr}"' + list_resp = await client.get( + f"{pb_url}/api/collections/{pb_collection}/records", + params={"page": 1, "perPage": 1, "filter": filter_exp, "sort": "-updated"}, + headers=headers, + ) + list_resp.raise_for_status() + items = ((list_resp.json() or {}).get("items")) or [] + + if items: + rid = items[0].get("id") + if rid: + patch_resp = await client.patch( + f"{pb_url}/api/collections/{pb_collection}/records/{rid}", + json=record, + headers=headers, + ) + patch_resp.raise_for_status() + return + + create_resp = await client.post( + f"{pb_url}/api/collections/{pb_collection}/records", + json=record, + headers=headers, + ) + create_resp.raise_for_status() + except httpx.HTTPError as e: + logger.error("PocketBase 同步失败:%s", str(e)) async def _sync_user_profile_from_b(data: dict) -> None: @@ -294,6 +416,16 @@ async def _sync_user_profile_from_b(data: dict) -> None: await _pb_upsert_user_profile(record) +async def _safe_sync_user_profile_from_b(data: dict) -> None: + """ + 后台任务安全包装:B/PocketBase 失败只记日志,不抛异常影响 ASGI。 + """ + try: + await _sync_user_profile_from_b(data) + except Exception: + logger.error("后台同步失败(已忽略,不影响A接口主回复)") + + @app.post("/debug/sync-test") async def debug_sync_test(payload: dict): """ @@ -431,12 +563,12 @@ async def _handle_wechat_request(request: Request, app_id: Optional[str], backgr ) raise HTTPException(status_code=400, detail="decrypt error") - # 4. 解析 JSON + # 4. 解析明文(兼容 JSON/XML) try: - data = json.loads(decrypted_json_str) - except json.JSONDecodeError: - logger.error(f"解密结果不是合法 JSON: {decrypted_json_str}") - raise HTTPException(status_code=400, detail="invalid json in decrypted body") + data = _parse_decrypted_payload(decrypted_json_str) + except Exception: + logger.error(f"解密结果无法解析为 JSON/XML: {decrypted_json_str}") + raise HTTPException(status_code=400, detail="invalid decrypted payload") # 5. 打印请求内容到终端日志(分析、显示,带中文注释) logger.info("收到微信第三方服务请求(已解密):") @@ -478,8 +610,15 @@ async def _handle_wechat_request(request: Request, app_id: Optional[str], backgr logger.exception("业务处理异常,将返回兜底文案") answer_text = "系统繁忙,请稍后再试。" + # XML 回调场景需要主动 sendmsg 才能给用户展示消息 + if str(data.get("_payload_format", "")) == "xml": + try: + await _send_kefu_msg_for_xml_callback(data) + except Exception: + logger.error("sendmsg 下发失败(不影响回调200)") + # 7.1 主回复确定后再后台触发 B 同步,避免影响回复链路耗时 - background_tasks.add_task(_sync_user_profile_from_b, data) + background_tasks.add_task(_safe_sync_user_profile_from_b, data) resp_obj = { # 文本类型,内容为可被 JSON.parse 的 H5 结构字符串