mirror of
https://github.com/fumiama/NanoBot.git
synced 2026-06-05 02:30:23 +08:00
finish base
This commit is contained in:
16
bot.go
Normal file
16
bot.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package nano
|
||||
|
||||
import "time"
|
||||
|
||||
// Bot 一个机器人实例的配置
|
||||
type Bot struct {
|
||||
AppID string // AppID is BotAppID(开发者ID)
|
||||
Token string // Token is 机器人令牌
|
||||
Key string // Key is 机器人密钥
|
||||
Timeout time.Duration // Timeout is API 调用超时
|
||||
}
|
||||
|
||||
// Authorization 返回 Authorization Header value
|
||||
func (bot *Bot) Authorization() string {
|
||||
return "Bot " + bot.AppID + "." + bot.Token
|
||||
}
|
||||
50
codegen/getopenapiof/main.go
Normal file
50
codegen/getopenapiof/main.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const head = `// Code generated by codegen/getopenapiof. DO NOT EDIT.
|
||||
|
||||
package nano
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
`
|
||||
|
||||
const template = `
|
||||
func (bot *Bot) getOpenAPIof[T any](ep string) (*[T any], error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
[T any]
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*[T any])(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
`
|
||||
|
||||
func main() {
|
||||
f, err := os.Create("openapi_codegen_getopenapiof.go")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(head)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, name := range os.Args[1:] {
|
||||
_, err = f.WriteString(strings.ReplaceAll(template, "[T any]", name))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
14
define.go
Normal file
14
define.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package nano
|
||||
|
||||
var (
|
||||
// StandardAPI 正式环境接口域名
|
||||
StandardAPI = `https://api.sgroup.qq.com`
|
||||
// SandboxAPI 沙箱环境接口域名
|
||||
SandboxAPI = `https://sandbox.api.sgroup.qq.com`
|
||||
)
|
||||
|
||||
// CodeMessageBase 各种消息都有的 code + message 基类
|
||||
type CodeMessageBase struct {
|
||||
C int `json:"code"`
|
||||
M string `json:"message"`
|
||||
}
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/fumiama/NanoBot
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/pkg/errors v0.9.1
|
||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
34
helper.go
Normal file
34
helper.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package nano
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// getCurrentFuncName 获取当前函数名
|
||||
func getCurrentFuncName() string {
|
||||
pc, _, _, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
fullname := runtime.FuncForPC(pc).Name()
|
||||
i := strings.LastIndex(fullname, ".") + 1
|
||||
if i <= 0 || i >= len(fullname) {
|
||||
return fullname
|
||||
}
|
||||
return fullname[i:]
|
||||
}
|
||||
|
||||
// getCallerFuncName 获取调用者函数名
|
||||
func getCallerFuncName() string {
|
||||
pc, _, _, ok := runtime.Caller(2)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
fullname := runtime.FuncForPC(pc).Name()
|
||||
i := strings.LastIndex(fullname, ".") + 1
|
||||
if i <= 0 || i >= len(fullname) {
|
||||
return fullname
|
||||
}
|
||||
return fullname[i:]
|
||||
}
|
||||
53
http.go
Normal file
53
http.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package nano
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewHTTPEndpointGetRequestWithAuth 新建带鉴权头的 HTTP 请求
|
||||
func NewHTTPEndpointGetRequestWithAuth(ep string, auth string) (req *http.Request, err error) {
|
||||
req, err = http.NewRequest("GET", StandardAPI+ep, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Add("Authorization", auth)
|
||||
return
|
||||
}
|
||||
|
||||
// WriteHTTPQueryIfNotNil 如果非空则将请求添加到 baseurl 后
|
||||
//
|
||||
// ex. WriteHTTPQueryIfNotNil("http://a.com/api", "a", 0, "b", 1, "c", 2) is http://a.com/api?b=1&c=2
|
||||
func WriteHTTPQueryIfNotNil(baseurl string, queries ...any) string {
|
||||
if len(queries) == 0 {
|
||||
return baseurl
|
||||
}
|
||||
hasstart := false
|
||||
queryname := ""
|
||||
sb := strings.Builder{}
|
||||
for i, q := range queries {
|
||||
if i%2 == 0 {
|
||||
queryname = q.(string)
|
||||
continue
|
||||
}
|
||||
if reflect.ValueOf(q).IsZero() {
|
||||
continue
|
||||
}
|
||||
if !hasstart {
|
||||
sb.WriteString(baseurl)
|
||||
sb.WriteByte('?')
|
||||
hasstart = true
|
||||
}
|
||||
sb.WriteString(queryname)
|
||||
sb.WriteByte('=')
|
||||
sb.WriteString(url.QueryEscape(fmt.Sprint(q)))
|
||||
sb.WriteByte('&')
|
||||
}
|
||||
if sb.Len() <= 4 {
|
||||
return baseurl
|
||||
}
|
||||
return sb.String()[:sb.Len()-1]
|
||||
}
|
||||
34
openapi.go
Normal file
34
openapi.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package nano
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
//go:generate go run codegen/getopenapiof/main.go ShardWSSGateway User Guild GuildArray Channel ChannelArray
|
||||
|
||||
// GetOpenAPI 从 ep 获取 json 结构化数据写到 ptr, ptr 必须在开头继承 CodeMessageBase
|
||||
func (bot *Bot) GetOpenAPI(ep string, ptr any) error {
|
||||
req, err := NewHTTPEndpointGetRequestWithAuth(ep, bot.Authorization())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, getCallerFuncName())
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, getCallerFuncName())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
err = json.NewDecoder(resp.Body).Decode(ptr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, getCallerFuncName())
|
||||
}
|
||||
respbbase := (*CodeMessageBase)(*(*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(&ptr), unsafe.Sizeof(uintptr(0)))))
|
||||
if respbbase.C != 0 {
|
||||
return errors.Wrap(errors.New("code: "+strconv.Itoa(respbbase.C)+", msg: "+respbbase.M), getCallerFuncName())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
36
openapi_channel.go
Normal file
36
openapi_channel.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package nano
|
||||
|
||||
// Channel 子频道对象
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/channel/model.html
|
||||
type Channel struct {
|
||||
ID string `json:"id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"`
|
||||
SubType int `json:"sub_type"`
|
||||
Position int `json:"position"`
|
||||
ParentID string `json:"parent_id"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
PrivateType int `json:"private_type"`
|
||||
SpeakPermission int `json:"speak_permission"`
|
||||
ApplicationID string `json:"application_id"`
|
||||
Permissions string `json:"permissions"`
|
||||
}
|
||||
|
||||
// ChannelArray []Channel 的别名
|
||||
type ChannelArray []Channel
|
||||
|
||||
// GetChannelsOfGuild 获取 id 指定的频道下的子频道列表
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/channel/get_channels.html
|
||||
func (bot *Bot) GetChannelsOfGuild(id string) (*ChannelArray, error) {
|
||||
return bot.getOpenAPIofChannelArray("/guilds/" + id + "/channels")
|
||||
}
|
||||
|
||||
// GetChannelByID 用于获取 id 指定的子频道的详情
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/channel/get_channel.html
|
||||
func (bot *Bot) GetChannelByID(id string) (*Channel, error) {
|
||||
return bot.getOpenAPIofChannel("/channels/" + id)
|
||||
}
|
||||
87
openapi_codegen_getopenapiof.go
Normal file
87
openapi_codegen_getopenapiof.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// Code generated by codegen/getopenapiof. DO NOT EDIT.
|
||||
|
||||
package nano
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (bot *Bot) getOpenAPIofShardWSSGateway(ep string) (*ShardWSSGateway, error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
ShardWSSGateway
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*ShardWSSGateway)(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
|
||||
func (bot *Bot) getOpenAPIofUser(ep string) (*User, error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
User
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*User)(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
|
||||
func (bot *Bot) getOpenAPIofGuild(ep string) (*Guild, error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
Guild
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*Guild)(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
|
||||
func (bot *Bot) getOpenAPIofGuildArray(ep string) (*GuildArray, error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
GuildArray
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*GuildArray)(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
|
||||
func (bot *Bot) getOpenAPIofChannel(ep string) (*Channel, error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
Channel
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*Channel)(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
|
||||
func (bot *Bot) getOpenAPIofChannelArray(ep string) (*ChannelArray, error) {
|
||||
resp := &struct {
|
||||
CodeMessageBase
|
||||
ChannelArray
|
||||
}{}
|
||||
err := bot.GetOpenAPI(ep, resp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, getCallerFuncName())
|
||||
return nil, err
|
||||
}
|
||||
return (*ChannelArray)(unsafe.Add(unsafe.Pointer(resp), unsafe.Sizeof(CodeMessageBase{}))), nil
|
||||
}
|
||||
28
openapi_guild.go
Normal file
28
openapi_guild.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package nano
|
||||
|
||||
import "time"
|
||||
|
||||
// Guild 频道对象
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/guild/model.html
|
||||
type Guild struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
Owner bool `json:"owner"`
|
||||
JoinedAt time.Time `json:"joined_at"`
|
||||
MemberCount int `json:"member_count"`
|
||||
MaxMembers int `json:"max_members"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// GuildArray []Guild 的别名
|
||||
type GuildArray []Guild
|
||||
|
||||
// GetGuildByID 获取 id 指定的频道的详情
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/guild/get_guild.html
|
||||
func (bot *Bot) GetGuildByID(id string) (*Guild, error) {
|
||||
return bot.getOpenAPIofGuild("/guilds/" + id)
|
||||
}
|
||||
31
openapi_user.go
Normal file
31
openapi_user.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package nano
|
||||
|
||||
// User 用户对象
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/user/model.html
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Avatar string `json:"avatar"`
|
||||
Bot bool `json:"bot"`
|
||||
UnionOpenid string `json:"union_openid"`
|
||||
UnionUserAccount string `json:"union_user_account"`
|
||||
}
|
||||
|
||||
// GetMe 获取当前用户(机器人)详情
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/user/me.html
|
||||
func (bot *Bot) GetMe() (*User, error) {
|
||||
return bot.getOpenAPIofUser("/users/@me")
|
||||
}
|
||||
|
||||
// GetMyGuilds 获取当前用户(机器人)频道列表,支持分页
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/user/guilds.html
|
||||
func (bot *Bot) GetMyGuilds(before, after string, limit int) (*GuildArray, error) {
|
||||
return bot.getOpenAPIofGuildArray(WriteHTTPQueryIfNotNil("/users/@me/guilds",
|
||||
"before", before,
|
||||
"after", after,
|
||||
"limit", limit,
|
||||
))
|
||||
}
|
||||
37
openapi_wss.go
Normal file
37
openapi_wss.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package nano
|
||||
|
||||
// GetGeneralWSSGateway 获取通用 WSS 接入点
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/wss/url_get.html
|
||||
func (bot *Bot) GetGeneralWSSGateway() (string, error) {
|
||||
resp := struct {
|
||||
CodeMessageBase
|
||||
U string `json:"url"`
|
||||
}{}
|
||||
err := bot.GetOpenAPI("/gateway", &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.U, nil
|
||||
}
|
||||
|
||||
// ShardWSSGateway 带分片 WSS 接入点响应数据
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/wss/shard_url_get.html#%E8%BF%94%E5%9B%9E
|
||||
type ShardWSSGateway struct {
|
||||
URL string `json:"url"`
|
||||
Shards int `json:"shards"`
|
||||
SessionStartLimit struct {
|
||||
Total int `json:"total"`
|
||||
Remaining int `json:"remaining"`
|
||||
ResetAfter int `json:"reset_after"`
|
||||
MaxConcurrency int `json:"max_concurrency"`
|
||||
} `json:"session_start_limit"`
|
||||
}
|
||||
|
||||
// GetShardWSSGateway 获取带分片 WSS 接入点
|
||||
//
|
||||
// https://bot.q.qq.com/wiki/develop/api/openapi/wss/shard_url_get.html
|
||||
func (bot *Bot) GetShardWSSGateway() (*ShardWSSGateway, error) {
|
||||
return bot.getOpenAPIofShardWSSGateway("/gateway/bot")
|
||||
}
|
||||
Reference in New Issue
Block a user