package qq_obv11 // 本文件承载 OneBot 事件里 id 的解析与几个轻量字符串工具,从 message.go 拆出 // (AGENTS §10 文件 ≤500 行)。判主用的 SenderID 解析规则就在这里:见 senderIDOf。 import ( "fmt" "strconv" ) // senderIDOf 解析本条消息的发送者 id(判主用的那个字段)。OneBot v11 规定消息事件 // 在**顶层**带 user_id(obEvent.UserID),但实现之间有出入:有的只在 sender.user_id // 里给。过去的实现只读顶层,顶层缺失/为 0 时 SenderID 就成了空串,而 // owner.IsOwner 对空 accountID 一律返回 false(fail-closed)——号码明明在白名单里 // 也永远判不成主人。这里给出兜底链:顶层 user_id → sender.user_id,两者都缺才是空。 // // 群聊同样走这条链(发送者就是 user_id):群号**不是**发送者,绝不能作为它的兜底, // 否则群里任何人都能顶着群号去撞白名单。见 toUnified 的 group/private 两个分支。 func senderIDOf(ev *obEvent) string { if ev == nil { return "" } if id := formatID(ev.UserID); id != "" { return id } if ev.Sender != nil { return formatID(ev.Sender.UserID) } return "" } // formatID 把 OneBot 的数字 id 归一成字符串;0(字段缺失)归一成空串,让 // 「没给」与「给了 0」在下游是同一种情况。 func formatID(id int64) string { if id == 0 { return "" } return strconv.FormatInt(id, 10) } func parseInt64(s string) (int64, error) { if s == "" { return 0, fmt.Errorf("empty id") } return strconv.ParseInt(s, 10, 64) } func firstNonEmpty(ss ...string) string { for _, s := range ss { if s != "" { return s } } return "" }