napcat: NapCatQQ (OneBot 11) Go SDK, initial release
This commit is contained in:
360
types.go
Normal file
360
types.go
Normal file
@ -0,0 +1,360 @@
|
||||
package napcat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNotConnected 表示当前没有任何可用的 OneBot 连接来执行 API 调用。
|
||||
var ErrNotConnected = errors.New("napcat: no connected session")
|
||||
|
||||
// Response 是 OneBot 11 的通用动作响应信封。
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Retcode int `json:"retcode"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Echo string `json:"echo"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Wording string `json:"wording,omitempty"`
|
||||
}
|
||||
|
||||
// Err 在响应失败时返回非 nil 错误。
|
||||
func (r *Response) Err() error {
|
||||
if r == nil || r.Retcode == 0 || r.Status == "ok" || r.Status == "async" {
|
||||
return nil
|
||||
}
|
||||
msg := r.Wording
|
||||
if msg == "" {
|
||||
msg = r.Message
|
||||
}
|
||||
if msg == "" {
|
||||
msg = "unknown error"
|
||||
}
|
||||
return fmt.Errorf("napcat: action failed retcode=%d status=%s: %s", r.Retcode, r.Status, msg)
|
||||
}
|
||||
|
||||
// Decode 将响应的 data 字段解码到 out。
|
||||
func (r *Response) Decode(out any) error {
|
||||
if r == nil || out == nil {
|
||||
return nil
|
||||
}
|
||||
if len(r.Data) == 0 || string(r.Data) == "null" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(r.Data, out)
|
||||
}
|
||||
|
||||
// IntString 兼容数字或字符串形式出现的整数字段(如群成员 level)。
|
||||
type IntString int64
|
||||
|
||||
func (v *IntString) UnmarshalJSON(b []byte) error {
|
||||
s := strings.Trim(string(b), `"`)
|
||||
if s == "" || s == "null" {
|
||||
*v = 0
|
||||
return nil
|
||||
}
|
||||
n, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("napcat: invalid integer %q: %w", s, err)
|
||||
}
|
||||
*v = IntString(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sender 是消息事件的发送者信息。群聊时含 Card/Role,私聊时可能为空。
|
||||
// 注意:NapCat v4 的消息事件 sender 是精简的,实测不含 Role 字段
|
||||
// (完整角色需调 GetGroupMemberInfo 查询);本结构保留 Role 以兼容
|
||||
// 标准 OneBot 11 实现和需要 role 的接口返回。
|
||||
type Sender struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Nickname string `json:"nickname"`
|
||||
Sex string `json:"sex,omitempty"`
|
||||
Card string `json:"card,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
}
|
||||
|
||||
type LoginInfo struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
type StrangerInfo struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Nickname string `json:"nickname"`
|
||||
Sex string `json:"sex,omitempty"`
|
||||
Age int64 `json:"age,omitempty"`
|
||||
QID string `json:"qid,omitempty"`
|
||||
LongNick string `json:"long_nick,omitempty"`
|
||||
Level int64 `json:"level,omitempty"`
|
||||
}
|
||||
|
||||
type FriendInfo struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Nickname string `json:"nickname"`
|
||||
Remark string `json:"remark"`
|
||||
Sex string `json:"sex,omitempty"`
|
||||
Level int64 `json:"level,omitempty"`
|
||||
}
|
||||
|
||||
type GroupInfo struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
GroupName string `json:"group_name"`
|
||||
MemberCount int64 `json:"member_count"`
|
||||
MaxMemberCount int64 `json:"max_member_count"`
|
||||
}
|
||||
|
||||
type GroupMemberInfo struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Nickname string `json:"nickname"`
|
||||
Card string `json:"card"`
|
||||
CardChangeable bool `json:"card_changeable"`
|
||||
Sex string `json:"sex"`
|
||||
Level IntString `json:"level"`
|
||||
Role string `json:"role"`
|
||||
Title string `json:"title"`
|
||||
JoinTime int64 `json:"join_time"`
|
||||
LastSentTime int64 `json:"last_sent_time"`
|
||||
ShutUpTimestamp int64 `json:"shut_up_timestamp"`
|
||||
Unfriendly bool `json:"unfriendly"`
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Online bool `json:"online"`
|
||||
Good bool `json:"good"`
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
AppName string `json:"app_name"`
|
||||
AppVersion string `json:"app_version"`
|
||||
ProtocolVersion string `json:"protocol_version"`
|
||||
}
|
||||
|
||||
// MessageInfo 是 get_msg 等 API 返回的单条消息。
|
||||
type MessageInfo struct {
|
||||
MessageID int64 `json:"message_id"`
|
||||
RealID int64 `json:"real_id"`
|
||||
MessageSeq int64 `json:"message_seq,omitempty"`
|
||||
Time int64 `json:"time"`
|
||||
Sender Sender `json:"sender"`
|
||||
Message json.RawMessage `json:"message"`
|
||||
RawMessage string `json:"raw_message"`
|
||||
Font int64 `json:"font,omitempty"`
|
||||
GroupID int64 `json:"group_id,omitempty"`
|
||||
}
|
||||
|
||||
// Segments 将 message 字段解码为消息段数组。
|
||||
func (m MessageInfo) Segments() ([]Segment, error) { return DecodeMessage(m.Message) }
|
||||
|
||||
// HistoryMessage 是历史消息接口(get_group_msg_history 等)返回的单条消息。
|
||||
type HistoryMessage struct {
|
||||
MessageID int64 `json:"message_id"`
|
||||
MessageSeq int64 `json:"message_seq"`
|
||||
Time int64 `json:"time"`
|
||||
Sender Sender `json:"sender"`
|
||||
Message json.RawMessage `json:"message"`
|
||||
RawMessage string `json:"raw_message"`
|
||||
GroupID int64 `json:"group_id,omitempty"`
|
||||
}
|
||||
|
||||
func (m HistoryMessage) Segments() ([]Segment, error) { return DecodeMessage(m.Message) }
|
||||
|
||||
// HistoryMessages 兼容 data 为 {"messages": [...]} 或直接数组两种返回形态。
|
||||
type HistoryMessages []HistoryMessage
|
||||
|
||||
func (h *HistoryMessages) UnmarshalJSON(b []byte) error {
|
||||
var probe struct {
|
||||
Messages []HistoryMessage `json:"messages"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &probe); err == nil && probe.Messages != nil {
|
||||
*h = probe.Messages
|
||||
return nil
|
||||
}
|
||||
var arr []HistoryMessage
|
||||
if err := json.Unmarshal(b, &arr); err == nil {
|
||||
*h = arr
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("napcat: unexpected history message payload: %s", truncate(b))
|
||||
}
|
||||
|
||||
// ForwardMessage 是合并转发中的一条消息。
|
||||
type ForwardMessage struct {
|
||||
MessageID int64 `json:"message_id"`
|
||||
Time int64 `json:"time"`
|
||||
Sender Sender `json:"sender"`
|
||||
Message json.RawMessage `json:"message"`
|
||||
Content json.RawMessage `json:"content,omitempty"`
|
||||
RawMessage string `json:"raw_message"`
|
||||
}
|
||||
|
||||
// Segments 将消息体解码为消息段数组,优先取 message 字段,其次 content。
|
||||
func (m ForwardMessage) Segments() ([]Segment, error) {
|
||||
if len(m.Message) > 0 {
|
||||
return DecodeMessage(m.Message)
|
||||
}
|
||||
return DecodeMessage(m.Content)
|
||||
}
|
||||
|
||||
// ForwardMessages 兼容 data 为 {"messages": [...]} 或直接数组两种返回形态。
|
||||
type ForwardMessages []ForwardMessage
|
||||
|
||||
func (f *ForwardMessages) UnmarshalJSON(b []byte) error {
|
||||
var probe struct {
|
||||
Messages []ForwardMessage `json:"messages"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &probe); err == nil && probe.Messages != nil {
|
||||
*f = probe.Messages
|
||||
return nil
|
||||
}
|
||||
var arr []ForwardMessage
|
||||
if err := json.Unmarshal(b, &arr); err == nil {
|
||||
*f = arr
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("napcat: unexpected forward message payload: %s", truncate(b))
|
||||
}
|
||||
|
||||
// SendForwardResult 是发送合并转发消息的返回。
|
||||
type SendForwardResult struct {
|
||||
MessageID IntString `json:"message_id"`
|
||||
ResID string `json:"res_id"`
|
||||
}
|
||||
|
||||
type EssenceMessage struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
SenderID int64 `json:"sender_id"`
|
||||
OperatorID int64 `json:"operator_id"`
|
||||
Time int64 `json:"time"`
|
||||
MessageID int64 `json:"message_id"`
|
||||
Message json.RawMessage `json:"message,omitempty"`
|
||||
RawMessage string `json:"raw_message,omitempty"`
|
||||
}
|
||||
|
||||
type GroupFile struct {
|
||||
FileID string `json:"file_id"`
|
||||
FileName string `json:"file_name"`
|
||||
BusID int64 `json:"busid"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
UploadTime int64 `json:"upload_time"`
|
||||
DeadTime int64 `json:"dead_time"`
|
||||
ModifyTime int64 `json:"modify_time"`
|
||||
DownloadTimes int64 `json:"download_times"`
|
||||
Uploader string `json:"uploader,omitempty"`
|
||||
UploaderID int64 `json:"uploader_id,omitempty"`
|
||||
}
|
||||
|
||||
type GroupFolder struct {
|
||||
FolderID string `json:"folder_id"`
|
||||
FolderName string `json:"folder_name"`
|
||||
CreateTime int64 `json:"create_time"`
|
||||
Creator string `json:"creator,omitempty"`
|
||||
CreatorID int64 `json:"creator_id,omitempty"`
|
||||
TotalFileCount int64 `json:"total_file_count"`
|
||||
}
|
||||
|
||||
type FileList struct {
|
||||
Files []GroupFile `json:"files"`
|
||||
Folders []GroupFolder `json:"folders"`
|
||||
}
|
||||
|
||||
type FileSystemInfo struct {
|
||||
FileCount int64 `json:"file_count"`
|
||||
LimitCount int64 `json:"limit_count"`
|
||||
UsedSpace int64 `json:"used_space"`
|
||||
TotalSpace int64 `json:"total_space"`
|
||||
}
|
||||
|
||||
type ImageResult struct {
|
||||
File string `json:"file"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type RecordResult struct {
|
||||
File string `json:"file"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type DownloadedFile struct {
|
||||
File string `json:"file"`
|
||||
URL string `json:"url,omitempty"`
|
||||
FileName string `json:"file_name,omitempty"`
|
||||
FileSize int64 `json:"file_size,omitempty"`
|
||||
}
|
||||
|
||||
type Cookies struct {
|
||||
Cookies string `json:"cookies"`
|
||||
}
|
||||
|
||||
type CSRFToken struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type Credentials struct {
|
||||
Cookies string `json:"cookies"`
|
||||
CSRFToken string `json:"csrf_token"`
|
||||
}
|
||||
|
||||
type URLSafety struct {
|
||||
Level int `json:"level"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type AtAllRemain struct {
|
||||
CanAtAll bool `json:"can_at_all"`
|
||||
RemainAtAllCount int64 `json:"remain_at_all_count"`
|
||||
RemainAtAllCountForGroup int64 `json:"remain_at_all_count_for_group"`
|
||||
RemainAtAllCountForUser int64 `json:"remain_at_all_count_for_user"`
|
||||
}
|
||||
|
||||
type RobotRange struct {
|
||||
MinUin int64 `json:"min_uin"`
|
||||
MaxUin int64 `json:"max_uin"`
|
||||
}
|
||||
|
||||
// RobotUinRanges 兼容 data 为 {"robot_uin_range": [...]} 或直接数组两种形态。
|
||||
type RobotUinRanges []RobotRange
|
||||
|
||||
func (r *RobotUinRanges) UnmarshalJSON(b []byte) error {
|
||||
var probe struct {
|
||||
RobotUinRange []RobotRange `json:"robot_uin_range"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &probe); err == nil && probe.RobotUinRange != nil {
|
||||
*r = probe.RobotUinRange
|
||||
return nil
|
||||
}
|
||||
var arr []RobotRange
|
||||
if err := json.Unmarshal(b, &arr); err == nil {
|
||||
*r = arr
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("napcat: unexpected robot uin range payload: %s", truncate(b))
|
||||
}
|
||||
|
||||
type OnlineClient struct {
|
||||
AppID int64 `json:"app_id"`
|
||||
DeviceName string `json:"device_name"`
|
||||
DeviceKind string `json:"device_kind"`
|
||||
}
|
||||
|
||||
type OnlineClients struct {
|
||||
Clients []OnlineClient `json:"clients"`
|
||||
Online bool `json:"online"`
|
||||
}
|
||||
|
||||
type MiniAppArk struct {
|
||||
Ark string `json:"ark"`
|
||||
}
|
||||
|
||||
func truncate(b []byte) string {
|
||||
s := string(b)
|
||||
if len(s) > 128 {
|
||||
s = s[:128] + "..."
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user