18 KiB
REST API 参考
本文档列出机器人对外提供的全部 REST API 端点、参数与返回。所有端点均支持两种认证方式(见下),除单独标注「仅浏览器 session」的端点外。
通用约定
认证
Authorization: Bearer tsmb_xxxxxxxxxxxx
# 或
X-API-Key: tsmb_xxxxxxxxxxxx
Key 在 WebUI 设置页创建,权限与所属账户一致。除标注「仅浏览器 session」的端点外,API Key 与浏览器 session(cookie)使用相同的账户权限。
管理员 API Key 保留完整的 REST 管理权限,包括 /api/users 的创建用户、重置密码与权限变更;因此也可以创建新的可登录账户。/api/keys 的 session 限制只约束直接密钥管理,不能作为管理员 Key 的权限隔离措施。
密钥吊销与密码变更
API Key 没有自动到期时间,可在设置页随时吊销。删除账户会同时删除其全部 Key。成功修改自己的密码或由管理员重置密码,都会吊销该账户的全部 Key;依赖这些 Key 的外部集成需要重新生成并更新凭据。失败的密码变更不会吊销 Key。
修改自己的密码会保留当前浏览器 session,使其余 session 失效。管理员重置其他账户的密码会使目标账户的全部 session 失效;重置自己的密码时同样保留当前浏览器 session。
错误格式
所有错误返回统一为 JSON { "error": "..." }:
| 状态码 | 含义 |
|---|---|
| 400 | 参数缺失或格式错误 |
| 401 | 未认证 / API Key 无效(invalid api key) |
| 403 | 无权限(能力不足、机器人未授权、API Key 试图管理 Key 等) |
| 404 | 资源不存在 |
| 409 | 冲突(如收藏已存在、Key 数量达上限) |
| 500 | 服务器内部错误 |
权限模型
| 标注 | 含义 |
|---|---|
| 公开 | 无需认证 |
| 已认证 | 任意登录用户 / 有效 API Key |
| 非游客 | API Key 用户恒满足(guest session 除外) |
player.control / player.queue / bot.manage / platform.auth / quality |
需要账户持有对应能力;管理员恒通过 |
| 机器人访问 | 成员只能操作被授予的机器人(账户权限中的 bot 范围),管理员不限 |
| 管理员 | 仅 role=admin |
平台(platform)取值
netease / qq / bilibili / youtube / kugou / jellyfin / local / spotify
省略 platform 时使用设置页配置的默认音源;已禁用的音源返回 400 音源未启用。
点歌归属
/play、/add、/play-*、/add-* 等入队端点会把 requestedBy 记为 Key 所属账户的用户名,队列与播放历史中可见。
数据模型
// 歌曲(搜索结果 / 队列元素)
interface Song {
id: string; // 平台内歌曲 id
name: string;
artist: string;
album: string;
duration: number; // 秒
coverUrl: string;
platform: Platform;
vip?: boolean; // VIP/版权受限(仅试听)
}
// 队列中的歌曲(Song + 归属;url 仅播放时内部解析,不出现在响应里)
interface QueuedSong extends Omit<Song, "vip"> {
requestedBy?: string;
}
interface Album { id: string; name: string; artist: string; coverUrl: string; songCount: number; platform: Platform }
interface Playlist { id: string; name: string; coverUrl: string; songCount: number; platform: Platform }
// 机器人实时状态
interface BotStatus {
id: string;
name: string;
connected: boolean;
playing: boolean;
paused: boolean;
currentSong: QueuedSong | null;
queueSize: number;
volume: number; // 0-100
playMode: "seq" | "loop" | "random" | "rloop";
elapsed: number; // 当前曲目已播秒数
effectiveDuration?: number; // 当前曲实际播放时长(试听片段=试听秒数)
}
公开端点(无需认证)
GET /api/health
{ "status": "ok", "version": "0.1.0" }
GET /api/config/public-url
{ "publicUrl": "https://bot.example.com" } // 未配置时为 null
机器人管理 /api/bot
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/bot |
已认证 | 机器人列表(成员只返回被授权的) |
| GET | /api/bot/settings |
非游客 | 全局行为设置 |
| POST | /api/bot/settings |
bot.manage |
保存全局设置(部分合并) |
| POST | /api/bot |
bot.manage |
创建机器人 |
| GET | /api/bot/:id |
机器人访问 | 单个机器人状态 |
| PUT | /api/bot/:id |
bot.manage + 机器人访问 |
更新连接配置 |
| DELETE | /api/bot/:id |
bot.manage + 机器人访问 |
删除机器人 |
| POST | /api/bot/:id/start |
bot.manage + 机器人访问 |
连接服务器 |
| POST | /api/bot/:id/stop |
bot.manage + 机器人访问 |
断开连接 |
| GET | /api/bot/:id/config |
bot.manage + 机器人访问 |
保存的连接配置(不含 identity/TS6 key) |
| GET / PUT / DELETE | /api/bot/:id/avatar |
bot.manage + 机器人访问 |
自定义头像 |
GET /api/bot
{ "bots": [ { "id": "…", "name": "客厅bot", "connected": true, "playing": true, "paused": false,
"currentSong": { "…": "QueuedSong" }, "queueSize": 3, "volume": 75,
"playMode": "seq", "elapsed": 42.5, "effectiveDuration": 269 } ] }
POST /api/bot
// 请求体(name、serverAddress、nickname 必填;serverPort 默认 9987)
{ "name": "客厅bot", "serverAddress": "ts.example.com", "serverPort": 9987,
"nickname": "♪ 音乐机器人", "defaultChannel": "音乐频道", "channelId": "12",
"channelPassword": "", "serverPassword": "", "autoStart": true }
// 201 返回 BotStatus
PUT /api/bot/:id
请求体字段同上(全部可选),返回 { "success": true }。连接相关修改需重启机器人(stop 后 start)生效。
POST /api/bot/settings(部分合并,未传的字段不变)
{
"idleTimeoutMinutes": 30, // 空闲自动断开,0=不启用
"autoPauseOnEmpty": true, // 频道无人自动暂停
"localAudioEnabled": true, // 本地音频
"voiceDucking": { "enabled": true, "volumePercent": 20 },
"savedQueuesEnabled": true,
"playKeepsQueue": false, // !play 是否保留队列
"adminGroups": [6],
"enabledProviders": ["netease","qq","bilibili","youtube","kugou"],
"defaultPlatform": "netease", // null/"" 清除
"guestMode": { "enabled": false, "bots": "all", "permissions": { "…": true } },
"spotify": { "enabled": false, "clientId": "…", "clientSecret": "…", "backend": "auto", "bitrate": 160, "deviceName": "…" },
"jellyfin": { "serverUrl": "…", "authMode": "userpass", "username": "…", "password": "…" }
}
// 返回:与 GET /settings 相同结构(spotify.clientSecret / jellyfin.password 永不回传,仅 hasClientSecret / hasPassword 布尔)
PUT /api/bot/:id/avatar
请求体 { "dataUrl": "data:image/png;base64,…" }(png/jpeg/webp,≤200KB),返回 { "path": "avatars/xx.png" }。
播放控制 /api/player/:botId
以下所有端点都要求机器人访问权限;标注能力的管理类操作还需对应能力。{ "message": "…" } 为命令执行回执文本(与聊天命令回执一致),失败时 message 中带原因或返回 4xx/5xx。
播放入口
| 方法 | 路径 | 能力 | 请求体 | 返回 |
|---|---|---|---|---|
| POST | /play |
player.control |
{ query, platform? }(搜索文本) |
{ message } |
| POST | /add |
player.queue |
{ query, platform? } |
{ message } |
| POST | /play-song |
player.control |
{ song }(Song 对象,清空队列播放) |
{ ok, message } |
| POST | /play-now-song |
player.control |
{ song }(插入当前曲后立即播放,保留队列) |
{ ok, message } |
| POST | /play-next-song |
player.control |
{ song }(插播下一首;空闲时直接播放) |
{ ok, message } |
| POST | /add-song |
player.queue |
{ song }(入队;空闲时立即播放) |
{ message } |
| POST | /add-by-id |
player.queue |
{ songId, platform? } |
{ message } |
| POST | /play-playlist |
player.control |
{ playlistId, platform? }(清队列载入歌单) |
{ ok, message } |
| POST | /play-album |
player.control |
{ albumId, platform? }(清队列载入专辑) |
{ ok, message } |
| POST | /playlist |
player.queue |
{ playlistId, platform? }(追加整个歌单) |
{ message } |
| POST | /fm |
player.control |
{ platform? }(私人 FM 模式) |
{ ok, message } |
/play 与 /add 接受搜索文本,内部按 platform 调对应音源搜索并播放/入队第一个结果;/play-song 系列接受 /api/music 返回的完整 Song 对象。B站多P视频的 Song id 形如 BVxxxx?p=2(见 /api/music/bilibili/parts),传对应分P的 id 即播放该分P。
/fm 的平台为网易时,若调用者账户已绑定个人网易账号(见 /api/me/music),FM 曲目按个人账号的口味推荐;未绑定则使用机器人共享登录。
播放器控制
| 方法 | 路径 | 能力 | 请求体 | 返回 |
|---|---|---|---|---|
| POST | /pause |
player.control |
— | { message } |
| POST | /resume |
player.control |
— | { message } |
| POST | /next |
player.control |
— | { message } |
| POST | /prev |
player.control |
— | { message } |
| POST | /stop |
player.control |
— | { message } |
| POST | /clear |
player.queue |
— | { message } |
| POST | /volume |
player.control |
{ volume: 0-100 } |
{ message } |
| POST | /mode |
player.control |
`{ mode: "seq" | "loop" |
| POST | /seek |
player.control |
{ position: 秒 } |
{ message, seekOffset } |
| POST | /play-at |
player.control |
{ index: 队列下标 } |
{ message },越界 400 |
状态与队列
| 方法 | 路径 | 返回 |
|---|---|---|
| GET | /queue |
{ queue: QueuedSong[], status: BotStatus } |
| GET | /elapsed |
{ elapsed: 42.5 } |
| DELETE | /queue/:index |
{ message }(移除指定下标,能力 player.queue) |
| GET | /history?limit=50 |
{ history: [{ id, name, artist, album, coverUrl, platform, playedAt, requestedBy }] } |
| GET | /profile |
ProfileConfig |
| PUT | /profile |
ProfileConfig(能力 bot.manage) |
ProfileConfig:{ avatarEnabled, descriptionEnabled, nicknameEnabled, awayStatusEnabled, channelDescEnabled, nowPlayingMsgEnabled }(机器人头像/昵称/频道描述等自动更新开关)。
音乐数据 /api/music
除特别标注外均为「已认证」;platform 为可选 query 参数。
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /search |
q(必填)、platform、limit(默认 20)、offset(默认 0) |
{ songs, albums, playlists } |
| GET | /search/all |
q(必填)、limit |
各音源合并的 { songs, albums, playlists }(不含 spotify) |
| GET | /song/:id |
platform |
Song 对象,无则 404 |
| GET | /album/:id |
platform |
{ songs: Song[] } |
| GET | /playlist/:id |
platform |
{ songs: Song[] } |
| GET | /playlist/:id/detail |
platform |
{ playlist: { id, name, description, coverUrl, songCount } }(音源不支持时 501) |
| GET | /lyrics/:id |
platform |
{ lyrics } |
| GET | /recommend/playlists |
platform |
{ playlists } |
| GET | /recommend/songs |
platform |
{ songs }(每日推荐;非游客) |
| GET | /personal/fm |
platform |
{ songs }(私人 FM;非游客) |
| GET | /user/playlists |
platform |
{ playlists }(当前登录音源账号的歌单;非游客) |
| GET | /bilibili/popular |
limit(默认 20) |
{ songs } |
| GET | /bilibili/parts |
bvid(BV 号或视频链接) |
{ bvid, title, coverUrl, artist, parts }(无此视频 404) |
| GET | /providers |
— | { enabled: Platform[], default: Platform } |
| GET | /quality |
— | { netease, qq, bilibili, local, kugou, spotify, jellyfin } |
| POST | /quality |
{ quality, platform? }(能力 quality;省略 platform 时对所有音源生效) |
{ success, quality } |
Jellyfin 音乐库
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /jellyfin/latest-albums |
limit(默认 12) |
{ albums } |
| GET | /jellyfin/most-played |
limit(默认 12) |
{ songs } |
| GET | /jellyfin/favorites |
limit(默认 100) |
{ songs }(非游客) |
| GET | /jellyfin/genres |
limit(默认 30) |
{ genres: [{ id, name }] } |
| GET | /jellyfin/genre/:id/songs |
limit(默认 100) |
{ songs } |
本地音频上传
POST /api/music/local/upload — 能力 player.queue。请求体为原始音频文件(audio/* 或 video/*,≤500MB,非 multipart;文件名放 x-filename 请求头)。返回 { song };本地音频关闭时 403。
curl -X POST -H "X-API-Key: $KEY" -H "x-filename: theme.mp3" \
-H "Content-Type: application/octet-stream" \
--data-binary @theme.mp3 http://127.0.0.1:3000/api/music/local/upload
收藏 /api/favorites(非游客,仅本人数据)
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | / |
— | { favorites: [{ id, platform, playlistId, name, coverUrl, songCount, createdAt }] } |
| POST | / |
{ platform, playlistId, name, coverUrl?, songCount? } |
{ success: true };已收藏 409 |
| GET | /check |
platform、playlistId |
{ favorited: bool } |
| DELETE | /:id |
收藏记录 id | { success: true } |
保存的队列 /api/saved-queues(非游客;需在设置页开启「保存队列」)
所有权:私有为本人,shared: true 保存到共享桶;列表返回本人的+共享的;他人私有队列 404。
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | / |
— | { queues: [{ id, ownerId, name, songCount, createdAt, updatedAt }] } |
| POST | / |
{ botId, name, shared? }(快照该 bot 当前队列,同名覆盖) |
{ queue };队列空 400 |
| POST | /:id/load |
`{ botId, mode?: "replace"(默认) | "append" }` |
| DELETE | /:id |
— | { ok: true } |
平台账号 /api/auth
| 方法 | 路径 | 权限 | 参数 | 返回 |
|---|---|---|---|---|
| GET | /status |
非游客 | platform |
{ platform, loggedIn, nickname?, avatarUrl? } |
| POST | /qrcode |
platform.auth |
{ platform }(netease/qq/bilibili/kugou) |
{ qrUrl, qrImg?(base64 data URL), key } |
| GET | /qrcode/status |
非游客 | key、platform |
`{ status: "waiting" |
| POST | /jellyfin/test |
platform.auth |
{ serverUrl?, authMode?, username?, password?, apiKey?, userId? }(空字段回退已存配置) |
{ ok, serverName?, version?, error? } |
| POST | /sms/send |
platform.auth |
{ phone }(网易手机号登录) |
{ success } |
| POST | /sms/verify |
platform.auth |
{ phone, code } |
{ success } |
| POST | /cookie |
platform.auth |
{ platform, cookie }(不支持 youtube/jellyfin) |
{ success: true } |
Spotify /api/spotify(配置 Spotify OAuth 后挂载)
| 方法 | 路径 | 权限 | 返回 |
|---|---|---|---|
| GET | /login |
platform.auth |
{ url }(accounts.spotify.com 授权页,浏览器打开) |
| GET | /callback |
— | OAuth 回调,重定向回 WebUI(浏览器流程,脚本无需调用) |
| GET | /status |
非游客 | { authorized, backend, deviceName, binaryAvailable } |
个人音乐账号 /api/me/music(非游客,仅本人数据)
绑定自己的网易账号,让 POST /api/player/:botId/fm 按个人口味推荐;cookie 只存服务端,任何接口都不会回传。与 /api/auth 的机器人共享登录互不影响。
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /netease/status |
— | { linked, loggedIn, nickname?, avatarUrl? } |
| POST | /netease/qrcode |
— | { qrUrl, qrImg?(base64 data URL), key }(个人绑定专用二维码) |
| GET | /netease/qrcode/status |
key |
`{ status: "waiting" |
| DELETE | /netease |
— | { ok: true }(解除绑定) |
API 密钥管理 /api/keys(仅浏览器 session)
API Key 不能直接调用这些密钥管理端点(403);游客 session 也被拒绝。浏览器登录后调用。管理员 Key 仍保留上文所述的用户管理权限。
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | / |
?all=1(管理员可看全部,含 username) |
{ keys: [{ id, userId, username?, name, keyPrefix, createdAt, lastUsedAt }] } |
| POST | / |
{ name: "1-64字符" } |
201 { key: {...}, rawKey: "tsmb_…" }(明文仅此一次);达上限 409 |
| DELETE | /:id |
— | { success: true }(仅本人;管理员可删任意) |
用户管理 /api/users(管理员)
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | / |
— | { users: [{ id, username, createdAt, role }] } |
| POST | / |
`{ username, password(≥8位), role: "admin" | "member" }` |
| DELETE | /:id |
— | 204(级联删除其 session 与 API Key) |
| POST | /:id/reset-password |
{ newPassword } |
204(该用户的 API Key 全部失效,session 按上文密码变更规则处理) |
| PATCH | /:id/role |
`{ role: "admin" | "member" }` |
| GET | /:id/permissions |
— | `{ capabilities: string[], bots: "all" |
| PUT | /:id/permissions |
`{ capabilities, bots: "all" | string[] }` |
操作审计 /api/audit(管理员)
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | / |
limit(1-500,默认 100)、offset(默认 0) |
{ entries: [{ id, timestamp, actorId, actorUsername, targetUserId, targetUsername, action }] } |
action 取值:admin.first_created、user.created、user.deleted、user.password_reset、user.password_changed、user.role_changed、user.permissions_changed、api_key.created、api_key.deleted。
会话 /api/session(仅浏览器,API Key 不可用)
会话登录本身无法用 API Key 完成:GET /needs-setup、POST /setup、POST /login、POST /guest、POST /logout、GET /me、POST /change-password 均基于 cookie。/login 有每 IP 每分钟 5 次、/setup 3 次的限流。