Files
napcat/types.go

361 lines
10 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}