Skip to content

Commit 42cb5b8

Browse files
rianliwalli
authored andcommitted
feat(botgo): 增加http回调校验逻辑 1.增加webhook回调验证逻辑 2.优化token manager功能逻辑 (merge request !98)
Squash merge branch 'feat_20240923_callback_validation_story_0' into 'master' 支持接入webhook事件链路: 1. 增加webhook回调验证逻辑 2. 按golang.org/x/oauth2标准实现token source 3. 实现定时刷现access token逻辑 4. 更新examples 5. 更新readme文档
1 parent df169eb commit 42cb5b8

File tree

94 files changed

+774
-5696
lines changed

Some content is hidden

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

94 files changed

+774
-5696
lines changed

README.md

Lines changed: 92 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,114 +2,129 @@
22

33
QQ频道机器人,官方 GOLANG SDK。
44

5-
![Build](https://github.com/tencent-connect/botgo/actions/workflows/build.yml/badge.svg)
65
[![Go Reference](https://pkg.go.dev/badge/github.com/tencent-connect/botgo.svg)](https://pkg.go.dev/github.com/tencent-connect/botgo)
76
[![Examples](https://img.shields.io/badge/BotGo-examples-yellowgreen)](https://github.com/tencent-connect/botgo/tree/master/examples)
87

8+
## 注意事项
9+
1. websocket 事件推送链路将在24年年底前逐步下线,后续官方不再维护。
10+
2. 新的webhook事件回调链路目前在灰度验证,灰度用户可体验通过页面配置事件监听及回调地址。如未在灰度范围,可联系QQ机器人反馈助手开通。
11+
12+
![反馈机器人](docs/img/feedback_bot.png)
13+
14+
灰度期间,原有机器人仍可使用websocket事件链路接收事件推送。
915
## 一、quick start
10-
### 1.打开 examples/receive-and-send
11-
### 2.复制 config.yaml.demo -> config.yaml
12-
![img.png](doc/img/copy-config-yaml.png)
13-
### 3.登录[开发者管理端](https://q.qq.com),将BotAppID和机器人秘钥分别填入config.yaml中的appid和secret字段
14-
![find-app-acc.png](doc/img/find-app-acc.png)
15-
![type-in-app-info.png](doc/img/type-in-app-info.png)
16-
### 4.执行go build,然后执行./receive-and-send, 即可收到消息并回复。
17-
![robot-start-console.png](doc/img/robot-start-console.png)
18-
### 5.根据机器人QQ号查找、添加机器人为好友
19-
![add-robot.png](doc/img/add-robot.png)
16+
### 1. QQ机器人创建与配置
17+
1. 创建开发者账号,创建QQ机器人 [QQ机器人开放平台](https://q.qq.com/qqbot)
2018

21-
### 6.发送消息,即可收到回复。
22-
![robot-reply.png](doc/img/robot-reply.png)
19+
![create_bot.png](docs/img/create_bot.png)
2320

24-
## 二、如何使用
21+
2. 配置沙箱成员 (QQ机器人上线前,仅沙箱环境可访问)。新创建机器人会默认将创建者加入沙箱环境。
2522

26-
### 1.请求 openapi 接口,操作资源
23+
![sandbox_setting.png](docs/img/sandbox_setting.png)
2724

28-
```golang
29-
func main() {
30-
token := token.BotToken(conf.AppID, conf.Token)
31-
api := botgo.NewOpenAPI(token).WithTimeout(3 * time.Second)
32-
ctx := context.Background()
33-
34-
ws, err := api.WS(ctx, nil, "")
35-
log.Printf("%+v, err:%v", ws, err)
36-
37-
me, err := api.Me(ctx, nil, "")
38-
log.Printf("%+v, err:%v", me, err)
39-
}
40-
```
25+
### 2. 云函数创建与配置
26+
1. 腾讯云账号开通scf服务 [快速入门](https://cloud.tencent.com/document/product/1154/39271)
27+
2. 创建函数
4128

42-
### 2.使用默认 SessionManager 启动 websocket 连接,接收事件
29+
* 选择模板
4330

44-
```golang
45-
func main() {
46-
token := token.BotToken(conf.AppID, conf.Token)
47-
api := botgo.NewOpenAPI(token).WithTimeout(3 * time.Second)
48-
ctx := context.Background()
49-
ws, err := api.WS(ctx, nil, "")
50-
if err != nil {
51-
log.Printf("%+v, err:%v", ws, err)
52-
}
53-
54-
// 监听哪类事件就需要实现哪类的 handler,定义:websocket/event_handler.go
55-
var atMessage websocket.ATMessageEventHandler = func(event *dto.WSPayload, data *dto.WSATMessageData) error {
56-
fmt.Println(event, data)
57-
return nil
58-
}
59-
intent := websocket.RegisterHandlers(atMessage)
60-
// 启动 session manager 进行 ws 连接的管理,如果接口返回需要启动多个 shard 的连接,这里也会自动启动多个
61-
botgo.NewSessionManager().Start(ws, token, &intent)
62-
}
63-
```
31+
![create_scf.png](docs/img/create_scf.png)
32+
33+
* 启用"公网访问"、"日志投递"
34+
35+
![turn_internet_access.png](docs/img/turn_internet_access.png)
6436

65-
## 三、什么是 SessionManager
37+
* 编辑云函数,启用"固定公网出口IP" (QQ机器人需要配置IP白名单,仅白名单内服务器/容器可访问OpenAPI)
6638

67-
SessionManager,用于管理 websocket 连接的启动,重连等。接口定义在:`session_manager.go`。开发者也可以自己实现自己的 SessionManager。
39+
![scf_setting.png](docs/img/scf_setting.png)
6840

69-
sdk 中实现了两个 SessionManager
41+
![get_internet_ip.png](docs/img/get_internet_ip.png)
7042

71-
- [local](./sessions/local/local.go) 用于在单机上启动多个 shard 的连接。下文用 `local` 代表
72-
- [remote](./sessions/remote/remote.go) 基于 redis 的 list 数据结构,实现分布式的 shard 管理,可以在多个节点上启动多个服务进程。下文用 `remote` 代表
43+
### 3. 使用示例构建、上传云函数部署包
44+
1. 打开 examples/receive-and-send
45+
2. 复制 config.yaml.demo -> config.yaml
7346

74-
另外,也有其他同事基于 etcd 实现了 shard 集群的管理,在 [botgo-plugns](https://github.com/tencent-connect/botgo-plugins) 中。
47+
![img.png](docs/img/copy-config-yaml.png)
7548

76-
## 四、生产环境中的一些建议
49+
3. 登录[开发者管理端](https://q.qq.com),将BotAppID和机器人秘钥分别填入config.yaml中的appid和secret字段
7750

78-
得益于 websocket 的机制,我们可以在本地就启动一个机器人,实现相关逻辑,但是在生产环境中需要考虑扩容,容灾等情况,所以建
79-
议从以下几方面考虑生产环境的部署:
51+
![find-app-acc.png](docs/img/find-app-acc.png)
8052

81-
### 1.公域机器人,优先使用分布式 shard 管理
53+
![type-in-app-info.png](docs/img/type-in-app-info.png)
8254

83-
使用上面提到的分布式的 session manager 或者自己实现一个分布式的 session manager
55+
4. 执行Makefile中build指令
56+
5. 将config.yaml、scf_bootstrap、qqbot-demo(二进制文件)打包,上传至云函数
8457

85-
### 2.提前规划好分片
58+
![上传压缩包](docs/img/upload_scf_zip.png)
8659

87-
分布式 SessionManager 需要解决的最大的问题,就是如何解决 shard 随时增加的问题,类似 kafka 的 rebalance 问题一样,
88-
由于 shard 是基于频道 id 来进行 hash 的,所以在扩容的时候所有的数据都会被重新 hash。
60+
### 4.配置QQ机器人事件监听、回调地址、IP白名单
8961

90-
提前规划好较多的分片,如 20 个分片,有助于在未来机器人接入的频道过多的时候,能够更加平滑的进行实例的扩容。比如如果使用的
91-
`remote`,初始化时候分 20 个分片,但是只启动 2 个进程,那么这2个进程将争抢 20 个分片的消费权,进行消费,当启动更多
92-
的实例之后,伴随着 websocket 要求一定时间进行一次重连,启动的新实例将会平滑的分担分片的数据处理。
62+
1. 复制云函数地址 + "/qqbot"后缀,填入回调地址输入框。点击确认。
9363

94-
### 3.接入和逻辑分离
64+
![img.png](docs/img/copy_scf_addr.png)
9565

96-
接入是指从机器人平台收到事件的服务。逻辑是指处理相关事件的服务
66+
2. 勾选 C2C_MESSAGE_CREATE 事件。点击确认
9767

98-
接入与逻辑分离,有助于提升机器人的事件处理效率和可靠性。一般实现方式类似于以下方案:
68+
![webhook配置](docs/img/webhook_setting.png)
9969

100-
- 接入层:负责维护与平台的 websocket 连接,并接收相关事件,生产到 kafka 等消息中间件中。
101-
如果使用 `local` 那么可能还涉及到分布式锁的问题。可以使用sdk 中的 `sessions/remote/lock` 快速基于 redis 实现分布式锁。
10270

103-
- 逻辑层:从 kafka 消费到事件,并进行对应的处理,或者调用机器人的 openapi 进行相关数据的操作。
71+
3. 将云函数 "固定公网出口IP" 配置到IP白名单中)
10472

105-
提前规划好 kafka 的分片,然后从容的针对逻辑层做水平扩容。或者使用 pulsar(腾讯云上叫 tdmq) 来替代 kafka 避免 rebalance 问题。
73+
![ip_whitlist_setting.png](docs/img/ip_whitlist_setting.png)
74+
75+
### 体验与机器人的对话
76+
77+
给机器人发送消息、富媒体文件,机器人回复消息
78+
79+
## 二、如何使用SDK
80+
81+
```golang
82+
83+
var api openapi.OpenAPI
84+
85+
func main() {
86+
//创建oauth2标准token source
87+
tokenSource := token.NewQQBotTokenSource(
88+
&token.QQBotCredentials{
89+
AppID: "",
90+
AppSecret: "",
91+
})
92+
//启动自动刷新access token协程
93+
if err = token.StartRefreshAccessToken(ctx, tokenSource); err != nil {
94+
log.Fatalln(err)
95+
}
96+
// 初始化 openapi,正式环境
97+
api = botgo.NewOpenAPI(credentials.AppID, tokenSource).WithTimeout(5 * time.Second).SetDebug(true)
98+
// 注册事件处理函数
99+
_ = event.RegisterHandlers(
100+
// 注册c2c消息处理函数
101+
C2CMessageEventHandler(),
102+
)
103+
//注册回调处理函数
104+
http.HandleFunc(path_, func (writer http.ResponseWriter, request *http.Request) {
105+
webhook.HTTPHandler(writer, request, credentials)
106+
})
107+
// 启动http服务监听端口
108+
if err = http.ListenAndServe(fmt.Sprintf("%s:%d", host_, port_), nil); err != nil {
109+
log.Fatal("setup server fatal:", err)
110+
}
111+
}
112+
113+
// C2CMessageEventHandler 实现处理 at 消息的回调
114+
func C2CMessageEventHandler() event.C2CMessageEventHandler {
115+
return func(event *dto.WSPayload, data *dto.WSC2CMessageData) error {
116+
//TODO use api do sth.
117+
return nil
118+
}
119+
}
120+
```
106121

107-
## 、SDK 开发说明
122+
## 、SDK 开发说明 (Deprecated)
108123

109-
请查看[开发说明](./DEVELOP.md)
124+
请查看: [开发说明](./DEVELOP.md)
110125

111-
## 、加入官方社区
126+
## 、加入官方社区
112127

113128
欢迎扫码加入 **QQ 频道开发者社区**
114129

115-
![开发者社区](https://mpqq.gtimg.cn/privacy/qq_guild_developer.png)
130+
![开发者社区](https://mpqq.gtimg.cn/privacy/qq_guild_developer.png)

botgo.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import (
66
"github.com/tencent-connect/botgo/log"
77
"github.com/tencent-connect/botgo/openapi"
88
v1 "github.com/tencent-connect/botgo/openapi/v1"
9-
"github.com/tencent-connect/botgo/token"
109
"github.com/tencent-connect/botgo/websocket/client"
10+
"golang.org/x/oauth2"
1111
)
1212

1313
func init() {
@@ -32,11 +32,11 @@ func SelectOpenAPIVersion(version openapi.APIVersion) error {
3232

3333
// NewOpenAPI 创建新的 openapi 实例,会返回当前的 openapi 实现的实例
3434
// 如果需要使用其他版本的实现,需要在调用这个方法之前调用 SelectOpenAPIVersion 方法
35-
func NewOpenAPI(token *token.Manager) openapi.OpenAPI {
36-
return openapi.DefaultImpl.Setup(token, false)
35+
func NewOpenAPI(appID string, tokenSource oauth2.TokenSource) openapi.OpenAPI {
36+
return openapi.DefaultImpl.Setup(appID, tokenSource, false)
3737
}
3838

3939
// NewSandboxOpenAPI 创建测试环境的 openapi 实例
40-
func NewSandboxOpenAPI(token *token.Manager) openapi.OpenAPI {
41-
return openapi.DefaultImpl.Setup(token, true)
40+
func NewSandboxOpenAPI(appID string, tokenSource oauth2.TokenSource) openapi.OpenAPI {
41+
return openapi.DefaultImpl.Setup(appID, tokenSource, true)
4242
}

constant/constant.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Package constant 常量定义
22
package constant
33

4-
// TraceIDKey 机器人openapi返回的链路追踪ID
5-
const TraceIDKey = "X-Tps-trace-ID"
4+
// HeaderTraceID 机器人openapi返回的链路追踪ID
5+
const HeaderTraceID = "X-Tps-trace-ID"
66

77
// APIDomain api domain
88
var APIDomain = "https://api.sgroup.qq.com"

doc/img/copy-config-yaml.png

-39.9 KB
Binary file not shown.

doc/img/type-in-app-info.png

-8.42 KB
Binary file not shown.
File renamed without changes.

docs/img/chat_with_bot.png

235 KB
Loading

docs/img/copy-config-yaml.png

17.7 KB
Loading

docs/img/copy_scf_addr.png

123 KB
Loading

docs/img/create_bot.png

72.4 KB
Loading

0 commit comments

Comments
 (0)