Repository navigation
message: exclude a send echo by platform message id - #12
Merged
std-microblock merged 1 commit intoOct 18, 2026
Merged
Conversation
std-external
force-pushed
the
fix/own-send-echo
branch
from
October 18, 2026 03:01
f80d8bd to
28600a9
Compare
std-external
force-pushed
the
fix/own-send-echo
branch
from
October 18, 2026 03:08
28600a9 to
dbacc86
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
crossgram 里发一条普通文本(
AA就够),发送端会先出现「消息 1」,紧接着消息 1 播「消息粉碎」动画,然后再出现「消息 2」。PC(tdesktop 系)和 Android 都能看到这两条:一条是本地乐观消息,一条是平台回显出来的服务端副本,没有合并。两端对账的入口都只看
random_id→ 本地占位:api_updates.cpp:1671:updateMessageID按random_id找到本地 item;newId上已经存在 item 时,两条分支都必须destroy一条。被移除的视图会走thanos_effect_controller.cpp:107的captureOnRemoval,就是那个粉碎动画。MessagesStorage.java:14008:把占位行的mid改成服务端 id,撞主键(副本已在库)就落到:14021的DELETE分支,占位气泡消失、副本留下。所以「预设消息 / 回显」没合上,前提是服务端副本比回包先到发送端。
relay 的设计本来保证发送端只收回包:
_localDelivery(..., true)按 auth key 排除本地发布,回包里带updateMessageID(send-observer-reconciliation.e2e.test.ts就是这条约定)。多出来的那条副本只可能来自平台(QQNT)对自己刚发出的消息的回显——它走普通事件发布,没有random_id,也不排除发送端。旧方案(本 PR 第一版)为什么不好
第一版是「发送 RPC 期间把 auth key 标记为正在发送,期间所有自己发出的消息都不推给它」:
finally(平台返回后才回显)、发送重试、离线补推,都落在窗口外;并发/重连时窗口归属也不准。候选方案
updateShortSentMessage)updateShortSentMessage既没有peer/from_id也没有投影后的媒体、按钮、回复;Android 侧当初就是因为这个从 short 回执改成完整 updates(见be3c62f前的注释)。会丢掉服务端投影字段,回归面大。(platform session, 平台消息 id) → auth key;UpdateManager发布这条消息时只把该 auth key 从投递里排除originRequestId过滤(platform-crossgram的 own-origin 规则)负责。选 b:一处判断、按消息身份、失败模式温和(最坏情况只是少推一次,客户端还能靠 difference 拿到)。
实现
packages/bridge/src/send-echoes.ts:SendEchoes保存(platformSessionId, 平台消息 id / sourceIds) → authKeyId,bind()/release()/excludeFor()。DialogRpc新增_withSendEchoes(sent, publish):在拿到平台发送结果之后、投递本地副本之前登记这批平台消息,投递结束(成功或失败)即解绑。用在sendMessage(文本)、sendRichContent(媒体/相册)、forwardMessages、系统会话转发。UpdateManager.publish():发布message/message-edit时,如果这条消息命中登记,就只给这次投递补excludeAuthKeyId;其他设备照常收到。eventKey已发布)挡掉,所以不需要长时间持有登记。边界
messages.sendMessage响应:本机制来不及登记,由平台侧originRequestIdown-origin 过滤覆盖(platform-crossgram/src/index.ts:461、:620)。两者互补,没有重叠判断。_authKeyId)时不登记,行为与改动前一致。测试
packages/bridge/src/send-echoes.test.ts:绑定 / 多段消息sourceIds/ 其他消息与其他 session 不受影响 / 解绑 / 无 auth key。send-observer-reconciliation.e2e.test.ts新增两条:excludes only the sending auth key for the echoed message, not every own message:绑定后回显只推给另一台设备;同一时刻从别的客户端发出的自己消息仍然两台都收到(直接覆盖旧方案「误杀」的问题);解绑后恢复全量推送。binds the sent platform message for the whole local publish:走真实messages.sendMessage,断言投递本地副本的那一刻登记已生效、RPC 结束后已解绑。vitest run packages/bridge/src/send-echoes.test.ts packages/bridge/src/dialogs.test.ts packages/bridge/src/update-manager.test.ts packages/bridge/src/session-resolver.test.ts→ 120 passed;tsc --noEmit通过。CI 状态
yarn typecheck(tsc -p tsconfig.test.json)在本分支为 0 错误;send-echoes.test.ts/dialogs.test.ts/update-manager.test.ts/session-resolver.test.ts120 passed;mtproto e2e 文件里的三条用例全过。上一版的ReferenceError: activeSends is not defined(createSessionResolver作用域)已随这版重做消失。main(7a1ecfc)的 Unit tests and typecheck 本来就是红的,tsc -p tsconfig.test.json报 4 个类型错,全部在platform-crossgram(index.ts:1041的refresh、invite-accept.e2e.test.ts两处、platform.test.ts的 card mock)。这是加群邀请按钮那次提交(22f7608)带进来的类型漂移,和本 PR 无关;main的 CI run 39050915894 可以直接看到。qqnt: keep the inline-button refresh flag in the return type),它已经 全绿(unit/typecheck 双平台、webui、compose、publish 全 pass)。