feat(channels): QQ 非消息事件全枚举投递 + systemnotice 补 request/interaction/unknown 类
- 适配器不再整类丢弃:notice/request/meta_event/message_sent/未知 post_type/JSON 解析失败( decode_error ) 一律归一化投递并留痕
- notice 映射补 group_ban/sub_type 全枚举,未知类型走 NoticeUnknown 兜底(fail-safe 只登记、绝不猜、绝不放行)
- 不投影 flag/comment/file/duration;归一化 DEBUG 只记结构(脱敏,变异实测可打红)
- 传输层端到端已覆盖(真实 http+server 上报路径:加好友请求/心跳/未知类型)
- ⚠️ 主人 2026-10-05 决定「先不连 QQ」(风控)⇒ 本片为通用红线(绝不静默丢弃)+ 现成证据链,QQ 侧后续增量工作暂停
This commit is contained in:
1 parent
ec3f6e810a
commit
ba17820040
9 files changed
+1315
-193
No files matched your search
@@ -5,14 +5,16 @@ import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// 本文件承载「系统通知传入前审查」的**归一化契约**(design/12 §6 策略一补充):
|
||||
// 各适配器把平台原生的系统通知 / 系统账号通知 / 群内系统提醒解析成**统一结构**
|
||||
// SystemNotice{Kind, SafeText, Ignorable, HighValue},交给 channelbridge 的传入前
|
||||
// 审查决定「仅登记 / 限定注入 / 升级真会话」。
|
||||
// 本文件承载「系统通知传入前审查」的**归一化契约**(design/12 §6 策略一补充;
|
||||
// 字段集按 design/26 §2.4 扩展):各适配器把平台原生的系统通知 / 系统账号通知 /
|
||||
// 群内系统提醒解析成**统一结构** SystemNotice{Kind, SafeText, Ignorable, HighValue,
|
||||
// Category, PlatformType, Actor, Target, SelfRelated, RefMessage},交给 channelbridge
|
||||
// 的传入前审查决定「仅登记 / 限定注入 / 升级真会话」;Category / PlatformType /
|
||||
// SelfRelated 等结构化字段则由桥接层用于**留存**(design/26 §4)。
|
||||
//
|
||||
// 红线(design/12 §6 策略一补充):判「是否系统 / 是否高价值」只看**结构化元数据**
|
||||
// (通知种类、事件类型、操作者 id),绝不看提示词、绝不看正文措辞;本文件不含任何
|
||||
// LLM 逻辑,也不做身份判定(身份永远来自 actor 元数据 + 白名单)。
|
||||
// 红线(design/12 §6 策略一补充 + design/26 §1.2):判「是否系统 / 是否高价值」只看
|
||||
// **结构化元数据**(通知种类、事件类型、操作者 id),绝不看提示词、绝不看正文措辞;
|
||||
// 本文件不含任何 LLM 逻辑,也不做身份判定(身份永远来自 actor 元数据 + 白名单)。
|
||||
|
||||
// NoticeKind 是归一化后的系统通知种类(design/12 §6 策略一补充给定的 kind 集合,
|
||||
// 末尾的 other 是「已识别为系统通知、但结构上不在已知集合内」的兜底值)。
|
||||
@@ -25,24 +27,98 @@ const (
|
||||
NoticeLeave NoticeKind = "leave"
|
||||
// NoticeRecall 是撤回类通知(消息被撤回)。
|
||||
NoticeRecall NoticeKind = "recall"
|
||||
// NoticeTyping 是轻提示类通知(正在输入 / 戳一戳 / 群内提醒)——最典型的小事件。
|
||||
// NoticeTyping 是轻提示类通知(正在输入 / 群内轻提醒)——最典型的小事件。
|
||||
NoticeTyping NoticeKind = "typing"
|
||||
// NoticeBotAdd 是机器人自身被加入/被移出某个会话的通知。
|
||||
NoticeBotAdd NoticeKind = "bot_add"
|
||||
// NoticeOther 是已识别但不在已知集合内的系统通知。
|
||||
NoticeOther NoticeKind = "other"
|
||||
// NoticeRequest 是**请求类**通知(加好友请求 / 加群请求 / 邀请入群):它不是
|
||||
// 「已经发生的事」,而是**等着被裁决的请求**(design/26 §3.1 的 request 行)。
|
||||
NoticeRequest NoticeKind = "request"
|
||||
// NoticeInteraction 是**定向交互**事件(戳一戳 / 表情回应 / 道具 / 按钮 / 投票):
|
||||
// 统一形态是「谁(Actor)对谁(Target)做了什么(Category)」(design/26 §2.5)。
|
||||
// 它默认**只登记、不自动回应**——回不回由参与度引擎 / 主人显式规则决定,绝不写死
|
||||
// 在归一化层(design/26 §5.5)。
|
||||
NoticeInteraction NoticeKind = "interaction"
|
||||
// NoticeUnknown 是**平台类型未能枚举**的兜底种类(design/26 §3.7):它不是
|
||||
// 「看不懂就当没有」,而是「如实记为未知」;它恒为可忽略(仅登记),
|
||||
// **绝不进她的上下文**(fail-safe),但必须留痕、可计数。
|
||||
NoticeUnknown NoticeKind = "unknown"
|
||||
)
|
||||
|
||||
// Valid 报告 k 是否是已知的通知种类。
|
||||
func (k NoticeKind) Valid() bool {
|
||||
switch k {
|
||||
case NoticeJoin, NoticeLeave, NoticeRecall, NoticeTyping, NoticeBotAdd, NoticeOther:
|
||||
case NoticeJoin, NoticeLeave, NoticeRecall, NoticeTyping, NoticeBotAdd, NoticeOther,
|
||||
NoticeRequest, NoticeInteraction, NoticeUnknown:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// NoticeCategory 是比 NoticeKind 细一档的**归一化类别**(design/26 §2.4/§4.1):
|
||||
// Kind 回答「这是哪一类系统事件」,Category 回答「具体是哪一件事」——同一种 Kind 下
|
||||
// 可以有多个类别(如 Kind=join 下的 join / friend_add,Kind=interaction 下的
|
||||
// poke / lucky_king)。
|
||||
//
|
||||
// 它与留存档(design/26 的 T1/T2/T3)是**两个正交轴**:留存档由桥接层按
|
||||
// Category + SelfRelated 映射(design/26 §2.3),不在适配器层落地。
|
||||
type NoticeCategory string
|
||||
|
||||
const (
|
||||
// NoticeCategoryJoin 是成员加入(进群 / 入频道)。
|
||||
NoticeCategoryJoin NoticeCategory = "join"
|
||||
// NoticeCategoryFriendAdd 是「已加为好友」的结果通知(与请求态 friend_request 区分)。
|
||||
NoticeCategoryFriendAdd NoticeCategory = "friend_add"
|
||||
// NoticeCategoryLeave 是成员离开(退群 / 被移出 / 删好友)。
|
||||
NoticeCategoryLeave NoticeCategory = "leave"
|
||||
// NoticeCategoryRecall 是消息被撤回。
|
||||
NoticeCategoryRecall NoticeCategory = "recall"
|
||||
// NoticeCategoryAdmin 是管理员 / 权限身份变更。
|
||||
NoticeCategoryAdmin NoticeCategory = "admin"
|
||||
// NoticeCategoryBan 是禁言状态变更(不含全员禁言)。
|
||||
NoticeCategoryBan NoticeCategory = "ban"
|
||||
// NoticeCategoryBanAll 是**全员禁言**开关(`user_id==0 && duration==-1`)。
|
||||
// 它必须与 ban 分开:归一成「用户 0」会把「整个群被禁言」读成「某个成员被禁言」
|
||||
// (design/26 §3.1 的 group_ban 行)。
|
||||
NoticeCategoryBanAll NoticeCategory = "ban_all"
|
||||
// NoticeCategoryUpload 是群文件上传。
|
||||
NoticeCategoryUpload NoticeCategory = "upload"
|
||||
// NoticeCategoryEssence 是精华消息变更。
|
||||
NoticeCategoryEssence NoticeCategory = "essence"
|
||||
// NoticeCategoryCard 是群名片变更。
|
||||
NoticeCategoryCard NoticeCategory = "card"
|
||||
// NoticeCategoryOfflineFile 是收到离线文件。
|
||||
NoticeCategoryOfflineFile NoticeCategory = "offline_file"
|
||||
// NoticeCategoryClientStatus 是**他人**客户端在线状态变更(存在性遥测)。
|
||||
NoticeCategoryClientStatus NoticeCategory = "client_status"
|
||||
// NoticeCategoryHonor 是群成员荣誉变更(龙王 / 群聊之火 / 快乐源泉)。
|
||||
NoticeCategoryHonor NoticeCategory = "honor"
|
||||
// NoticeCategoryTitle 是群成员头衔变更。
|
||||
NoticeCategoryTitle NoticeCategory = "title"
|
||||
// NoticeCategoryPoke 是戳一戳。
|
||||
NoticeCategoryPoke NoticeCategory = "poke"
|
||||
// NoticeCategoryLuckyKing 是红包运气王。
|
||||
NoticeCategoryLuckyKing NoticeCategory = "lucky_king"
|
||||
// NoticeCategoryFriendRequest 是加好友请求。
|
||||
NoticeCategoryFriendRequest NoticeCategory = "friend_request"
|
||||
// NoticeCategoryGroupRequest 是加群请求 / 邀请入群请求。
|
||||
NoticeCategoryGroupRequest NoticeCategory = "group_request"
|
||||
// NoticeCategoryHeartbeat 是 OneBot 心跳(链路遥测)。
|
||||
NoticeCategoryHeartbeat NoticeCategory = "heartbeat"
|
||||
// NoticeCategoryLifecycle 是 OneBot 生命周期(connect / enable / disable)。
|
||||
NoticeCategoryLifecycle NoticeCategory = "lifecycle"
|
||||
// NoticeCategoryMessageSent 是她自己发出消息的回声(防自我回声,只登记不回应)。
|
||||
NoticeCategoryMessageSent NoticeCategory = "message_sent"
|
||||
// NoticeCategoryDecodeError 是**载荷无法解析**(JSON 坏 / 类型字段全缺)的兜底类别:
|
||||
// «解析失败» 也是一条事件,绝不是「什么都没有」(design/26 §3.7)。
|
||||
NoticeCategoryDecodeError NoticeCategory = "decode_error"
|
||||
// NoticeCategoryUnknown 是**类型未枚举**的兜底类别(design/26 §3.7)。
|
||||
NoticeCategoryUnknown NoticeCategory = "unknown"
|
||||
)
|
||||
|
||||
// NoticeClass 是传入前审查对归一化结构的**结构分类**结果(design/12 §6 策略一补充的
|
||||
// 三段式分级的类型化表达)。
|
||||
type NoticeClass int
|
||||
@@ -56,11 +132,17 @@ const (
|
||||
NoticeEscalate
|
||||
)
|
||||
|
||||
// SystemNotice 是一条系统通知归一化后的统一结构(design/12 §6 策略一补充)。
|
||||
// SystemNotice 是一条系统通知归一化后的统一结构(design/12 §6 策略一补充,
|
||||
// 字段集按 design/26 §2.4 扩展)。
|
||||
//
|
||||
// Ignorable / HighValue 由**适配器**依结构化事件类型(notice_type / sub_type /
|
||||
// 操作者 id 等)填充。两者皆为 false 表示「已识别为系统通知,但结构上无法归类」——
|
||||
// 它交给传入前审查用既有审查模型剔噪,而不是由模型判身份。
|
||||
//
|
||||
// 刻意**不设** RefFlag:OneBot 的 `request.flag` 是**可执行凭据**(拿它能直接同意
|
||||
// 好友 / 加群请求),design/26 §4.2 明令它不得入表、不得进日志。把它挂在会流向留存
|
||||
// 层的结构上,等于给它开一条渗漏路径;将来若要做「自动同意」,那一刻再从内存载荷里
|
||||
// 临时取用(design/26 §8-1),绝不经过这里。
|
||||
type SystemNotice struct {
|
||||
// Kind 是归一化后的通知种类。
|
||||
Kind NoticeKind
|
||||
@@ -70,6 +152,24 @@ type SystemNotice struct {
|
||||
Ignorable bool
|
||||
// HighValue 报告该通知在结构上是高价值(敏感操作预警 / 她本人被移出等)。
|
||||
HighValue bool
|
||||
// Category 是比 Kind 细一档的归一化类别(design/26 §2.4/§4.1);零值表示适配器
|
||||
// 未给类别,留存侧按 Kind 兜底。
|
||||
Category NoticeCategory
|
||||
// PlatformType 是平台**原生**类型码(OneBot 的 notice_type / request_type /
|
||||
// meta_event_type / post_type、KOOK 的 extra.type、Telegram 的 update 键名),
|
||||
// **原样保留、绝不改写**——它是排障时回答「上游到底报了什么类型」的唯一凭据
|
||||
// (design/26 §2.4)。
|
||||
PlatformType string
|
||||
// Actor 是事件的发起者(结构化字段)。它**绝不参与判主**——身份永远由会话层的
|
||||
// 白名单 + 策略引擎裁决(项目红线)。零值表示平台没给。
|
||||
Actor Actor
|
||||
// Target 是被作用者(戳一戳的目标 / 被禁言者 / 运气王…)。无则零值。
|
||||
Target Actor
|
||||
// SelfRelated 报告该事件是否命中**她自己**(OneBot 的 self_id / 自己的成员身份),
|
||||
// 是 design/26 §2.2「self 命中升档」的判据(结构化 id 比对,与正文措辞无关)。
|
||||
SelfRelated bool
|
||||
// RefMessage 是关联的消息 id(撤回 / 回应 / 按钮 / 置顶)。无则空串。
|
||||
RefMessage string
|
||||
}
|
||||
|
||||
// Classify 是结构分类的**单一事实来源**(design/12 §6 策略一补充):
|
||||
@@ -90,9 +190,14 @@ func (n SystemNotice) Classify() NoticeClass {
|
||||
// 补充)。channelbridge 收到一条被结构化标记为系统事件的消息(ContentType 为
|
||||
// ContentAction)时调用它,把平台原生载荷解析成统一结构。
|
||||
//
|
||||
// ok=false 表示**无法归一化**——格式异常 / 未知通知类型 / 解析失败 / 载荷缺失。
|
||||
// 调用方据此走 fail-safe 静默(仅登记、绝不驱动 loop,绝不让畸形数据进入 LLM
|
||||
// 上下文)。实现必须无副作用:不调用 LLM、不判身份、不做 I/O。
|
||||
// ok=false 表示**压根读不出事件**——载荷缺失 / 解析失败。调用方据此走 fail-safe 静默
|
||||
// (仅登记、绝不驱动 loop,绝不让畸形数据进入 LLM 上下文)。实现必须无副作用:
|
||||
// 不调用 LLM、不判身份、不做 I/O。
|
||||
//
|
||||
// ⚠️ 口径(design/26 §1.1 / §3.7):**「类型未知」不等于「读不出事件」**。适配器应当
|
||||
// 把能解析、但类型未被枚举的帧归一化成 `NoticeUnknown` 兜底并返回 ok=true(带
|
||||
// PlatformType 原样留痕),而不是用 ok=false 让它消失在「未识别」里——「被丢弃」与
|
||||
// 「从未到达」必须在数据里能分开。ok=false 只保留给**根本解不出**的载荷。
|
||||
type SystemNoticeNormalizer interface {
|
||||
NormalizeSystemNotice(msg UnifiedMessage) (SystemNotice, bool)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user