fix: 个人微信发送对齐iLink规范(base_info/client_id/ret检查); 会话过期检测+重扫UI; 仅发送成功才写历史

This commit is contained in:
WorkBuddy committed 2026-07-10 08:00:25 +08:00
1 parent 62f3986e04
commit 2eeaf1d293
2 files changed
+88 -31

No files matched your search

+69 -26
View File
@@ -164,16 +164,23 @@ def _wechat_get_updates(baseurl: str, bot_token: str, cursor: str):
return [], cursor
def _wechat_send_message(baseurl: str, bot_token: str, to_user_id: str, context_token: str, text: str) -> bool:
"""发送文本回复(必须带 context_token 关联到正确的对话窗口)。失败重试一次。"""
payload = {"msg": {
"to_user_id": to_user_id,
"message_type": 2,
"message_state": 2,
"context_token": context_token,
"item_list": [{"type": 1, "text_item": {"text": text}}],
}}
last = False
def _wechat_send_message(baseurl: str, bot_token: str, to_user_id: str, context_token: str, text: str):
"""发送文本回复(必须带 context_token 关联到正确的对话窗口)。
返回 (ok, ret, body)。ret=-14=会话过期需重扫;ret=-2=token 失效。"""
import uuid as _uuid
payload = {
"msg": {
"from_user_id": "",
"to_user_id": to_user_id,
"client_id": "py-" + str(_uuid.uuid4()),
"message_type": 2,
"message_state": 2,
"context_token": context_token,
"item_list": [{"type": 1, "text_item": {"text": text}}],
},
"base_info": {"channel_version": "1.0.2"},
}
last_body = {}
for _ in range(2):
try:
resp = requests.post(
@@ -182,12 +189,25 @@ def _wechat_send_message(baseurl: str, bot_token: str, to_user_id: str, context_
json=payload,
timeout=20,
)
last = resp.ok
if last:
return True
try:
body = resp.json() if resp.content else {}
except Exception:
body = {}
last_body = body
ret = body.get("ret") if isinstance(body, dict) else None
try:
with open("/tmp/wechat_send.log", "a", encoding="utf-8") as _f:
_f.write(datetime.now().isoformat() + " SEND_HTTP=%s ret=%s body=%s\n"
% (resp.status_code, ret, json.dumps(body, ensure_ascii=False)[:300]))
except Exception:
pass
if resp.ok and (ret is None or ret == 0 or not body):
return True, ret, body
if ret in (-2, -14):
return False, ret, body # 鉴权/会话失效,立即停止重试
except Exception:
last = False
return last
pass
return False, (last_body.get("ret") if isinstance(last_body, dict) else None), last_body
def _wechat_extract_text(msg: dict) -> str:
@@ -247,6 +267,22 @@ def _log_chat(user_id, platform, from_user, context_token, role, content):
# ============================================================
# 后台接收线程(每用户独立)
# ============================================================
def _mark_wechat_expired(account_id: int):
"""发送会话失效(ret=-14/-2):标记账号需重扫,并清除失效 token。"""
db = None
try:
db = SessionLocal()
_execute_raw(db, text(
"UPDATE im_bot_wechat_account SET status='expired', bot_token=NULL, "
"updated_at=:ts WHERE id=:aid"
), {"aid": account_id, "ts": datetime.now()})
except Exception:
pass
finally:
if db:
db.close()
def _wechat_receiver_loop(account_id: int):
"""长轮询个人微信消息 → 七虎AI 回复 → 发送。"""
cursor = ""
@@ -284,17 +320,23 @@ def _wechat_receiver_loop(account_id: int):
pass
# 先生成回复(此时历史尚未包含本条,避免重复)
reply = _qihua_reply(content, owner_uid)
ok = _wechat_send_message(baseurl, bot_token, sender, ctx, reply)
# 持久化对话历史
if owner_uid:
_log_chat(owner_uid, "personal_wechat", sender, ctx, "user", content)
_log_chat(owner_uid, "personal_wechat", sender, ctx, "assistant", reply)
try:
with open("/tmp/wechat_send.log", "a", encoding="utf-8") as _f:
_f.write(datetime.now().isoformat() + " ok=" + str(ok)
+ " sender=" + repr(sender) + " ctx=" + repr(ctx) + "\n")
except Exception:
pass
ok, ret, _ = _wechat_send_message(baseurl, bot_token, sender, ctx, reply)
if ok:
# 仅发送成功才写对话历史(避免“发了但没收到”的误导记录)
if owner_uid:
_log_chat(owner_uid, "personal_wechat", sender, ctx, "user", content)
_log_chat(owner_uid, "personal_wechat", sender, ctx, "assistant", reply)
else:
try:
with open("/tmp/wechat_send.log", "a", encoding="utf-8") as _f:
_f.write(datetime.now().isoformat() + " SEND_FAIL ret=%s sender=%s\n"
% (ret, repr(sender)))
except Exception:
pass
if ret in (-2, -14):
# 会话/鉴权失效:标记需重扫并停止接收线程
_mark_wechat_expired(d["id"])
return
time.sleep(1)
except Exception:
time.sleep(5)
@@ -324,6 +366,7 @@ async def wechat_status(user: User = Depends(get_current_user)):
d = _row_to_dict(row)
return {"success": True, "data": {
"connected": d.get("status") == "connected",
"status": d.get("status"),
"configured": True,
"baseurl": d.get("baseurl"),
"ilink_bot_id": d.get("ilink_bot_id"),