package qq_obv11 // 本文件承载「这条消息是不是在叫她」的**结构化接线**:把 OneBot 事件里已有的 // self_id(她自己在该平台的账号 id)与 at 段(点名 / @全体)写进统一契约 // adapter.UnifiedMessage,让下游不必再靠「Mentions 非空」这种 **@ 谁不论**的判据 // (design/18 §2 触发① 的 2026-10-13 修订;主人裁决「甲 = 要」)。 // // 红线:判据**全部来自平台元数据**(self_id + at 段的 qq),绝不匹配正文里的 // "@<数字>" —— 正文里恰好只有这种文本,而它正是不可靠的原因(@ 她与 @ 别人长得一模一样)。 // // 只覆盖**真消息**那条路径(toUnified)。debug 帧日志(inbound_debug.go)与 notice 的 // self 命中(notice.go / event.go)各有各的语义,本片不动它们。 import ( "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter" ) // mentionAllQQ 是 OneBot v11 at 段里 **@全体成员** 的 qq 取值 // (数组段 `{"type":"at","data":{"qq":"all"}}`,CQ 码形态 `[CQ:at,qq=all]`)。 // design/26 §2.4 把它登记为随消息到达的**结构化标志**(mention_all),不另立一类事件。 // 该字面量是 OneBot 协议约定,所以落在平台侧(这里),不污染平台中立的 adapter 包。 const mentionAllQQ = "all" // mentionsContainAll 报告 mentions 里是否含 @全体。只比对结构化的 UserID 字段, // 不看正文;判据是精确相等(QQ 号是纯数字,与 "all" 不可能撞号)。 func mentionsContainAll(mentions []adapter.Mention) bool { for _, m := range mentions { if m.UserID == mentionAllQQ { return true } } return false } // applyAddressing 把平台元数据写成统一契约里的结构化字段:SelfID(她自己在该平台的 // 账号 id)、MentionAll(是否 @全体)、AddressedToMe(本条是否指向她本人,推导见 // adapter.UnifiedMessage.AddressedToSelf)。三个字段同源同步,下游读哪个都不会自相矛盾。 // // 调用点必须在 ChannelType 判定**之后**:私聊与群聊的 AddressedToMe 取值不同。 // self_id 取不到(字段缺失 / 为 0)时 SelfID 如实留空、AddressedToMe 随之 false —— // fail-closed,绝不退回正文匹配(见 adapter.MentionedSelf)。 func applyAddressing(msg *adapter.UnifiedMessage, selfID string, mentions []adapter.Mention) { msg.SelfID = selfID msg.MentionAll = mentionsContainAll(mentions) msg.AddressedToMe = msg.AddressedToSelf() }