A feature-rich Discord music bot powered by Lavalink 4.2.2 and discord.js v14, with a Next.js playlist management web UI. Supports Discord's DAVE (E2EE voice encryption) protocol enforced since March 2026.
| Platform | How to Use | Status |
|---|---|---|
| YouTube | /play <search or URL> |
Ready |
| Spotify | /play <spotify link> |
Ready |
| SoundCloud | /play <soundcloud link> |
Ready |
| Audius | /play audius:<search> or /play <audius.co link> |
Ready |
| Bandcamp | /play <bandcamp link> |
Ready |
| Twitch | /play <twitch stream link> |
Ready |
| Vimeo | /play <vimeo link> |
Ready |
| Direct URL | /play <any .mp3/.wav/audio URL> |
Ready |
| Deezer | /play <deezer link> |
Needs key (see Setup) |
| Apple Music | /play <apple music link> |
Needs token (see Setup) |
- Multi-platform search and playback via Lavalink
- Queue management with shuffle, loop (track/queue), and autoplay
- Audio filters: Bass Boost, Nightcore, Vaporwave, Karaoke, Tremolo, Vibrato, Rotation, Low Pass
- Now-playing embed with progress bar and active filters display
- Source icons showing where tracks come from
- Default volume starts at 50%
/radio <artist>— searches Spotify for an artist's tracks, shuffles them, and plays on queue loop 24/7/lofi— queues 8 shuffled lofi tracks from varied search queries (jazz, piano, rain, cafe, ambient, etc.) at 5% volume- Every run gives a different randomized mix
/volume <1-100>— set exact volume/volumeup//volumedown— small adjustments (+/- 5%)/volumebigup//volumebigdown— big jumps (+/- 20%)- Player buttons:
---+++on every now-playing embed
| Command | Description | DJ Only |
|---|---|---|
/play <query> |
Play a song, URL, or audius:<search> |
No |
/pause |
Pause playback | Yes |
/resume |
Resume playback | Yes |
/skip |
Skip current song | No |
/stop |
Stop and clear queue | Yes |
/seek <seconds> |
Seek to position | No |
/queue [page] |
Show queue | No |
/nowplaying |
Show current song with progress bar | No |
/shuffle |
Shuffle the queue | No |
/remove <pos> |
Remove a track from queue | Yes |
/move <from> <to> |
Reorder a track in the queue | Yes |
/clear |
Clear queue without stopping current song | Yes |
/loop <off/song/queue> |
Set loop mode | No |
/autoplay |
Toggle autoplay | Yes |
/volume <1-100> |
Set exact volume | Yes |
/volumeup |
Volume +5% | Yes |
/volumedown |
Volume -5% | Yes |
/volumebigup |
Volume +20% | Yes |
/volumebigdown |
Volume -20% | Yes |
/filter <name> |
Toggle audio filter | Yes |
/playlist list |
List all website playlists | No |
/playlist load <name> |
Load a playlist into queue | No |
/save <name> |
Save current queue as a website playlist | No |
/lofi |
Queue shuffled lofi tracks at 5% volume | No |
/radio <artist> |
Play an artist on shuffle loop 24/7 (Spotify) | No |
/lyrics [query] |
Show lyrics for current or given song | No |
/help |
Show all commands | No |
Every now-playing embed has two rows of buttons:
- Row 1: Pause/Resume | Skip | Stop | Loop | Shuffle
- Row 2:
--Vol -20% |-Vol -5% |+Vol +5% |++Vol +20%
- Browse and view playlists
- Create playlists with individual track rows (title + URL)
- Edit playlists — add, remove, and reorder tracks
- Delete playlists with confirmation dialog
ZAOMusicBot/
├── bot/ # Discord bot (Node.js)
│ └── src/
│ ├── commands/ # Slash commands
│ ├── components/ # Embeds & button builders
│ ├── events/ # Discord event handlers
│ ├── lavalink/ # Lavalink event setup
│ ├── services/ # External API integrations (Audius)
│ ├── utils/ # Helpers (DJ perms, formatting)
│ ├── config.js # Config loader
│ ├── index.js # Bot entry point
│ ├── launcher.js # Combined launcher (Lavalink + bot)
│ └── deploy-commands.js
├── web/ # Next.js playlist web UI
│ ├── src/app/
│ │ ├── api/playlists/ # REST API routes
│ │ └── playlists/ # UI pages (view, create, edit)
│ └── data/playlists.json
├── lavalink/ # Lavalink server
│ ├── application.yml
│ └── Lavalink.jar # v4.2.2 (download separately)
└── package.json # Workspace root
- Node.js 18+
- Java 17+ (for Lavalink — the audio engine)
- A Discord bot application with token and client ID
Why two things? The bot has two parts: (1) the Discord bot (Node.js) that handles commands and chat, and (2) Lavalink (Java) that handles all audio streaming. They communicate over a WebSocket on port 2333.
- Go to https://discord.com/developers/applications
- Click New Application, give it a name
- Go to Bot tab, click Reset Token, copy the token — this is your
DISCORD_TOKEN - Copy the Application ID from the General Information tab — this is your
CLIENT_ID - Under Bot tab, enable these Privileged Gateway Intents:
- Server Members Intent
- Message Content Intent
- Go to OAuth2 > URL Generator, select scopes:
bot+applications.commands - Select bot permissions:
Connect,Speak,Send Messages,Embed Links,Use Slash Commands - Copy the generated URL and open it in your browser to invite the bot to your server
- (Optional) Copy your server's ID — this is your
GUILD_ID(right-click server > Copy Server ID, with Developer Mode enabled in Discord settings) - (Optional) Create a "DJ" role in your server and copy its ID — this is your
DJ_ROLE_ID
git clone https://github.com/bettercallzaal/zaomusicbot.git
cd ZAOMusicBot
npm installDownload Lavalink 4.2.2+ (required for Discord DAVE E2EE support):
curl -L -o lavalink/Lavalink.jar https://github.com/lavalink-devs/Lavalink/releases/download/4.2.2/Lavalink.jarImportant: Lavalink 4.2.0+ is required. Discord enforced DAVE (E2EE voice encryption) on March 2, 2026. Older versions (4.0.x) cannot send audio — the bot will join the voice channel but you will hear nothing.
macOS:
brew install openjdk@17Ubuntu/Debian:
sudo apt update && sudo apt install openjdk-17-jre-headlessWindows: Download from https://adoptium.net/temurin/releases/?version=17
Verify with:
java -version
# Should show: openjdk version "17.x.x" or highercp .env.example bot/.envEdit bot/.env and fill in your values:
# Required
DISCORD_TOKEN=your_bot_token_from_step_1
CLIENT_ID=your_application_id_from_step_1
# Optional
GUILD_ID=your_server_id # Commands register instantly in this server
DJ_ROLE_ID=your_dj_role_id # Omit = everyone can use DJ commands
# Lavalink (defaults work for local development)
LAVALINK_HOST=localhost
LAVALINK_PORT=2333
LAVALINK_PASSWORD=zaomusicbot
# Playlist web UI integration (optional)
PLAYLIST_API_URL=http://localhost:3000
PLAYLIST_API_KEY=This registers all /play, /skip, etc. commands with Discord. Run once, or after adding new commands:
npm run deploy-commandsIf you set GUILD_ID, commands appear instantly in that server. Without it, commands register globally (takes up to 1 hour).
Option A: Two terminals (recommended for development)
# Terminal 1 — Start Lavalink
npm run lavalink
# Terminal 2 — Start the bot (after Lavalink says "ready to accept connections")
npm run dev:botOption B: Single command with launcher
npm startThis runs launcher.js which starts Lavalink, waits for it to be ready, then starts the bot automatically.
Option C: Start everything including web UI
# Terminal 1
npm run lavalink
# Terminal 2
npm run dev:bot
# Terminal 3
npm run dev:web- Join a voice channel in your Discord server
- Type
/play never gonna give you up - The bot should join your voice channel and play music
If the bot joins but you hear no audio, check:
- Lavalink is running and shows
Lavalink is ready to accept connections - Lavalink version is 4.2.0+ (check the banner when it starts)
- The bot logs show
dave-jvm: successfully loaded(DAVE encryption is working)
In lavalink/application.yml, set the source to true and add credentials:
Deezer — requires a master decryption key:
deezer:
masterDecryptionKey: "your_deezer_master_key"Apple Music — requires an Apple Media API token:
applemusic:
mediaAPIToken: "your_apple_media_token"
countryCode: "US"To host on bot-hosting.net, create two free servers:
- Go to https://bot-hosting.net/panel/create
- Select Java as the language
- Upload
lavalink/Lavalink.jarandlavalink/application.ymlto the server files - Set the startup JAR file to
Lavalink.jar - Start the server and verify Lavalink starts successfully
- Note the server's IP/hostname — you'll need this for the bot's
LAVALINK_HOST
- Create another server, select Node.js
- Upload everything inside the
bot/folder (includingpackage.json— node_modules will be installed automatically) - Set startup file to
src/index.js - In the Startup tab, set environment variables:
DISCORD_TOKEN— your bot tokenCLIENT_ID— your bot's client IDLAVALINK_HOST— Server 1's hostname/IP from step 6 aboveLAVALINK_PORT—2333LAVALINK_PASSWORD—zaomusicbot
- Start the server — the bot should connect to Lavalink and log in to Discord
If you want to run both on one Node.js server:
- Create a server, select Node.js
- Upload the entire project (bot/ + lavalink/ folders)
- Set startup file to
src/launcher.js - The launcher auto-downloads a portable Java 17 JRE and starts Lavalink before the bot
- Requires ~512MB+ RAM
- Set the same environment variables as Server 2 above, but use
LAVALINK_HOST=localhost
| Problem | Cause | Fix |
|---|---|---|
| Bot joins voice but no audio | Lavalink version too old (pre-4.2.0) | Download Lavalink 4.2.2+ — DAVE E2EE is required since March 2026 |
| Bot joins voice but very quiet | Default volume was 10% | Volume now defaults to 50%, use /volume to adjust |
| "No results found" for YouTube | YouTube blocks requests | Restart Lavalink — YouTube plugin rotates clients automatically |
| Audius tracks fail to load | Rate limited or API issue | app_name is now included in all requests; retry |
| "Could not play" error | Lavalink not running | Start Lavalink first, then the bot |
| Commands not showing in Discord | Commands not deployed | Run npm run deploy-commands |
| Commands show but nothing happens | Bot missing permissions | Re-invite bot with Connect, Speak, Send Messages permissions |
| Lavalink won't start | Wrong Java version | Need Java 17+, check with java -version |
- Favorites system —
/favto save a song,/favlistto view saved favorites - Song history —
/historyto see recently played tracks - DJ request queue — users submit song requests, DJ approves or denies
- Auto-lyrics — automatically show lyrics when a song starts
- Playlist import — paste a Spotify/YouTube playlist URL and save it
- 24/7 mode —
/radio <artist>plays on shuffle loop indefinitely - Custom playlists per user — personal playlists tied to Discord user IDs
- Web dashboard auth — Discord OAuth login for the web UI
- Bot: discord.js v14, lavalink-client v2.9.7
- Audio: Lavalink 4.2.2 with DAVE E2EE, YouTube plugin 1.18.0, LavaSrc 4.8.1 (Spotify), custom Audius integration
- Web: Next.js 15, React 19
- Data: JSON file storage