|
2 | 2 |
|
3 | 3 | QQ频道机器人,官方 GOLANG SDK。
|
4 | 4 |
|
5 |
| - |
6 | 5 | [](https://pkg.go.dev/github.com/tencent-connect/botgo)
|
7 | 6 | [](https://github.com/tencent-connect/botgo/tree/master/examples)
|
8 | 7 |
|
| 8 | +## 注意事项 |
| 9 | +1. websocket 事件推送链路将在24年年底前逐步下线,后续官方不再维护。 |
| 10 | +2. 新的webhook事件回调链路目前在灰度验证,灰度用户可体验通过页面配置事件监听及回调地址。如未在灰度范围,可联系QQ机器人反馈助手开通。 |
| 11 | + |
| 12 | + |
| 13 | + |
| 14 | +灰度期间,原有机器人仍可使用websocket事件链路接收事件推送。 |
9 | 15 | ## 一、quick start
|
10 |
| -### 1.打开 examples/receive-and-send |
11 |
| -### 2.复制 config.yaml.demo -> config.yaml |
12 |
| - |
13 |
| -### 3.登录[开发者管理端](https://q.qq.com),将BotAppID和机器人秘钥分别填入config.yaml中的appid和secret字段 |
14 |
| - |
15 |
| - |
16 |
| -### 4.执行go build,然后执行./receive-and-send, 即可收到消息并回复。 |
17 |
| - |
18 |
| -### 5.根据机器人QQ号查找、添加机器人为好友 |
19 |
| - |
| 16 | +### 1. QQ机器人创建与配置 |
| 17 | +1. 创建开发者账号,创建QQ机器人 [QQ机器人开放平台](https://q.qq.com/qqbot) |
20 | 18 |
|
21 |
| -### 6.发送消息,即可收到回复。 |
22 |
| - |
| 19 | + |
23 | 20 |
|
24 |
| -## 二、如何使用 |
| 21 | +2. 配置沙箱成员 (QQ机器人上线前,仅沙箱环境可访问)。新创建机器人会默认将创建者加入沙箱环境。 |
25 | 22 |
|
26 |
| -### 1.请求 openapi 接口,操作资源 |
| 23 | + |
27 | 24 |
|
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. 创建函数 |
41 | 28 |
|
42 |
| -### 2.使用默认 SessionManager 启动 websocket 连接,接收事件 |
| 29 | +* 选择模板 |
43 | 30 |
|
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 | + |
| 32 | + |
| 33 | +* 启用"公网访问"、"日志投递" |
| 34 | + |
| 35 | + |
64 | 36 |
|
65 |
| -## 三、什么是 SessionManager |
| 37 | +* 编辑云函数,启用"固定公网出口IP" (QQ机器人需要配置IP白名单,仅白名单内服务器/容器可访问OpenAPI) |
66 | 38 |
|
67 |
| -SessionManager,用于管理 websocket 连接的启动,重连等。接口定义在:`session_manager.go`。开发者也可以自己实现自己的 SessionManager。 |
| 39 | + |
68 | 40 |
|
69 |
| -sdk 中实现了两个 SessionManager |
| 41 | + |
70 | 42 |
|
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 |
73 | 46 |
|
74 |
| -另外,也有其他同事基于 etcd 实现了 shard 集群的管理,在 [botgo-plugns](https://github.com/tencent-connect/botgo-plugins) 中。 |
| 47 | + |
75 | 48 |
|
76 |
| -## 四、生产环境中的一些建议 |
| 49 | +3. 登录[开发者管理端](https://q.qq.com),将BotAppID和机器人秘钥分别填入config.yaml中的appid和secret字段 |
77 | 50 |
|
78 |
| -得益于 websocket 的机制,我们可以在本地就启动一个机器人,实现相关逻辑,但是在生产环境中需要考虑扩容,容灾等情况,所以建 |
79 |
| -议从以下几方面考虑生产环境的部署: |
| 51 | + |
80 | 52 |
|
81 |
| -### 1.公域机器人,优先使用分布式 shard 管理 |
| 53 | + |
82 | 54 |
|
83 |
| -使用上面提到的分布式的 session manager 或者自己实现一个分布式的 session manager |
| 55 | +4. 执行Makefile中build指令 |
| 56 | +5. 将config.yaml、scf_bootstrap、qqbot-demo(二进制文件)打包,上传至云函数 |
84 | 57 |
|
85 |
| -### 2.提前规划好分片 |
| 58 | + |
86 | 59 |
|
87 |
| -分布式 SessionManager 需要解决的最大的问题,就是如何解决 shard 随时增加的问题,类似 kafka 的 rebalance 问题一样, |
88 |
| -由于 shard 是基于频道 id 来进行 hash 的,所以在扩容的时候所有的数据都会被重新 hash。 |
| 60 | +### 4.配置QQ机器人事件监听、回调地址、IP白名单 |
89 | 61 |
|
90 |
| -提前规划好较多的分片,如 20 个分片,有助于在未来机器人接入的频道过多的时候,能够更加平滑的进行实例的扩容。比如如果使用的 |
91 |
| -是 `remote`,初始化时候分 20 个分片,但是只启动 2 个进程,那么这2个进程将争抢 20 个分片的消费权,进行消费,当启动更多 |
92 |
| -的实例之后,伴随着 websocket 要求一定时间进行一次重连,启动的新实例将会平滑的分担分片的数据处理。 |
| 62 | +1. 复制云函数地址 + "/qqbot"后缀,填入回调地址输入框。点击确认。 |
93 | 63 |
|
94 |
| -### 3.接入和逻辑分离 |
| 64 | + |
95 | 65 |
|
96 |
| -接入是指从机器人平台收到事件的服务。逻辑是指处理相关事件的服务。 |
| 66 | +2. 勾选 C2C_MESSAGE_CREATE 事件。点击确认。 |
97 | 67 |
|
98 |
| -接入与逻辑分离,有助于提升机器人的事件处理效率和可靠性。一般实现方式类似于以下方案: |
| 68 | + |
99 | 69 |
|
100 |
| -- 接入层:负责维护与平台的 websocket 连接,并接收相关事件,生产到 kafka 等消息中间件中。 |
101 |
| - 如果使用 `local` 那么可能还涉及到分布式锁的问题。可以使用sdk 中的 `sessions/remote/lock` 快速基于 redis 实现分布式锁。 |
102 | 70 |
|
103 |
| -- 逻辑层:从 kafka 消费到事件,并进行对应的处理,或者调用机器人的 openapi 进行相关数据的操作。 |
| 71 | +3. 将云函数 "固定公网出口IP" 配置到IP白名单中) |
104 | 72 |
|
105 |
| -提前规划好 kafka 的分片,然后从容的针对逻辑层做水平扩容。或者使用 pulsar(腾讯云上叫 tdmq) 来替代 kafka 避免 rebalance 问题。 |
| 73 | + |
| 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 | +``` |
106 | 121 |
|
107 |
| -## 五、SDK 开发说明 |
| 122 | +## 三、SDK 开发说明 (Deprecated) |
108 | 123 |
|
109 |
| -请查看:[开发说明](./DEVELOP.md) |
| 124 | +请查看: [开发说明](./DEVELOP.md) |
110 | 125 |
|
111 |
| -## 六、加入官方社区 |
| 126 | +## 四、加入官方社区 |
112 | 127 |
|
113 | 128 | 欢迎扫码加入 **QQ 频道开发者社区**。
|
114 | 129 |
|
115 |
| - |
| 130 | + |
0 commit comments