Skip to content

Commit 496eae5

Browse files
vissongwsqDevTeam
authored andcommitted
botgo: 频道机器人新SDK第一个版本
Squash merge branch 'feature_first_version_issue_1' into 'master' - openapi 封装 - websocket 封装 - 具体使用方法请阅读 README && demo
1 parent cad5a1c commit 496eae5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2074
-7
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@
1313

1414
# Dependency directories (remove the comment below to include it)
1515
# vendor/
16-
.idea
16+
.idea
17+
# 用于存储demo中使用的 token,不提交到仓库
18+
demo/config.yaml

README.md

+78
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,80 @@
11
# botgo
22
a golang sdk for guild bot
3+
4+
## 设计模式
5+
分为三个主要模块
6+
7+
- openapi 用于请求 http 的 openapi
8+
- websocket 用于监听事件网关,接收事件消息
9+
- oauth 用于处理 oauth 的 token 获取
10+
11+
openapi 接口定义:`openapi/iface.go`,同时 sdk 中提供了 v1 的实现,后续 openapi 有新版本的时候,可以增加对应新版本的实现。
12+
websocket 接口定义:`websocket/iface.go`,sdk 实现了默认版本的 client,如果开发者有更好的实现,也可以进行替换
13+
14+
## 使用
15+
16+
### 1.请求 openapi
17+
18+
```golang
19+
func main() {
20+
token := token.BotToken(conf.AppID, conf.Token)
21+
api := botgo.NewOpenAPI(token).WithTimeout(3 * time.Second)
22+
ctx := context.Background()
23+
24+
ws, err := api.WS(ctx, nil, "")
25+
log.Printf("%+v, err:%v", ws, err)
26+
27+
me, err := api.Me(ctx, nil, "")
28+
log.Printf("%+v, err:%v", me, err)
29+
}
30+
```
31+
32+
### 2.请求 websocket
33+
34+
```golang
35+
func main() {
36+
token := token.BotToken(conf.AppID, conf.Token)
37+
api := botgo.NewOpenAPI(token).WithTimeout(3 * time.Second)
38+
ctx := context.Background()
39+
ws, err := api.WS(ctx, nil, "")
40+
if err != nil {
41+
log.Printf("%+v, err:%v", ws, err)
42+
}
43+
44+
// 监听哪类事件就需要实现哪类的 handler,定义:websocket/event_handler.go
45+
var message websocket.MessageEventHandler = func(event *dto.WSMsg, data *dto.MessageData) error {
46+
fmt.Println(event, data)
47+
return nil
48+
}
49+
intent := websocket.RegisterHandlers(message)
50+
// 启动 session manager 进行 ws 连接的管理,如果接口返回需要启动多个 shard 的连接,这里也会自动启动多个
51+
botgo.Session.Start(ws, token, &intent)
52+
}
53+
```
54+
55+
### 3.请求 oauth
56+
57+
待补充
58+
59+
### 4. session manager
60+
接口定义:`session_manager.go`
61+
62+
sdk 实现了 `localSession` 主要是在单机上启动多个 shard 的连接,在实际生产中,如果需要启动多个 shard,那么有可能会采用分布式的管理方法,那么
63+
就需要开发者自己实现一个分布式的 session manager 来进行连接管理。
64+
65+
## 开发说明
66+
67+
### 1. 如何增加新的 openapi 接口调用方法
68+
69+
- Step1: dto 中增加对应的对象
70+
- Step2: openapi 的接口定义中,增加新方法的定义
71+
- Step3:在 openapi 的实现中,实现这个新的方法
72+
73+
### 2. 如何增加新的 websocket 事件
74+
75+
- Step1: dto 中增加对应的对象 `dto/websocket_msg.go`
76+
- Step2: 新增事件类型 `dto/websocket_msg.go`
77+
- Step3: 新增 intent,以及事件对应的 intent(如果有)`dto/intents.go`
78+
- Step4: 新增 event handler 类型,`websocket/event_handler.go`
79+
- Step5:websocket 的具体实现中,针对收到的 message 进行解析,判断 type 是否符合新添加的时间类型,解析为 dto 之后,调用对应的 handler `websocket/client/event.go`
80+

botgo.go

+40
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,42 @@
11
// Package botgo 是一个QQ频道机器人 sdk 的 golang 实现
22
package botgo
3+
4+
import (
5+
"github.com/tencent-connect/botgo/errs"
6+
"github.com/tencent-connect/botgo/internal/log"
7+
"github.com/tencent-connect/botgo/openapi"
8+
v1 "github.com/tencent-connect/botgo/openapi/v1"
9+
"github.com/tencent-connect/botgo/token"
10+
"github.com/tencent-connect/botgo/websocket/client"
11+
)
12+
13+
func init() {
14+
v1.Setup() // 注册 v1 接口
15+
client.Setup() // 注册 websocket client 实现
16+
}
17+
18+
// NewSessionManager 获得 session manager 实例
19+
func NewSessionManager() SessionManager {
20+
return defaultSessionManager
21+
}
22+
23+
// SelectOpenAPIVersion 指定使用哪个版本的 api 实现,如果不指定,sdk将默认使用第一个 setup 的 api 实现
24+
func SelectOpenAPIVersion(version openapi.APIVersion) error {
25+
if _, ok := openapi.VersionMapping[version]; !ok {
26+
log.Errorf("version %v openapi not found or setup", version)
27+
return errs.ErrNotFoundOpenAPI
28+
}
29+
openapi.DefaultImpl = openapi.VersionMapping[version]
30+
return nil
31+
}
32+
33+
// NewOpenAPI 创建新的 openapi 实例,会返回当前的 openapi 实现的实例
34+
// 如果需要使用其他版本的实现,需要在调用这个方法之前调用 SelectOpenAPIVersion 方法
35+
func NewOpenAPI(token *token.Token) openapi.OpenAPI {
36+
return openapi.DefaultImpl.New(token, false)
37+
}
38+
39+
// NewSandboxOpenAPI 创建测试环境的 openapi 实例
40+
func NewSandboxOpenAPI(token *token.Token) openapi.OpenAPI {
41+
return openapi.DefaultImpl.New(token, true)
42+
}

botgo_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package botgo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/tencent-connect/botgo/openapi"
7+
)
8+
9+
func TestUseOpenAPIVersion(t *testing.T) {
10+
type args struct {
11+
version openapi.APIVersion
12+
}
13+
tests := []struct {
14+
name string
15+
args args
16+
wantErr bool
17+
}{
18+
{
19+
"not found", args{version: 0}, true,
20+
},
21+
{
22+
"v1 found", args{version: 1}, false,
23+
},
24+
}
25+
for _, tt := range tests {
26+
t.Run(tt.name, func(t *testing.T) {
27+
if err := SelectOpenAPIVersion(tt.args.version); (err != nil) != tt.wantErr {
28+
t.Errorf("SelectOpenAPIVersion() error = %v, wantErr %v", err, tt.wantErr)
29+
}
30+
})
31+
}
32+
}

demo/.keep

Whitespace-only changes.

demo/config.yaml.demo

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 在这个配置文件中补充你的 appid 和 bot token,并修改文件名为 config.yaml
2+
appid:
3+
token:

demo/go.mod

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module main
2+
3+
go 1.16
4+
5+
require (
6+
github.com/tencent-connect/botgo v0.0.0-20210909101121-cad5a1c08d6c
7+
gopkg.in/yaml.v2 v2.4.0 // indirect
8+
)
9+
10+
replace github.com/tencent-connect/botgo => ../

demo/go.sum

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKYS4=
3+
github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
4+
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
5+
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
6+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
9+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
10+
github.com/tidwall/gjson v1.9.1 h1:wrrRk7TyL7MmKanNRck/Mcr3VU1sdMvJHvJXzqBIUNo=
11+
github.com/tidwall/gjson v1.9.1/go.mod h1:jydLKE7s8J0+1/5jC4eXcuFlzKizGrCKvLmBVX/5oXc=
12+
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
13+
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
14+
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
15+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
16+
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
17+
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
18+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
19+
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
20+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
21+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
22+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
23+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
25+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
26+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

demo/main.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"io/ioutil"
6+
"log"
7+
"os"
8+
"time"
9+
10+
"github.com/tencent-connect/botgo"
11+
"github.com/tencent-connect/botgo/dto"
12+
"github.com/tencent-connect/botgo/token"
13+
"github.com/tencent-connect/botgo/websocket"
14+
"gopkg.in/yaml.v2"
15+
)
16+
17+
var conf struct {
18+
AppID uint64 `yaml:"appid"`
19+
Token string `yaml:"token"`
20+
}
21+
22+
func init() {
23+
content, err := ioutil.ReadFile("./config.yaml")
24+
if err != nil {
25+
log.Println("read conf failed")
26+
os.Exit(1)
27+
}
28+
if err := yaml.Unmarshal(content, &conf); err != nil {
29+
log.Println(err)
30+
os.Exit(1)
31+
}
32+
log.Println(conf)
33+
}
34+
35+
type runFunc func() (interface{}, error)
36+
37+
func main() {
38+
token := token.BotToken(conf.AppID, conf.Token)
39+
api := botgo.NewOpenAPI(token).WithTimeout(3 * time.Second)
40+
ctx := context.Background()
41+
ws, err := api.WS(ctx, nil, "")
42+
log.Printf("%+v, err:%v", ws, err)
43+
44+
// me, err := api.Me(ctx, nil, "")
45+
// log.Printf("%+v, err:%v", me, err)
46+
//
47+
// run(func() (interface{}, error) {
48+
// return api.MeGuilds(ctx)
49+
// })
50+
51+
// run(func() (interface{}, error) {
52+
// return api.Guild(ctx, "13034202056525133443")
53+
// })
54+
55+
// run(func() (interface{}, error) {
56+
// return api.GuildMembers(ctx, "13034202056525133443", &dto.GuildMembersPager{
57+
// After: "0",
58+
// Limit: "100",
59+
// })
60+
// })
61+
62+
// run(func() (interface{}, error) {
63+
// return api.Channels(ctx, "13034202056525133443")
64+
// })
65+
//
66+
// run(func() (interface{}, error) {
67+
// return api.Channel(ctx, "1107217")
68+
// })
69+
//
70+
// run(func() (interface{}, error) {
71+
// return api.PostMessage(ctx, "1107217", &dto.MessageToCreate{Content: "abc"})
72+
// })
73+
74+
var message websocket.MessageEventHandler = func(event *dto.WSPayload, data *dto.WSMessageData) error {
75+
log.Println(event, data)
76+
return nil
77+
}
78+
intent := websocket.RegisterHandlers(message)
79+
botgo.NewSessionManager().Start(ws, token, &intent)
80+
}
81+
82+
func run(runFunc2 runFunc) {
83+
ret, err := runFunc2()
84+
log.Printf("%+v, err:%v", ret, err)
85+
}

dto/channel.go

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ type Channel struct {
2020
ID string `json:"id"`
2121
// 群ID
2222
GuildID string `json:"guild_id"`
23+
ChannelValueObject
24+
}
25+
26+
// ChannelValueObject 频道的值对象部分
27+
type ChannelValueObject struct {
2328
// 频道名称
2429
Name string `json:"name"`
2530
// 频道类型

dto/message.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dto
2+
3+
// Message 消息结构体定义
4+
type Message struct {
5+
// 消息ID
6+
ID string `json:"id"`
7+
// 子频道ID
8+
ChannelID string `json:"channel_id"`
9+
// 频道ID
10+
GuildID string `json:"guild_id"`
11+
// 内容
12+
Content string `json:"content"`
13+
// 发送时间
14+
Timestamp Timestamp `json:"timestamp"`
15+
// 消息编辑时间
16+
EditedTimestamp Timestamp `json:"edited_timestamp"`
17+
// 是否@all
18+
MentionEveryone bool `json:"mention_everyone"`
19+
// 消息发送方
20+
Author *User `json:"author"`
21+
// 消息发送方Author的member属性,只是部分属性
22+
Member *Member `json:"member"`
23+
// 附件
24+
Attachments []*MessageAttachment `json:"attachments"`
25+
// 结构化消息-embeds
26+
Embeds []*Embed `json:"embeds"`
27+
// 消息中的提醒信息(@)列表
28+
Mentions []*User `json:"mentions"`
29+
// ark 消息
30+
Ark *Ark `json:"ark"`
31+
}
32+
33+
// Embed 结构
34+
type Embed struct {
35+
Title string `json:"title,omitempty"`
36+
Description string `json:"description,omitempty"`
37+
Timestamp Timestamp `json:"timestamp,omitempty"`
38+
Fields []*EmbedField `json:"fields,omitempty"`
39+
}
40+
41+
// EmbedField Embed字段描述
42+
type EmbedField struct {
43+
Name string `json:"name,omitempty"`
44+
Value string `json:"value,omitempty"`
45+
}
46+
47+
// MessageAttachment 附件定义
48+
type MessageAttachment struct {
49+
URL string `json:"url"`
50+
}

dto/message_ark.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dto
2+
3+
// MessageArk ark模板消息
4+
type MessageArk struct {
5+
Ark *Ark `json:"ark,omitempty"`
6+
}
7+
8+
// Ark 消息模版
9+
type Ark struct {
10+
TemplateID int `json:"template_id,omitempty"` // ark 模版 ID
11+
KV []*ArkKV `json:"kv,omitempty"` // ArkKV 数组
12+
}
13+
14+
// ArkKV Ark 键值对
15+
type ArkKV struct {
16+
Key string `json:"key,omitempty"`
17+
Value string `json:"value,omitempty"`
18+
Obj []*ArkObj `json:"obj,omitempty"`
19+
}
20+
21+
// ArkObj Ark 对象
22+
type ArkObj struct {
23+
ObjKV []*ArkObjKV `json:"obj_kv,omitempty"`
24+
}
25+
26+
// ArkObjKV Ark 对象键值对
27+
type ArkObjKV struct {
28+
Key string `json:"key,omitempty"`
29+
Value string `json:"value,omitempty"`
30+
}

0 commit comments

Comments
 (0)