napcat: NapCatQQ (OneBot 11) Go SDK, initial release

This commit is contained in:
2026-10-18 18:37:07 +08:00
commit 48d56c64d0
14 changed files with 2879 additions and 0 deletions

160
event.go Normal file
View File

@ -0,0 +1,160 @@
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
}