7.5 KiB
veretube-bot
Python library for writing bots on sync 4.0 aka veretube channels.
Bots connect over socket.io for real-time events (chat, user list, playlist changes) and use a REST API for commands (queue media, kick/ban, manage emotes, read/write settings). Both surfaces are covered by this library.
Installation
pip install veretube-bot
Getting a token
Tokens are issued in the channel settings modal on the Bots tab. You need at least moderator rank to see it. Fill in a name and rank, click Issue Token, and copy the token immediately — it starts with cbt_ and is only shown once.
Quick start
from veretube_bot import Bot
bot = Bot(
token="cbt_...",
channel="mychannel",
socket_url="http://your-server:1337", # socket.io port (default 1337)
api_url="http://your-server:8080/api/v1", # HTTP port (default 8080)
)
@bot.on("chatMsg")
def on_chat(data):
if data["msg"] == "!hello":
bot.send_message("Hello!", to=data["username"])
if data["msg"] == "!np":
if bot.now_playing:
bot.send_message(f"Now playing: {bot.now_playing['title']}")
@bot.on("changeMedia")
def on_media(data):
print(f"Now playing: {data['title']}")
bot.run() # connect and block until disconnected
Connection
Bot(
token, # str — bot token starting with 'cbt_'
channel, # str — channel name (lowercase, no '#')
socket_url, # str — socket.io server URL (uses io.port, not the HTTP port)
api_url, # str — REST API base URL, e.g. http://host:8080/api/v1
reconnection=False, # set True to reconnect automatically on drop
reconnection_delay=3, # seconds between reconnect attempts
)
| Method | Description |
|---|---|
bot.run() |
Connect and block until disconnected |
bot.connect() |
Open the connection (returns immediately) |
bot.wait() |
Block until disconnected (call after connect()) |
bot.disconnect() |
Close the connection |
By default reconnection=False — the expectation is that an external process supervisor (systemd, supervisord, etc.) handles restarts. Pass reconnection=True for bots that should self-recover.
Event handling
Register handlers with @bot.on(event_name). Multiple handlers for the same event are all called. Exceptions in a handler are logged and skipped so one bad handler doesn't affect the others.
@bot.on("chatMsg")
def on_chat(data):
print(data["username"], data["msg"])
@bot.on("userLeave")
def on_leave(data):
print(f"{data['name']} left")
Socket events
| Event | Payload | Description |
|---|---|---|
connect |
None |
Socket connected |
disconnect |
reason string or None |
Socket disconnected |
connect_error |
error object | Connection failed |
login |
{ success, name, guest } |
Server accepted the token |
chatMsg |
{ username, msg, meta, time } |
Chat message sent |
pm |
{ username, msg, meta } |
Private message received |
userlist |
[{ name, rank, meta }, ...] |
Full user list on join |
addUser |
{ name, rank, meta } |
User joined |
userLeave |
{ name } |
User left |
setUserMeta |
{ name, meta } |
User AFK/muted state changed |
setUserRank |
{ name, rank } |
User rank changed |
changeMedia |
{ id, type, title, seconds, ... } |
New video started |
playlist |
[{ uid, media, queueby, temp }, ...] |
Full playlist on join |
queue |
{ item, after } |
Item added to playlist |
delete |
{ uid } |
Playlist item removed |
channelOpts |
settings dict | Channel options updated |
clearchat |
None |
Chat cleared by a moderator |
errorMsg |
{ msg } |
Error from the server |
kick |
{ reason } |
Bot was kicked |
announcement |
{ title, text } |
Server-wide announcement |
updateEmote |
{ name, image } |
Emote added or changed |
removeEmote |
{ name } |
Emote removed |
meta.is_bot is True in chatMsg and userlist payloads for bots.
In-memory state
These are updated automatically from socket events before your handlers are called:
bot.users # list of { name, rank, meta } — current user list
bot.now_playing # { id, type, title, seconds, ... } or None
bot.playlist # list of playlist items
bot.channel_opts # channel options dict
Look up a specific user:
user = bot.get_user("Alice") # returns dict or None
Chat
bot.send_message("Hello!")
bot.send_message("Hey!", to="Alice") # sends "Alice: Hey!"
bot.send_action("waves") # sends "/me waves"
bot.send_pm("Alice", "Hello privately")
Playlist
bot.queue("dQw4w9WgXcQ", "yt") # add to end (rank >= MOD)
bot.queue("dQw4w9WgXcQ", "yt", "next") # add as next up
bot.skip() # skip to next item (rank >= MOD)
bot.skip_to(uid) # jump to specific uid (rank >= MOD)
bot.delete_item(uid) # remove by uid (rank >= ADMIN)
bot.shuffle_playlist() # shuffle (rank >= ADMIN)
bot.clear_playlist() # clear all (rank >= ADMIN)
Media types: yt YouTube, sc SoundCloud, tw Twitch stream, tc Twitch clip, vm Vimeo, dm Dailymotion, fi direct file URL, cu custom embed.
Moderation
bot.kick("BadUser") # rank >= MOD
bot.kick("BadUser", reason="Spamming")
bot.ban("BadUser") # rank >= ADMIN
bot.ban("BadUser", reason="Evading")
bot.unban("BadUser") # rank >= ADMIN
bot.set_rank("Alice", Rank.MOD) # rank >= OWNER
Emotes
Emote endpoints read/write the database directly and work even when the channel is offline.
emotes = bot.get_emotes() # list of { name, image, source }
bot.add_emote("KEKW", "https://...") # rank >= OWNER
bot.update_emote("KEKW", image="https://...")
bot.update_emote("KEKW", new_name="KEKWait")
bot.delete_emote("KEKW") # rank >= OWNER
Settings
settings = bot.get_settings() # rank >= OWNER, channel must be active
bot.update_settings(pagetitle="Now Playing: Chill Beats")
bot.update_settings(allow_voteskip=False, afk_timeout=300)
Available setting 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.
Direct REST access
Every REST endpoint is also accessible on bot.api if you need something not covered by the shortcuts:
playlist = bot.api.get_playlist() # { items, currentIndex, locked }
bot.api.skip_to(uid)
bot.api.set_user_rank("Alice", 2)
Error handling
REST calls raise BotAPIError on failure:
from veretube_bot import Bot, BotAPIError
@bot.on("chatMsg")
def on_chat(data):
if data["msg"].startswith("!add "):
_, type, id = data["msg"].split(None, 2)
try:
bot.queue(id, type)
except BotAPIError as e:
bot.send_message(f"Error: {e}") # e.status_code has the HTTP code
Rank constants
from veretube_bot import Rank
Rank.MOD # 2
Rank.ADMIN # 3
Rank.OWNER # 4
Rank.CREATOR # 5
A bot's effective rank is capped at the rank of the user who issued its token.