Discord Bots Blog
Get your latest news about Discord Bot List here!

Connecting your Bot to Discord Voice Channels with JDA

Voice channel connections with JDA

Hello there, and welcome to the tutorial on connecting/disconnecting bots to and from voice channels! Whilst it does not sound very exciting or adventerous, it is an important step in creating music bots. You have to learn to crawl before you can learn to run.

Requirements

There are a couple of requirements you must meet before being able to jump straight into the tutorial. This tutorial will not guide you through obtaining them, but it will link you to a few resources you will most likely find useful.

Requirement 1: Java

This requirement may be quite obvious, but before you can code a bot in Java you will need to have Java installed. Java essentially comes in two components: the Java Runtime Environment and the Java Development Kit. The Java Runtime Environment, also abbreviated as the JRE is what you most likely have installed on your computer, if at all. The JRE allows Java programs to run on your computer. The Java Development Kit, abbreviated as JDK, contains the tools required to code Java. Installing the JDK will install the JRE, but not vice versa. It is important that you have the JDK installed. If not, please check Oracle's installation guide.

Requirement 2: IntelliJ

IntelliJ is the IDE that will be used throughout this tutorial. IDE stands for Integrated Development Environment, and they are essentially fancy text editors with built-in tools to help you produce quality code. IDEs will make your life a lot easier, especially with dependency management softwares, such as Gradle. JetBrains, the developers of IntelliJ, have a detailed installation guide.

Requirement 3: Gradle

Gradle is the dependency management tool that will be used throughout this tutorial. It will ensure that your bot can access JDA files, and that everything will run smoothly throughout deployment. Please refer to the official installation guide.

Creating an application

In order to write the code for a Discord bot, you will need to actually create a Discord bot. Luckily, this process is easy peasy!

Step 0

Make sure you are logged in on the Discord website.

Step 1

Click on this link in order to be redirected to Discord's application page. You should now see something like this (minus the censor):

Discord

Step 2

Click on "New App". The following should appear on your screen.

Discord

Step 3

Fill in the application name, and optionally add a description + avatar. I named mine Cyborg. Once you're done, click on "Create App".

Step 4

Your new application should say hello by greeting you with the following page:

Discord

Step 5

Don't mess around with any of the spooky settings, you want to scroll right down until you see the following:

Discord

Step 6

Hit that "Create a Bot User" button, and acknowledge the fact that this action is irreversible. You should then be prompted with the following (obviously your name and/or discriminator will be different):

Discord

Don't check any of the buttons!

Step 7

Now it's time to invite the bot to your server. Create a brand new server, if you haven't already. This will be your super secret (or public - your choice) testing laboratory! Inviting your bot may seem difficult at first, but it's actually quite simple.

On your application page, there will be a button called "Generate OAuth2 URL". Click that. You will be prompted with the following (ID will vary):

Discord

Make sure to only check the "bot" button for the scopes, and check any permissions you want it to have. Then, click on the "copy" button, and paste the contents into your browser URL bar. Wait until the page loads, select your testing guild, and select "Authorize".

Step 8

Congratulations, you're done! Make sure to click on "Save changes" at the bottom if you haven't already, and close the tab/window. In order to go to the applications page again, click here, and then click on the application name.

Writing code

Now comes the exciting part! Writing the actual code. Creating a bot that can join and leave channels is surprisingly simple. A large of the code is actually just checking whether certain conditions are met. Don't get me wrong -- this is equally as important as the connection part.

Step 1

Firstly, it's time to create a project. Open up IntelliJ, and you should see the following screen:

IntelliJ

If you see a project open instead, you can get to this screen by clickin on File -> Close Project.

Now, click on "Create New Project". You should be prompted with the following:

IntelliJ

If you do not see this, make sure to click on the gradle tab/window on the left. For the additional libraries just check Java, nothing else, and click Next.

IntelliJ

We will be following Java package naming conventions throughout this tutorial, so please read up on those. For the group ID, enter your package name without the project name. Mine is de.arraying. For the artifact ID, enter your project name. My bot is called "Cyborg", hence my project name will be cyborg. For the version, you can put whatever version you like. Click on Next.

IntelliJ

Make sure you check "Use auto-import"! Other than that, you don't need to change anything, although you can use the local gradle distribution if you like. Click on Next.

IntelliJ

You can leave everything like it is, and click on Finish.

Step 2

You should now be given a window that looks like this:

IntelliJ

Note: If you look closely, you can see that my project is actually called CyborgExample. This is because I already have a project called Cyborg, it is the project from which I am copying the code, step by step.

It may take several seconds, up to a minute, for the src content directories to be created. Be patient.

Open up the build.gradle file. Copy and paste the following content into the file:

plugins {
    id'java'
    id'application'
    id'com.github.johnrengelman.shadow' version '2.0.1'
}

mainClassName = 'de.arraying.cyborg.Bot' // IMPORTANT

version '1.0.0'

sourceCompatibility = 1.8

repositories {
    jcenter()
}

dependencies {
    compile 'net.dv8tion:JDA:3.5.0_331'
}

compileJava.options.encoding = 'UTF-8'

IntelliJ

It is very important that you change the mainClassName! This should be set to the following: <group>.<artifact>.Bot In this case, <group> is your group ID and <artifact> is your artifact ID.

Note: This tutorial uses the latest version of JDA, currently at 3.5.0_331. In the future, this may be very outdated!

You may close the build.gradle tab.

Now, right-click src/java, and press New -> Package.

IntelliJ

For the package name, enter <group>.<artifact>, just like in the previous step, just without the .Bot.

Right-click the newly created package, and press New -> Java Class.

IntelliJ

For the name, enter Bot, and click OK.

IntelliJ will open up a new tab for you, looking something like this (you may have a comment above the class):

package de.arraying.cyborg

public class Bot {

}

Now's it's time to implement the main function! This is the entry point for your bot. This tutorial will use runtime parameters to pass in your Discord bot's token. Why? Because hard-coding a token is very bad practice, and creating a configuration file handler would be too much for this tutorial.

Add the following code to your bot:

package de.arraying.cyborg;

public class Bot {

    // Main function, program entry point.
    public static void main(String[] args) {
        // Checks if there were any parameters passed in.
        if(args.length == 0) {
            // We need at least one -- our token!
            System.out.println("Please provide a token!");
            return;
        }
        String token = args[0]; // 0 based indexing.
    }

}

Now it's time to add JDA, and make the bot connect to Discord.

package de.arraying.cyborg;

import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;

import javax.security.auth.login.LoginException;

public class Bot {

    // Main function, program entry point.
    public static void main(String[] args) {
        // Checks if there were any parameters passed in.
        if(args.length == 0) {
            // We need at least one -- our token!
            System.out.println("Please provide a token!");
            return;
        }
        String token = args[0]; // 0 based indexing.
        try {
            JDA jda = new JDABuilder(AccountType.BOT) // New bot builder.
                    .setToken(token) // Set the token.
                    .addEventListener(new Handler()) // Registers the event listener.
                    .buildBlocking(); // Block the current thread until JDA is 100% ready.
            // Not required, but useful to demonstrate that everything worked.
            System.out.println("Logged in as " + jda.getSelfUser().getName() + "#" + jda.getSelfUser().getDiscriminator() + "!");
        } catch(LoginException | InterruptedException exception) {
            // Print the error.
            exception.printStackTrace();
        }
    }

}

With that done, there's nothing stopping you from doing a test run! Click on Run -> Run.

IntelliJ

Select "Bot" from the menu.

IntelliJ

And everything should run. But wait -- there's an error! The program doesn't know what your token is, because you never specified it.

Specifying a runtime parameter is really easy. Click on Run -> Edit Configurations.

IntelliJ

Select the "Bot" tab if it hasn't selected by default. Now you need to get your token. Open the applications page provided by Discord (mentioned earlier). Scroll down, to the "Bot" section. Then, click "click to reveal", and copy the token. Now, paste the token under "Environment variables", like this:

IntelliJ

Click on OK. Click on Run -> Run 'Bot'. You should get a console output like this (username and discriminator may vary):

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
SLF4J: Failed to load class "org.slf4j.impl.StaticMDCBinder".
SLF4J: Defaulting to no-operation MDCAdapter implementation.
SLF4J: See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details.
[main] INFO JDA - Login Successful!
[JDA MainWS-ReadThread] INFO WebSocketClient - Connected to WebSocket
[JDA MainWS-ReadThread] INFO JDA - Finished Loading!
Logged in as Cyborg#6668!

For now, we can ignore the SLF4J warnings. If you hop over to your testing server, you will notice that your bot has come online! Witchcraft!

Step 3

You have a working bot that can come online on Discord, but it doesn't connect to voice channels - yet! That's where this last step comes in.

Right-click your package, and click on New -> Java Class.

IntelliJ

Enter "Handler" as the name, and hit OK.

Make the class extend "ListenerAdapter":

package de.arraying.cyborg;

import net.dv8tion.jda.core.hooks.ListenerAdapter;

public class Handler extends ListenerAdapter {

}

Go back to Bot.java, and modify your JDA code to the following:

            JDA jda = new JDABuilder(AccountType.BOT) // New bot builder.
                    .setToken(token) // Set the token.
                    .addEventListener(new Handler()) // Add an event listener.
                    .buildBlocking(); // Block the current thread until JDA is 100% ready.

This will register your event handler. Go back to Handler.java and add the following method:

package de.arraying.cyborg;

import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;

public class Handler extends ListenerAdapter {

    // Only listening to guild messages.
    @Override
    public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
        // Good practise to ignore bots.
        if(event.getAuthor().isBot()) {
            return;
        }
        // Gets the raw message content and binds it to a local variable.
        String message = event.getMessage().getContentRaw().toLowerCase();
        // So we don't have to access event.getChannel() every time.
        TextChannel channel = event.getChannel();
    }
}

You have an event handler that handles the event, and you have a method that handles messages. Now it's time to add an insanely basic command handler. The command handler will presume that your prefix is !. Quality bots have a command executor, with command objects, something left out in this tutorial. Update your method to the following:

// Only listening to guild messages.
    @Override
    public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
        // Good practise to ignore bots.
        if(event.getAuthor().isBot()) {
            return;
        }
        // Gets the raw message content and binds it to a local variable.
        String message = event.getMessage().getContentRaw().toLowerCase();
        // So we don't have to access event.getChannel() every time.
        TextChannel channel = event.getChannel();
        // Checks if the command is !join.
        if(message.equals("!join")) {

        } else if(message.equals("!leave")) { // Checks if the command is !leave.

        }
    }

Add the code (with the checks!) that connects to the user's voice channel. Edit the if statement to be the following (you may need to import, click on the red name and hit ALT + ENTER to import):

         if(message.equals("!join")) {
            // Checks if the bot has permissions.
            if(!event.getGuild().getSelfMember().hasPermission(channel, Permission.VOICE_CONNECT)) {
                // The bot does not have permission to join any voice channel. Don't forget the .queue()!
                channel.sendMessage("I do not have permissions to join a voice channel!").queue();
                return;
            }
            // Creates a variable equal to the channel that the user is in.
            VoiceChannel connectedChannel = event.getMember().getVoiceState().getChannel();
            // Checks if they are in a channel -- not being in a channel means that the variable = null.
            if(connectedChannel == null) {
                // Don't forget to .queue()!
                channel.sendMessage("You are not connected to a voice channel!").queue();
                return;
            }
            // Gets the audio manager.
            AudioManager audioManager = event.getGuild().getAudioManager();
            // When somebody really needs to chill.
            if(audioManager.isAttemptingToConnect()) {
                channel.sendMessage("The bot is already trying to connect! Enter the chill zone!").queue();
                return;
            }
            // Connects to the channel.
            audioManager.openAudioConnection(connectedChannel);
            // Obviously people do not notice someone/something connecting.
            channel.sendMessage("Connected to the voice channel!").queue();
        } else if(message.equals("!leave")) { // Checks if the command is !leave.

        }

Add the code (again, with the checks!) that disconnects from any voice channel to the else if statement:

        } else if(message.equals("!leave")) { // Checks if the command is !leave.
            // Gets the channel in which the bot is currently connected.
            VoiceChannel connectedChannel = event.getGuild().getSelfMember().getVoiceState().getChannel();
            // Checks if the bot is connected to a voice channel.
            if(connectedChannel == null) {
                // Get slightly fed up at the user.
                channel.sendMessage("I am not connected to a voice channel!").queue();
                return;
            }
            // Disconnect from the channel.
            event.getGuild().getAudioManager().closeAudioConnection();
            // Notify the user.
            channel.sendMessage("Disconnected from the voice channel!").queue();
        }

If your code is not yet terminated, you need to do so by clicking the red stop button in the top right corner:

IntelliJ

Run the code (Run -> Run 'Bot'), and test the functionality!

Discord

Discord