fix(channelbridge): 出站气泡不再携带入站载荷(真机:发文件后回复全丢)
根因(bubbles.go:43):`out := target` 把【整条入站消息】复制成出站气泡, 入站 Media 被一起带出 ⇒ 适配器去上传那个附件 ⇒ weixin_ilink 的 getUploadURL 拿不到 url ⇒ sendToOne 连已组装好的文本 item 一起丢掉 ⇒ 用户收不到任何回复。 修法:新增 outboundBubble(白名单只带路由字段 + 本气泡文本),一次清掉 Media / RawData / Mentions / ReplyToID / ReplyToText / MessageID / SenderName 六类入站专属字段;将来新加进 UnifiedMessage 的字段默认不外发(失败方向安全)。 测试 2 条:入站带附件 ⇒ 出站 Media 为空;带附件 vs 不带 ⇒ 出站除时间戳逐字节相同。 变异:破坏白名单把 Media 带出 ⇒ 2 条红。 影响面:仅「入站带附件」的消息;纯文本聊天不受影响(已真机确认)。
This commit is contained in:
1 parent
fbbbb4ea64
commit
fd13b49b24
2 files changed
+164
-5
No files matched your search
@@ -24,7 +24,8 @@ type bubbleSender func(ctx context.Context, msg adapter.UnifiedMessage) error
|
||||
// 它直接渲染已持久化的流,不经过这里。
|
||||
//
|
||||
// target 携带平台/渠道路由字段,并被复制到每个
|
||||
// 气泡上(每个气泡只有 Content/ContentType/Timestamp 不同);sender 是
|
||||
// 气泡上(每个气泡只有 Content/ContentType/Timestamp 不同,入站载荷字段一律
|
||||
// 不带 —— 见 outboundBubble);sender 是
|
||||
// 适配器的出站调用。被剥成空的内容(纯动作行)
|
||||
// 直接丢弃不发送;单个气泡失败时会带上其下标包装返回。
|
||||
func (b *Bridge) sendIMBubbles(ctx context.Context, target adapter.UnifiedMessage, sender bubbleSender, content string) error {
|
||||
@@ -40,10 +41,7 @@ func (b *Bridge) sendIMBubbles(ctx context.Context, target adapter.UnifiedMessag
|
||||
if i > 0 {
|
||||
time.Sleep(b.bubbleGapFn())
|
||||
}
|
||||
out := target
|
||||
out.Content = bubble
|
||||
out.ContentType = adapter.ContentText
|
||||
out.Timestamp = time.Now().UTC()
|
||||
out := outboundBubble(target, bubble)
|
||||
// 每个气泡各自发送一次,间隔才会落在消息之间。
|
||||
if err := sender(ctx, out); err != nil {
|
||||
return fmt.Errorf("channelbridge: send bubble #%d of %d: %w", i+1, len(bubbles), err)
|
||||
@@ -51,3 +49,40 @@ func (b *Bridge) sendIMBubbles(ctx context.Context, target adapter.UnifiedMessag
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// outboundBubble 依据一条入站消息的**路由字段**构造一个纯文本出站气泡。
|
||||
//
|
||||
// 为什么不能直接 `out := target`:target 是**入站**消息,整条复制会把入站事件的
|
||||
// 载荷(Media 附件、原始平台载荷、提及列表、回复引用、入站消息 id、发送者昵称)
|
||||
// 一并带到出站。真机事故(2026-10-04 22:21:53):主人发了一条带附件的微信消息,
|
||||
// 回复气泡因此捎上入站的 Media,weixin_ilink 去上传那个附件、拿不到 upload url
|
||||
// (weixin_ilink/send.go 的 getUploadURL)⇒ sendToOne 在**已经组装好文本 item
|
||||
// 之后**整条返回错误,主人连文本都收不到。
|
||||
//
|
||||
// 因此这里按白名单只带路由字段(适配器据此解析投递对端),新加进 UnifiedMessage
|
||||
// 的字段默认不外发:出站是纯文本气泡,本派发器只接收文本。
|
||||
func outboundBubble(target adapter.UnifiedMessage, content string) adapter.UnifiedMessage {
|
||||
return adapter.UnifiedMessage{
|
||||
// 路由字段:平台 / 渠道 / 群 / 线程 / 发送者 id。
|
||||
Platform: target.Platform,
|
||||
ChannelID: target.ChannelID,
|
||||
ChannelType: target.ChannelType,
|
||||
GroupID: target.GroupID,
|
||||
GroupName: target.GroupName,
|
||||
ThreadID: target.ThreadID,
|
||||
SenderID: target.SenderID,
|
||||
// Sender 只保留路由子字段:qq-obv11 的 resolveTarget 用
|
||||
// Sender.GroupID / Sender.UserID 作为对端回退,而 DisplayName /
|
||||
// GroupName 是入站的展示信息,出站不需要。
|
||||
Sender: adapter.Actor{
|
||||
Role: target.Sender.Role,
|
||||
UserID: target.Sender.UserID,
|
||||
IsGroup: target.Sender.IsGroup,
|
||||
GroupID: target.Sender.GroupID,
|
||||
},
|
||||
// 本气泡的内容:出站永远是纯文本。
|
||||
Content: content,
|
||||
ContentType: adapter.ContentText,
|
||||
Timestamp: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user