|
| 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 | + |
0 commit comments