Skip to content

Commit d8a8c70

Browse files
feat: 添加webhook相关调用方式
1 parent a345b49 commit d8a8c70

File tree

13 files changed

+936
-15
lines changed

13 files changed

+936
-15
lines changed

README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ QQ 机器人 SDK,基于 [官方 SDK](https://github.com/tencent-connect/bot-no
1010
npm i qq-bot-sdk --registry=https://registry.npmjs.org
1111
```
1212

13-
## 引用
13+
## `webhook` 方式使用(新增)
14+
15+
详情请见 `example`[`webhook`](/example/webhook) 使用案例
16+
17+
## `websocket` 方式引用
1418

1519
> 可参见[example](/example)中样例
1620
@@ -31,6 +35,10 @@ const ws = createWebsocket(testConfigWs); // 创建ws实例(用于接收消息
3135

3236
# 对比优化内容
3337

38+
## 新增 `webhook` 方式调用
39+
40+
详情请见 `example`[`webhook`](/example/webhook) 使用案例
41+
3442
## 新增群消息订阅事件
3543

3644
```js

example/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// 以下仅为用法示意,详情请参照文档:https://bot.q.qq.com/wiki/develop/nodesdk/
1+
/**
2+
* 以下仅为用法示意,详情请参照文档:https://bot.q.qq.com/wiki/develop/nodesdk/
3+
* 其中对官方逻辑扩展,添加了诸多功能,修复了许多问题
4+
*/
25
const { createOpenAPI, createWebsocket, AvailableIntentsEventsEnum } = require('qq-bot-sdk');
36

47
const testConfigWs = {

example/package.json

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
{
2-
"name": "example",
3-
"version": "1.0.0",
4-
"description": "",
5-
"main": "index.js",
6-
"scripts": {
7-
"example": "node index.js"
8-
},
9-
"author": "",
10-
"license": "ISC"
2+
"name": "example",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"example": "node index.js",
8+
"example:webhook": "node webhook/index.js"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"@types/koa-router": "^7.4.8",
14+
"koa": "^2.15.3",
15+
"koa-body": "^6.0.1",
16+
"koa-router": "^13.0.1"
17+
}
1118
}

example/webhook/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# 简介
2+
3+
基于官方文档 https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html 实现
4+
5+
此案例中 `web` 服务基于 `koa2` + `koa-router` + `koa-body``http` 服务, 官方需要使用 `https` 协议 `443`端口(其余端口亲测无法验证), 因此需要 `nginx` 设置 `ssl` 反代, `nginx` 代理设置详见同级目录下的 `nginx.conf`
6+
7+
> `webhook` 相关功能需要在设置了 `secret` 情况下使用,此时仍要设置 `token`
8+
9+
详细流程详见 `index.js` 代码内解释

example/webhook/index.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* 基于官方文档 https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html
3+
* nginx 代理设置详见同级目录下的 nginx.conf
4+
* 注意:webhook 相关功能需要在设置了 secret 情况下使用,此时仍要设置 token
5+
*/
6+
const { createOpenAPI } = require('qq-bot-sdk');
7+
8+
const Koa = require("koa");
9+
const { koaBody } = require("koa-body");
10+
const Router = require("koa-router");
11+
12+
13+
const client = createOpenAPI({
14+
appID: "",
15+
token: "", // 如果不使用 websocket 可留空
16+
secret: "", // 如果使用 webhook 必填
17+
}); // 创建 client
18+
19+
20+
const PORT = 2333; // 定义 web 的端口, 需要与 nginx.conf 内端口一致
21+
const app = new Koa(); // 定义 koa 实例
22+
const router = new Router(); // 定义路由
23+
app.use(async (ctx, next) => {
24+
let rawData = '';
25+
ctx.req.on('data', chunk => rawData += chunk);
26+
ctx.req.on('end', () => ctx.request.rawBody = rawData);
27+
await next();
28+
}); // 对 body 进行验证需要原数据 (格式化后无法使用)
29+
30+
router.post("/webhook", async (ctx, next) => {
31+
const sign = ctx.req.headers["x-signature-ed25519"]; // 获取服务器传来的 sign
32+
const timestamp = ctx.req.headers["x-signature-timestamp"]; // 获取服务器传来的 timestamp
33+
const rawBody = ctx.request.rawBody; // 获取原数据用来进行验证
34+
const isValid = client.webhookApi.validSign(timestamp, rawBody, sign); // 进行验证, 通过后才可继续执行下一步操作
35+
if (!isValid) {
36+
ctx.status = 400;
37+
ctx.body = { msg: "invalid signature" };
38+
return;
39+
}// 没有通过验证就一边玩去
40+
41+
const body = ctx.request.body;
42+
43+
if (body.op == 13) return ctx.body = {
44+
plain_token: body.d.plain_token,
45+
signature: client.webhookApi.getSign(body.d.event_ts, body.d.plain_token),
46+
}; // 当 op = 13 进行 webhook 验证
47+
48+
/**
49+
*
50+
* 此处已经正式通过验证, 你可以对数据做出相应的处理(如回复消息), 数据格式遵从 websocket 接收到的格式
51+
*
52+
*/
53+
console.log(body);
54+
55+
56+
}); // 监听对应的url
57+
58+
app.use(async (ctx, next) => {
59+
await next();
60+
ctx.status = ctx.body?.status || ctx.status || 200;
61+
}); // 返回的 body 里面如果有 status 时, 则返回的 status 设置为与之相同的值
62+
app.use(koaBody({ multipart: true })); // 使用 koaBody
63+
app.use(router.routes()); // 使用路由
64+
app.use(router.allowedMethods()); // 使用路由
65+
app.listen(PORT, async () => {
66+
console.log("webhook 服务运行中......");
67+
}); // 正式监听相应的端口
68+

example/webhook/nginx.conf

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
server {
2+
listen 443 ssl; # 监听 443 端口,并设置 ssl
3+
server_name bot.example.com; # 这里设置对应的域名
4+
5+
ssl_certificate /path/to/it.crt; # 设置证书 公钥
6+
ssl_certificate_key /path/to/it.key; # 设置证书 私钥
7+
8+
location / {
9+
add_header Access-Control-Allow-Origin *;
10+
add_header Access-Control-Allow-Methods *;
11+
12+
if ($request_method = 'OPTIONS') {
13+
add_header 'Access-Control-Allow-Origin' '*';
14+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
15+
add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
16+
add_header 'Access-Control-Max-Age' 1728000;
17+
add_header 'Content-Type' 'text/plain; charset=utf-8';
18+
add_header 'Content-Length' 0;
19+
return 204;
20+
}
21+
22+
proxy_pass http://127.0.0.1:2333; # 设置代理的域名,需要与 index.js 里面的 PORT 相同
23+
proxy_redirect off;
24+
proxy_set_header Host $host; # 传递域名
25+
proxy_set_header X-Real-IP $remote_addr; # 传递ip
26+
proxy_set_header X-Scheme $scheme; # 传递协议
27+
}
28+
}

0 commit comments

Comments
 (0)