Skip to main content

nagisa_types/
event.rs

1//! 统一事件 [`Event`] 及其各类载荷:[`MessageEvent`](消息)、[`Notice`](通知)、
2//! [`Request`](请求,配 [`RequestToken`] 回传同意/拒绝)、[`Meta`](连接/就绪/心跳等元事件),
3//! 外加逃生口 [`Event::Raw`]。事件按细粒度变体建模(OneBot 的重载哨兵在适配器侧拆成具体变体),
4//! 各变体字段的跨协议来源/缺口写在对应 `///` 上。[`Event`] 上的 [`peer`](Event::peer) /
5//! [`group`](Event::group) / [`sender`](Event::sender) 是跨变体抽取寻址信息的便捷方法。
6use crate::capability::Protocol;
7use crate::entity::{FileMeta, FriendInfo, GroupInfo, ImplStatus, MemberInfo};
8use crate::id::{MessageId, Peer, Scene, Uin};
9use crate::message::Message;
10use serde_json::Value;
11
12#[derive(Clone, Debug)]
13#[non_exhaustive]
14pub enum Event {
15    /// `MessageEvent` 较大且是热路径;装箱以避免 `Event` 整体膨胀
16    /// (`Event` 通常以 `Arc<Event>` 传递,模式匹配会自动穿透 `Box`)。
17    Message(Box<MessageEvent>),
18    Notice(Notice),
19    Request(Request),
20    Meta(Meta),
21    /// 逃生口:未知/协议私有事件,绝不丢弃。
22    Raw(RawEvent),
23}
24
25/// 群匿名消息发送者标识(OneBot 群消息 `sub_type=anonymous` 的 `anonymous` 子对象)。
26/// `flag` 用于 `set_group_anonymous_ban`。OneBot-only;Milky 为 None。
27/// OFFICIAL: https://github.com/botuniverse/onebot-11/blob/master/event/message.md (§群消息 anonymous)
28#[derive(Clone, Debug)]
29pub struct Anonymous {
30    pub id: i64,
31    pub name: String,
32    pub flag: String,
33}
34
35/// 消息气泡样式(Lagrange.OneBot 群/私聊消息事件的 `message_style` 块)。
36/// 仅在 Lagrange 端出现;其余协议为 None。`bubble_id`/`pendant_id` 为气泡/挂件
37/// 资源 id,`pal_type` 为伙伴类型;`raw` 保留完整原始块以防协议追加字段(绝不丢弃)。
38/// ENDPOINT: Lagrange.OneBot Lagrange.OneBot/Message/MessageStyle(message_style 块)
39///   (https://github.com/LagrangeDev/Lagrange.Core)。
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct MessageStyle {
42    pub bubble_id: Option<i64>,
43    pub pendant_id: Option<i64>,
44    pub pal_type: Option<i64>,
45    pub raw: Value,
46}
47
48#[derive(Clone, Debug)]
49pub struct MessageEvent {
50    pub id: MessageId,
51    pub peer: Peer,
52    pub sender: Uin,
53    pub self_id: Uin,
54    pub time: i64,
55    pub content: Message,
56    pub is_self: bool,
57    pub group: Option<GroupInfo>,
58    pub member: Option<MemberInfo>,
59    /// 好友场景消息附带的好友实体(Milky friend-scene 给实体;OneBot 为 None)。
60    pub friend: Option<FriendInfo>,
61    /// 群匿名消息的匿名发送者标识(OneBot-only;非匿名/Milky 为 None)。
62    pub anonymous: Option<Anonymous>,
63    /// 消息字体(OneBot `font`,遗留字段;Milky 为 None)。
64    pub font: Option<i32>,
65    /// 私聊临时会话的来源对端(OneBot/LLOneBot 私聊消息的 `target_id`:本号给
66    /// 对端发私聊时对端 uin);群消息/Milky 为 None。
67    /// ENDPOINT: LLOneBot/NapCat 私聊消息事件 `target_id` 字段。
68    pub target_id: Option<Uin>,
69    /// 消息气泡样式(Lagrange `message_style` 块);其余协议为 None。
70    pub message_style: Option<MessageStyle>,
71    pub raw: Value,
72}
73
74#[derive(Clone, Debug)]
75pub struct NudgeDisplay {
76    pub action: String,
77    pub suffix: String,
78    pub action_img_url: Option<String>,
79}
80
81#[derive(Clone, Copy, PartialEq, Eq, Debug)]
82pub enum ReactionKind {
83    Face,
84    Emoji,
85}
86
87/// 群荣誉类型。
88/// OFFICIAL: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md (§群成员荣誉变更)
89#[derive(Clone, Copy, PartialEq, Eq, Debug)]
90pub enum HonorKind {
91    /// 龙王。
92    Talkative,
93    /// 群聊之火。
94    Performer,
95    /// 快乐源泉。
96    Emotion,
97    /// 协议未知值——降级于此,绝不 panic。
98    Unknown,
99}
100
101/// 群成员减少的原因(OneBot `group_decrease` 的 sub_type)。
102/// OFFICIAL: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md (§群成员减少)
103///
104/// **跨协议非对称(固有限制)**:[`KickMe`](Self::KickMe) 仅 **OneBot** wire 能产出
105/// (`group_decrease` sub_type=`kick_me`)。Milky 的 `GroupMemberDecreaseNotification`
106/// 不区分「被踢的是不是 bot 自己」,只能解码为 [`Leave`](Self::Leave)/[`Kick`](Self::Kick)
107/// ——故下游「bot 自身被踢」的 gating 逻辑**不得依赖 Milky 后端下的 `KickMe`**
108/// (Milky 永不产出它)。这是 Milky wire 的天然缺口,非解码缺陷。
109#[derive(Clone, Copy, PartialEq, Eq, Debug)]
110pub enum MemberDecreaseReason {
111    /// 主动退群。
112    Leave,
113    /// 被管理员踢出。
114    Kick,
115    /// 机器人自身被踢出。**仅 OneBot 可产出**(Milky wire 无此区分,详见枚举级文档)。
116    KickMe,
117    /// 群解散(NapCat `group_decrease` sub_type=`disband`,群主解散群时对每个成员产出)。
118    /// ENDPOINT: NapCat packages/napcat-onebot/event/notice/OB11GroupDecreaseEvent.ts
119    ///   (https://github.com/NapNeko/NapCatQQ)。
120    Disband,
121    /// 协议未提供/未知。
122    Unknown,
123}
124
125/// 单条表情回应(`group_msg_emoji_like` 的 `likes[]` 元素 / Milky/Lagrange 单 reaction)。
126/// `count` 为该 emoji 的累计回应人数(缺省 None)。
127/// ENDPOINT: NapCat packages/napcat-onebot/event/notice/OB11MsgEmojiLikeEvent.ts
128///   (https://github.com/NapNeko/NapCatQQ);cross-checked LLOneBot
129///   src/onebot11/event/notice/OB11MsgEmojiLikeEvent.ts。
130#[derive(Clone, Debug, PartialEq, Eq)]
131pub struct EmojiLike {
132    pub face_id: String,
133    pub count: Option<i64>,
134}
135
136/// 在线(临时)文件 notice 的方向(NapCat `online_file_send`/`online_file_receive`)。
137/// ENDPOINT: NapCat(在线文件收发 notice)。
138#[derive(Clone, Copy, PartialEq, Eq, Debug)]
139pub enum OnlineFileDirection {
140    /// 我方/对端发出在线文件(`online_file_send`)。
141    Send,
142    /// 收到在线文件(`online_file_receive`)。
143    Receive,
144}
145
146/// 闪传(flash transfer)notice 的进度阶段(LLOneBot `flash_file` 的 sub_type)。
147/// ENDPOINT: LLOneBot src/onebot11/event/notice/OB11FlashTransferNoticeEvent.ts
148///   (https://github.com/LLOneBot/LLOneBot)。
149#[derive(Clone, Copy, PartialEq, Eq, Debug)]
150pub enum FlashFilePhase {
151    Downloading,
152    Downloaded,
153    Uploading,
154    Uploaded,
155    /// 协议未知阶段——降级于此,绝不 panic。
156    Unknown,
157}
158
159#[derive(Clone, Debug)]
160#[non_exhaustive]
161pub enum Notice {
162    Recall {
163        peer: Peer,
164        id: MessageId,
165        sender: Uin,
166        operator: Uin,
167        suffix: Option<String>,
168    },
169    MemberIncrease {
170        group: Uin,
171        user: Uin,
172        operator: Option<Uin>,
173        invitor: Option<Uin>,
174    },
175    MemberDecrease {
176        group: Uin,
177        user: Uin,
178        operator: Option<Uin>,
179        reason: MemberDecreaseReason,
180    },
181    AdminChange {
182        group: Uin,
183        user: Uin,
184        operator: Option<Uin>,
185        is_set: bool,
186    },
187    Mute {
188        group: Uin,
189        user: Uin,
190        operator: Uin,
191        duration: i32,
192    },
193    WholeMute {
194        group: Uin,
195        operator: Uin,
196        is_mute: bool,
197    },
198    GroupNameChange {
199        group: Uin,
200        new_name: String,
201        operator: Uin,
202    },
203    /// 群红包运气王(OneBot `notify/lucky_king`)。`user` = 发红包者,`target` = 运气王。
204    /// OFFICIAL: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md (§群红包运气王)
205    LuckyKing {
206        group: Uin,
207        user: Uin,
208        target: Uin,
209    },
210    /// 群成员荣誉变更(龙王/群聊之火/快乐源泉)。
211    /// OFFICIAL: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md (§群成员荣誉变更)
212    Honor {
213        group: Uin,
214        user: Uin,
215        honor: HonorKind,
216    },
217    /// 群名片变更。
218    /// ENDPOINT: NapCat https://github.com/NapNeko/NapCatQQ/blob/main/src/onebot/event/notice/OB11GroupCardEvent.ts
219    GroupCardChange {
220        group: Uin,
221        user: Uin,
222        old_card: String,
223        new_card: String,
224    },
225    FriendNudge {
226        user: Uin,
227        is_self_send: bool,
228        is_self_receive: bool,
229        display: NudgeDisplay,
230    },
231    GroupNudge {
232        group: Uin,
233        sender: Uin,
234        receiver: Uin,
235        display: NudgeDisplay,
236    },
237    /// 表情回应。`face_id`/`count` 反映首个 emoji(向后兼容单事件语义);
238    /// `likes` 携带本次 notice 的**全部** emoji(NapCat/LLOneBot `group_msg_emoji_like`
239    /// 可一次回应多个 emoji),其余协议为单元素。绝不丢弃非首 like。
240    /// `sub_type` 为原始 wire 子类型(Lagrange `reaction` 的 `sub_type`:`add`/`remove`),
241    /// `is_add` 由其派生;非 Lagrange 来源(Milky/emoji_like 等无独立 sub_type)为 None。
242    Reaction {
243        group: Uin,
244        user: Uin,
245        seq: i64,
246        face_id: String,
247        kind: ReactionKind,
248        is_add: bool,
249        sub_type: Option<String>,
250        count: Option<i64>,
251        likes: Vec<EmojiLike>,
252    },
253    /// 群精华消息变更。`sender` 为被设/取消精华消息的原作者(Lagrange `essence`
254    /// 的 `sender_id`);OneBot v11/Milky 无该字段时为 None。
255    EssenceChange {
256        group: Uin,
257        seq: i64,
258        sender: Option<Uin>,
259        operator: Option<Uin>,
260        is_set: bool,
261    },
262    GroupFileUpload {
263        group: Uin,
264        user: Uin,
265        file: FileMeta,
266    },
267    FriendFileUpload {
268        user: Uin,
269        file: FileMeta,
270        is_self: bool,
271    },
272    /// 新增好友成功(OneBot `friend_add`)。常用于自动打招呼。
273    FriendAdd {
274        user: Uin,
275    },
276    PeerPinChange {
277        peer: Peer,
278        is_pinned: bool,
279    },
280    /// 机器人离线(Lagrange `bot_offline`)。`reason` 为离线原因文案,`tag` 为
281    /// Lagrange 区分离线种类的标签(与 `reason` 不同维度,如踢下线/网络等);
282    /// 无 tag 的来源为 None。
283    BotOffline {
284        reason: String,
285        tag: Option<String>,
286    },
287    /// 群解散(LLOneBot `group_dismiss`:群主解散整个群时下发一次,区别于逐成员的
288    /// `group_decrease`/`disband`)。`operator` 为解散者(通常为群主)。
289    /// ENDPOINT: LLOneBot src/onebot11/event/notice/(群解散 notice)
290    ///   (https://github.com/LLOneBot/LLOneBot)。
291    GroupDismiss {
292        group: Uin,
293        operator: Uin,
294    },
295    /// 群头衔变更(NapCat/LLOneBot `notify` sub_type=`title`)。`title` 为新头衔。
296    /// ENDPOINT: NapCat packages/napcat-onebot/event/notice/OB11GroupTitleEvent.ts
297    ///   (https://github.com/NapNeko/NapCatQQ);cross-checked LLOneBot。
298    GroupTitleChange {
299        group: Uin,
300        user: Uin,
301        title: String,
302    },
303    /// 输入状态(对端正在输入,NapCat/LLOneBot `notify` sub_type=`input_status`)。
304    /// 群场景 `group` 为 Some;私聊为 None。`status_text` 为展示文案,`event_type` 为
305    /// 原始状态码(0=输入中等,端相关)。
306    /// ENDPOINT: NapCat packages/napcat-onebot/event/notice/OB11InputStatusEvent.ts
307    ///   (https://github.com/NapNeko/NapCatQQ);cross-checked LLOneBot。
308    InputStatus {
309        user: Uin,
310        group: Option<Uin>,
311        status_text: String,
312        event_type: i64,
313    },
314    /// 资料卡点赞通知(NapCat/LLOneBot `notify` sub_type=`profile_like`)。
315    /// `operator` 为点赞者,`times` 为本次点赞次数,`operator_nick` 为点赞者昵称(可空)。
316    /// ENDPOINT: NapCat packages/napcat-onebot/event/notice/OB11ProfileLikeEvent.ts
317    ///   (https://github.com/NapNeko/NapCatQQ);cross-checked LLOneBot。
318    ProfileLike {
319        operator: Uin,
320        operator_nick: String,
321        times: i64,
322    },
323    /// 灰字提示(NapCat/LLOneBot `notify` sub_type=`gray_tip`)。承载群/好友灰条系统提示
324    /// 文本;具体子类繁多,统一以 `content` 透出文本,结构细节保留在事件 `raw`。
325    /// ENDPOINT: NapCat packages/napcat-onebot/event/notice/(gray tip notice)
326    ///   (https://github.com/NapNeko/NapCatQQ);cross-checked LLOneBot。
327    GrayTip {
328        group: Option<Uin>,
329        user: Option<Uin>,
330        content: String,
331    },
332    /// 戳一戳撤回(LLOneBot `notify` sub_type=`poke_recall`)。对端撤回了一次戳一戳。
333    /// `group` 群场景为 Some,私聊为 None。
334    /// ENDPOINT: LLOneBot src/onebot11/event/notice/(poke_recall notice)
335    ///   (https://github.com/LLOneBot/LLOneBot)。
336    PokeRecall {
337        group: Option<Uin>,
338        user: Uin,
339    },
340    /// 在线(临时)文件收发 notice(NapCat `online_file_send`/`online_file_receive`)。
341    /// `direction` 区分发出/接收;群场景 `group` 为 Some,私聊为 None。
342    /// ENDPOINT: NapCat(在线文件 notice)。
343    OnlineFile {
344        direction: OnlineFileDirection,
345        user: Uin,
346        group: Option<Uin>,
347    },
348    /// 闪传进度 notice(LLOneBot `flash_file`:downloading/downloaded/uploading/uploaded)。
349    /// `phase` 为进度阶段;`group` 群场景为 Some。完整闪传载荷保留在事件 `raw`。
350    /// ENDPOINT: LLOneBot src/onebot11/event/notice/OB11FlashTransferNoticeEvent.ts
351    ///   (https://github.com/LLOneBot/LLOneBot)。
352    FlashFile {
353        phase: FlashFilePhase,
354        user: Uin,
355        group: Option<Uin>,
356    },
357    /// 单侧/未知 notice:OneBot 的 group_card/honor 等也归此。
358    Other {
359        protocol: Protocol,
360        kind: String,
361        raw: Value,
362    },
363}
364
365/// 不透明请求令牌——同意/拒绝时 round-trip;内部按协议打包不同字段。
366/// 业务侧视为不透明(只在事件里收到、回传给 `Bot::handle_request`);
367/// 内部 `RequestTokenInner` 标 `#[doc(hidden)]`,仅供各 adapter crate 构造/解构。
368#[derive(Clone, Debug)]
369pub struct RequestToken(#[doc(hidden)] pub RequestTokenInner);
370
371#[doc(hidden)]
372#[derive(Clone, Debug)]
373pub enum RequestTokenInner {
374    /// OneBot:不透明 flag 字符串。
375    OneBotFlag(String),
376    /// Milky 好友请求:按 initiator_uid + is_filtered。
377    MilkyFriend { initiator_uid: String, is_filtered: bool },
378    /// Milky 入群/邀请他人入群:notification_seq + 类型 + group_id + is_filtered。
379    MilkyGroupNotification { notification_seq: i64, notification_type: String, group_id: Uin, is_filtered: bool },
380    /// Milky 邀请自身入群:group_id + invitation_seq。
381    MilkyInvitation { group_id: Uin, invitation_seq: i64 },
382}
383
384/// 请求处理状态(Milky `FriendRequest.state` 枚举)。
385/// OFFICIAL: https://github.com/SaltifyDev/milky/blob/main/protocol/src/ir/api/friend.ts
386///   (get_friend_requests → FriendRequest.state)。OneBot 无此字段 → `Unknown`。
387#[derive(Clone, Copy, PartialEq, Eq, Debug)]
388pub enum RequestState {
389    /// 待处理。
390    Pending,
391    /// 已同意。
392    Accepted,
393    /// 已拒绝。
394    Rejected,
395    /// 已忽略。
396    Ignored,
397    /// 协议未提供或为未知枚举值(绝不 panic)。
398    Unknown,
399}
400
401#[derive(Clone, Debug)]
402#[non_exhaustive]
403pub enum Request {
404    /// 好友请求。`target_user_id`/`state`/`time`/`is_filtered` 为 Milky
405    /// `FriendRequest` 富字段(OneBot 无:`target_user_id`/`time` → None,
406    /// `state` → `RequestState::Unknown`,`is_filtered` → false)。
407    Friend {
408        initiator: Uin,
409        initiator_uid: Option<String>,
410        comment: String,
411        via: String,
412        source_group: Option<Uin>,
413        target_user_id: Option<Uin>,
414        state: RequestState,
415        time: Option<i64>,
416        is_filtered: bool,
417        token: RequestToken,
418    },
419    /// 加群请求。`invitor` 为邀请人(Lagrange `request/group` 的 `invitor_id`:
420    /// 经邀请链接申请入群时携带邀请人 uin);无邀请人/其余协议为 None。
421    GroupJoin {
422        group: Uin,
423        initiator: Uin,
424        comment: String,
425        invitor: Option<Uin>,
426        is_filtered: bool,
427        token: RequestToken,
428    },
429    GroupInvitedJoin {
430        group: Uin,
431        initiator: Uin,
432        target: Uin,
433        token: RequestToken,
434    },
435    GroupInvite {
436        group: Uin,
437        initiator: Uin,
438        comment: String,
439        source_group: Option<Uin>,
440        token: RequestToken,
441    },
442}
443
444#[derive(Clone, Debug)]
445#[non_exhaustive]
446pub enum Meta {
447    /// 协议端(传输层)连上:由**框架的事件源**在底层 socket 连接成功时发出(正向/反向 WS、
448    /// Milky WS 等),跨协议口径一致;每次(重)连都发一次。是「协议端连接」的权威信号。
449    Connect,
450    /// 协议端(传输层)断开:由**框架的事件源**在 socket 掉线、即将重连前发出。`reason`
451    /// 为底层错误文案(如有)。正常停机不发。注意:传输断开 ≠ 账号掉线(见 [`Meta::BotOffline`])。
452    Disconnect {
453        reason: Option<String>,
454    },
455    /// 框架就绪:登录已解析出**可用账号**(`self_id != 0`)、`Bot` 句柄可正常动作。每次
456    /// `run_*` 仅发**一次**(重连不重放)。需要「机器人完全就绪且有可用账号」才跑的逻辑
457    /// (定时任务 / 加载后初始化)监听此事件即可:无可用账号则它根本不发、相关逻辑自然不跑。
458    /// `nickname` 为登录账号的昵称(来自 `get_login_info`;未知时为空串)。
459    Ready {
460        self_id: Uin,
461        nickname: String,
462    },
463    Heartbeat {
464        interval: i64,
465        status: ImplStatus,
466    },
467    /// 机器人上线(Lagrange `bot_online` / OneBot lifecycle enable)。`reason` 为
468    /// Lagrange 上线原因文案(如重连/扫码);无该信息的来源为 None。
469    BotOnline {
470        reason: Option<String>,
471    },
472    BotOffline,
473}
474
475#[derive(Clone, Debug)]
476pub struct RawEvent {
477    pub protocol: Protocol,
478    pub kind: String,
479    pub raw: Value,
480}
481
482impl RequestToken {
483    /// 由 OneBot flag 构造。
484    pub fn onebot_flag(flag: impl Into<String>) -> Self {
485        RequestToken(RequestTokenInner::OneBotFlag(flag.into()))
486    }
487}
488
489impl Event {
490    /// 本事件所属的可寻址会话(消息取其 peer;通知/请求取群或好友 peer),无则 `None`。
491    /// 供开关门控与 waiter 限定作用域使用。
492    pub fn peer(&self) -> Option<Peer> {
493        use Notice as N;
494        use Request as R;
495        match self {
496            Event::Message(m) => Some(m.peer),
497            Event::Notice(n) => match n {
498                N::Recall { peer, .. } => Some(*peer),
499                N::PeerPinChange { peer, .. } => Some(*peer),
500                N::MemberIncrease { group, .. }
501                | N::MemberDecrease { group, .. }
502                | N::AdminChange { group, .. }
503                | N::Mute { group, .. }
504                | N::WholeMute { group, .. }
505                | N::GroupNameChange { group, .. }
506                | N::Honor { group, .. }
507                | N::GroupCardChange { group, .. }
508                | N::GroupNudge { group, .. }
509                | N::Reaction { group, .. }
510                | N::EssenceChange { group, .. }
511                | N::GroupFileUpload { group, .. }
512                | N::GroupDismiss { group, .. }
513                | N::GroupTitleChange { group, .. }
514                | N::LuckyKing { group, .. } => Some(Peer::group(*group)),
515                N::FriendNudge { user, .. } | N::FriendFileUpload { user, .. } | N::FriendAdd { user } => {
516                    Some(Peer::friend(*user))
517                }
518                // 群/私聊均可的 notice:有 group 即群 peer,否则好友 peer。
519                N::InputStatus { user, group, .. }
520                | N::PokeRecall { user, group, .. }
521                | N::OnlineFile { user, group, .. }
522                | N::FlashFile { user, group, .. }
523                | N::GrayTip { user: Some(user), group, .. } => Some(match group {
524                    Some(g) => Peer::group(*g),
525                    None => Peer::friend(*user),
526                }),
527                // gray_tip 无 user(纯群灰条)时退化为群 peer(若有 group)。
528                N::GrayTip { user: None, group: Some(g), .. } => Some(Peer::group(*g)),
529                N::GrayTip { user: None, group: None, .. }
530                | N::ProfileLike { .. }
531                | N::BotOffline { .. }
532                | N::Other { .. } => None,
533            },
534            Event::Request(r) => match r {
535                R::Friend { initiator, .. } => Some(Peer::friend(*initiator)),
536                R::GroupJoin { group, .. } | R::GroupInvitedJoin { group, .. } | R::GroupInvite { group, .. } => {
537                    Some(Peer::group(*group))
538                }
539            },
540            Event::Meta(_) | Event::Raw(_) => None,
541        }
542    }
543
544    /// 若本事件发生在群里,返回群号,否则 `None`。
545    pub fn group(&self) -> Option<Uin> {
546        self.peer().and_then(|p| match p.scene {
547            Scene::Group => Some(p.id),
548            _ => None,
549        })
550    }
551
552    /// 行为主体(消息发送者 / 通知操作者 / 请求发起者),无则 `None`。
553    pub fn sender(&self) -> Option<Uin> {
554        use Notice as N;
555        use Request as R;
556        match self {
557            Event::Message(m) => Some(m.sender),
558            Event::Notice(n) => match n {
559                N::Recall { sender, .. } => Some(*sender),
560                N::MemberIncrease { user, .. }
561                | N::MemberDecrease { user, .. }
562                | N::AdminChange { user, .. }
563                | N::Honor { user, .. }
564                | N::GroupCardChange { user, .. }
565                | N::Reaction { user, .. }
566                | N::GroupFileUpload { user, .. }
567                | N::GroupTitleChange { user, .. }
568                | N::InputStatus { user, .. }
569                | N::PokeRecall { user, .. }
570                | N::OnlineFile { user, .. }
571                | N::FlashFile { user, .. }
572                | N::LuckyKing { user, .. } => Some(*user),
573                N::Mute { operator, .. }
574                | N::WholeMute { operator, .. }
575                | N::GroupNameChange { operator, .. }
576                | N::GroupDismiss { operator, .. }
577                | N::ProfileLike { operator, .. } => Some(*operator),
578                N::GroupNudge { sender, .. } => Some(*sender),
579                N::FriendNudge { user, .. } | N::FriendFileUpload { user, .. } | N::FriendAdd { user } => Some(*user),
580                N::GrayTip { user, .. } => *user,
581                N::EssenceChange { .. } | N::PeerPinChange { .. } | N::BotOffline { .. } | N::Other { .. } => None,
582            },
583            Event::Request(r) => match r {
584                R::Friend { initiator, .. }
585                | R::GroupJoin { initiator, .. }
586                | R::GroupInvitedJoin { initiator, .. }
587                | R::GroupInvite { initiator, .. } => Some(*initiator),
588            },
589            Event::Meta(_) | Event::Raw(_) => None,
590        }
591    }
592}