sync/docs/bot-api.md
2026-05-04 16:07:59 +02:00

12 KiB
Raw Blame History

Bot API

Bots connect to a channel in two ways:

  • WebSocket (socket.io) — real-time events: chat, user list, playlist changes, media updates
  • REST (/api/v1/...) — commands: queue/delete/shuffle/clear playlist, manage emotes, read/write settings, kick/ban users

Chat can only be sent and received over the WebSocket. The REST API has no chat endpoint.


Ranks

Name Value
Moderator 2
Admin 3
Owner 4
Creator 5

A bot's rank is capped at the rank of the user who issued its token. Every REST endpoint that modifies state has a minimum rank requirement listed in its description.


Issuing a token

Tokens are issued in the channel settings modal on the Bots tab. You need at least moderator rank (2) to see this tab.

Fill in a name (120 alphanumeric, -, _ characters), choose a rank, and click Issue Token. The token is shown once — copy it immediately. It looks like:

cbt_a3f8e2...64 hex characters...

To revoke a token, click Revoke next to it in the table. Any live WebSocket connections using that token are disconnected immediately.


WebSocket connection

The bot authenticates by passing the token in the socket.io auth object:

const io = require('socket.io-client');

const socket = io('http://your-server:1337', {
    auth: { token: process.env.BOT_TOKEN }
});

Port note: socket.io runs on the port defined by io.port in your server config (default 1337), which is separate from the HTTP port used for the web UI and REST API.

Join sequence

After connecting you must wait for the login event before emitting joinChannel, otherwise the server will ignore it:

socket.once('login', () => {
    socket.emit('joinChannel', { name: 'your-channel-name' });
});

Sending chat

socket.emit('chatMsg', { msg: 'Hello from a bot!' });
// Action message:
socket.emit('chatMsg', { msg: '/me waves' });

Events the bot receives

Event Payload Description
login { success, name, guest } Server accepted the connection
chatMsg { username, msg, meta, time } A chat message was sent
userlist [{ name, rank, meta }, ...] Full user list on join
addUser { name, rank, meta } A user joined the channel
userLeave { name } A user left the channel
setUserMeta { name, meta } A user's meta (AFK, muted) changed
changeMedia { id, type, title, seconds, ... } A new video started playing
playlist [{ uid, media, queueby, temp }, ...] Full playlist on join
queue { item, after } An item was added to the playlist
delete { uid } A playlist item was removed
errorMsg { msg } An error from the server
kick { reason } The bot was kicked
announcement { title, text } Server-wide announcement

meta.is_bot is set to true in chatMsg and user list payloads for bots, which the web UI renders as a [bot] badge.


REST API

Base URL

http://your-server:8080/api/v1

All channel-scoped endpoints are under /channels/:channel/.

Authentication

Every REST request must include the bot token as a Bearer token:

Authorization: Bearer cbt_...

The token is validated against the channel in the URL — a token issued for #general will be rejected for /channels/gaming/....

Error responses

All errors return JSON:

{ "error": "Human readable message" }

Common status codes:

Code Meaning
400 Bad request (missing/invalid fields)
401 Missing or invalid token
403 Token not authorized for this channel, or rank too low
404 Resource not found
503 Channel is not currently active (no users in it)

Playlist

Playlist endpoints require the channel to be active (at least one user present).

GET /channels/:channel/playlist

Returns the current playlist and which item is playing.

No minimum rank.

Response:

{
  "items": [
    {
      "uid": 1,
      "id": "dQw4w9WgXcQ",
      "type": "yt",
      "title": "Rick Astley - Never Gonna Give You Up",
      "seconds": 212,
      "duration": "3:32",
      "thumb": { "url": "..." },
      "meta": {}
    }
  ],
  "currentIndex": 0,
  "locked": false
}

uid is the server-assigned unique ID for the playlist slot. Use it for delete/jump operations.

POST /channels/:channel/playlist

Queue a new item. Minimum rank: 2 (Mod).

Returns 202 Accepted immediately because media lookup is asynchronous. If the bot's permissions don't allow the add (e.g. playlist is locked and rank is too low), a 400 is returned synchronously.

Body:

{ "id": "dQw4w9WgXcQ", "type": "yt", "pos": "end" }
Field Required Values Default
id yes media ID string
type yes see media types below
pos no "end" or "next" "end"

Media types:

Type Source
yt YouTube
sc SoundCloud
tw Twitch stream
tc Twitch clip
rt Dailymotion
vm Vimeo
dm Dailymotion
gd Google Drive
fi Direct file URL
cu Custom embed (HTML)

DELETE /channels/:channel/playlist/:uid

Remove a playlist item by uid. Minimum rank: 3 (Admin).

PUT /channels/:channel/playlist/playing

Skip to a specific item. Minimum rank: 2 (Mod).

Body:

{ "uid": 3 }

POST /channels/:channel/playlist/shuffle

Shuffle the playlist. Minimum rank: 3 (Admin).

POST /channels/:channel/playlist/clear

Clear the entire playlist. Minimum rank: 3 (Admin).


Emotes

Emote endpoints read and write directly to the database and work even when the channel is offline. If the channel happens to be active, changes are broadcast live to connected users.

GET /channels/:channel/emotes

Returns the full emote list. No minimum rank.

Response:

[
  { "name": "KEKW", "image": "https://cdn.example.com/kekw.png", "source": "..." }
]

POST /channels/:channel/emotes

Add a new emote. Minimum rank: 4 (Owner).

Body:

{ "name": "KEKW", "image": "https://cdn.example.com/kekw.png" }

Returns 409 Conflict if the name is already taken.

PUT /channels/:channel/emotes/:name

Update an emote's image or rename it. Minimum rank: 4 (Owner).

Body (all fields optional, but at least one must differ):

{ "image": "https://cdn.example.com/kekw-v2.png", "newName": "KEKWait" }

Omit newName to keep the existing name. Returns 409 if the new name is already taken.

DELETE /channels/:channel/emotes/:name

Delete an emote by name. Minimum rank: 4 (Owner).


Users / Moderation

These endpoints require the channel to be active.

GET /channels/:channel/users

List currently connected users. No minimum rank.

Response:

[
  { "name": "Alice", "rank": 3, "afk": false, "is_bot": false },
  { "name": "MyBot",  "rank": 2, "afk": false, "is_bot": true  }
]

POST /channels/:channel/users/:name/kick

Kick a user. Minimum rank: 2 (Mod). Cannot kick users with equal or higher rank.

Body:

{ "reason": "Spamming" }

reason is optional, defaults to "Kicked by bot".

POST /channels/:channel/users/:name/ban

Ban a user by name. Minimum rank: 3 (Admin). Cannot ban users with equal or higher rank.

Body:

{ "reason": "Ban evasion" }

reason is optional, defaults to "Banned by bot".

DELETE /channels/:channel/users/:name/ban

Remove a ban. Minimum rank: 3 (Admin).

PUT /channels/:channel/users/:name/rank

Change a user's channel rank. Minimum rank: 4 (Owner). Cannot assign a rank equal to or higher than your own, and cannot target users with equal or higher rank.

Body:

{ "rank": 2 }

Settings

Settings endpoints require the channel to be active. Minimum rank: 4 (Owner) for both read and write.

GET /channels/:channel/settings

Returns current channel options.

Response:

{
  "pagetitle": "My Channel",
  "allow_voteskip": true,
  "voteskip_ratio": 0.5,
  "maxlength": 0,
  "afk_timeout": 600,
  "password": "",
  "show_public": false,
  ...
}

Available keys:

allow_voteskip, allow_dupes, voteskip_ratio, maxlength, playlist_max_duration_per_user, afk_timeout, enable_link_regex, chat_antiflood, chat_antiflood_burst, chat_antiflood_sustained, new_user_chat_delay, new_user_chat_link_delay, pagetitle, password, externalcss, externaljs, show_public, torbanned, block_anonymous_users, allow_ascii_control, playlist_max_per_user

PUT /channels/:channel/settings

Update one or more settings. Unknown keys are silently ignored.

Body:

{ "pagetitle": "Now Playing: Chill Beats", "allow_voteskip": false }

Bot management

These endpoints use session cookie auth (the normal logged-in web session), not a bot token. They are intended for the channel settings UI.

GET /channels/:channel/bots

List all bots for the channel. Returns id, name, rank, creator, creation time, last connection time. Requires channel rank ≥ 2.

POST /channels/:channel/bots

Issue a new bot token. Requires channel rank ≥ 2. The returned token is shown once and not stored.

Body:

{ "name": "MyBot", "rank": 2 }

Response:

{ "id": 7, "name": "MyBot", "rank": 2, "token": "cbt_..." }

DELETE /channels/:channel/bots/:id

Revoke a bot token. Any live socket connections for that bot are disconnected immediately. Requires channel rank ≥ 2 and your rank must be ≥ the bot's rank.


Demo bot

A working example with a TUI is in examples/demo-bot/.

cd examples/demo-bot
npm install
cp .env.example .env
# Edit .env: fill in BOT_TOKEN, CHANNEL, SERVER_URL, API_BASE
node bot.js

.env fields:

Variable Description Default
BOT_TOKEN Bot token from the channel settings Bots tab
CHANNEL Channel name (lowercase, no #)
SERVER_URL socket.io URL (use the io.port, not the HTTP port) http://localhost:1337
API_BASE Base URL for REST requests http://localhost:8080/api/v1

TUI keybindings: Enter to send, PgUp/PgDn to scroll, Ctrl-C to quit.

Bot commands (prefix with / in the input box):

Command Description
/help List commands
/playlist Show playlist via REST
/emotes List emotes via REST
/settings Show channel settings via REST
/users Show connected user list
/add <type> <id> Queue media, e.g. /add yt dQw4w9WgXcQ
/skip Skip to next item
/clear Clear the playlist
/kick <name> [reason] Kick a user
/me <text> Send an action message