Files
webhook/README.md
zhilv af0f6c7ec6 feat(command): 回调响应自动回复到 QQ
- 回调服务器可返回 reply 或 messages 字段,插件自动回复到原消息来源
- reply 为纯文本回复,messages 格式同 /webhook 接口
- 支持通过 group_id/user_id 覆盖回复目标
- 无需回复时返回空 JSON 即可
- 更新 README 文档说明回调响应格式
2026-05-02 19:29:18 +08:00

304 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Webhook Plugin
NcatBot 插件,对外暴露 HTTP 接口,接收外部消息转发至 QQ。
## 功能特性
- 发送群消息 / 私聊消息(文本、图片、文件、视频)
- 批量发送消息,自动组合消息段
- 文件上传接口,支持本地文件发送
- API Key 鉴权,未配置时自动生成随机密钥
- 上传文件自动清理(超过 24 小时删除)
- 完善的错误处理与重试机制
## 快速开始
```bash
# 安装依赖
uv sync
# 复制环境变量模板
cp .env.example .env
# 编辑 .env 配置(可选,不设置 API_KEY 会自动生成)
vim .env
# 启动 NcatBot
uv run python -m ncatbot
```
启动后日志会打印 `WEBHOOK_API_KEY`,未配置时为自动生成的 UUID。
## 环境变量
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| `WEBHOOK_API_KEY` | 否 | 自动生成 | API 鉴权密钥,未设置时自动生成 UUIDv4 |
| `WEBHOOK_HOST` | 否 | `0.0.0.0` | 监听地址 |
| `WEBHOOK_PORT` | 否 | `8081` | 监听端口 |
| `UPLOAD_DIR` | 否 | `./uploads` | 上传文件保存目录 |
| `MAX_UPLOAD_SIZE` | 否 | `20971520` | 单文件最大字节数(默认 20 MB |
| `ALLOWED_EXTENSIONS` | 否 | 空(不限) | 允许的扩展名,逗号分隔,如 `jpg,png,pdf` |
| `QQ_API_TIMEOUT` | 否 | `10` | QQ API 超时秒数 |
| `QQ_API_MAX_RETRIES` | 否 | `2` | QQ API 失败重试次数 |
| `COMMAND_PREFIX` | 否 | `#` | 命令前缀 |
| `COMMAND_LENGTH` | 否 | `4` | 命令名字符数(中文字数) |
| `COMMAND_CALLBACK_URL` | 否 | 空(不监听) | 命令匹配后的回调 URL |
## 接口说明
所有接口响应格式统一:
```json
// 成功
{"code": 0, "msg": "ok", "data": {...}}
// 失败
{"code": 400, "msg": "invalid json", "data": null}
```
鉴权方式(除 `/healthz` 外必填):
```
Authorization: Bearer <API_KEY>
# 或
X-API-Key: <API_KEY>
```
### GET /healthz
健康检查,无需鉴权。
**响应:**
```json
{"code": 0, "msg": "ok", "data": {"health": "ok"}}
```
### POST /upload
上传文件到服务器,返回文件 ID 供后续消息引用。
**请求:** `multipart/form-data`,字段名不限,带 `filename` 即可。
**响应:**
```json
{
"code": 0,
"msg": "ok",
"data": {
"files": ["document.pdf", "photo.jpg"],
"path": "document.pdf"
}
}
```
- `files`:所有上传文件的相对路径(文件 ID
- `path`:单文件上传时的快捷字段
上传的文件超过 24 小时会被自动清理。
### POST /webhook
发送 QQ 消息,支持单条和批量两种模式。
#### 单条发送
**请求:**
```json
{
"group_id": "123456789",
"type": "text",
"msg": "Hello!"
}
```
| 字段 | 必填 | 说明 |
|---|---|---|
| `group_id` | 二选一 | 群号 |
| `user_id` | 二选一 | QQ 号(私聊) |
| `type` | 否 | 消息类型:`text`(默认)、`image``file``video` |
| `msg` | text 时必填 | 文本内容 |
| `url` | image/file/video 时必填 | 资源链接或上传返回的文件 ID |
**示例:**
```bash
# 发送文本
curl -X POST http://localhost:8081/webhook \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{"group_id": "123456789", "type": "text", "msg": "Hello!"}'
# 发送图片URL
curl -X POST http://localhost:8081/webhook \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{"group_id": "123456789", "type": "image", "url": "https://example.com/photo.jpg"}'
# 发送文件(本地已上传)
curl -X POST http://localhost:8081/webhook \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{"group_id": "123456789", "type": "file", "url": "document.pdf"}'
```
#### 批量发送
使用 `messages` 数组批量发送多条消息。`text``image``video` 会组合成一条消息发送(框架自动处理冲突拆分),`file` 每条单独发送。
**请求:**
```json
{
"group_id": "123456789",
"messages": [
{"type": "text", "msg": "文件已发送"},
{"type": "image", "url": "preview.jpg"},
{"type": "file", "url": "document.pdf"},
{"type": "video", "url": "demo.mp4"}
]
}
```
**响应:**
```json
{
"code": 0,
"msg": "ok",
"data": {
"total": 4,
"success": 4,
"failed": 0,
"results": [
{"index": 0, "success": true},
{"index": 1, "success": true},
{"index": 2, "success": true},
{"index": 3, "success": true}
]
}
}
```
每条消息独立处理,一条失败不影响其他消息。`results` 数组按原始 `index` 排序,包含每条消息的发送结果。
### 命令监听
插件会自动监听 QQ 消息,当消息以 `#四个中文字+空格` 开头时,将命令内容 POST 到 `COMMAND_CALLBACK_URL`
**匹配规则:**
```
#测试命令 你好世界
│ │ │ │
│ │ │ └── 命令内容content
│ └── 空格分隔
└── 命令名4个中文字
└── 前缀(默认 #
```
- 前缀、命令名长度可通过 `COMMAND_PREFIX``COMMAND_LENGTH` 配置
- 不配置 `COMMAND_CALLBACK_URL` 则不监听
**回调请求体POST JSON**
```json
{
"command": "测试命令",
"content": "你好世界",
"raw_message": "#测试命令 你好世界",
"user_id": "123456",
"group_id": "789012",
"message_id": "abc123"
}
```
| 字段 | 说明 |
|---|---|
| `command` | 命令名4个中文字 |
| `content` | 命令后的内容 |
| `raw_message` | 原始消息文本 |
| `user_id` | 发送者 QQ 号 |
| `group_id` | 群号(私聊消息无此字段) |
| `message_id` | 消息 ID |
**回调响应格式(自动回复到 QQ**
回调服务器返回 JSON插件会自动将内容回复到原消息来源群/私聊)。
纯文本回复:
```json
{"reply": "收到你的命令了"}
```
批量回复text/image/video 组合发送file 单独发送):
```json
{
"messages": [
{"type": "text", "msg": "处理结果如下"},
{"type": "image", "url": "https://example.com/result.png"},
{"type": "file", "url": "report.pdf"}
]
}
```
| 字段 | 说明 |
|---|---|
| `reply` | 纯文本回复(与 `messages` 二选一,`messages` 优先) |
| `messages` | 批量回复数组,格式同 `/webhook``messages` 字段 |
| `group_id` | 可选,覆盖回复目标群号(默认回复到原群) |
| `user_id` | 可选,覆盖回复目标 QQ 号(默认回复到原发送者) |
不需要回复时返回 `{}` 或空响应即可。
## 项目结构
```
├── plugin.py # 插件入口,组装并启动 HTTP 服务
├── config.py # 配置(环境变量)
├── middleware.py # 鉴权 & 请求 ID 中间件
├── response.py # 统一响应格式
├── handlers/
│ ├── __init__.py
│ ├── command.py # 命令监听匹配与回调
│ ├── health.py # GET /healthz
│ ├── message.py # POST /webhook
│ └── upload.py # POST /upload
├── manifest.toml # NcatBot 插件元数据
├── .env.example # 环境变量模板
└── pyproject.toml # Python 依赖声明
```
## 部署建议
- 放在 Nginx/Caddy 反向代理后,启用 HTTPS
- 配置请求限流,防止滥用
- 使用 systemd 管理进程:
```ini
# /etc/systemd/system/ncatbot.service
[Unit]
Description=NcatBot Webhook Plugin
After=network.target
[Service]
Type=simple
User=ncatbot
WorkingDirectory=/opt/ncatbot
EnvironmentFile=/opt/ncatbot/.env
ExecStart=/opt/ncatbot/.venv/bin/python -m ncatbot
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
日志默认输出到 stdout可由 `journald` 收集查看:
```bash
journalctl -u ncatbot -f
```