Files
MusicBot/README.md
T
snowy-yang 324fe86962 按钮回调防抖与先应答:10 秒去重、进门回应互动、播放/下载先回占位消息
- 同一互动 uuid(平台重复推送)或同群同按钮 10 秒内只处理一次;连点
  仍应答互动,关掉客户端 loading
- 播放/下载结果以 msg_seq=2 补发(同一事件相同 msg_seq 的二次回复会被
  QQ 拒绝)
- 一并入库此前未提交的 /sources 平台发现(直链回退链按服务器能力动态
  解析);uv.lock 随上游 qqbotsdk main 更新 sdist hash
2026-09-30 18:59:17 +08:00

63 lines
5.7 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.
# musicbot
QQ 群点歌 bot,基于 [qqbotsdk](https://github.com/snowy-yang/qqbotsdk),两步点歌:
1. **选歌菜单**——`点歌 <歌名 [歌手]>`(或斜杠指令 `/点歌 <歌名 [歌手]>`)只负责选歌:Markdown 歌单(编号 + 歌名/歌手,默认 QQ 音乐源,搜不到自动聚合兜底),下方每行 5 个编号回调按钮 + `[上一页][下一页]`(每页 `MENU_PAGE_SIZE` 首,默认 10,上限 20)
2. **歌曲卡片**——点编号弹出卡片:专辑封面图 + 歌名/歌手/来源 + `[▶ 播放][⬇ 下载]` 回调按钮,再点才执行
- **专辑封面**:卡片用 Markdown 图片语法 `![封面 #240px #240px](img)` 显示封面(QQ 后台会代理并缓存图片到客户端,不落库);封面来自 MusicServer 的 `img` 字段,tx/wy 有值,kw/kg 无(这些源卡片就不带图)
- 回调按钮(type=1)点一下直接生效,无需再按发送;播放发语音、下载发文件或直链(见 `SEND_MODE`),失败回退直链文本;无 Markdown 权限自动回退文本
- **下载发送方式**(`SEND_MODE`,默认 `link`):群文件上传仅限群主/管理员(40093005),故默认直接发直链;`file` 则先尝试上传群文件、失败(含无权限)回退直链。播放始终发语音
- **直链候选源回退**:tx 等源直链常受 CDN 风控抓不动(bot 抓不动时 QQ 服务器侧上传同样报 40093007),上传前先探测直链可抓性,不可抓则按 kw/wy/mg 逐个搜索换源
- **自动撤回**(`WITHDRAW`,默认开):翻页撤旧菜单、选歌后撤菜单、下载/播放点击后撤卡片(无论发送成败都撤,卡片是一次性 UI);关闭后所有消息都保留
- **回调防抖与先应答**:按钮回调进门先回应互动(`PUT /interactions`,几秒内不应答 QQ 客户端就报超时),同一互动 uuid(平台重复推送)或同群同按钮 10 秒内只处理一次;播放/下载先回「正在获取」占位消息再取直链,结果以递增的 `msg_seq` 补发(同一事件相同 `msg_seq` 的第二次回复会被 QQ 拒绝)
- **日志**:控制台 + `LOG_DIR/musicbot.log`(按天轮转、保留 7 天),级别由 `LOG_LEVEL` 控制;目录不可写时自动退化为仅控制台
- **热重载(开发用)**:监控 `musicbot/` 下的 `.py` 与 `intents.toml`/`.env`,变更后重启 bot 子进程(重连一次 websocket)。默认关,`uv run musicbot --reload`(`start.bat` 已带)或 `RELOAD=on` 开启
- 按钮回调需 `intents.toml` 开启 `INTERACTION`(本仓库已开启)
## 配置
复制 `.env.example` 为 `.env`,填入开放平台 `APPID`/`APPSECRET`。`intents.toml` 控制事件订阅。MusicServer(lx-music 聚合音源)需在本机或 `MUSIC_SERVER_URL` 指向处运行。
点歌相关开关:
| 变量 | 默认 | 说明 |
|---|---|---|
| `MENU_PAGE_SIZE` | `10` | 选歌菜单每页候选数,1~20(内嵌键盘 5 行所限) |
| `WITHDRAW` | `on` | 自动撤回旧菜单/卡片;`off` 则全部保留 |
| `SEND_MODE` | `link` | 下载按钮:`link` 直接发直链 / `file` 先传群文件(需群主/管理员) |
| `LOG_LEVEL` | `INFO` | 日志级别 TRACE/DEBUG/INFO/SUCCESS/WARNING/ERROR/CRITICAL |
| `LOG_DIR` | `logs` | 日志目录(自动按天轮转、保留 7 天) |
| `RELOAD` | `off` | 热重载;`on` 等价于命令行 `--reload`(`start.bat` 已带) |
### 不 @ 机器人也能点歌
1. **群侧开关**:群主/管理员在 QQ 里打开机器人资料页,把消息接收切换为「接收群内全部消息」——三种取值为 `all`(全量)/ `only_mention`(仅@)/ `mention_and_context`(@+上下文),切换时平台会推 `GROUP_MSG_RECEIVE`/`GROUP_MSG_REJECT` 事件。未开启时网关不推送非 @ 消息,bot 行为与现在完全一致。当前群设置可用 `GET /v2/groups/{group_openid}/bot_state` 查(字段 `recv_msg_setting`)。
2. `intents.toml` 订阅 `GROUP_AND_C2C_EVENT`(本仓库已开启)——@消息(`GROUP_AT_MESSAGE_CREATE`)与全量消息(`GROUP_MESSAGE_CREATE`)同属这一个分组,无需额外配置。
两个事件共用同一个 dataclass 与 handler,被动回复都用该消息的 `msg_id`(群聊 5 分钟内有效、每条最多回复 5 次)。全量事件**不支持 `event_id`**——官方 event_id 支持列表只有 `INTERACTION_CREATE`、`GROUP_ADD_ROBOT`、`GROUP_MSG_RECEIVE`。
## 斜杠指令(输入框 `/` 菜单)
群里输入框敲 `/` 唤出的「指令面板」由 bot **调接口自动注册**(开放平台 2026-08 新增的 `/v2/panels` 系列接口):每次建连(READY)时把 `点歌`/`歌词`/`帮助` 三条指令同步上去——没有就创建(`target_type=all` 对全部群生效)、内容有变就更新、一致则跳过,重连即自愈;失败只记日志不影响运行。`COMMAND_PANEL=off` 可关闭,改在开放平台后台「功能配置 → 指令」手工配置。
指令消息以 `/点歌 稻香` 形式推给 bot(走 `GROUP_AT_MESSAGE_CREATE`,指令自动 @ 机器人),与文本命令共用同一套解析——`_normalize` 会剥掉开头的 `/` 与 `@机器人` 前缀,`点歌 稻香`、`/点歌 稻香`、`@bot /点歌 稻香` 三种写法等价。只敲命令不带参数(斜杠指令参数可为空)时回复用法提示。
「帮助」消息带 `[🎤 点歌][📝 歌词]` 指令按钮(action `type=2`):点击把 `/点歌 ` 填进输入框,补上歌名发送即可;键盘无权限时自动回退纯文本。
## 运行
```bash
uv sync
uv run musicbot # 正常启动
uv run musicbot --reload # 开发:改源码/配置自动重启
```
Windows 上双击 `start.bat` 即以热重载模式启动。
## 开发
```bash
uv run pytest
uv run ruff check musicbot tests conftest.py && uv run pyright
```