Files
teamspeak-music-bot/docs/API.md
T

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 次的限流。