Matrix Music Bot Dev 2dd94daa54 docs: README.md 增加 Docker 部署章节(快速启动 / Dockerfile 设计 / 调试)
新增内容(145 行):
  - 快速启动 6 步(config init → login → .env → up -d → logs → down)
  - 首次登录两种方案:
      A. 先在主机登录(推荐)
      B. docker-compose run --rm 容器内登录
  - Dockerfile 设计要点(多阶段 / 层缓存 / strip / tini / 非 root)
  - 镜像版本管理(build-arg + tag/push)
  - 容器内调试命令(exec / version / ffmpeg / yt-dlp / logs)
  - 常见 Docker 问题:
      - crates.io 拉取慢 → rsproxy 镜像源
      - 容器立即退出 → 缺环境变量 / 配置 / data
      - 升级后 crypto.db 不兼容 → 删除重建 + 重新信任

参考 README 系统要求章节了解详细依赖。
2026-07-02 23:55:20 +08:00

Matrix 点歌机器人 (matrix-music-bot)

一个运行在 Matrix(兼容 Element / Element Call)房间中的点歌机器人,使用 matrix-rust-sdk 实现 端到端加密 (E2EE) 房间内搜索、点歌与音频投递。

v1 已完成基础闭环:登录 → 加入加密房间 → 接收 !req / !req-url 命令 → yt-dlp 搜索 → 用户通过 emoji 反应选择 → 后台下载 → ffmpeg 转码 → 通过 Timeline::send 投递加密 m.audio


目录


功能特性

能力 说明
E2EE 房间 完全支持 Megolm 加密(聊天消息 + 音频文件)
Element Call 兼容 MSC3401 call.member 状态事件观测(不参与通话媒体)
多种点歌入口 !req <关键词> 搜索 + !req-url <URL> 直传
用户两步选择 搜索后发带 1️⃣~5️⃣ emoji 的候选列表,点击 emoji 选定
yt-dlp 通用后端 支持 YouTube / B 站 / SoundCloud 等
立体声 Opus 128k 通过 ffmpeg 转码为 audio/ogg; codecs=opus
每房间独立队列 tokio mpsc 通道,房间间互不阻塞
队列去重 同 (source_kind, source_id) 同房间去重
反应超时清理 60 秒未选择自动取消搜索会话
加密会话存储 access_token 用 Argon2id + AES-256-GCM 加密保存到 data/secret.bin
自动交叉签名 homeserver 支持 MSC3967 时自动建立交叉签名

架构概览

┌─────────────────────────────────────────────────────────┐
│ matrix-music-bot (Rust binary)                           │
│                                                          │
│  main.rs (CLI)                                           │
│    ├─ login → bootstrap → secret_store::save             │
│    ├─ run   → bootstrap::restore → event_loop           │
│    └─ config → init / path / print                       │
│                                                          │
│  event_loop.rs                                           │
│    ├─ client.sync() ──────────────────┐                  │
│    ├─ add_event_handler (m.room.message)                  │
│    ├─ add_event_handler (m.reaction)   │ Megolm 解密     │
│    ├─ add_event_handler (MSC3401 state)│                │
│    └─ commands::parse → 派发                            │
│                                                          │
│  worker.rs (每房间一个)                                  │
│    resolve → downloader (yt-dlp) → transcoder (ffmpeg)   │
│    → delivery::audio_upload (m.audio via Timeline::send) │
│                                                          │
│  数据存储:                                                │
│    data/crypto.db   matrix-sdk crypto (Olm/Megolm)       │
│    data/bot.db      (预留,应用状态;v1 暂用内存)       │
│    data/secret.bin  AES-256-GCM 加密的 access_token       │
│    data/cache/opus  转码后的 Opus/WebM                    │
└─────────────────────────────────────────────────────────┘
         │              │              │
         ▼              ▼              ▼
  Matrix homeserver   yt-dlp        ffmpeg
  (E2EE Megolm)     (HTTP 下载)   (音频转码)

详细架构见 C:\Users\Administrator\.claude\plans\lexical-meandering-lighthouse.md


系统要求

操作系统

  • Windows 10/11x86_64
  • Linux(理论上可编译,需把 Cargo.toml.cargo/config.tomllinker = "C:/msys64/mingw64/bin/gcc.exe" 改为本地 gcc

必备外部工具

工具 版本要求 安装方式(Windows 验证命令
Rust stable-x86_64-pc-windows-gnu ≥ 1.96 winget install Rustlang.Rustup rustc --version
MinGW gcc ≥ 13(链接器) winget install MSYS2.MSYS2pacman -S mingw-w64-x86_64-gcc gcc --version
SQLite3 系统库(矩阵 SDK 链接) pacman -S mingw-w64-x86_64-sqlite3 系统 PATH 中
FFmpeg ≥ 5.0(音频转码) https://www.gyan.dev/ffmpeg/builds/ 或 choco/scoop ffmpeg -version
yt-dlp ≥ 2024(音乐下载) pip install yt-dlp 或下载 standalone binary yt-dlp --version

验证脚本

rustc --version       # rustc 1.96.x
cargo --version       # cargo 1.96.x
gcc --version         # gcc.exe (Rev built by MSYS2 project)
ffmpeg -version       # ffmpeg version 8.x
yt-dlp --version      # 2026.x.x
sqlite3 --version     # 3.x.x

快速部署

1. 克隆与编译

git clone http://192.168.6.200:3080/1076code/matrix.git
cd matrix

# Debug 编译(开发用)
cargo build

# Release 编译(生产用)
cargo build --release

二进制产物:

  • Debugtarget/x86_64-pc-windows-gnu/debug/matrix-music-bot.exe
  • Releasetarget/x86_64-pc-windows-gnu/release/matrix-music-bot.exe

2. 创建配置文件

./target/release/matrix-music-bot.exe config init
# 生成 ./config/bot.toml

编辑 ./config/bot.toml

[homeserver]
url = "https://matrix.example.cn"            # ← 改成你的 homeserver

[bot]
user_id = "@musicbot:example.cn"             # ← 改成 bot 账户 MXID
device_name = "matrix-music-bot-v1"
admin_user_ids = ["@owner:example.cn"]       # ← 可选

[data]
dir = "./data"                                # 默认即可

[commands]
prefix = "!"
mention_required_in_rooms = true             # 房间中需 @-mention bot
allow_dm = true

[crypto]
auto_enable_cross_signing = true              # 自动建立交叉签名
auto_enable_key_backup = false                # bot 不参与密钥备份

[music]
ytdlp_path = "yt-dlp"                          # yt-dlp 路径或 PATH 中
search_limit = 5                                # reactions 候选数
search_timeout_secs = 30                        # 搜索超时

3. 首次登录

# 必须设置 passphrase(用于加密 secret.bin 中的 access_token
export MATRIX_BOT_SECRET_PASSPHRASE="your-strong-passphrase-min-8-chars"

./target/release/matrix-music-bot.exe login \
    --user-id "@musicbot:example.cn" \
    --homeserver "https://matrix.example.cn"

# CLI 会提示输入密码(也可通过 MATRIX_BOT_PASSWORD 环境变量)

成功后输出类似:

╭─────────────────────────────────────────────────────────────╮
│ ✅ 登录成功                                                  │
│                                                             │
│ user_id:    @musicbot:example.cn                            │
│ device_id:  AAAAAAAAAA                                      │
│ homeserver: https://matrix.example.cn                        │
╰─────────────────────────────────────────────────────────────╯

📋 下一步(重要):
   在 Element 桌面客户端登录 @musicbot:example.cn
   进入 设置 → 设备 → 找到 device_id `AAAAAAAAAA`
   点击 "信任"

4. Element 客户端信任 bot 设备

这一步必须做,否则交叉签名不通过、其他客户端会显示黄色盾牌。

  1. 在 Element Web/Desktop 用 @musicbot:example.cn 登录
  2. 设置 → 设备与会话 → 找到 matrix-music-bot-v1
  3. 点击"信任"(或选中"Verify all sessions"

5. 启动 bot

export MATRIX_BOT_SECRET_PASSPHRASE="your-strong-passphrase-min-8-chars"

./target/release/matrix-music-bot.exe run

预期日志:

2026-07-02T13:00:00  INFO 恢复登录成功 user_id=@musicbot:example.cn
2026-07-02T13:00:01  INFO 开始初始 sync_once...
2026-07-02T13:00:05  INFO 初始 sync 完成
2026-07-02T13:00:05  INFO 已注册 m.room.message 事件 handler
2026-07-02T13:00:05  INFO 已注册 MSC3401 call.member state event handler
2026-07-02T13:00:05  INFO 开始 long-running sync(按 Ctrl-C 停止)

6. 加入加密房间

在 Element 中把 bot 邀请进一个加密房间。bot 会自动发现并启动 worker。

7. 测试点歌

在房间中发送:

@musicbot:example.cn !help
@musicbot:example.cn !req 周杰伦 晴天
@musicbot:example.cn !req-url https://www.youtube.com/watch?v=xxxxxxxxxxx

配置说明

MATRIX_BOT_SECRET_PASSPHRASE 环境变量(必填)

用于加密 data/secret.bin 中的 access_token + device_id。必须 ≥ 8 字符

# Linux/macOS
export MATRIX_BOT_SECRET_PASSPHRASE="correct-horse-battery-staple"

# Windows cmd
set MATRIX_BOT_SECRET_PASSPHRASE=correct-horse-battery-staple

# Windows PowerShell
$env:MATRIX_BOT_SECRET_PASSPHRASE = "correct-horse-battery-staple"

如果忘记此环境变量,bot 会输出明确错误:

ERROR 密钥存储错误: 未设置 MATRIX_BOT_SECRET_PASSPHRASE 环境变量

MATRIX_BOT_PASSWORD 环境变量(仅 login 时需要)

仅在交互式登录时使用。可选——不设置则 login 提示输入密码。


命令列表

命令 别名 说明
!help !h, !? 显示帮助
!req <关键词> !request, !点歌, !来一首 关键词搜索(用 reactions 选)
!req-url <URL> !url 直传 URLYouTube/B站等 yt-dlp 支持的源)
!queue !q, !队列 查看当前房间队列
!np !nowplaying, !当前 当前正在播放
!skip !next, !跳过 跳过当前播放

房间权限

  • DM:无需 @-mention,直接发命令即可
  • 房间:默认需 @-mention bot 才会响应(可在 [commands] 配置关闭)

reactions 选择流程

@musicbot !req 周杰伦 晴天
        ↓
bot: 🔍 搜索「周杰伦 晴天」找到 5 个结果,点击 emoji 选择:
    1️⃣ 晴天 - 周杰伦 (3:42)
    2️⃣ 晴天 (Live) - 周杰伦 (5:18)
    3️⃣ 晴天 (Piano) - 周杰伦 (4:01)
    4️⃣ 晴天 (Remix) - 周杰伦 (3:30)
    5️⃣ 晴天故事 - 周杰伦 (4:12)
    ⏰ 60 秒后未选择自动取消
        ↓
你:点击 2️⃣(emoji 反应)
        ↓
bot: ✅ 已加入队列:晴天 (Live) - 周杰伦
bot: 🎵 [内嵌音频播放器]  ← Element 内显示,可直接播放

Docker 部署(推荐用于生产服务器)

仓库自带 Dockerfile(多阶段 Debian bookworm-slim 构建)+ docker-compose.yml(单服务编排)。 镜像内含 Rust 工具链、ffmpeg、yt-dlp standalone 二进制(锁版本)、tiniPID 1)。

快速启动

# 1. 准备配置
./target/release/matrix-music-bot.exe config init    # 或在 Linux 上:./matrix-music-bot config init
# 编辑 ./config/bot.toml

# 2. 首次登录(如未在本机登录过)
./target/release/matrix-music-bot.exe login
# 或在 docker 内执行(见下方"首次登录")

# 3. 创建 .env
cp .env.example .env
# 编辑 .env,至少设置 MATRIX_BOT_SECRET_PASSPHRASE

# 4. 启动容器
docker-compose up -d --build

# 5. 查看日志
docker-compose logs -f matrix-music-bot

# 6. 停止
docker-compose down

首次登录(如果未在本机登录过)

两种方案:

方案 A:先在主机登录,再启动容器(推荐)

# 在 Linux 主机上 build 并登录
cargo build --release
MATRIX_BOT_SECRET_PASSPHRASE="..." ./target/release/matrix-music-bot login
# 登录成功后 data/secret.bin 与 data/crypto.db 已生成
# 启动容器时会自动用这些文件
docker-compose up -d

方案 B:在容器内登录

# 启动一次性容器执行 login
docker-compose run --rm matrix-music-bot login
# 按提示输入 homeserver / user_id / password
# 完成登录后退出,secret.bin 与 crypto.db 已写入 ./data/
docker-compose up -d

Dockerfile 设计要点

  • 多阶段builder=rust:1.96-bookworm(约 1.5 GB),runtime=debian:bookworm-slim(约 400 MB 含 ffmpeg
  • 层缓存优化:先复制 Cargo.toml 预编译 deps,再复制 src 编译
  • strip 二进制:runtime 镜像中减小可执行文件体积
  • 非 root 运行bot:bot 用户
  • tini 作为 PID 1:正确转发 SIGTERM/SIGINT 给 bot(避免僵尸进程)
  • 健康检查:仅检测进程存活(bot 无内置 health endpoint
  • yt-dlp checksum 校验:用 ADD --checksum=sha256 确保下载完整

镜像版本管理

# 锁定 yt-dlp 版本(构建参数)
docker-compose build --build-arg YTDLP_VERSION=2026.03.17

# 推送镜像到 registry
docker tag matrix-music-bot:latest your-registry/matrix-music-bot:v0.1.0
docker push your-registry/matrix-music-bot:v0.1.0

容器内手动调试

# 进入运行中的容器
docker-compose exec matrix-music-bot /bin/bash

# 查看版本
docker-compose exec matrix-music-bot matrix-music-bot version

# 检查 ffmpeg / yt-dlp
docker-compose exec matrix-music-bot ffmpeg -version
docker-compose exec matrix-music-bot yt-dlp --version

# 查看日志
docker-compose logs --tail=100 matrix-music-bot

常见 Docker 问题

镜像构建失败:failed to fetch

国内服务器拉取 crates.io 慢。方案:

# 在 Cargo 配置中加镜像源(项目内 .cargo/config.toml 已配 git-fetch-with-cli=true
# 或临时设环境变量:
CARGO_NET_GIT_FETCH_WITH_CLI=true docker-compose build

或编辑 ~/.cargo/config.toml(主机):

[source.crates-io]
replace-with = "rsproxy-sparse"

[source.rsproxy-sparse]
registry = "sparse+https://rsproxy.cn/index/"

容器启动后立即退出

查看日志:docker-compose logs matrix-music-bot。常见原因:

  • MATRIX_BOT_SECRET_PASSPHRASE 未设置 → Environment 块报错退出
  • ./config/bot.toml 不存在 → 用 config init 生成
  • ./data/ 为空且未在主机登录过 → 用方案 A 或 B 先登录

升级 bot 后旧配置不兼容

data/crypto.db 是 matrix-sdk 的 crypto store,升级 bot 版本可能导致 schema 不匹配。 如果 bot 启动后 sync 异常,删除 data/crypto.db 重启(需要重新在 Element 信任 bot 设备)。


故障排查

登录失败:error sending request

  • 检查 [homeserver] url 是否正确
  • 检查 bot 账户密码是否正确
  • 检查 homeserver 是否能从 bot 所在服务器访问

linker 'gcc.exe' not found

# 安装 MinGW gcc
winget install MSYS2.MSYS2
# 在 MSYS2 中:
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-sqlite3

cannot find -lsqlite3

pacman -S mingw-w64-x86_64-sqlite3

ffmpeg not found

确保 ffmpeg.exe 在 PATH 中,或在 [music] ytdlp_path 中设置绝对路径。

yt-dlp not found

pip install yt-dlp
# 或下载 standalone exe 放到 PATH 中

反应选择不生效

v1 妥协:SendHandle 的 transaction_id 是私有的,reactions 监听使用占位 event_id, 可能匹配不到对应的搜索会话。M8 修复方向是通过 timeline 订阅事件→匹配 transaction_id 与真实 event_id

临时方案:使用 !req-url(不走 reactions)作为可靠路径。

UnableToDecrypt 警告

  • bot 加入加密房间后需要几秒同步 room keys
  • 检查 bot 账户的设备是否在 Element 中被信任
  • 若 homeserver 不支持 MSC3967bot 自动建立交叉签名可能失败

CPU 占用高 / 卡顿

  • 减少 [music] search_limit(默认 5
  • 检查 yt-dlp 是否在并发拉取(搜索时短暂占用)

开发指南

项目结构

matrix-music-bot/
├── Cargo.toml              依赖
├── .cargo/config.toml     MinGW gcc 链接器配置
├── config/bot.toml        操作员配置(不进 git)
├── data/
│   ├── crypto.db          matrix-sdk crypto storeOlm/Megolm
│   ├── secret.bin         AES-256-GCM 加密的 access_token
│   └── cache/opus/        转码缓存
└── src/
    ├── main.rs            CLI 入口 + tokio runtime
    ├── config.rs          AppConfig 结构 + TOML 加载
    ├── error.rs           AppError 枚举(thiserror
    ├── secret_store.rs    Argon2id + AES-256-GCM
    ├── client.rs          matrix-sdk Client 构造
    ├── bootstrap.rs       login / restore
    ├── commands.rs        !req / !queue 等命令解析
    ├── event_loop.rs      sync + add_event_handler + Timeline 加密通道
    ├── queue.rs           队列(in-memory + 去重)
    ├── reaction_store.rs  reaction 选择存储
    ├── rtc_handler.rs     MSC3401 call.member 状态事件
    ├── transcoder.rs      ffmpeg 子进程 → Opus 128k
    ├── delivery/
    │   ├── mod.rs         DeliveryStrategy trait
    │   ├── audio_upload.rs  Pattern Am.audio via Timeline
    │   └── url_link.rs    Pattern Bv2 stub
    ├── music/
    │   ├── mod.rs         MusicSource trait + SearchFacade
    │   ├── ytdlp.rs       YtDlpBackend(完整)
    │   ├── netease.rs     NetEaseBackendstub
    │   ├── qq.rs          QqMusicBackendstub
    │   └── selfhosted.rs  LocalFsBackendstub
    ├── downloader.rs      yt-dlp 子进程下载
    └── worker.rs          每房间 workerresolve → download → transcode → upload

重新登录(重新生成 secret.bin)

rm data/secret.bin
export MATRIX_BOT_SECRET_PASSPHRASE="..."
./target/release/matrix-music-bot.exe login

清理 crypto.db(重置 E2EE 状态)

rm data/crypto.db
# 下次启动会重新建立 crypto store
# 注意:其他客户端需要重新信任 bot 设备

运行测试

cargo test                          # 全部
cargo test commands                # 仅 commands 模块
cargo test -- --ignored            # 包含需要 yt-dlp/ffmpeg/网络的测试

添加新音乐源

实现 MusicSource trait 即可自动接入:

// src/music/mysource.rs
use crate::music::{MusicSource, SongCandidate, ResolvedSong};

pub struct MySource { /* ... */ }

#[async_trait]
impl MusicSource for MySource {
    fn kind(&self) -> &'static str { "mysource" }
    fn display_name(&self) -> &'static str { "我的音乐源" }
    async fn search(&self, query: &str, limit: u8) -> Result<Vec<SongCandidate>, AppError> { /* ... */ }
    async fn resolve(&self, candidate: &SongCandidate) -> Result<ResolvedSong, AppError> { /* ... */ }
}

src/event_loop.rs::build_facade() 中加入你的 backend。


已知限制

限制 原因 影响 计划修复
event_id 跟踪用占位 SendHandle.transaction_id 私有 reactions 选择 v1 可能不生效 M8: timeline subscription 匹配
队列进程重启清空 rusqlite 与 matrix-sdk-sqlite 共享 libsqlite3-sys 冲突 重启后未完成的歌曲丢失 v2:解决链接冲突后用 SqliteQueue
HEAD 探测未实现 未引入 reqwest 上传前无法预知大小 M5+ 加 reqwest
不参与通话媒体 v1 范围外(需要 matrix_sdk::rtc + livekit-rs 不能在 Element Call 中"唱歌" v2
国内 YouTube 不稳定 网络限制 部分搜索/下载失败 fallback 到 NetEase/QQM3+ 可实现)
候选仅按 title+artist 去重 简单实现 不同版本可能被错误合并 v2 加 (duration, album) 二次校验

路线图

v1(已完成)

  • M1 骨架 + 配置 + 加密 secret_store
  • M2 命令解析 + E2EE Timeline 加密通道
  • M3 MusicSource trait + yt-dlp backend
  • M4 转码 + 投递
  • M5 端到端(!req/!req-url/!queue/!np/!skip
  • M6 Element Call 兼容(MSC3401 状态事件)
  • M7 reactions 两步选择

v1.x(可选优化)

  • M8: event_id 跟踪修复(reactions 选择真实可用)
  • M9: NetEase / QQ 音乐 backend 完整实现
  • M10: 队列持久化(解决 libsqlite3-sys 冲突)
  • M11: Pattern B(URL 链接兜底,当文件过大时降级)
  • M12: HEAD 探测(reqwest 集成)

v2(未来)

  • 🔮 bot 作为 Element Call 参与者(matrix_sdk::rtc + livekit-rs
  • 🔮 多语言 NLP 解析(自然语言点歌)
  • 🔮 用户点歌历史 + 收藏夹
  • 🔮 歌单导入(网易云 / QQ 音乐)
  • 🔮 投票跳过(多用户协同)

测试

cargo test                          # 29 passed + 4 ignored

测试覆盖:

  • 命令解析(11 测试):DM/房间 mention、缺参、中英文别名
  • TOML 配置(2):最小配置解析、示例配置往返
  • secret_store(2):加密往返、错误口令拒绝
  • music4):AudioFormat 推断、serde 往返
  • queue3):入队去重、不同 source_id 不去重、skip
  • reaction_store4):emoji 解析、会话记录消费、过期清理

ignored 测试需要 yt-dlp / ffmpeg / 真实网络。


许可证

待定(项目内未声明)。


致谢

S
Description
No description provided
Readme 361 KiB
Languages
Rust 89.9%
Shell 7.6%
Dockerfile 2.5%