The music bot joins the voice room of whoever types /play and plays the song asked for; later requests go into a
queue. Its audio is end-to-end encrypted like everyone else’s, so nobody, the server included, can listen in.
What it does
- Searches YouTube, or plays links from YouTube, SoundCloud, Bandcamp and Twitch (live streams included).
- Runs the queue: adding, skipping, going back, shuffling, repeating, moving within a song.
- Shows a now playing panel with buttons for every new song.
- Optionally, only one group controls it and the others vote to skip a song.
The bot is an ordinary bot account written with the anten-bot library. It runs in its own container beside
antend and reaches it over an internal network on the same machine, so its audio never leaves the machine on the
way to antend.
Speed
The part that finds and fetches songs (anten-source) stays running. yt-dlp (the tool that finds a video’s audio) is loaded once and keeps its connections open. Searches are remembered. The first 20 seconds or so of the next song are fetched while the current one plays. A song is not downloaded first; it plays as it arrives. Since YouTube’s audio is already Opus, it is passed on without encoding (it is encoded when the volume is changed).
Measured times, from the command reaching the bot to the first sound: moving to the next song about 50 ms; the first song right after the bot starts about 1.5 seconds. The first join of a room adds setting up the voice connection and exchanging the encryption keys. The bot uses the server’s internet connection; the listeners’ connection speed does not affect how fast a song starts.
While playing, the whole bot uses about 110 MB of memory and about 2% of one processor core; idle, about 65 MB and almost no processor time.
Setup
These steps assume Anten running with Docker Compose as in the setup guide. The commands run on the server, in the
deploy folder.
-
Make the bot account. Under Server settings → Integrations → Bots, enter “Music” and press Make a bot, then copy the token shown. It is shown only once; this needs the Manage bots permission.
-
Put the token in
deploy/.env. Keep it in this file only; never in a repository or a chat.MUSIC_BOT_TOKEN=anten_bot_... -
In the same file, add the music bot to the end of the
COMPOSE_FILEline. If there is no such line, or it starts with#, write this one. On an internet-facing server with the HTTPS addition:COMPOSE_FILE=docker-compose.yml:docker-compose.server.yml:docker-compose.music.ymlIf the server has its own reverse proxy, use
docker-compose.nginx.ymlinstead ofdocker-compose.server.yml. On your own computer,COMPOSE_FILE=docker-compose.yml:docker-compose.music.ymlis enough. -
Start the bot and follow its logs. The first build takes a few minutes. If the logs show no errors, the bot is connected.
docker compose up -d --build docker compose logs -f music -
Decide with its groups where the bot may play. The bot joins every server in the default groups. In the rooms it plays in, its View power, Join power and Talk power must meet what the room needs, and it needs Send messages to reply.
-
If needed, keep the bot out of the AFK move. If your server moves people who stay quiet into the AFK room, give the bot the Never moved to the AFK room permission.
-
Try it. Join a voice room, then type
/playand a song’s name in the message box.
Using it
Commands are typed like ordinary messages. Typing / in the message box lists the commands available in the room
and their options. The bot plays in one voice room at a time: the one the person who typed the command is in.
Commands
The table shows the English names. When the bot runs in Turkish (the default, see Language), the names in the last column are used.
| Command | What it does | Turkish |
|---|---|---|
/play <song> |
Plays the song named or linked; adds it to the end of the queue if something is playing | /çal |
/search <song> |
Searches and shows the results in a menu; plays the one you pick | /ara |
/playnext <song> |
Adds the song to the front of the queue | /sıradaki |
/skip |
Skips to the next song | /geç |
/back |
Goes back to the previous song | /önceki |
/pause, /resume |
Pauses; carries on where it stopped | /dur, /devam |
/seek <time> |
Jumps to a point in the song (1:30 or 90) |
/sar |
/forward [seconds], /rewind [seconds] |
Moves forward or back (10 seconds by default) | /ileri, /geri |
/replay |
Plays the song from the start | /başa |
/loop <mode> |
Repeats the song or the queue; off stops repeating |
/tekrar (şarkı, kuyruk, kapalı) |
/shuffle |
Shuffles the queue | /karıştır |
/queue, /now, /history |
Shows the queue, the song playing, what played last | /kuyruk, /şimdi, /geçmiş |
/remove <position> |
Takes a song out of the queue | /çıkar |
/move <position> <to> |
Moves a song to another place in the queue | /taşı |
/clear |
Empties the queue | /temizle |
/autoplay <on/off> |
When the queue ends, plays songs like the last one (from YouTube’s mix) | /otomatik (açık, kapalı) |
/lyrics [song] |
Shows the lyrics of the song playing, or of the one given | /söz |
/volume <level> |
Sets the volume in percent (0–200) | /ses |
/stop |
Ends the music and leaves the room | /bitir |
Only people in the same voice room as the bot can use the commands that change what plays.
The now playing panel
For every new song the bot posts a message with the song’s title, length, who asked for it and how many songs are queued; the previous panel is removed. Its buttons: previous, pause/resume, next, stop, queue, Repeat, Shuffle and Lyrics. The buttons follow the same rules as the commands; only the person who pressed sees the reply. The bot also shows the song next to its name as Listening to.
Search
/search shows the first five results in a menu, with channel and length. Only the person who searched can pick
from it; Cancel closes it. The results can be picked for 10 minutes.
Sources
- Any text that is not a link is a YouTube search.
- Links from YouTube, SoundCloud, Bandcamp and Twitch play directly. Links to other sites are refused.
- From Spotify and Apple Music, only single song links work: the bot reads the song’s title and artist from the page and searches YouTube. Those sites do not provide the audio; playlist and album links are not supported.
- Lyrics come from lrclib.net. The service needs no key; only the song’s title is sent there.
Settings
The bot’s settings go in deploy/.env. After a change, restart the bot with docker compose up -d. Every
variable is listed in Settings reference.
Language
Commands and replies follow the installation’s language (ANTEN_LANGUAGE), or Turkish if that is not set. Choose
the bot’s language on its own with MUSIC_LANG=tr or MUSIC_LANG=en.
When it leaves
The bot leaves by itself 2 minutes after nobody is left listening, and 5 minutes after nothing is left to play.
MUSIC_ALONE_MINUTES and MUSIC_IDLE_MINUTES change these times; with 0 the bot never leaves.
DJ group
If MUSIC_DJ_GROUP is the name of a server group, only its members can use the commands that change what plays.
Asking for songs (/play, /search) stays open to everyone.
- Others vote when they type
/skip. The song is skipped once more than half of the room asks. - Whoever asked for a song can always skip it.
- Someone alone in the room with the bot can use every command.
When it is not set, everyone can use every command.
Panel, lyrics and logs
MUSIC_PANEL=0 turns the now playing panel off. MUSIC_LYRICS=0 removes lyrics and the /lyrics command.
MUSIC_DEBUG=1 writes to the logs how many milliseconds each command took to start the music.
If YouTube blocks the server
YouTube often takes servers in data centres for bots and will not give the song. The bot then says so in the chat,
and the logs (docker compose logs music) show Sign in to confirm you're not a bot. The same bot may work on a
home connection; the problem is the server’s address, not the code.
There are three ways around it. Try them in order and add the next one if needed; they also work together.
When it starts, the bot writes which ones are set up in a log line beginning with against the bot check:.
1. A PO token provider
It needs no account; try this first. A helper that produces YouTube’s “not a bot” token (PO token) runs beside the bot in a container of its own. It holds no keys, and only the bot reaches it. It uses about 100–200 MB of memory. It is enough on most servers, but there is no guarantee.
-
Add the helper to the end of the
COMPOSE_FILEline.COMPOSE_FILE=...:docker-compose.music.yml:docker-compose.music-pot.yml -
Restart the bot.
docker compose up -d --build
2. A cookies file
This is the surest way: the bot connects to YouTube as if signed in with a Google account.
-
On your computer, open a private browser window and sign in to YouTube with the spare account.
-
In the same tab, open
https://www.youtube.com/robots.txtand export the cookies in the Netscape cookies.txt format. A browser extension can do this; choose one whose source code is open. -
Close the private window right away. If it stays open, YouTube renews the cookies and those in the file stop working.
-
Save the file on the server as
deploy/youtube-cookies.txtand make it readable by the bot only.sudo chown 1000:1000 youtube-cookies.txt && sudo chmod 600 youtube-cookies.txt -
Add
:docker-compose.music-cookies.ymlto the end of theCOMPOSE_FILEline and restart the bot.docker compose up -d --build
If the file is elsewhere, put MUSIC_COOKIES_FILE=/path/file.txt in .env. The bot only reads the file and keeps
its own copy in /tmp; it never writes to the file itself. Cookies expire over time: if the bot says it is
blocked again, repeat the steps.
3. A proxy
The bot reaches YouTube from another address instead of the server’s; for example a home connection or a proxy
service. Put its address in deploy/.env:
MUSIC_PROXY=http://user:password@proxy.example.org:3128
Only http:// proxies are supported, because ffmpeg fetches live streams itself and cannot use SOCKS. https sites
still pass through the proxy encrypted. Songs, searches and live streams all go through this address. The
encryption of the audio in the room is not affected: the proxy sees only the conversation with YouTube.
Age-restricted videos
YouTube serves some videos only to signed-in adult accounts; the logs show Sign in to confirm your age. This is
not a bot block but specific to that video. If the first result of a search is such a video, the bot tries the next
results and plays an unrestricted upload of the same song, leaving altered versions to the end. If none works, or a
link to such a video was given directly, the bot says so. Playing such a video needs a cookies file (option 2).
Privacy and security
Audio and connections
- The bot’s audio is end-to-end encrypted. Without a key it sends no sound at all; unencrypted audio never leaves the bot.
- The bot connects to YouTube and the allowed sites from your server; those sites see the server’s address. Anten itself connects to no outside address; you choose these connections by installing the music bot.
- For lyrics, only the song’s title is sent to lrclib.net. If you do not want this, set
MUSIC_LYRICS=0.
Where typed text goes
- Typed text never reaches a shell or a program option. It becomes either a YouTube search or a link to an allowed site. This way nobody can make the bot send requests to addresses in the server’s own network.
- YouTube requires the JavaScript on its page to run. That code runs in Deno, with no access to files, the network or environment variables. It never runs in the Node process, which could reach the bot’s keys.
What anten-source can do
- anten-source receives only searches and links the bot has already checked; it never runs what it is given.
- It never gets the bot’s token: the bot removes it from the environment before starting it.
- It serves audio only inside the machine (127.0.0.1), under a random secret address; it cannot be reached from outside.
- It is the part that uses the cookies file and the proxy address, which is why the cookies file must stay readable by the bot only.
The container
The container is read-only, has no extra privileges (cap_drop: ALL, no-new-privileges) and runs as a non-root
user. It writes only to its key volume and to /tmp. The versions of yt-dlp and Deno are pinned.
Copyright in what is played and the sites’ terms of use are the responsibility of whoever runs the server.
Maintenance
If YouTube stops working
YouTube changes from time to time, and yt-dlp then needs an update. Raise the YTDLP version (and YTDLP_EJS if
needed) in bots/music/Dockerfile to yt-dlp’s latest release and rebuild the bot:
docker compose up -d --build music
Your own voice bot
The music bot is written with the voice support of the anten-bot library (bots/music/src/main.js).
VoiceHelper starts the voice helper (bots/voice), bot.joinVoice(guild, room, helper) joins the room, and the
encryption keys are agreed by themselves the way browsers do it. helper.play(...) plays a file, a stream address
or a downloader’s output. The details are in the voice rooms section of the bots guide.
Settings reference
| Variable | Default | Meaning |
|---|---|---|
MUSIC_BOT_TOKEN |
The bot’s token (required) | |
MUSIC_LANG |
ANTEN_LANGUAGE, else tr |
Language of commands and replies: tr or en |
MUSIC_SITES |
youtube.com,youtu.be,music.youtube.com,soundcloud.com,bandcamp.com,twitch.tv |
The sites links may point to (subdomains included) |
MUSIC_MAX_MINUTES |
180 |
The longest song, in minutes |
MUSIC_QUEUE |
100 |
The most songs in the queue |
MUSIC_VOLUME |
100 |
Starting volume, in percent (0–200) |
MUSIC_IDLE_MINUTES |
5 |
Minutes until it leaves once nothing is left to play; 0: never |
MUSIC_ALONE_MINUTES |
2 |
Minutes until it leaves once nobody is listening; 0: never |
MUSIC_DJ_GROUP |
Only this server group changes what plays; others skip by vote | |
MUSIC_PANEL |
1 |
0: no now playing panel |
MUSIC_LYRICS |
1 |
0: no lyrics and no /lyrics |
MUSIC_DEBUG |
1: the time from command to music in the logs |
|
MUSIC_PROXY |
An http:// proxy to use if YouTube blocks the server |
|
MUSIC_COOKIES_FILE |
./youtube-cookies.txt |
Where the cookies file is (with docker-compose.music-cookies.yml) |