How to create a music bot using Discord.js

discord-bot-thumbnail

The discord API provides you with an easy tool to create and use your own bots and tools. Today we are going to take a look at how we can create a basic music bot and add it to our server. The bot will be able to play, skip and stop the music and will also support queuing functionality.

Setting up a discord bot

First, we need to create a new application on the discord development portal.

We can do so by visiting the portal and clicking on new application.

new-discord-bot

After that, we need to give our application a name and click the create button.

create-discord-bot

After that, we need to select the bot tab and click on add bot.

discord-bot-info

Now our bot is created and we can continue with inviting it to our server.

Adding the bot to your server

After creating our bot we can invite it using the OAuth2 URL Generator.

For that, we need to navigate to the OAuth2 page and select bot in the scope tap.

discord-bot-oauth

After that, we need to select the needed permissions to play music and read messages.

discord-bot-permissions

Then we can copy our generated URL and paste it into our browser.

discord-bot-invite

After pasting it, we add it to our server by selecting the server and clicking the authorize button.

adding-discord-bot-to-server

Creating our project

Now we can start creating our project using our terminal.

First, we create a directory and move into it. We can do so by using these two commands.

mkdir musicbot && cd musicbot

After that, we can create our project modules using the npm init command. After entering the command you will be asked some questions just answer them and continue.

Then we just need to create the two files we will work in.

touch index.js && touch config.json

Now we just need to open our project in our text editor. I personally use VS Code and can open it with the following command.

code .

Discord js basics

Now we just need to install some dependencies before we can get started.

npm install discord.js ffmpeg-binaries opusscript ytdl-core --save

After the installation finished we can continue with writing our config.json file. Here we save the token of our bot and the prefix he should listen for.

{
"prefix": "!",
"token": "your-token"
}

To get your token you need to visit the discord developer portal again and copy it from the bot section.

discord-bot-token

That are the only things we need to do in our config.json file. So let’s start writing our javascript code.

First, we need to import all our dependencies.

const Discord = require('discord.js');
const {
	prefix,
	token,
} = require('./config.json');
const ytdl = require('ytdl-core');

After that, we can create our client and login using our token.

const client = new Discord.Client();
client.login(token);

Now let’s add some basic listeners that console.log when they get executed.

client.once('ready', () => {
 console.log('Ready!');
});
client.once('reconnecting', () => {
 console.log('Reconnecting!');
});
client.once('disconnect', () => {
 console.log('Disconnect!');
});

After that, we can start our bot using the node command and he should be online on discord and print “Ready!” in the console.

node index.js

Reading messages

Now that our bot is on our server and able to go online, we can start reading chat messages and responding to them.

To read messages we only need to write one simple function.

client.on('message', async message => {
  
}

Here we create a listener for the message event and get the message and save it into a message object if it is triggered.

Now we need to check if the message is from our own bot and ignore it if it is.

if (message.author.bot) return;

In this line, we check if the author of the message is our bot and return if it is.

After that, we check if the message starts with the prefix we defined earlier and return if it doesn’t.

if (!message.content.startsWith(prefix)) return;

After that, we can check which command we need to execute. We can do so using some simple if statements.

const serverQueue = queue.get(message.guild.id);

if (message.content.startsWith(`${prefix}play`)) {
 execute(message, serverQueue);
 return;
} else if (message.content.startsWith(`${prefix}skip`)) {
 skip(message, serverQueue);
 return;
} else if (message.content.startsWith(`${prefix}stop`)) {
 stop(message, serverQueue);
 return;
} else {
message.channel.send('You need to enter a valid command!')

In this code block, we check which command to execute and call the command. If the input command isn’t valid we write an error message into the chat using the send() function.

Now that we know which command we need to execute we can start implementing these commands.

Adding songs

Let's start by adding the play command. For that, we need a song and a guild (A guild represent an isolated collection of users and channels and is often referred to as a server). We also need the ytdl library we installed earlier.

First, we need to create a map with the name of the queue where we save all the songs we type in the chat.

const queue = new Map();

After that, we create an async function called execute and check if the user is in a voice chat and if the bot has the right permission. If not we write an error message and return.

async function execute(message, serverQueue) {
 const args = message.content.split(' ');
 const voiceChannel = message.member.voiceChannel;
 if (!voiceChannel) return message.channel.send('You need to be in a voice channel to play music!');
  const permissions =     voiceChannel.permissionsFor(message.client.user);
 if (!permissions.has('CONNECT') || !permissions.has('SPEAK')) {
  return message.channel.send('I need the permissions to join and   speak in your voice channel!');
 }
}

Now we can continue with getting the song info and saving it into a song object. For that, we use our ytdl library which gets the song information from the youtube link.

const songInfo = await ytdl.getInfo(args[1]);
const song = {
 title: songInfo.title,
 url: songInfo.video_url,
};

This will get the information of the song using the ytdl library we installed earlier. Then we save the information we need into a song object.

After saving the song info we just need to create a contract we can add to our queue. To do so we first need to check if our serverQueue is already defined which means that music is already playing. If so we just need to add the song to our existing serverQueue and send a success message. If not we need to create it and try to join the voice channel and start playing music.

if (!serverQueue) {

}else {
 serverQueue.songs.push(song);
 console.log(serverQueue.songs);
 return message.channel.send(`${song.title} has been added to the queue!`);
}

Here we check if the serverQueue is empty and add the song to it if it’s not. Now we just need to create our contract if the serverQueue is null.

// Creating the contract for our queue
const queueContruct = {
 textChannel: message.channel,
 voiceChannel: voiceChannel,
 connection: null,
 songs: [],
 volume: 5,
 playing: true,
};
// Setting the queue using our contract
queue.set(message.guild.id, queueContruct);
// Pushing the song to our songs array
queueContruct.songs.push(song);

try {
 // Here we try to join the voicechat and save our connection into our object.
 var connection = await voiceChannel.join();
 queueContruct.connection = connection;
 // Calling the play function to start a song
 play(message.guild, queueContruct.songs[0]);
} catch (err) {
 // Printing the error message if the bot fails to join the voicechat
 console.log(err);
 queue.delete(message.guild.id);
 return message.channel.send(err);
}

In this code block, we create a contract and add our song to the songs array. After that, we try to join the voice chat of the user and call our play() function we will implement after that.

Playing songs

Now that we can add our songs to our queue and create a contract if there isn’t one yet we can start implementing our play functionality.

First, we will create a function called play which takes two parameters (the guild and the song we want to play) and checks if the song is empty. If so we will just leave the voice channel and delete the queue.

function play(guild, song) {
 const serverQueue = queue.get(guild.id);
 if (!song) {
  serverQueue.voiceChannel.leave();
  queue.delete(guild.id);
  return;
 }
}

After that, we will start playing our song using the playStream() function and the URL of our song.

const dispatcher = serverQueue.connection.playStream(ytdl(song.url))
 .on('end', () => {
  console.log('Music ended!');
  // Deletes the finished song from the queue
  serverQueue.songs.shift();
  // Calls the play function again with the next song
  play(guild, serverQueue.songs[0]);
})
 .on('error', error => {
  console.error(error);
 });
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);

Here we create a stream and pass it the URL of our song. We also add two listeners that handle the end and error event.

Note: This is a recursive function which means that it calls itself over and over again. We use recursion so it plays the next song when the song is finished.

Now we are ready to play a song by just typing !play URL in the chat.

Skipping songs

Now we can start implementing the skipping functionality. For that, we just need to end the dispatcher we created in our play() function so it starts the next song.

function skip(message, serverQueue) {
 if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
 if (!serverQueue) return message.channel.send('There is no song that I could skip!');
 serverQueue.connection.dispatcher.end();
}

Here we check if the user that typed the command is in a voice channel and if there is a song to skip.

Stoping songs

The stop() function is almost the same as the skip() except that we clear the songs array which will make our bot delete the queue and leave the voice chat.

function stop(message, serverQueue) {
 if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
 serverQueue.songs = [];
 serverQueue.connection.dispatcher.end();
}

Complete source code for the index.js:

Here you can get the complete source code for our music bot:

const Discord = require('discord.js');
const {
	prefix,
	token,
} = require('./config.json');
const ytdl = require('ytdl-core');

const client = new Discord.Client();

const queue = new Map();

client.once('ready', () => {
	console.log('Ready!');
});

client.once('reconnecting', () => {
	console.log('Reconnecting!');
});

client.once('disconnect', () => {
	console.log('Disconnect!');
});

client.on('message', async message => {
	if (message.author.bot) return;
	if (!message.content.startsWith(prefix)) return;

	const serverQueue = queue.get(message.guild.id);

	if (message.content.startsWith(`${prefix}play`)) {
		execute(message, serverQueue);
		return;
	} else if (message.content.startsWith(`${prefix}skip`)) {
		skip(message, serverQueue);
		return;
	} else if (message.content.startsWith(`${prefix}stop`)) {
		stop(message, serverQueue);
		return;
	} else {
		message.channel.send('You need to enter a valid command!')
	}
});

async function execute(message, serverQueue) {
	const args = message.content.split(' ');

	const voiceChannel = message.member.voiceChannel;
	if (!voiceChannel) return message.channel.send('You need to be in a voice channel to play music!');
	const permissions = voiceChannel.permissionsFor(message.client.user);
	if (!permissions.has('CONNECT') || !permissions.has('SPEAK')) {
		return message.channel.send('I need the permissions to join and speak in your voice channel!');
	}

	const songInfo = await ytdl.getInfo(args[1]);
	const song = {
		title: songInfo.title,
		url: songInfo.video_url,
	};

	if (!serverQueue) {
		const queueContruct = {
			textChannel: message.channel,
			voiceChannel: voiceChannel,
			connection: null,
			songs: [],
			volume: 5,
			playing: true,
		};

		queue.set(message.guild.id, queueContruct);

		queueContruct.songs.push(song);

		try {
			var connection = await voiceChannel.join();
			queueContruct.connection = connection;
			play(message.guild, queueContruct.songs[0]);
		} catch (err) {
			console.log(err);
			queue.delete(message.guild.id);
			return message.channel.send(err);
		}
	} else {
		serverQueue.songs.push(song);
		console.log(serverQueue.songs);
		return message.channel.send(`${song.title} has been added to the queue!`);
	}

}

function skip(message, serverQueue) {
	if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
	if (!serverQueue) return message.channel.send('There is no song that I could skip!');
	serverQueue.connection.dispatcher.end();
}

function stop(message, serverQueue) {
	if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
	serverQueue.songs = [];
	serverQueue.connection.dispatcher.end();
}

function play(guild, song) {
	const serverQueue = queue.get(guild.id);

	if (!song) {
		serverQueue.voiceChannel.leave();
		queue.delete(guild.id);
		return;
	}

	const dispatcher = serverQueue.connection.playStream(ytdl(song.url))
		.on('end', () => {
			console.log('Music ended!');
			serverQueue.songs.shift();
			play(guild, serverQueue.songs[0]);
		})
		.on('error', error => {
			console.error(error);
		});
	dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
}

client.login(token);

Conclusion

You made it all the way until the end! Hope that this article helped you understand the Discord API and how you can use it to create a simple bot. If you want to see an example of a more advanced discord bot you can visit my Github repository.

If you have found this useful, please consider recommending and sharing it with other fellow developers.

If you have any questions or feedback, let me know in the comments down below.

Read these next: