mirror of
https://github.com/fumiama/NanoBot.git
synced 2026-06-05 02:30:23 +08:00
finish Init Connect
This commit is contained in:
166
bot.go
166
bot.go
@@ -1,6 +1,21 @@
|
||||
package nano
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
"github.com/RomiChan/websocket"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var clients = syncx.Map[string, *Bot]{}
|
||||
|
||||
// Bot 一个机器人实例的配置
|
||||
type Bot struct {
|
||||
@@ -11,15 +26,158 @@ type Bot struct {
|
||||
Timeout time.Duration // Timeout is API 调用超时
|
||||
Handler *Handler // Handler 注册对各种事件的处理
|
||||
|
||||
handlers map[string]GeneralHandleType // handlers 方便调用的 handler
|
||||
gateway string // gateway 获得的网关
|
||||
shard [2]byte // shard 分片
|
||||
seq uint32
|
||||
handlers map[string]GeneralHandleType // handlers 方便调用的 handler
|
||||
mu sync.Mutex // 写锁
|
||||
conn *websocket.Conn
|
||||
heartbeat uint32 // heartbeat 心跳周期, 单位毫秒
|
||||
intents uint32
|
||||
properties json.RawMessage
|
||||
ready EventReady
|
||||
}
|
||||
|
||||
// Init 初始化, 只需执行一次
|
||||
func (b *Bot) Init() {
|
||||
|
||||
func (b *Bot) Init(gateway string, shard [2]byte, intents uint32, properties json.RawMessage) *Bot {
|
||||
b.gateway = gateway
|
||||
b.shard = shard
|
||||
b.intents = intents
|
||||
b.properties = properties
|
||||
if b.Handler != nil {
|
||||
h := reflect.ValueOf(b.Handler).Elem()
|
||||
t := h.Type()
|
||||
b.handlers = make(map[string]GeneralHandleType, h.NumField()*4)
|
||||
for i := 0; i < h.NumField(); i++ {
|
||||
f := h.Field(i)
|
||||
if f.IsZero() {
|
||||
continue
|
||||
}
|
||||
tp := t.Field(i).Name[2:] // skip On
|
||||
log.Infoln("[bot] 注册处理函数", tp)
|
||||
handler := f.Interface()
|
||||
b.handlers[tp] = *(*GeneralHandleType)(unsafe.Add(unsafe.Pointer(&handler), unsafe.Sizeof(uintptr(0))))
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Authorization 返回 Authorization Header value
|
||||
func (bot *Bot) Authorization() string {
|
||||
return "Bot " + bot.AppID + "." + bot.Token
|
||||
}
|
||||
|
||||
// receive 收一个 payload
|
||||
func (bot *Bot) reveive() (payload WebsocketPayload, err error) {
|
||||
err = bot.conn.ReadJSON(&payload)
|
||||
return
|
||||
}
|
||||
|
||||
// Connect 连接到 Gateway + 鉴权连接
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/gateway/reference.html#_1-%E8%BF%9E%E6%8E%A5%E5%88%B0-gateway
|
||||
func (bot *Bot) Connect() {
|
||||
network, address := resolveURI(bot.gateway)
|
||||
log.Infoln("[bot] 开始尝试连接到网关:", network, ", AppID:", bot.AppID)
|
||||
dialer := websocket.Dialer{
|
||||
NetDial: func(_, addr string) (net.Conn, error) {
|
||||
if network == "unix" {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
host = addr
|
||||
}
|
||||
filepath, err := base64.RawURLEncoding.DecodeString(host)
|
||||
if err == nil {
|
||||
addr = BytesToString(filepath)
|
||||
}
|
||||
}
|
||||
return net.Dial(network, addr) // support unix socket transport
|
||||
},
|
||||
}
|
||||
for {
|
||||
conn, resp, err := dialer.Dial(address, http.Header{})
|
||||
if err != nil {
|
||||
log.Warnf("[bot] 连接到网关 %v 时出现错误: %v", bot.gateway, err)
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
bot.conn = conn
|
||||
_ = resp.Body.Close()
|
||||
payload, err := bot.reveive()
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 获取心跳间隔时出现错误:", err)
|
||||
_ = conn.Close()
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
bot.heartbeat, err = payload.GetHeartbeatInterval()
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 解析心跳间隔时出现错误:", err)
|
||||
_ = conn.Close()
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
payload.Op = OpCodeIdentify
|
||||
err = payload.WrapData(&OpCodeIdentifyMessage{
|
||||
Token: bot.Authorization(),
|
||||
Intents: bot.intents,
|
||||
Shard: bot.shard,
|
||||
Properties: bot.properties,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 包装 Identify 时出现错误:", err)
|
||||
_ = conn.Close()
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
err = bot.SendPayload(&payload)
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 发送 Identify 时出现错误:", err)
|
||||
_ = conn.Close()
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
payload, err = bot.reveive()
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 获取 EventReady 时出现错误:", err)
|
||||
_ = conn.Close()
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
bot.ready, err = payload.GetEventReady()
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 解析 EventReady 时出现错误:", err)
|
||||
_ = conn.Close()
|
||||
time.Sleep(2 * time.Second) // 等待两秒后重新连接
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
clients.Store(bot.ready.User.ID, bot)
|
||||
log.Infoln("[bot] 连接到网关成功, 用户名:", bot.ready.User.Username)
|
||||
go bot.doheartbeat()
|
||||
}
|
||||
|
||||
// doheartbeat 按指定间隔进行心跳包收发
|
||||
func (bot *Bot) doheartbeat() {
|
||||
payload := struct {
|
||||
Op OpCode `json:"op"`
|
||||
D *uint32 `json:"d"`
|
||||
}{Op: OpCodeHeartbeat}
|
||||
t := time.NewTicker(time.Duration(bot.heartbeat) * time.Millisecond)
|
||||
defer t.Stop()
|
||||
time.Sleep(time.Minute)
|
||||
for range t.C {
|
||||
if bot.seq == 0 {
|
||||
payload.D = nil
|
||||
} else {
|
||||
payload.D = &bot.seq
|
||||
}
|
||||
bot.mu.Lock()
|
||||
err := bot.conn.WriteJSON(&payload)
|
||||
bot.mu.Unlock()
|
||||
if err != nil {
|
||||
log.Warnln("[bot] 发送心跳时出现错误:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
define.go
69
define.go
@@ -1,69 +0,0 @@
|
||||
package nano
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
const (
|
||||
// StandardAPI 正式环境接口域名
|
||||
StandardAPI = `https://api.sgroup.qq.com`
|
||||
// SandboxAPI 沙箱环境接口域名
|
||||
SandboxAPI = `https://sandbox.api.sgroup.qq.com`
|
||||
)
|
||||
|
||||
var (
|
||||
OpenAPI = StandardAPI // OpenAPI 实际使用的 API, 默认 StandardAPI, 可自行赋值配置
|
||||
)
|
||||
|
||||
// CodeMessageBase 各种消息都有的 code + message 基类
|
||||
type CodeMessageBase struct {
|
||||
C int `json:"code"`
|
||||
M string `json:"message"`
|
||||
}
|
||||
|
||||
// OpCode https://bot.q.qq.com/wiki/develop/api/gateway/opcode.html
|
||||
type OpCode int
|
||||
|
||||
const (
|
||||
OpCodeDispatch OpCode = iota // Receive
|
||||
OpCodeHeartbeat // Send/Receive
|
||||
OpCodeIdentify // Send
|
||||
OpCodeEmpty1
|
||||
OpCodeEmpty2
|
||||
OpCodeEmpty3
|
||||
OpCodeResume // Send
|
||||
OpCodeReconnect // Receive
|
||||
OpCodeEmpty4
|
||||
OpCodeInvalidSession // Receive
|
||||
OpCodeHello // Receive
|
||||
OpCodeHeartbeatACK // Receive/Reply
|
||||
OpCodeHTTPCallbackACK // Reply
|
||||
)
|
||||
|
||||
// WebsocketPayload payload 指的是在 websocket 连接上传输的数据,网关的上下行消息采用的都是同一个结构
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/gateway/reference.html
|
||||
type WebsocketPayload struct {
|
||||
Op OpCode `json:"op"`
|
||||
D json.RawMessage `json:"d"`
|
||||
S int `json:"s"`
|
||||
T string `json:"t"`
|
||||
}
|
||||
|
||||
// https://bot.q.qq.com/wiki/develop/api/gateway/intents.html
|
||||
const (
|
||||
IntentGuilds = 1 << 0
|
||||
IntentGuildMembers = 1 << 1
|
||||
IntentGuildMessages = 1 << 9
|
||||
IntentGuildMessageReactions = 1 << 10
|
||||
IntentDirectMessage = 1 << 12
|
||||
IntentOpenForumsEvent = 1 << 18
|
||||
IntentAudioOrLiveChannelMember = 1 << 19
|
||||
IntentInteraction = 1 << 26
|
||||
IntentMessageAudit = 1 << 27
|
||||
IntentForumsEvent = 1 << 28
|
||||
IntentAudioAction = 1 << 29
|
||||
IntentPublicGuildMessages = 1 << 30
|
||||
|
||||
IntentAll = IntentGuilds | IntentGuildMembers | IntentGuildMessages | IntentGuildMessageReactions |
|
||||
IntentDirectMessage | IntentOpenForumsEvent | IntentAudioOrLiveChannelMember | IntentInteraction |
|
||||
IntentMessageAudit | IntentForumsEvent | IntentAudioAction | IntentPublicGuildMessages
|
||||
)
|
||||
8
go.mod
8
go.mod
@@ -3,8 +3,14 @@ module github.com/fumiama/NanoBot
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5
|
||||
github.com/fumiama/go-base16384 v1.7.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.3.7 // indirect
|
||||
require (
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
)
|
||||
|
||||
12
go.sum
12
go.sum
@@ -1,14 +1,24 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA=
|
||||
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5 h1:bBmmB7he0iVN4m5mcehfheeRUEer/Avo4ujnxI3uCqs=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5/go.mod h1:0UcFaCkhp6vZw6l5Dpq0Dp673CoF9GdvA8lTfst0GiU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
|
||||
github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
49
helper.go
49
helper.go
@@ -1,8 +1,11 @@
|
||||
package nano
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getFuncNameWithSkip(n int) string {
|
||||
@@ -66,3 +69,49 @@ func UnderlineToCamel(s string) string {
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// resolveURI github.com/wdvxdr1123/ZeroBot/driver/uri.go
|
||||
func resolveURI(addr string) (network, address string) {
|
||||
network, address = "tcp", addr
|
||||
uri, err := url.Parse(addr)
|
||||
if err == nil && uri.Scheme != "" {
|
||||
scheme, ext, _ := strings.Cut(uri.Scheme, "+")
|
||||
if ext != "" {
|
||||
network = ext
|
||||
uri.Scheme = scheme // remove `+unix`/`+tcp4`
|
||||
if ext == "unix" {
|
||||
uri.Host, uri.Path, _ = strings.Cut(uri.Path, ":")
|
||||
uri.Host = base64.StdEncoding.EncodeToString(StringToBytes(uri.Host)) // special handle for unix
|
||||
}
|
||||
address = uri.String()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// slice is the runtime representation of a slice.
|
||||
// It cannot be used safely or portably and its representation may
|
||||
// change in a later release.
|
||||
//
|
||||
// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
|
||||
// data it references will not be garbage collected.
|
||||
type slice struct {
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
cap int
|
||||
}
|
||||
|
||||
// BytesToString 没有内存开销的转换
|
||||
func BytesToString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// StringToBytes 没有内存开销的转换
|
||||
func StringToBytes(s string) (b []byte) {
|
||||
bh := (*slice)(unsafe.Pointer(&b))
|
||||
sh := (*slice)(unsafe.Pointer(&s))
|
||||
bh.data = sh.data
|
||||
bh.len = sh.len
|
||||
bh.cap = sh.len
|
||||
return b
|
||||
}
|
||||
|
||||
21
intent.go
Normal file
21
intent.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package nano
|
||||
|
||||
// https://bot.q.qq.com/wiki/develop/api/gateway/intents.html
|
||||
const (
|
||||
IntentGuilds = 1 << 0
|
||||
IntentGuildMembers = 1 << 1
|
||||
IntentGuildMessages = 1 << 9
|
||||
IntentGuildMessageReactions = 1 << 10
|
||||
IntentDirectMessage = 1 << 12
|
||||
IntentOpenForumsEvent = 1 << 18
|
||||
IntentAudioOrLiveChannelMember = 1 << 19
|
||||
IntentInteraction = 1 << 26
|
||||
IntentMessageAudit = 1 << 27
|
||||
IntentForumsEvent = 1 << 28
|
||||
IntentAudioAction = 1 << 29
|
||||
IntentPublicGuildMessages = 1 << 30
|
||||
|
||||
IntentAll = IntentGuilds | IntentGuildMembers | IntentGuildMessages | IntentGuildMessageReactions |
|
||||
IntentDirectMessage | IntentOpenForumsEvent | IntentAudioOrLiveChannelMember | IntentInteraction |
|
||||
IntentMessageAudit | IntentForumsEvent | IntentAudioAction | IntentPublicGuildMessages
|
||||
)
|
||||
30
opcode.go
Normal file
30
opcode.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package nano
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// OpCode https://bot.q.qq.com/wiki/develop/api/gateway/opcode.html
|
||||
type OpCode int
|
||||
|
||||
const (
|
||||
OpCodeDispatch OpCode = iota // Receive
|
||||
OpCodeHeartbeat // Send/Receive
|
||||
OpCodeIdentify // Send
|
||||
OpCodeEmpty1
|
||||
OpCodeEmpty2
|
||||
OpCodeEmpty3
|
||||
OpCodeResume // Send
|
||||
OpCodeReconnect // Receive
|
||||
OpCodeEmpty4
|
||||
OpCodeInvalidSession // Receive
|
||||
OpCodeHello // Receive
|
||||
OpCodeHeartbeatACK // Receive/Reply
|
||||
OpCodeHTTPCallbackACK // Reply
|
||||
)
|
||||
|
||||
// OpCodeIdentifyMessage https://bot.q.qq.com/wiki/develop/api/gateway/reference.html#_2-%E9%89%B4%E6%9D%83%E8%BF%9E%E6%8E%A5
|
||||
type OpCodeIdentifyMessage struct {
|
||||
Token string `json:"token"`
|
||||
Intents uint32 `json:"intents"`
|
||||
Shard [2]byte `json:"shard"`
|
||||
Properties json.RawMessage `json:"properties"`
|
||||
}
|
||||
17
openapi.go
17
openapi.go
@@ -11,6 +11,23 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// StandardAPI 正式环境接口域名
|
||||
StandardAPI = `https://api.sgroup.qq.com`
|
||||
// SandboxAPI 沙箱环境接口域名
|
||||
SandboxAPI = `https://sandbox.api.sgroup.qq.com`
|
||||
)
|
||||
|
||||
var (
|
||||
OpenAPI = StandardAPI // OpenAPI 实际使用的 API, 默认 StandardAPI, 可自行赋值配置
|
||||
)
|
||||
|
||||
// CodeMessageBase 各种消息都有的 code + message 基类
|
||||
type CodeMessageBase struct {
|
||||
C int `json:"code"`
|
||||
M string `json:"message"`
|
||||
}
|
||||
|
||||
func checkrespbaseunsafe(ptr any) error {
|
||||
respbase := (*CodeMessageBase)(*(*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(&ptr), unsafe.Sizeof(uintptr(0)))))
|
||||
if respbase.C != 0 {
|
||||
|
||||
64
payload.go
Normal file
64
payload.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package nano
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// WebsocketPayload payload 指的是在 websocket 连接上传输的数据,网关的上下行消息采用的都是同一个结构
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/gateway/reference.html
|
||||
type WebsocketPayload struct {
|
||||
Op OpCode `json:"op"`
|
||||
D json.RawMessage `json:"d,omitempty"`
|
||||
S int `json:"s,omitempty"`
|
||||
T string `json:"t,omitempty"`
|
||||
}
|
||||
|
||||
// GetHeartbeatInterval OpCodeHello 获得心跳周期 单位毫秒
|
||||
func (wp *WebsocketPayload) GetHeartbeatInterval() (uint32, error) {
|
||||
if wp.Op != OpCodeHello {
|
||||
return 0, errors.New(getThisFuncName() + " unexpected OpCode " + strconv.Itoa(int(wp.Op)))
|
||||
}
|
||||
data := &struct {
|
||||
H uint32 `json:"heartbeat_interval"`
|
||||
}{}
|
||||
err := json.Unmarshal(wp.D, data)
|
||||
return data.H, err
|
||||
}
|
||||
|
||||
// SendPayload 发送 ws 包
|
||||
func (bot *Bot) SendPayload(wp *WebsocketPayload) error {
|
||||
bot.mu.Lock()
|
||||
defer bot.mu.Unlock()
|
||||
return bot.conn.WriteJSON(wp)
|
||||
}
|
||||
|
||||
// WrapData 将结构体序列化到 wp.D
|
||||
func (wp *WebsocketPayload) WrapData(v any) (err error) {
|
||||
wp.D, err = json.Marshal(v)
|
||||
return
|
||||
}
|
||||
|
||||
// EventReady https://bot.q.qq.com/wiki/develop/api/gateway/reference.html#_2-%E9%89%B4%E6%9D%83%E8%BF%9E%E6%8E%A5
|
||||
type EventReady struct {
|
||||
Version int `json:"version"`
|
||||
SessionID string `json:"session_id"`
|
||||
User *User `json:"user"`
|
||||
Shard [2]byte `json:"shard"`
|
||||
}
|
||||
|
||||
// GetEventReady OpCodeDispatch READY
|
||||
func (wp *WebsocketPayload) GetEventReady() (er EventReady, err error) {
|
||||
if wp.Op != OpCodeDispatch {
|
||||
err = errors.New(getThisFuncName() + " unexpected OpCode " + strconv.Itoa(int(wp.Op)))
|
||||
return
|
||||
}
|
||||
if wp.T != "READY" {
|
||||
err = errors.New(getThisFuncName() + " unexpected event type " + wp.T)
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(wp.D, &er)
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user