Discord is a community based instant messaging platform. It features servers which are synonymous to groups and a server can have multiple channels where users can send text messages (text-channels) or communicate via voice (voice-channels).

another pic

Discord supports creation of bots which users in a server can interact with by sending messages in the text channels.

Mallory is a Discord bot which treats messages begining with the . prefix as commands. For instance .help is a command which when sent on a text channel, Mallory will respond with a list of commands that it can respond to.

another pic

Disclaimer: This is not a tutorial on how you can make a Discord bot but rather a high-level perspective of how Discord bots work. Links to tutorials I used are included at the end of the post.

Event-Driven Programming

Discord provides a library which enables developers to create bots. The bots are simply console applications/scripts running in the cloud (or even locally) and can be written in Python or Javascript.

Via Discord’s developer portal, you can create a bot which will act as a new user and then you can add it to your server.

Each message sent on a text channel is viewed as an event and the bot listens for such events.

To listen to an events the following function signature is used. When an “event” is emmited the callback function is called. More on callbacks can be found on my previous post

client.on("event",function(){
    // do something
})

const { Client } = require('discord.js');

const client = new Client();
 
client.on('message',(message)=>{

    let content = message.content;

    let authorID = message.author.id;

})

The client variable represents the library provided by Discord which enables you to interact with your bot. When a chat is sent on a text channel the message event is emmited and using the code above you can handle such an event.

The content variable represents the message sent in the chat and authorID is the user who sent the message.

Commands

To be able to differentiate conversations with bot commands, it is best to use a prefix for your commands. In this case, I have used the . prefix. All messages begining with a . will be treated as a command for the bot to respond to.

client.on('message',(message)=>{
    
    let content = message.content; 
    
    // check is message sent was a command or not
    if(content.startsWith(".")){

        let cmd = content.slice(1,content.length)
        
        switch(cmd){
            case "help":
                message.channel.send("")

            case "..":

            default :
                message.channel.send("Command not found")
        }
    }
})

The above snippet takes the message sent and checks if it begins with the . prefix. The command is then extracted using the slice() method. slice() is used to extract a section of a string by specifying a starting point(inclusive) and an end point (exclusive).

.|help|

So .help becomes help.

Using the switch statement, commands can be mapped to a certain action. For instance the help command can trigger the bot to reply with a list of commands it can respond to.

The bot sends a reply using the message.channel.send() function.

Mallory Commands

I worked on Mallory during the lock-down period and at the time classes had moved online. Therefore, a number of the commands were centered on notifying me when class was about to start and when the next class was.

another pic

The .next command would trigger Mallory to respond with details about the next class of the day.

another pic another pic

Discord bots can also be used to make a server interactive by allowing users to play simple chat games for instance a riddle game.

another pic

The Hangman Game

another pic

Due to the complexity of this command, I implemented it using Object Oriented Programming. In hangman, players guess letters randomly until they either guess the correct word or run out of chances. Some key attributes of the game are:

  • The word being guessed
  • Correctly guessed letters
  • How many chances players have
  • The currently guessed letters
class Hangman{
    constructor(word,channel){
        this.word = word; // represents word being guessed
        this.channel = channel; // text-channel the game is being played on
        this.wrongCount = 0; // number of failed attempts so far
        this.guesses = []; // letters guessed so far
        this.wordProgress = []; // portion of word guessed
        this.isFinished = false; // game status
    }

Initializing the game

[...this.word].forEach(c => {
    this.wordProgress.push(`šŸ”µ`)
});

To initialize the game the following code is also included within the constructor.

The word being guessed for instance “dog” is broken into its respective letters and then each letter is replaced with “šŸ”µ” to represent a blank space. So “dog” becomes “šŸ”µšŸ”µšŸ”µ” and this is stored in the this.wordProgress array as individual characters.

Guessing letters

When a player guesses a letter the guess() method is called and the letter is passed to it as an argument.

guess(letter){
    // Check is letter has already been guessed
    if(this.guesses.includes(letter)){
        this.channel.send(`already guessed letter \`${letter}\``);
        return;
    // Check if guessed letter is part of the word    
    }else if(!this.word.includes(letter)){
        this.wrongCount++;
    }

    // Check if players have run out of guessing chances
    if(this.wrongCount >= 6){
        this.isFinished = true;
        this.channel.send(`Game over. Word was: ${this.word}`);
        return;
    }
}

If the guessed letter is part of the word, we need to figure out which position the letter is in the word. For instance if the word is apple and someone guesses p, we need to update “šŸ”µšŸ”µšŸ”µšŸ”µšŸ”µ” to “šŸ”µ p pšŸ”µšŸ”µ”.

   for (let i = 0; i < word.length; i++) {
        if(this.word[i] == letter){
            this.wordProgress[i] = `${letter.toUpperCase()}`;
        }
    }

Looping through the word e.g apple, we compare each letter with the guessed letter and if they match, replace the “šŸ”µ” character at that index of this.wordProgress array with the guessed letter.

this.wordProgress array before guessing p

[šŸ”µ,šŸ”µ,šŸ”µ,šŸ”µ,šŸ”µ]

this.wordProgress array after guessing p

[šŸ”µ, p , p ,šŸ”µ,šŸ”µ]

Game completion

Finally, to check if the game is complete, we need to compare the word being guessed and the correct letters guessed so far (stored in the this.wordProgress array as characters). This should be done with each guess.

If the word progress is equal to the word being guessed the game is won.

if(this.wordProgress.toString()== this.word.toUpperCase()){
    this.channel.send("Game won")
}

array.toString() method converts our array of letters into a single string. [a,p,p,l,e] becomes apple

Playing the game

To start a game the .hangman command can be used and .guess [letter] command for guessing

client.on('message',(message)=>{
    
    let content = message.content; 
  
    if(content.startsWith(".")){

        let cmd = content.slice(1,content.length)

        // split "guess a" to an array: ["guess","a"] 
        // Then extract the guessed letter at index 1 of the array
        let guess = cmd.split(" ")[1]
        
        // OR

        let guess = cmd.split(" ")
        guess = guess[1]
        
        switch(cmd){
            case "hangman":
                // pass the word to be guessed and channel where the bot will be responding to
                // (same channel the command was typed in for this case)
                let game = new Hangman("apple",message.channel)

            case "guess":
                game.guess(guess)
        }
    }
})

Other commands

Mallory could also join Voice channels and play music from Youtube which was made possible using discord-youtube-api and node-ytdl-core libraries.

Below are the commands which can be used to interact with the music player

another pic

Adding music to the queue and showing what tracks are currently in the queue.

another pic

Dicord bots are a good addition to any server since they provide users with fun activities making a server more active.

Cheers.

The source code can be found on my Github Repository

The tutorial I used in learning how to create a Discord bot can be found here