109 lines
5.5 KiB
Markdown
109 lines
5.5 KiB
Markdown
# AuthBot — QQ 群入群验证机器人
|
||
|
||
基于 [qqbotsdk-py](https://pypi.org/project/qqbotsdk-py/)(QQ 官方开放平台 SDK)的群验证机器人:
|
||
|
||
新成员进群 → 机器人发**数学选择题 + 按钮验证码** → 完成验证前该成员处于**禁言**状态 →
|
||
限时内点对按钮立即解禁;**超时未通过则移出群**(若踢人接口未获官方内邀权限,自动降级为
|
||
长期禁言 + 通知管理员)。
|
||
|
||
## 功能一览
|
||
|
||
- 进群自动验证:`GROUP_MEMBER_ADD` 事件触发,数学题 4 选 1 按钮(题目随机、答错换题、倒计时不重置)
|
||
- 验证前禁言:按 `member_openid` 单独禁言(不碰全员禁言),通过后立即解禁
|
||
- 超时踢出:默认 10 分钟;踢人失败(业务码 11253)自动兜底为禁言 72 小时 + 群内提示管理员
|
||
- 旧题防重放:按钮 data 携带题目 token,换题后旧按钮点击无效
|
||
- 反捣乱:点击**非本人的验证按钮**(他人的当前题或已失效的残留按钮),30 分钟窗口内
|
||
累计 3 次即禁言 5 分钟,警告会 @ 点击者(次数与时长可配:`FOREIGN_CLICK_LIMIT` /
|
||
`FOREIGN_CLICK_MUTE_MINUTES`)
|
||
- 状态持久化:`data/` 下 JSON 存储,进程重启自动恢复禁言与倒计时(已过期立即处理)
|
||
- 轻量管理指令:群里 `@机器人 验证状态` 查看待验证名单与剩余时间;
|
||
`@机器人 验证我`(或 `测试验证`)对发起人本人走一遍完整验证流程,方便上线前自测
|
||
- 免验信任期(可选):通过后 N 天内退群重进不再验证,默认 0(每次都验)
|
||
|
||
## 环境要求
|
||
|
||
- Python **3.12+**(SDK 硬性要求;开发环境为 3.14)
|
||
- `qqbotsdk-py >= 0.2.1`(0.2.1 修复了按成员禁言的请求体——旧版平铺写法会被服务端静默忽略)
|
||
- QQ 开放平台机器人([q.qq.com](https://q.qq.com) 注册,拿到 AppID / AppSecret)
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
# 1. 安装依赖
|
||
python -m venv .venv
|
||
.venv\Scripts\pip install -r requirements.txt
|
||
|
||
# 2. 配置凭据
|
||
copy .env.example .env
|
||
# 编辑 .env,填入 APPID / APPSECRET
|
||
|
||
# 3. 启动(WebSocket 方式,开箱即用)
|
||
.venv\Scripts\python main.py
|
||
```
|
||
|
||
事件订阅开关已配好在 `intents.toml`:`GROUP_AND_C2C_EVENT`(@消息)、
|
||
`GROUP_MEMBER_EVENT`(进退群,**默认关闭的关键开关**)、`INTERACTION`(按钮回调)。
|
||
|
||
## 平台侧准备(重要)
|
||
|
||
1. **markdown 消息与按钮(键盘)权限**:在 q.qq.com 开发者后台为机器人申请。
|
||
没有该权限验证码发不出去——机器人此时**不会禁言新成员**(防止把人永久锁死),
|
||
并在日志输出 CRITICAL/ERROR 提示排查方向。
|
||
2. **机器人入群并设为群管理员**:禁言(`restrict_chat_setting`)、移出成员都需要管理员身份。
|
||
3. **踢人接口处于官方"内邀接入中"**:未开通的机器人调用返回业务码 11253。
|
||
机器人会自动降级:延长禁言(默认 72 小时,可配)并在群里提示管理员手动处理。
|
||
4. 群消息接收方式保持默认(@机器人)即可,无需"接收所有消息"权限。
|
||
|
||
## 配置项(.env)
|
||
|
||
| 变量 | 默认 | 说明 |
|
||
| --- | --- | --- |
|
||
| `APPID` / `APPSECRET` | 必填 | 开放平台凭据 |
|
||
| `CONNECTER` | `websocket` | `websocket` 或 `webhook` |
|
||
| `VERIFY_TIMEOUT_MINUTES` | `10` | 验证限时,超时触发处理 |
|
||
| `PENDING_MUTE_HOURS` | `24` | 待验证期间禁言时长 |
|
||
| `TIMEOUT_KICK` | `true` | 超时是否尝试移出群 |
|
||
| `TIMEOUT_FALLBACK_MUTE_HOURS` | `72` | 踢出失败后的兜底禁言时长 |
|
||
| `CAPTCHA_OPTIONS` | `4` | 选项个数(2~5) |
|
||
| `VERIFIED_TRUST_DAYS` | `0` | 通过后免验天数(0=每次都验) |
|
||
| `FOREIGN_CLICK_LIMIT` | `3` | 他人乱点验证按钮的处罚阈值(30 分钟窗口) |
|
||
| `FOREIGN_CLICK_MUTE_MINUTES` | `5` | 乱点处罚的禁言时长(分钟) |
|
||
| `DATA_DIR` | `data` | 状态文件目录 |
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
main.py 入口
|
||
authbot/
|
||
config.py .env 业务参数读取与校验
|
||
captcha.py 验证码生成、按钮编解码、消息模板
|
||
state.py pending/verified 持久化 + 超时定时器
|
||
handlers.py 事件处理器(进群/按钮/退群/@指令/超时流程)
|
||
intents.toml 事件订阅开关
|
||
data/ 运行时状态(自动创建)
|
||
tests/ pytest 单元测试
|
||
```
|
||
|
||
## 已知限制(官方 API 能力边界)
|
||
|
||
- 群场景只有 **openid**(`group_openid` / `member_openid`),拿不到真实 QQ 号,
|
||
所以提示信息里只能显示 openid 尾号。
|
||
- **群主/管理员/机器人无法被禁言**(官方限制,接口会静默返回成功但不生效);
|
||
机器人会在回查禁言列表后发现这一点,`验证我` 测试时会明确提示。因此请用
|
||
普通成员账号测试禁言效果。
|
||
- 禁言接口请求体为嵌套结构 `{"members": [{"op": "add|update|del", ...}]}`
|
||
(平铺写法会被服务端静默忽略,实测踩坑;SDK 0.2.1 起已内置正确封装);
|
||
写入后机器人会回查确认生效。
|
||
- 被动回复有窗口限制:群内同一凭据 5 分钟内最多 5 条;主动消息默认未开通,
|
||
超时通知发送失败时仅记录日志(禁言/踢人不受影响)。
|
||
- 兜底禁言同样需要机器人是群管理员。
|
||
|
||
## 测试
|
||
|
||
```bash
|
||
.venv\Scripts\python -m pytest tests/ -v
|
||
```
|
||
|
||
覆盖验证码生成/编解码、状态持久化、定时器触发与取消、重启恢复、配置校验、
|
||
处理器装配(不发起任何网络请求)。
|