package napcat import ( "encoding/json" "fmt" ) // Event 是收到的一条 OneBot 事件。按 PostType 取对应的子事件指针。 type Event struct { Time int64 `json:"time"` SelfID int64 `json:"self_id"` PostType string `json:"post_type"` Raw json.RawMessage `json:"-"` Message *MessageEvent // post_type == "message" / "message_sent" Notice *NoticeEvent // post_type == "notice" Request *RequestEvent // post_type == "request" Meta *MetaEvent // post_type == "meta_event" } type BaseEvent struct { Time int64 `json:"time"` SelfID int64 `json:"self_id"` PostType string `json:"post_type"` } // MessageEvent 是私聊/群聊消息事件。bot 自己发出的消息以 message_sent 上报。 type MessageEvent struct { BaseEvent MessageType string `json:"message_type"` // private | group SubType string `json:"sub_type"` // friend/group/normal/anonymous/notice MessageID int64 `json:"message_id"` UserID int64 `json:"user_id"` GroupID int64 `json:"group_id"` TargetID int64 `json:"target_id"` // 群临时会话目标 TempSource int64 `json:"temp_source"` // 群临时会话来源 Message json.RawMessage `json:"message"` // 段数组或 CQ 码字符串 RawMessage string `json:"raw_message"` Font int64 `json:"font"` Sender Sender `json:"sender"` } // NoticeEvent 是通知事件(撤回、入群、禁言、戳一戳等)。 // 未映射的字段收进 Extra,避免 NapCat 增加字段时丢数据。 type NoticeEvent struct { BaseEvent NoticeType string `json:"notice_type"` SubType string `json:"sub_type"` GroupID int64 `json:"group_id"` UserID int64 `json:"user_id"` OperatorID int64 `json:"operator_id"` TargetID int64 `json:"target_id"` MessageID int64 `json:"message_id"` Duration int64 `json:"duration"` // 禁言时长(秒) File *FileNotice `json:"file"` HonorType string `json:"honor_type"` CardNew string `json:"card_new"` CardOld string `json:"card_old"` Extra map[string]any `json:"-"` } func (n *NoticeEvent) UnmarshalJSON(data []byte) error { type alias NoticeEvent var a alias if err := json.Unmarshal(data, &a); err != nil { return err } *n = NoticeEvent(a) var m map[string]any if err := json.Unmarshal(data, &m); err == nil { for k := range knownNoticeKeys { delete(m, k) } if len(m) > 0 { n.Extra = m } } return nil } var knownNoticeKeys = map[string]struct{}{ "time": {}, "self_id": {}, "post_type": {}, "notice_type": {}, "sub_type": {}, "group_id": {}, "user_id": {}, "operator_id": {}, "target_id": {}, "message_id": {}, "duration": {}, "file": {}, "honor_type": {}, "card_new": {}, "card_old": {}, } // FileNotice 是群文件上传/离线文件通知里的文件信息。 type FileNotice struct { ID string `json:"id"` Name string `json:"name"` Size int64 `json:"size"` BusID int64 `json:"busid"` } // RequestEvent 是好友/群请求事件(加好友、加群、被邀请入群)。 type RequestEvent struct { BaseEvent RequestType string `json:"request_type"` // friend | group SubType string `json:"sub_type"` // group 时为 add/invite UserID int64 `json:"user_id"` GroupID int64 `json:"group_id"` Comment string `json:"comment"` Flag string `json:"flag"` } // MetaEvent 是元事件(连接生命周期与心跳)。 type MetaEvent struct { BaseEvent MetaType string `json:"meta_event_type"` // lifecycle | heartbeat SubType string `json:"sub_type"` // connect/enable/disable HeartbeatInterval int64 `json:"interval"` Status *Status `json:"status,omitempty"` } func decodeEvent(data []byte) (*Event, error) { var probe struct { PostType string `json:"post_type"` } if err := json.Unmarshal(data, &probe); err != nil { return nil, err } if probe.PostType == "" { return nil, fmt.Errorf("napcat: missing post_type: %s", truncate(data)) } ev := &Event{Raw: append(json.RawMessage(nil), data...)} var base BaseEvent _ = json.Unmarshal(data, &base) ev.Time, ev.SelfID, ev.PostType = base.Time, base.SelfID, probe.PostType switch probe.PostType { case "message", "message_sent": var m MessageEvent if err := json.Unmarshal(data, &m); err != nil { return nil, err } ev.Message = &m case "notice": var n NoticeEvent if err := json.Unmarshal(data, &n); err != nil { return nil, err } ev.Notice = &n case "request": var r RequestEvent if err := json.Unmarshal(data, &r); err != nil { return nil, err } ev.Request = &r case "meta_event": var m MetaEvent if err := json.Unmarshal(data, &m); err != nil { return nil, err } ev.Meta = &m } return ev, nil }