Matrix Element Call MusicBot:Docker 安装、配置与电台播放教程

记录在自建 Matrix Synapse / Element Call / LiveKit 环境中安装 matrix-element-call-musicbot,用 Docker 运行音乐机器人,配置自动接受邀请、直播电台短命令,并说明 !join、!play、!load、!stop、!leave 的实际用法和避坑。

这篇记录如何在已经运行 Synapse、Element Call、LiveKit 和 Matrix LiveKit JWT Service 的 Docker 主机上,额外安装一个可以进入 Element Call 播放音乐、电台或 Podcast 的机器人。

上游项目是 SultanAlburaq/matrix-element-call-musicbot。本文使用的是我整理后的 Docker 部署包 hanigege/matrix-element-call-musicbot-docker-pro,它保留上游镜像,通过只读挂载增强版 patches/bot.py,补上电台短命令、Podcast RSS 别名、房间白名单和管理员控制边界。

本文使用的实际环境如下:

Matrix server_name: 666666.xyz
Synapse client API: https://matrix.666666.xyz:12335
Docker 数据目录: /root/data/docker_data
MusicBot 账号: @musicbot:666666.xyz
MusicBot 目录: /root/data/docker_data/matrix-musicbot
Docker 网络: matrix

这个机器人适合在 Matrix 房间里用命令点歌、播放 URL 或网络电台,然后把声音推入 Element Call。它不是普通聊天窗口里的音频播放器:要听到声音,用户自己的 Element 客户端也必须点击右上角“加入”进入通话

如果只是想复刻本文的生产方案,推荐直接使用 Docker 部署包:

git clone https://github.com/hanigege/matrix-element-call-musicbot-docker-pro.git
cd matrix-element-call-musicbot-docker-pro
cp .env.example .env
vi .env
chmod +x scripts/*.sh
./scripts/install.sh

这会渲染 config/config.toml、准备 data/radio_aliases.jsondata/podcast_aliases.json,再启动 matrix-musicbot。后面的手工步骤保留下来,是为了说明每个文件为什么这样写,以及生产排障时该看哪里。

一、重要限制

先说限制,避免后面踩坑。

这个项目目前不支持加密通话媒体。如果房间或通话强制端到端加密,机器人可能无法正常播放。普通 Element Call 房间更适合测试。

!load 对普通歌曲和有限长度音频可以用,但对直播电台容易卡住。直播电台没有自然结束点,如果机器人按“下载完整音频”的方式处理,就会一直停在 Loading...。后文会通过配置和补丁把单条直播 URL 的保存队列转为流式播放。

!join 的含义是让 机器人 加入 Element Call,不是让你自己的客户端加入通话。你本人仍然要点击 Element 右上角的“加入”。

生产环境还要注意单实例模型:一个 matrix-musicbot 容器只有一个全局播放器。多个房间共用同一个 bot 时,任何有权限的人都能切台、停止、跳过或清队列,所以建议同时设置房间白名单和管理员白名单。示例配置只允许指定房间里的 @admin:666666.xyz@owner:666666.xyz 控制播放。

二、创建目录

在 Docker 主机上创建独立服务目录:

mkdir -p /root/data/docker_data/matrix-musicbot/config
mkdir -p /root/data/docker_data/matrix-musicbot/logs
mkdir -p /root/data/docker_data/matrix-musicbot/data
mkdir -p /root/data/docker_data/matrix-musicbot/cache
mkdir -p /root/data/docker_data/matrix-musicbot/patches

镜像内进程固定使用 UID 10001 运行,所以可写目录要交给这个 UID:

chown -R 10001:10001 \
  /root/data/docker_data/matrix-musicbot/logs \
  /root/data/docker_data/matrix-musicbot/data \
  /root/data/docker_data/matrix-musicbot/cache

配置目录只读挂载即可:

chmod 755 /root/data/docker_data/matrix-musicbot/config

三、创建 Matrix 机器人账号

如果 Synapse 配置里有 registration_shared_secret,可以直接在 Synapse 容器里创建普通用户:

docker exec matrix-synapse register_new_matrix_user \
  -u musicbot \
  -p '替换成一个强密码' \
  --no-admin \
  --exists-ok \
  -c /data/homeserver.yaml \
  http://localhost:8008

然后登录获取 access token:

curl -sS -X POST http://127.0.0.1:8008/_matrix/client/v3/login \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "m.login.password",
    "identifier": {"type": "m.id.user", "user": "musicbot"},
    "password": "替换成上面的密码",
    "initial_device_display_name": "matrix-musicbot-docker"
  }'

返回 JSON 里的 access_token 要保存到下一节的 config.toml。不要把真实 token 提交到公开仓库。

可以用 whoami 验证 token:

curl -sS \
  -H "Authorization: Bearer 这里填机器人token" \
  http://127.0.0.1:8008/_matrix/client/v3/account/whoami

期望返回:

{"user_id":"@musicbot:666666.xyz","is_guest":false}

四、写入 config.toml

创建配置文件:

vi /root/data/docker_data/matrix-musicbot/config/config.toml

写入:

[matrix]
homeserver = "https://matrix.666666.xyz:12335"
user_id = "@musicbot:666666.xyz"
access_token = "替换成真实 access_token"

[bot]
name = "Music Bot"
history_limit = 10
auto_accept_invites = true

[access]
# 单实例 musicbot 只有一个全局播放器;只允许这些房间控制,避免陌生房间或私聊抢占播放。
allowed_rooms = [
  "!roomid1:666666.xyz",
  "!roomid2:666666.xyz",
]
# 播放器是单实例全局资源;只有这些 Matrix 账号可以查看节目列表、点播或控制播放,不能随便留空开放。
admin_users = [
  "@admin:666666.xyz",
  "@owner:666666.xyz",
]

[paths]
audio_dir = "/tmp/musicbot_audio"
saved_queues_file = "data/saved_queues.json"

[audio]
cache_mode = "size_lru"
cache_max_bytes = 1073741824
cache_delete_after_playback = false
cache_delete_on_shutdown = true
search_mode = "fast"
search_timeout_seconds = 8.0
extractor_retries = 1
download_format = "wav"
audio_quality = "best"
stream_first_idle = true
# 直播电台没有自然结束点,后台预取和失败转下载会让 ffmpeg / yt-dlp 挂住。
stream_prefetch_current = false
stream_retry_to_file_on_fail = false
auto_advance_buffer = 2.0
preroll_silence = 1.0
normalize_audio = false
fade_in_ms = 120
volume_percent = 100

[worker]
max_restart_attempts = 3
heartbeat_interval_seconds = 10.0
skip_cooldown_seconds = 1.0
stop_timeout_restart_threshold = 2
membership_mode = "legacy"
log_max_bytes = 2000000
log_backups = 5

[playlist]
max_tracks_per_request = 50
background_load_concurrency = 4

[logging]
file = "logs/musicbot.log"
clean_enabled = true
clean_file = "logs/musicbot.clean.log"
clean_filter_matrixrtc_noise = true
max_bytes = 2000000
backups = 5

[ui]
show_progress_messages = false
rich_formatting = false
quiet_mode = true

关键点:

  • auto_accept_invites = true:否则机器人收到邀请后不会自动加入房间,房间里发 !help 也没有反应。
  • [access].allowed_rooms:生产环境建议填写允许控制播放的房间 ID,避免机器人被私聊或陌生房间抢占。
  • [access].admin_users:生产环境必须填写管理员账号;留空时增强补丁会拒绝所有播放控制命令,避免误开放。
  • stream_first_idle = true:空闲时直接用流式播放,适合电台。
  • stream_prefetch_current = false:不要后台预取直播流。
  • stream_retry_to_file_on_fail = false:流式失败后不要改成下载完整文件。
  • membership_mode = "legacy":兼容当前 Synapse / Element Call 栈。
  • cache_mode = "size_lru" + cache_max_bytes = 1073741824:普通歌曲和 Podcast 下载后最多保留 1GB 缓存,超过后删最旧文件;同一集下次再听通常不用重新下载。
  • cache_delete_after_playback = false:配合 1GB LRU 缓存,不在播完立刻删除下载文件。

设置权限:

chmod 644 /root/data/docker_data/matrix-musicbot/config/config.toml

五、创建 docker-compose.yaml

创建 Compose 文件:

vi /root/data/docker_data/matrix-musicbot/docker-compose.yaml

写入:

services:
  matrix-musicbot:
    image: ghcr.io/sultanalburaq/matrix-element-call-musicbot:latest
    container_name: matrix-musicbot
    restart: unless-stopped
    environment:
      - CONFIG_FILE=/app/config/config.toml
    volumes:
      - ./config:/app/config:ro
      - ./patches/bot.py:/app/bot.py:ro
      - ./logs:/app/logs
      - ./data:/app/data
      - ./cache:/tmp/musicbot_audio
    networks:
      - matrix
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "3"

networks:
  matrix:
    external: true
    name: matrix

这里接入现有 matrix Docker 网络,不需要开放宿主机端口。日志使用 Docker 自带轮转,避免容器日志无限增长。

如果 Docker 主机本身可以直连外网,但 Docker daemon 配了失效代理,拉取 GHCR 镜像时可能看到类似错误:

proxyconnect tcp: dial tcp 10.20.20.6:9666: connect: connection refused

这时检查 Docker daemon 的代理环境。例如 Alpine / OpenRC 常见位置:

grep -n 'HTTP_PROXY\|HTTPS_PROXY\|proxy' /etc/conf.d/docker

如果旧代理已不可用,可以注释掉后重启 Docker:

rc-service docker restart

重启 Docker daemon 会让现有容器短暂重启,操作前应确认业务可接受。

六、为直播电台增加别名文件

原项目支持 !play URL,但每次输入长电台地址很麻烦。推荐把电台保存到独立 JSON 文件里,让 tw1tw2jp1 这类名字变成可维护的电台别名。

创建别名文件:

vi /root/data/docker_data/matrix-musicbot/data/radio_aliases.json

示例:

{
  "dinner1": "http://stream.srg-ssr.ch/m/rsj/mp3_128",
  "dinner2": "https://west-mp3-128.streamthejazzgroove.com/stream",
  "jazz1": "http://jazzblues.ice.infomaniak.ch/jazzblues-high.mp3",
  "jp1": "https://listen.moe/stream",
  "relax1": "http://stream.srg-ssr.ch/m/rsc_de/mp3_128",
  "study1": "http://stream.zeno.fm/0r0xa792kwzuv",
  "tw1": "http://59.120.88.155:8000/live.mp3",
  "tw2": "https://stream.pbs.gov.tw/live/TPS/playlist.m3u8",
  "tw3": "https://mangoradio.stream.laut.fm/mangoradio",
  "tw4": "https://azuracast.conceptradio.fr/radio/8030/stream.mp3",
  "tw5": "https://n10.rcs.revma.com/h4ta80hbqwuvv",
  "us1": "http://stream-uk1.radioparadise.com/aac-320",
  "us2": "https://icecast.walmradio.com:8443/classic"
}

设置权限:

chown 10001:10001 /root/data/docker_data/matrix-musicbot/data/radio_aliases.json
chmod 644 /root/data/docker_data/matrix-musicbot/data/radio_aliases.json
python3 -m json.tool /root/data/docker_data/matrix-musicbot/data/radio_aliases.json >/dev/null

以后新增或删除电台,只改这个 JSON 文件,不需要再改 bot.py。JSON 的 key 不需要带 !,房间里输入 tw1!tw1 都能触发。

先从镜像里复制原始文件:

docker cp matrix-musicbot:/app/bot.py \
  /root/data/docker_data/matrix-musicbot/patches/bot.py

如果容器还没启动过,也可以先临时启动一次,复制后再重建。

编辑:

vi /root/data/docker_data/matrix-musicbot/patches/bot.py

补丁的目标不是把短命令简单转成 !play,而是让别名统一进入安全换台逻辑:先停止旧直播流、清空队列,再启动新电台。否则正在播放直播流时再次点台,容易回到下载或排队路径。

bot.py 顶部补充:

import json

并在 logger = logging.getLogger(__name__) 后加入:

RADIO_ALIASES_FILE = Path("data/radio_aliases.json")

handle_command 前添加读取别名的方法:

def _load_radio_aliases(self) -> dict[str, str]:
    try:
        raw_aliases = json.loads(RADIO_ALIASES_FILE.read_text(encoding="utf-8"))
    except FileNotFoundError:
        return {}
    except Exception as exc:
        logger.warning("Failed to read radio aliases from %s: %s", RADIO_ALIASES_FILE, exc)
        return {}

    if not isinstance(raw_aliases, dict):
        logger.warning("Radio aliases file %s must contain a JSON object", RADIO_ALIASES_FILE)
        return {}

    aliases: dict[str, str] = {}
    for raw_name, raw_url in raw_aliases.items():
        if not isinstance(raw_name, str) or not isinstance(raw_url, str):
            continue
        name = raw_name.strip().lower()
        url = raw_url.strip()
        if not name or not url:
            continue
        aliases[name[1:] if name.startswith("!") else name] = url
    return aliases

找到 handle_command 里的:

command = aliases.get(command, command)
handler = self._command_handlers.get(command)

在它前面加入:

alias_name = command[1:] if command.startswith("!") else command
radio_aliases = self._load_radio_aliases()
if alias_name in radio_aliases:
    # 电台别名来自 data/radio_aliases.json;新增 jp1/tw3 时不改代码,且换台必须走安全切流逻辑。
    await self._switch_radio_stream(room, radio_aliases[alias_name])
    return

再改 !load 的处理逻辑。找到:

if not tracks:
    await self.send_message(room.room_id, f"❌ Saved queue `{name}` is empty")
    return

if not await self._join_call_for_room(room.room_id):

在中间加入:

saved_sources = [
    track.get("source_url")
    for track in tracks
    if isinstance(track, dict) and track.get("source_url")
]
if len(saved_sources) == 1 and self.audio_queue.looks_like_url(saved_sources[0]):
    # 直播电台没有自然结束点;单条 URL 保存队列直接走 !play 的流式路径,避免卡在下载完整音频。
    await self._handle_command_internal(room, "!play", saved_sources[0])
    return

检查语法:

python3 -m py_compile /root/data/docker_data/matrix-musicbot/patches/bot.py

这个补丁做了两件事:

  • tw1!tw1jp1!jp1 这类别名都从 data/radio_aliases.json 读取,不用每次输入长链接。
  • 单条 URL 保存队列执行 !load 时,自动走 !play 的流式播放路径,避免直播流卡在 Loading...

七、为 Podcast 增加最新一集别名

Podcast 和直播电台不是一类音源。直播电台是持续不断的流,适合放在 radio_aliases.json;Podcast 是有限长度单集音频,适合放在单独的 podcast_aliases.json

创建:

vi /root/data/docker_data/matrix-musicbot/data/podcast_aliases.json

示例:

{
  "blg": "https://feed.firstory.me/rss/user/cmjaz594i0000hdvpdpnd4fw8",
  "twpo1": "https://feed.firstory.me/rss/user/ckga7ibs77fgl0875bxwgl0y0",
  "twpo2": "https://feed.firstory.me/rss/user/cknb77ey7f2ob0853hehms3e9",
  "dateline": "https://podcastfeeds.nbcnews.com/dateline-true-crime-weekly"
}

这里的 blg 是百靈果 News 的 Podcast 别名,twpo1twpo2 是另外两个台湾 Podcast RSS,dateline 是 Dateline True Crime Weekly。机器人收到 blg!blg 后,会读取 RSS,取最新一集的 enclosure 音频地址,然后停止当前播放、清空队列,再立即播放这一集。

新增 Podcast 前,先在服务器上测试 RSS 能否访问、能否解析出音频 enclosure。如果某个 RSS 在服务器上出现 TLS、geofence、403 或没有音频 enclosure,就不要写入别名文件,避免房间里输入后报错。

设置权限并检查 JSON:

chown 10001:10001 /root/data/docker_data/matrix-musicbot/data/podcast_aliases.json
chmod 644 /root/data/docker_data/matrix-musicbot/data/podcast_aliases.json
python3 -m json.tool /root/data/docker_data/matrix-musicbot/data/podcast_aliases.json >/dev/null

用法:

blg       播放百靈果 News 最新一集
!blg      同上
blg 3     播放第 3 新的一集
twpo1     播放 twpo1 最新一集
twpo2 3   播放 twpo2 第 3 新的一集
dateline  播放 Dateline 最新一集

Podcast 别名和电台别名一样按“输入即切换”处理,不会排在当前长节目后面慢慢等。已经下载过的单集仍受 1GB LRU 缓存管理,下次播放同一集时尽量复用本地缓存。

继续添加新的 Podcast 主播时,只要给 podcast_aliases.json 增加一组短名和 RSS 地址:

{
  "blg": "https://feed.firstory.me/rss/user/cmjaz594i0000hdvpdpnd4fw8",
  "twpo1": "https://feed.firstory.me/rss/user/ckga7ibs77fgl0875bxwgl0y0",
  "twpo2": "https://feed.firstory.me/rss/user/cknb77ey7f2ob0853hehms3e9",
  "dateline": "https://podcastfeeds.nbcnews.com/dateline-true-crime-weekly",
  "newpod": "https://example.com/new-podcast/rss"
}

检查 JSON 后重启机器人:

python3 -m json.tool /root/data/docker_data/matrix-musicbot/data/podcast_aliases.json >/dev/null
cd /root/data/docker_data/matrix-musicbot
docker compose restart matrix-musicbot

之后在房间里输入:

newpod       播放新 Podcast 最新一集
!newpod      同上
newpod 3     播放第 3 新的一集

注意这里要填 Podcast RSS/feed 地址,不是 Apple Podcasts、Spotify、YouTube 或 Firstory 的普通页面地址。RSS 常见形式类似:

https://feed.firstory.me/rss/user/xxxx
https://feeds.buzzsprout.com/xxxx.rss
https://anchor.fm/s/xxxx/podcast/rss

如果 RSS 里没有音频 enclosure,机器人会提示 Podcast 解析失败;这通常表示该平台没有在 RSS 中公开 MP3,或 feed 临时不可访问。

实际测试中,百靈果 News 的 RSS 大约 9MB。补丁里的 RSS 读取上限建议至少保留 16MB,并在超过上限时明确报错;不要只读前几 MB,否则 XML 会被截断,可能出现 unclosed CDATA section 这类解析错误。

八、启动和验证

如果使用 Docker 部署包,先在项目目录里跑一次本地校验:

./scripts/validate.sh

它会检查增强补丁语法、JSON 示例文件,以及 docker-compose.yaml 是否能被 Compose 正常解析。手工部署时也建议至少执行:

cd /root/data/docker_data/matrix-musicbot
python3 -m py_compile patches/bot.py
python3 -m json.tool data/radio_aliases.json >/dev/null
python3 -m json.tool data/podcast_aliases.json >/dev/null
docker compose config >/dev/null

启动:

cd /root/data/docker_data/matrix-musicbot
docker compose up -d

检查容器:

docker compose ps
docker logs --tail=100 matrix-musicbot

正常日志类似:

Music Bot Core Starting
Startup checks passed
Bot ready

如果看到:

PermissionError: [Errno 13] Permission denied: '/app/config/config.toml'

说明配置文件或目录权限不对。确认:

chmod 755 /root/data/docker_data/matrix-musicbot/config
chmod 644 /root/data/docker_data/matrix-musicbot/config/config.toml

如果看到:

PermissionError: [Errno 13] Permission denied: '/app/logs/musicbot.log'

说明日志目录没有给 UID 10001 写入权限:

chown -R 10001:10001 \
  /root/data/docker_data/matrix-musicbot/logs \
  /root/data/docker_data/matrix-musicbot/data \
  /root/data/docker_data/matrix-musicbot/cache

生产机当前容器正常时,docker compose ps 应看到 matrix-musicbot 处于 Up / healthy 状态。日志里如果已经在通话中播放,可能会持续出现 MatrixRTC membership 心跳,这不等于启动失败。

九、日常使用

先把机器人邀请进房间:

@musicbot:666666.xyz

如果 auto_accept_invites = true,它会自动接受邀请。测试命令:

!help

播放电台最简单,带不带 ! 都可以:

tw1

或:

!tw2

你自己的 Element 客户端必须点击右上角“加入”进入通话,才能听到声音。机器人回:

Joined Element Call
Now playing: live

表示机器人已经进通话并开始推音频。

!join 只表示让机器人加入通话,不会让你的客户端加入通话。通常听电台不需要手动发 !join,因为 !tw1 / !tw2 会自动触发机器人加入。

完整命令速查:

基础播放
!help / !h                 查看帮助
!join / !j                 让机器人加入当前房间的 Element Call
!leave / !lv               让机器人离开 Element Call
!play URL                  播放一个音频 URL
!play 搜索词               让 yt-dlp 搜索并播放
!queue / !q                查看当前队列和预计时间
!nowplaying / !np          查看当前播放
!skip / !s                 跳过当前曲目
!stop / !x                 停止播放并清空队列
!loop / !lp                切换循环模式
!history / !hist           查看最近播放历史

电台别名
!stations / !radios        查看可用电台别名
tw1 / !tw1                 播放 tw1 电台
tw2 / !tw2                 播放 tw2 电台
jp1 / !jp1                 播放 jp1 电台
study1 / !study1           播放 study1 电台
relax1 / !relax1           播放 relax1 电台
us1 / !us1                 播放 us1 电台

Podcast 别名
!podcasts / !pods          查看可用 Podcast 别名
blg / !blg                 播放百靈果 News 最新一集
blg 3 / !blg 3             播放百靈果 News 第 3 新的一集
twpo1 / !twpo1             播放 twpo1 最新一集
dateline / !dateline       播放 Dateline 最新一集

保存队列
!save 名字                 保存当前播放和后续队列
!save 名字 --force         覆盖同名保存队列
!load 名字                 加载保存队列并自动加入通话
!queues / !qs              列出当前房间保存过的队列名
!deletequeue 名字 / !dq 名字 删除保存队列
!renamequeue 旧名 新名 / !rq 旧名 新名 重命名保存队列

音频和诊断
!audio / !a                查看当前音频设置
!normalize on|off          开关音量归一化
!fadein 300                设置淡入毫秒数,范围 0-5000
!volume 80 / !v 80         设置音量百分比,范围 0-200
!status / !st              查看机器人状态
!diag / !d                 查看诊断信息
!config / !cfg             查看当前配置摘要
!defaults / !df            查看默认配置值

保存一组临时歌单

data/saved_queues.json 就是给 !save / !load 用的运行状态文件。它不是电台配置,也不是 Podcast 配置;它只记录某个房间里用户保存过的队列。

举个例子,先排一组晚饭歌单:

!play https://example.com/song-a.mp3
!play https://example.com/song-b.mp3
!play https://example.com/song-c.mp3
!queue
!save dinner

这时机器人会把当前正在播的曲目和后续队列保存成 dinner。以后想重新播放这组临时歌单:

!load dinner

查看保存过哪些队列:

!queues

覆盖同名队列:

!save dinner --force

改名:

!renamequeue dinner dinner-jazz

删除:

!deletequeue dinner-jazz

短写也可以:

!sv dinner --force
!ld dinner
!qs
!rq dinner dinner-jazz
!dq dinner-jazz

这个文件按房间分组保存。A 房间里的 dinner 和 B 房间里的 dinner 不是同一个队列。它会包含真实房间 ID 和历史播放 URL,所以属于运行状态文件,不适合写进恢复仓库,也不适合作为公开教程里的固定模板。

这里要分清三个名字来源:

  • data/radio_aliases.json 里的 jp2 是电台别名。房间里直接输入 jp2!jp2,会安全换台或播放。
  • data/podcast_aliases.json 里的 blg 是 Podcast 别名。房间里直接输入 blgblg 3,会解析 RSS 最新或第 3 新的一集。
  • !save jp2 保存的是 MusicBot 的 saved queue。房间里直接输入 jp2 不会加载这个队列,必须用 !load jp2

所以如果新增的是直播电台,推荐写进 data/radio_aliases.json

{
  "tw1": "http://59.120.88.155:8000/live.mp3",
  "tw2": "https://stream.pbs.gov.tw/live/TPS/playlist.m3u8",
  "jp2": "https://example.com/jp2/live.mp3"
}

这样后续直接输入:

jp2

就会按直播电台的安全换台逻辑处理。!save 更适合普通歌曲或有限长度音频队列,不建议把它当作电台别名管理方式。

注意:删除 saved queue 里的 tw1 不会删除电台别名。要取消电台别名,需要从 data/radio_aliases.json 删除对应项,然后重启或重建 matrix-musicbot

十、推荐操作顺序

听第一个电台:

tw1

听第二个电台:

!tw2

换台:

!stop
tw2

退出:

!leave

如果机器人卡在直播流加载或命令无响应,可以在服务器上重启它:

cd /root/data/docker_data/matrix-musicbot
docker compose restart matrix-musicbot

这只影响 matrix-musicbot 容器,不影响 Synapse、Postgres、LiveKit、coturn、Element Call、Nginx Proxy Manager 等其它容器。

十一、恢复仓库模板

如果把它纳入 /root/data/docker_data 恢复蓝图,建议至少保存这些文件:

docker_data/matrix-musicbot/docker-compose.yaml
docker_data/matrix-musicbot/config/config.toml
docker_data/matrix-musicbot/data/radio_aliases.json
docker_data/matrix-musicbot/data/podcast_aliases.json
docker_data/matrix-musicbot/patches/bot.py

不要保存这些运行产物:

docker_data/matrix-musicbot/config/config.toml 里的真实 access_token
docker_data/matrix-musicbot/logs/
docker_data/matrix-musicbot/cache/
docker_data/matrix-musicbot/data/saved_queues.json
docker_data/matrix-musicbot/data/*.state.json
docker_data/matrix-musicbot/patches/__pycache__/
*.pyc

config.toml 里不要提交真实 token,可以用环境变量占位:

access_token = "${MATRIX_MUSICBOT_ACCESS_TOKEN}"

恢复脚本应创建运行目录并修正 UID:

mkdir -p \
  /root/data/docker_data/matrix-musicbot/logs \
  /root/data/docker_data/matrix-musicbot/data \
  /root/data/docker_data/matrix-musicbot/cache

chown -R 10001:10001 \
  /root/data/docker_data/matrix-musicbot/logs \
  /root/data/docker_data/matrix-musicbot/data \
  /root/data/docker_data/matrix-musicbot/cache

启动顺序上,先启动 Synapse、LiveKit、Matrix LiveKit JWT Service,再启动 matrix-musicbot。它不需要独立开放端口,只需要能访问 Matrix client API 和 MatrixRTC / LiveKit 配置。

我的恢复仓库里对应模板目前应保持这几类文件:docker-compose.yaml、带 ${MATRIX_MUSICBOT_ACCESS_TOKEN} 占位的 config/config.tomldata/radio_aliases.jsondata/podcast_aliases.jsonpatches/bot.py。恢复脚本要创建 logsdatacache 并把可写目录交给 UID 10001,否则容器会启动但写日志或缓存时报权限错误。