9
9
10
10
## Webhook方式
11
11
12
- ** 当前方式灰度中 ,仅灰度用户可使用** 其它用户请使用 [ websocket方式] ( #websocket方式 )
12
+ ** Webhook方式灰度中 ,仅灰度用户可使用** 其它用户请使用 [ websocket方式] ( #websocket方式 ) 。
13
13
14
14
灰度用户如遇问题可通过 [ QQ机器人反馈助手] ( https://mpqq.gtimg.cn/bot-wiki/online/images/api-231017/qqrobot-feedback.jpg ) 反馈
15
15
@@ -36,10 +36,7 @@ QQ机器人开放平台支持通过使用HTTP接口接收事件。开发者可
36
36
| ** CODE** | ** 名称** | ** 客户端行为** | ** 描述** |
37
37
| --- | --- | --- | --- |
38
38
| 0 | Dispatch | Receive | 服务端进行消息推送 |
39
- | 12 | HTTP Callback ACK | Reply | 仅用于 http 回调模式的回包,代表机器人收到了平台推送的数据 |
40
39
| 13 | 回调地址验证 | Receive | 开放平台对机器人服务端进行验证 |
41
- | 14 | 回调地址验证 ACK | Reply | 机器人服务端响应开放平台的验证请求 |
42
-
43
40
44
41
### 签名校验
45
42
机器人服务端需要对回调请求进行签名验证以保证数据没有被篡改过。
@@ -50,21 +47,73 @@ QQ机器人开放平台支持通过使用HTTP接口接收事件。开发者可
50
47
开发者需要提供一个HTTPS回调地址。并选定监听的事件类型。开放平台会将事件通过回调的方式推送给机器人。
51
48
<img :src =" $withBotBase('/images/api-231017/event_subscription.png') " alt =" event_subscription " >
52
49
53
- 开发者配置回调地址时 ,开放平台会对回调地址进行验证。机器人服务端需要按格式返回签名信息。签名算法同上。 机器人服务端需要在 3 秒内响应200或204,表示接受到事件。
54
- * 请求结构
50
+ 配置回调地址后 ,开放平台会对回调地址进行验证:
51
+ * 请求结构(Payload.d)
55
52
56
- | ** 字段** | ** 描述** |
57
- | --- | ------|
58
- | plain_token | 要计算hash的字符串 |
59
- | event_ts | 时间戳 |
53
+ | ** 字段** | ** 描述** |
54
+ | --- | ------------ |
55
+ | plain_token | 需要计算签名的字符串 |
56
+ | event_ts | 计算签名使用时间戳 |
60
57
61
58
* 返回结果
62
59
63
60
| ** 字段** | ** 描述** |
64
61
| --- | -------------|
65
- | plain_token | 要计算hash的字符串 |
62
+ | plain_token | 需要计算签名的字符串 |
66
63
| signature | 签名 |
67
64
65
+ 计算过程如下(golang):
66
+ ``` go
67
+ func handleValidation (rw http .ResponseWriter , r *http .Request , botSecret string ) {
68
+ httpBody , err := io.ReadAll (r.Body )
69
+ if err != nil {
70
+ log.Println (" read http body err" , err)
71
+ return
72
+ }
73
+ payload := &Payload{}
74
+ if err = json.Unmarshal (httpBody, payload); err != nil {
75
+ log.Println (" parse http payload err" , err)
76
+ return
77
+ }
78
+ validationPayload := &ValidationRequest{}
79
+ if err = json.Unmarshal (payload.Data , validationPayload);err != nil {
80
+ log.Println (" parse http payload failed:" , err)
81
+ return
82
+ }
83
+ seed := botSecret
84
+ for len (seed) < ed25519.SeedSize {
85
+ seed = strings.Repeat (seed, 2 )
86
+ }
87
+ seed = seed[:ed25519.SeedSize ]
88
+ reader := strings.NewReader (seed)
89
+ // GenerateKey 方法会返回公钥、私钥,这里只需要私钥进行签名生成不需要返回公钥
90
+ _ , privateKey , err := ed25519.GenerateKey (reader)
91
+ if err != nil {
92
+ log.Println (" ed25519 generate key failed:" , err)
93
+ return
94
+ }
95
+ var msg bytes.Buffer
96
+ msg.WriteString (validationPayload.EventTs )
97
+ msg.WriteString (validationPayload.PlainToken )
98
+ signature := hex.EncodeToString (ed25519.Sign (privateKey, msg.Bytes ()))
99
+ if err != nil {
100
+ log.Println (" generate signature failed:" , err)
101
+ return
102
+ }
103
+ rspBytes , err := json.Marshal (
104
+ &ValidationResponse{
105
+ PlainToken: validationPayload.PlainToken ,
106
+ Signature: signature,
107
+ })
108
+ if err != nil {
109
+ log.Println (" handle validation failed:" , err)
110
+ return
111
+ }
112
+ rw.Write (rspBytes)
113
+ }
114
+
115
+ ```
116
+
68
117
例如机器人账号
69
118
```
70
119
appid: 11111111
0 commit comments