npm.io
0.1.13 • Published yesterday

koishi-plugin-super-event-message-push

Licence
MIT
Version
0.1.13
Deps
0
Size
13 kB
Vulns
0
Weekly
0

koishi-plugin-super-event-message-push

npm

用于接收后端推送的消息并通过 OneBot 协议转发到 QQ 群的 Koishi 插件。

功能

  • 接收来自后端的 webhook 请求
  • 使用 HMAC-SHA256 + 时间戳验证请求签名
  • 通过 OneBot 协议将消息转发到指定 QQ 群
  • 防重放攻击(5 秒时间窗口)

配置

插件配置

在 Koishi 控制台中配置以下参数:

参数 类型 默认值 说明
endpoint string /super-event/webhook 接收 webhook 的路径
secret string change-me 用于验证签名的密钥(需与后端配置一致)
apiBaseUrl string http://localhost:4000/api 后端 API 地址(保留用于未来功能)
依赖

需要安装以下插件:

  • @koishijs/plugin-server - HTTP 服务器支持
  • @koishijs/plugin-adapter-onebot - OneBot 协议适配器

使用方法

1. 配置后端

在后端的 .env 文件中配置:

KOISHI_WEBHOOK_URL=http://your-koishi-server:5140/super-event/webhook
KOISHI_WEBHOOK_SECRET=your-secret-key
2. 后端调用示例
方式一:通过公共 Webhook 接口(推荐)
import crypto from 'crypto';
import axios from 'axios';

/**
 * 推送消息到 Koishi
 */
async function pushToKoishi(channel: number, message: string) {
  const secret = process.env.KOISHI_WEBHOOK_SECRET || 'change-me';
  const url = 'http://localhost:4000/api/push/webhook';
  
  const body = JSON.stringify({ channel, message });
  const timestamp = Math.floor(Date.now() / 1000);
  const signature = crypto
    .createHmac('sha256', secret)
    .update(timestamp + body)
    .digest('hex');

  const response = await axios.post(url, { channel, message }, {
    headers: {
      'x-super-event-signature': signature,
      'x-super-event-timestamp': String(timestamp),
      'content-type': 'application/json',
    },
  });

  return response.data;
}

// 使用示例
await pushToKoishi(123456789, '这是一条测试消息');
方式二:直接调用后端服务(需要权限)
import axios from 'axios';

async function pushToKoishiWithAuth(channel: number, message: string, token: string) {
  const response = await axios.post(
    'http://localhost:4000/api/push',
    { channel, message },
    {
      headers: {
        'Authorization': `Bearer ${token}`,
        'content-type': 'application/json',
      },
    }
  );

  return response.data;
}
3. 前端调用示例
import crypto from 'crypto-js';

/**
 * 前端推送消息到 Koishi(通过后端中转)
 */
async function sendMessageToGroup(channel: number, message: string) {
  const secret = 'your-secret-key'; // 注意:实际项目中不要在前端硬编码密钥
  const body = JSON.stringify({ channel, message });
  const timestamp = Math.floor(Date.now() / 1000);
  
  // 使用 crypto-js 计算签名
  const signature = crypto.HmacSHA256(timestamp + body, secret).toString();

  const response = await fetch('http://localhost:4000/api/push/webhook', {
    method: 'POST',
    headers: {
      'x-super-event-signature': signature,
      'x-super-event-timestamp': String(timestamp),
      'content-type': 'application/json',
    },
    body,
  });

  return response.json();
}

API 规范

请求格式

URL: POST {koishi-url}{endpoint}

Headers:

  • x-super-event-signature: HMAC-SHA256(secret, timestamp + body)
  • x-super-event-timestamp: Unix 时间戳(秒)
  • content-type: application/json

Body:

{
  "channel": 123456789,
  "message": "要发送的消息内容"
}
响应格式
成功响应 (200)
{
  "success": true,
  "message": "Message sent successfully"
}
错误响应
  • 400 Bad Request: 缺少签名/时间戳,或请求体格式错误
{
  "error": "Missing signature or timestamp"
}
  • 401 Unauthorized: 签名验证失败
{
  "error": "Invalid signature"
}
  • 404 Not Found: 请求已过期(超过 5 秒)
{
  "error": "Request expired"
}
  • 500 Internal Server Error: 发送消息失败或没有可用的 OneBot 适配器
{
  "error": "Failed to send message",
  "details": "错误详情"
}

安全性

签名验证

使用 HMAC-SHA256 算法生成签名,确保消息来自可信来源:

signature = HMAC_SHA256(secret, timestamp + body)
防重放攻击
  • 每个请求必须包含时间戳
  • 服务器只接受 5 秒内的请求
  • 超过时间窗口的请求返回 404
最佳实践
  1. 使用强密钥: KOISHI_WEBHOOK_SECRET 应使用随机生成的强密钥
  2. HTTPS 传输: 生产环境中使用 HTTPS 保护传输过程
  3. 密钥管理: 不要在前端代码中硬编码密钥,通过后端中转
  4. 访问控制: 配置防火墙规则,限制只有后端服务器可以访问 Koishi webhook

故障排查

消息发送失败
  1. 检查 OneBot 适配器是否已启动并连接
  2. 查看 Koishi 日志确认是否收到请求
  3. 确认群号是否正确,机器人是否在该群内
签名验证失败
  1. 确认后端和 Koishi 的 secret 配置一致
  2. 检查时间戳是否在 5 秒内
  3. 确认请求体格式是否正确(JSON 字符串)
请求过期
  1. 检查服务器时间是否同步(使用 NTP)
  2. 网络延迟过大时适当调整时间窗口

开发

构建
pnpm install
pnpm build
测试
pnpm test

许可证

MIT