Free Discord Music Bot Source Code: Setup Guide & GitHub Repo

Written by

in

Decoding Discord Music Bots: Complete Source Code Breakdown & Guide

Music bots are among the most popular and vibrant additions to any Discord community. Whether you are throwing a virtual listening party, chilling in a late-night voice channel, or keeping a lofi beat running 24/7, having your own custom music bot adds a unique flavor to your server.

While thousands of public bots exist, building your own gives you full control over the audio quality, custom commands, queue management, and UI embeds—all without annoying paywalls or ads.

In this comprehensive guide, we will break down everything you need to know about Discord music bot source code, architecture, and how to set up a functional, modern music bot from scratch using Node.js and discord.js.


1. Understanding the Modern Music Bot Stack

Historically, building a Discord music bot was notoriously difficult due to how Discord handles voice data (Opus encoding and UDP networking). Today, robust libraries make this much easier. A standard modern JavaScript/TypeScript music bot stack relies on:

  1. discord.js: The interaction layer for handling Discord gateway events, slash commands, messages, and guild states.
  2. @discordjs/voice: The official library for managing UDP voice connections and WebSockets to Discord voice servers.
  3. @distube/yt-dlp or play-dl: Audio extraction tools that parse URLs (YouTube, Spotify, SoundCloud) and stream raw audio buffers.
  4. prism-media: Transcoding utility used under-the-hood to convert various audio formats into playable PCM streams.

2. Setting Up Your Project Environment

Before writing code, ensure you have Node.js (v18.x or higher) installed. Open your terminal and run the following commands to initialize a project:

mkdir discord-music-bot
cd discord-music-bot
npm init -y

Next, install the required dependencies:

npm install discord.js @discordjs/voice @distube/yt-dlp play-dl dotenv

(Note: You will also need a system-level tool like ffmpeg installed on your machine or server for audio decoding and resampling).


3. Complete Source Code: A Modern Slash Command Music Bot

Below is a clean, well-structured, self-contained implementation of a modular Discord music bot using modern discord.js v14 and @discordjs/voice.

Project Structure

discord-music-bot/
├── .env
├── config.json
└── index.js

Step 1: Configuration (config.json)

{
  "prefix": "!"
}

Step 2: Environment Variables (.env)

DISCORD_TOKEN=your_bot_token_here
CLIENT_ID=your_client_id_here

Step 3: Main Application (index.js)

require('dotenv').config();
const { 
    Client, 
    GatewayIntentBits, 
    REST, 
    Routes, 
    SlashCommandBuilder, 
    EmbedBuilder 
} = require('discord.js');
const { 
    joinVoiceChannel, 
    createAudioPlayer, 
    createAudioResource, 
    AudioPlayerStatus, 
    VoiceConnectionStatus 
} = require('@discordjs/voice');
const play = require('play-dl');

// Initialize Discord Client with necessary intents
const client = new Client({
    intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildVoiceStates,
        GatewayIntentBits.GuildMessages
    ]
});

// In-memory queue storage per guild
const queues = new Map();

client.once('ready', async () => {
    console.log(`Logged in as ${client.user.tag}!`);

    // Register Slash Commands
    const commands = [
        new SlashCommandBuilder()
            .setName('play')
            .setDescription('Plays a song from YouTube')
            .addStringOption(option => 
                option.setName('query')
                    .setDescription('The URL or search term of the song')
                    .setRequired(true)),
        new SlashCommandBuilder()
            .setName('skip')
            .setDescription('Skips the current song'),
        new SlashCommandBuilder()
            .setName('stop')
            .setDescription('Stops music and disconnects the bot')
    ].map(command => command.toJSON());

    const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);

    try {
        console.log('Started refreshing application (/) commands.');
        await rest.put(
            Routes.applicationCommands(process.env.CLIENT_ID),
            { body: commands },
        );
        console.log('Successfully reloaded application (/) commands.');
    } catch (error) {
        console.error(error);
    }
});

// Handle Slash Commands
client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;

    const { commandName } = interaction;
    const guildId = interaction.guildId;
    const voiceChannel = interaction.member.voice.channel;

    if (!voiceChannel) {
        return interaction.reply({ content: '❌ You need to be in a voice channel to play music!', ephemeral: true });
    }

    if (commandName === 'play') {
        const query = interaction.options.getString('query');
        await interaction.deferReply();

        let serverQueue = queues.get(guildId);

        try {
            // Search or fetch track
            let searchResult = await play.search(query, { limit: 1 });
            if (!searchResult || searchResult.length === 0) {
                return interaction.editReply('❌ No results found for your query.');
            }
            const song = {
                title: searchResult[0].title,
                url: searchResult[0].url,
                duration: searchResult[0].durationRaw
            };

            if (!serverQueue) {
                // Initialize queue
                serverQueue = {
                    textChannel: interaction.channel,
                    voiceChannel: voiceChannel,
                    connection: null,
                    player: createAudioPlayer(),
                    songs: []
                };

                queues.set(guildId, serverQueue);
                serverQueue.songs.push(song);

                try {
                    const connection = joinVoiceChannel({
                        channelId: voiceChannel.id,
                        guildId: guildId,
                        adapterCreator: interaction.guild.voiceAdapterCreator,
                    });
                    serverQueue.connection = connection;
                    
                    playSong(guildId, serverQueue.songs[0]);
                    await interaction.editReply(`🎶 Now playing: **${song.title}**`);
                } catch (err) {
                    console.error(err);
                    queues.delete(guildId);
                    return interaction.editReply('❌ Could not connect to the voice channel.');
                }
            } else {
                serverQueue.songs.push(song);
                return interaction.editReply(`✅ Added to queue: **${song.title}**`);
            }
        } catch (error) {
            console.error(error);
            return interaction.editReply('❌ An error occurred while processing your request.');
        }
    } 
    else if (commandName === 'skip') {
        const serverQueue = queues.get(guildId);
        if (!serverQueue) return interaction.reply({ content: '❌ There is no song playing!', ephemeral: true });

        serverQueue.player.stop();
        return interaction.reply('⏭️ Skipped the current song!');
    } 
    else if (commandName === 'stop') {
        const serverQueue = queues.get(guildId);
        if (!serverQueue) return interaction.reply({ content: '❌ There is no music playing!', ephemeral: true });

        serverQueue.songs = [];
        serverQueue.player.stop();
        serverQueue.connection.destroy();
        queues.delete(guildId);
        return interaction.reply('🛑 Stopped music and left the voice channel.');
    }
});

// Helper function to handle queue progression
async function playSong(guildId, song) {
    const serverQueue = queues.get(guildId);

    if (!song) {
        serverQueue.connection.destroy();
        queues.delete(guildId);
        return;
    }

    try {
        const stream = await play.stream(song.url);
        const resource = createAudioResource(stream.stream, {
            inputType: stream.type
        });

        serverQueue.player.play(resource);
        serverQueue.connection.subscribe(serverQueue.player);

        serverQueue.player.once(AudioPlayerStatus.Idle, () => {
            serverQueue.songs.shift();
            playSong(guildId, serverQueue.songs[0]);
        });

    } catch (error) {
        console.error('Error playing song:', error);
        serverQueue.songs.shift();
        playSong(guildId, serverQueue.songs[0]);
    }
}

client.login(process.env.DISCORD_TOKEN);

4. Key Architectural Concepts Explained

If you are expanding this source code into a production-ready application, you should master a few critical pillars:

  • Queue Management (Map): The code uses a JavaScript Map (queues) indexed by guildId. This ensures that multiple servers can play music concurrently without their queues colliding.
  • Audio Streams & Buffering: Rather than downloading entire audio files onto your hard drive, libraries like play-dl stream content on-the-fly as a readable stream piped straight into @discordjs/voice.
  • Lifecycle Events: Handling AudioPlayerStatus.Idle is crucial. When a track finishes, the listener removes the completed track from the array (serverQueue.songs.shift()) and recursively triggers the next song in line.

5. Scaling Up: Best Practices & Advanced Features

Once you have your basic source code running, you can level up your bot with these features:

  1. Modular Architecture: Split your commands into separate files inside a commands/ folder using a dynamic file loader. This keeps your codebase maintainable as it grows.
  2. Error Resilience: Wrap stream creation inside robust try/catch blocks. YouTube often rate-limits or updates its signatures; keeping your extraction libraries updated is essential.
  3. Advanced Controls: Add interactive buttons and selectors (using Discord Message Components) so users can pause, resume, loop, or view the queue via interactive UI panels.
  4. Sharding: If your bot scales to thousands of servers (guilds), implement Discord sharding to distribute network loads across multiple worker processes.

Conclusion

Building a Discord music bot from scratch is one of the most rewarding programming projects for developers looking to understand real-time event-driven architecture, streaming data, and Discord’s API. With the source code provided above, you have a solid foundation to launch your own bot, customize its commands, and bring seamless audio playback to your community.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *