- 回调服务器可返回 reply 或 messages 字段,插件自动回复到原消息来源 - reply 为纯文本回复,messages 格式同 /webhook 接口 - 支持通过 group_id/user_id 覆盖回复目标 - 无需回复时返回空 JSON 即可 - 更新 README 文档说明回调响应格式
304 lines
7.6 KiB
Markdown
304 lines
7.6 KiB
Markdown
# 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
|
||
```
|