discord-bot: a Discord music and moderation bot built on discord.js
Simple discord bot to play music and manage your server
At a glance
- What is it?
- This bot plays YouTube audio in a Discord voice channel and adds moderation commands like purge, ban and userinfo, built on discord.js 14 and discord-player with slash commands rather than prefix commands. It suits a server owner who wants a music bot with a few moderation tools, and it depends on a ytdl-core fork that has to be kept in step with YouTube changes.
- Who is it for?
- discord-bot fits a server owner who wants a self-hosted music bot in a voice channel plus a purge and ban command, and who is willing to keep an eye on a dependency that breaks when YouTube changes. It is the wrong choice for a large community that needs role management, logging or a moderation audit trail, and the wrong choice for a server that does not want to run its own Node process.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A music bot with a moderation tail
The project is described as a simple Discord bot that helps you manage your server by providing useful commands like playing music or deleting text messages. The README points to a tutorial on building a Discord music bot, which tells you something about the intended audience: a developer who wants to learn by reading, or who wants a working starting point rather than a finished product.
The command list tells you the shape of it. The music half is the substantial part: play, playtop, pause, resume, nowplaying, queue, shuffle, loop, skip, remove, move, swap, stop and volume, which adjusts between 0 and 200. The moderation half is three commands, userinfo, ban and purge, where purge deletes the latest chat messages given a count.
Two details in the command list are worth more attention than they first appear. The /move command takes a track position and a target position and shifts everything else up or down, and the /swap command exchanges two tracks by position. Those are queue manipulation operations rather than playback controls, and they are the kind of thing that distinguishes a bot people keep using from one they delete after a week. The /play command also takes either a YouTube URL or a song name, and /playtop inserts at the top of the queue while /play appends to the bottom.
The repository also notes that it now uses the new Discord slash commands, so the old prefix style is gone, and the licence is MIT.
discord.js 14, discord-player and the extractor stack
The dependency list in package.json describes the architecture more precisely than the README does. The bot runs on discord.js 14.12.1, and discord-player 6.6.2 handles the queue and the voice connection. The audio extraction is split: @discord-player/extractor at 4.4.1 alongside @distube/ytdl-core pinned at 4.14.4, with prism-media for stream handling, @discordjs/opus for the voice codec, and fluent-ffmpeg with the ffmpeg wrapper for transcoding. conf handles configuration and dotenv loads the environment.
The @distube/ytdl-core pin is the single most consequential line in that list, and the README explains why in the environment variables section. The DP_FORCE_YTDL_MOD environment variable has to be set to override ytdl-core for the discord-player, with the value being @distube/ytdl-core. So the project depends on a fork of the YouTube downloader rather than the original, and the override has to be configured or the extractor will not work as intended.
The reason for the fork is the same thing that makes every YouTube downloader fragile: the original project stopped being maintained, and the extraction of stream URLs from the YouTube page is a moving target. The README's own troubleshooting section says the packages in the repository get updated often, especially the ytdl-core package, and recommends updating them if you hit an invalid URL: undefined error or if the bot crashes on the play command, with the fix given as a single command:
npm install ytdl-core@latestThat error is the signature of a broken extractor, and the frequency with which it appears is the honest cost of this project.
Configuration, the .env file and the Discord permissions
Configuration is split in two, and the split matters for a deployment.
The token and the extractor override go into a .env file, written during installation:
# Configure Discord Bot Token
echo "DISCORD_TOKEN='INSERT_YOUR_TOKEN_HERE'" > .envAnd the two environment variables the bot needs are:
DISCORD_TOKEN=INSERT_YOUR_TOKEN_HERE
DP_FORCE_YTDL_MOD="@distube/ytdl-core"The second file is config.json, which holds the bot's presence. The activity and activityType variables inside it set the status, and activityType has to be an integer chosen from the Discord API ActivityType options.
Then there are the three permissions in the developer portal, and getting these wrong is the most common reason a bot starts and then does nothing. The applications.commands application scope has to be enabled, found under the OAuth2 tab. The Server Members Intent and the Message Content Intent have to be enabled under the Bot tab.
The last step is registering the commands with the server itself, and the README is specific that this happens before the slash commands work. You use the !deploy command, which registers the bot's slash commands with your Discord server, and only after that can you see and access them by typing a slash.
That !deploy command is the one holdover of the prefix style in an otherwise slash-only bot, and it is worth knowing about in advance, because a bot that appears to have no commands in the client is usually one that has not been deployed yet.
Running it natively, and the FFMPEG requirement
Starting the application is a single command:
node index.jsThe requirements are Node 16 or higher, NPM, and FFMPEG. FFMPEG is not optional, and the troubleshooting section explains why: the play command requires FFMPEG to be installed on the machine running the bot, and you can download it from the official site. The same section notes that this is not relevant if you use the Dockerfile, because the image installs FFMPEG inside the container.
That last point is the reason the requirements list also names Docker as an optional tool, and the Docker section gives you a prebuilt image. The image is a multiarch build for amd64 and arm64 published to the GitHub Container Registry:
docker pull ghcr.io/TannerGabriel/discord-bot:latestOnly the :latest tag is supported, and the README explains how to pin to a specific commit by using the SHA256 digest from the packages page on GitHub. That is a sensible instruction, since a music bot is a long-running process and pulling latest on every restart is a way to have a bad afternoon.
Building the image yourself is also documented:
# Build the image
docker build --tag discordbot .
# Run the image
docker run -d discordbotThe repository also ships a docker-compose.yaml with a single service, container name discord-bot and restart: always, which is the configuration you want for something that should come back after a reboot.
The Dockerfile shrinks the runtime image with UPX
The Dockerfile is more interesting than a typical Node image, and the reason is image size.
It has two stages. The build stage is node:20, and it copies a static FFMPEG binary from a separate image at version 5.1.2, then installs the dependencies with npm ci --omit=dev. The second half of the build stage is the interesting part: it installs wget and xz-utils, downloads UPX version 3.96, and runs upx -1 on the FFMPEG binary. UPX is an executable packer, and compressing the FFMPEG binary is a large win in a container image, because FFMPEG is tens of megabytes of static executable and the runtime stage otherwise has to carry it in full.
The runtime stage is gcr.io/distroless/nodejs20, a distroless base with no shell and no package manager, and the FFMPEG binary is copied in separately at /bin/. The container runs as the nonroot user and the command is index.js. The result is a runtime image with no operating system surface to speak of.
That design has a practical consequence for anyone debugging it. A distroless image has no shell, so you cannot exec into the container and run apt or ls, and a crashed container gives you no interactive shell to look at. The compose file compensates by adding a healthcheck, and the Dockerfile comment explains that wget backs the healthcheck so a crash-looping container shows up as unhealthy rather than silently restarting. Given that a music bot fails in interesting ways, an image with no shell is a deliberate trade for size and attack surface.
Where this bot is the wrong choice
The first case is a server that needs real moderation. Purge, ban and userinfo are three commands, and ban in particular takes a user and does not mention a reason, an audit entry or a duration. A community that needs moderation tooling with an audit trail, with role-based permission checks per command, or with a log channel, needs a different project, and running this bot and calling it moderation is a stretch.
The second case is a server with a big library. The bot plays a YouTube URL or a search term and manages one queue. There is no playlist support, no per-user queue, no library, and no persistence of the queue across restarts that the documentation describes. The conf dependency holds configuration, and the queue is described as belonging to the player rather than to a database, so a restart loses it.
The third case is a deployment that cannot run its own process. The bot is a long-running Node process that has to stay connected to a voice channel to play anything, and it needs FFMPEG either on the host or in the image. On a platform where you cannot run a container or a long-lived process, this does not fit, and a hosted music service does.
The alternative worth comparing is a purpose-built Discord music bot, of which there are many, where the differentiator is usually exactly the two things this project does not have, persistence and moderation depth. The other alternative is a self-hosted media server plus a thin bot, which gives you a library, a web interface and reliable audio without a queue model. The difference in approach is between a bot that owns the playback session and one that points at a media server that owns it, and for a small server playing a few requests the simpler model here is enough.
Editorial conclusion
discord-bot fits a server owner who wants a self-hosted music bot in a voice channel plus a purge and ban command, and who is willing to keep an eye on a dependency that breaks when YouTube changes. It is the wrong choice for a large community that needs role management, logging or a moderation audit trail, and the wrong choice for a server that does not want to run its own Node process. Before deploying it, install @distube/ytdl-core rather than the abandoned ytdl-core, set DISCORD_TOKEN and DP_FORCE_YTDL_MOD in the .env file, enable the Message Content and Server Members intents plus the applications.commands scope in the developer portal, and run the !deploy command in your server before the slash commands appear.
Frequently asked questions
How do I install the Discord music bot?
Clone the repository, change into the discord-bot directory, run npm install, and write your token into a .env file with echo "DISCORD_TOKEN='INSERT_YOUR_TOKEN_HERE'" > .env. The requirements are Node 16 or higher, NPM and FFMPEG, and Docker is an optional alternative.
Why do I need the DP_FORCE_YTDL_MOD environment variable?
It overrides ytdl-core for discord-player, and the value the project expects is @distube/ytdl-core, a fork pinned at version 4.14.4 in the dependency list. The original ytdl-core is no longer maintained, which is why the extractor breaks periodically.
What permissions does the Discord bot need in the developer portal?
The applications.commands application scope has to be enabled under the OAuth2 tab, and the Server Members Intent and Message Content Intent have to be enabled under the Bot tab. After that you use the !deploy command in your server to register the slash commands before they appear in the client.
How do I run the bot with Docker?
Pull the published multiarch image with docker pull ghcr.io/TannerGabriel/discord-bot:latest, or build it yourself with docker build --tag discordbot . and run it with docker run -d discordbot. A checked-in compose file with restart always is also provided.
What licence is the Discord bot released under?
The project is licensed under the MIT License, with the licence file at the repository root. The repository is not archived and the last push was on 2026-10-10, and there are no GitHub releases.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tannergabriel-discord-bot)Was this useful?