Repository navigation
message send 恒返回 ok:true(不读 ack);register() 的 ackDiff 返回 400 导致消息不落库 #32
Description
Activity
更正 + 已定位真正根因(附验证过的修法)
抱歉,原帖里关于 ackDiff 的两条判断都是错的,先撤回:
- ❌ 「把
register()的pts改成0可修」—— 实测改了,ackDiff 仍返回 400。 - ❌ 「ackDiff 的 400 可能是消息不落库的直接原因」—— 它和发送失败是同一个根因的两个症状,不是因果。
原帖第一条结论(
ok: True硬编码、不读 ack)成立,且正是它掩盖了下面这个真正的问题。真正根因:
/r/请求在/s/vulcan到达之前发出会被 400 拒绝对比同文件内一个能成功的
/r/调用和两个失败的:core/ws.pylist_user_messages()(成功):async for raw in ws: ... with suppress(Exception): await ws.send(json.dumps(build_ack(msg))) # ① 对每个下行帧回 ack lwp = msg.get("lwp") if lwp == "/s/vulcan": await ws.send(json.dumps(req)) # ② 等到 /s/vulcan 才发请求 continue
commands/message/send.py_send()(失败):await register(ws, session, token) hb = asyncio.create_task(heartbeat_loop(ws)) ... mid = await send_text(...) # 紧跟 register 立即发,既不等 /s/vulcan 也不回 ack
按 mid 精确配对回包,规律无例外:
调用点 是否等 /s/vulcanack code list_user_messages()(message history)是 200 collect_session_cids()只监听,不发 /r/— register()内的ackDiff否(紧跟 /reg)400 _send()的sendByReceiverScope否 400 验证过的修法
在
_send()里register()之后、发送之前插入「等/s/vulcan并对下行帧回build_ack」,其余不变。改完后同一 cid、同一账号:{ "ok": true, "ack_code": 200, "ack_body": { "messageId": "<server-side id>", "receiverCount": 2, "unreadCount": 1, "createAt": <ts> } }message history回读也确认消息已落库(原本 2 条 → 3 条)。同一台机器、同一账号,改动前后唯一差异就是这个等待。建议
register()里的ackDiff也一并移到/s/vulcan之后再发,或者由调用方在就绪后自行发送。另一个独立问题:
session_id混了两个命名空间commands/message/list_chats.py:"session_id": str(session.get("sessionId", "")) # baseline:MTOP sessionId "session_id": str(w["cid"]) # watch:IM cid
message history/message send需要的是 IM cid,而默认(不带--watch-secs)的list-chats返回的全是 baseline 的 MTOPsessionId。拿它去调 history 会得到空数组、调 send 会得到 400,且都没有提示。建议拆成两个字段(如
session_id/cid),或在 record 里标注该 id 能否用于 history/send。这个也解释了为什么goofish-reply-buyer/SKILL.md里写的 “list-chats 返回 cid” 与实际 schema 不一致。环境
goofish-cli 0.4.0 (PyPI),源码核对
6fb870a,macOS 26.6 / Python 3.14.4,auth login <cookies.json>导入登录态。(所有账号 ID、cid、sid、IP、messageId 均已去除或占位。)
- ❌ 「把
再更正一次:
session_id命名空间那条也不成立上一条评论里我说「
list_chats的 baselinesession_id是 MTOP sessionId,拿去调history会得空数组、调send会得 400」——这条是错的,撤回。修好
/s/vulcan等待之后复测:拿 baseline 来源(source: baseline、session_type: 25)的session_id发送,成功,服务端签发了messageId;随后message history用同一个 id 也能读回该消息。之前
history对这个 id 返回[],原因只是那个会话本来没有可检索的用户消息(推送频道里的营销内容不算),不是 id 类型不对。发送成功一条之后,history就返回 1 条了。所以 baseline 和 watch 两个来源的 id 对
history/send都可用;两者的差别只在会话列表完整度(baseline 只有活跃 Top N,漏掉部分会话),而这一点list_chats.py的模块 docstring 已经写清楚了,是设计取舍不是缺陷。最终结论:真正的根因只有一个——
/r/请求早于/s/vulcan发出会被 400 拒绝,而ok: True硬编码把它完全藏住了。已在 #33 提交修复。抱歉来回更正了两次,前两个猜测都没实测到底就发了出来。#33 里的每一条都有对应的实测记录。
补充一次当前状态的真实业务验证:
auth login --qr:扫码成功,获得完整 session cookiesauth status:valid=truemessage list-chats:真实返回会话列表message history:真实读取目标会话- 使用当前
main(旧版发送逻辑)发送文本hi message send返回ok=true- 发送后再次调用
message history,成功回读到由当前账号发送的hi
因此 Issue #32 的现象在本次账号、会话和环境中未复现;但这不能证明问题不存在。当前代码仍然存在客观缺陷:
ok=true是本地硬编码,未读取或匹配服务端发送 ack,因此服务端拒绝或超时仍可能报告假成功。建议暂不把一次成功视为问题已解决,也不建议仅凭单元测试合并修复。请在失败场景补充脱敏后的
/reg、ackDiff、sendByReceiverScope回包 code/mid 及发送后 history 回读证据,以区分偶发时序问题、服务端拒绝和回读延迟。
现象
message send对每一次发送都返回ok: true+mid,但消息实际没有送达。在 v0.4.0 上复现两次(一次发给系统号session_type=25,一次发给真实session_type=1会话),账号方均确认对话里没有出现该消息。message history回读同一 cid 也看不到发出的消息(多次重试均如此)。排查过程中确认不是以下原因:
auth status→valid: true~/.goofish-cli/guard.json,RGV587 熔断未触发limiter.json正常记录message.write/reg鉴权成功(code: 200,reg-uid正确),服务端持续下推/s/sync、/s/vulcan原因一:
ok与mid都是本地构造的,不携带投递信息core/ws.py_send_custom(L171、L197):commands/message/send.pyL98-L102:注释写的是「等一轮 ack 回包」,但实现只
sleep(1.0),从未读取或校验该 ack;ok: True为硬编码。_send内也没有任何 recv 循环,因此服务端返回的任何错误都不可见。结果:
ok恒为 true,只要ws.send()未抛异常。对需要投递确认的上层(例如 #27 讨论的「发送成功 / 发送未知」状态契约)而言,当前返回值无法作为依据。原因二:
register()的 ackDiff 返回code: 400,sync 状态未建立用仓库自身的
connect()+register()连接并读取回包,收到的帧依次为:sendByReceiverScope应依赖有效的 sync 状态,ackDiff被拒可能是消息不落库的直接原因。对比同仓库内两处 ackDiff 的参数差异:
ptscore/ws.pyL101register()current_ms * 1000code: 400core/ws.pyL263collect_session_cids()0list-chats --watch-secs能发现会话)collect_session_cids的 docstring 也明确写的是ackDiff(pts=0)。建议修法
把
register()L101 的"pts": current_ms * 1000改为"pts": 0,与collect_session_cids()中已验证可用的值保持一致。旁证:断连频率异常
run_foreverdocstring 记录「服务端会不定时关连接(观察约每 1030 分钟一次)」。在 sync 未建立的情况下实测为 6 分钟内断连 5 次(间隔 12 分钟,退避 1→2→4→8→16s),比预期频繁约一个数量级。修好 ackDiff 后这一项值得复测。建议
send_text/send_image读取并校验 ack 帧,把服务端返回的 code 反映到ok;无法确认时返回第三态(如ok: "unknown")而非true。register()校验/reg与ackDiff的响应码,失败时报错而非静默继续。register()的pts参数(见上)。环境
6fb870aauth login <cookies.json>(浏览器 cookie 导出导入,非--qr)(帖内所有账号 ID、会话 cid、sid、IP 均已去除。)