DEV Community 👩‍💻👨‍💻

Cover image for Using KotlinJS and NPM to create a Discord Bot - Discord Bot Series (Part 2)
Kevin Schildhorn
Kevin Schildhorn

Posted on

Using KotlinJS and NPM to create a Discord Bot - Discord Bot Series (Part 2)

Discord is a great platform for keeping in touch with friends, and adding a bot can enrich that experience. A bot can respond to messages, perform an action based on a command, or even add music to a voice chat.

In this series we'll be going over how to create a small example bot by using the NPM module for DiscordJS in a KotlinJS project.

Note: This is the second post in a series of three posts, about how to use KotlinJS and NPM modules to create a full fledged project, in this case a Discord Bot. You can find the first post here.

Previously

In my last post we went over how to import node modules into our KotlinJS project, and created a simple project to test our imports. In this post we'll go over how to add DiscordJS to our project, and start creating our bot! Let's get started.

As a refresher, in this series we're going to create a small discord bot that responds to a specific message with its computer name.

Registering a Discord Bot

Before digging into code make sure you register a bot. Choose New Application, add a name and click create.

You can find more documentation on this process here.

After you create your bot you should see this screen.
Image description

Click on the Bot tab on the left and in the Build-a-bot section click add bot. Once you've done this you'll have created a bot! Be sure to keep the token from this screen for use in the project.

Also be sure to register your bot to your Channel using this guide.

Initializing DiscordJS

For our KotlinJS project we'll be using DiscordJs, which is a well documented and widely used node module for discord.

So first we'll add the dependency to the build.gradle file like before:

dependencies {
    implementation(npm("discord.js", "13.6.0"))
}
Enter fullscreen mode Exit fullscreen mode

Then we can reference the Creating the main file documents to find out how to launch the bot. We can see that the client is initialized, then it listens for messages, then finally calls to login.

This is in Javascript but the kotlin implementation will look mostly the same. First we need to define the Client class. So we'll go to the documentation for the client, and see what we need to define. We can see that:

  • Client takes in ClientOptions to init.
  • Client inherits from EventEmitter, which is how once is called
  • Client login function takes in a string token and returns a string promise.

Init

Before we go defining ClientOptions and potentially going down the rabbit hole, we can see that it's defined as an object. In this case we can just pass a Json object in the constructor.

@JsModule("discord.js")
external class Client(config: Json?)

const val GUILDS = 1 // 1 << 0
val client = Client(json(Pair("intents", arrayOf(GUILDS))))
Enter fullscreen mode Exit fullscreen mode

The client takes in "intents", which is an array of just Intents.FLAGS.GUILDS. If we follow the documentation from DiscordJS to the intents we can find their definition here. We see that Intents.FLAGS.GUILDS is 1 << 0 (or 1) so we'll pass that.

Once

This function we can figure out by the simple call. This is just to let us know the client is ready, so we'll just print out a confirmation.

@JsModule("discord.js")
external class Client(config: Json?) {
    fun once(event: String, cb: (Unit) -> Unit)
}

client.once("ready") {
    println("Hello From ${computerName()}!")
}
Enter fullscreen mode Exit fullscreen mode

We can tell that it takes in a string, and returns a block. Since there's no input or output we can just use Unit, which acts as void.

Login

Based on what we learned we can define this easily. The token we will pass in comes from your created bot on the discord site. This was the token that was grabbed from the beginning of this post.

@JsModule("discord.js")
external class Client(config: Json?) {
    //...
    fun login(token: String): Promise<String>
}

//Main.kt
client.login("YOUR_TOKEN")
Enter fullscreen mode Exit fullscreen mode

In this case we can tell the function returns a Promise from the DiscordJS documentation.

Running your bot

At this point all the calls should be in, your Main.kt should look something like this:

const val GUILDS = 1 // 1 << 0
val client = Client(json(Pair("intents", arrayOf(GUILDS))))
client.once("ready") {
    println("Hello From ${computerName()}!")
}
client.login(token)
Enter fullscreen mode Exit fullscreen mode

If you run it you should be able to see your computer name in the terminal and your bot should be online!

Responding to Messages

This is great that the bot is running, however right now it's not doing anything. So let's get the bot to respond to a message.

Note: intent

In order for the bot to get messages, the client needs to register another intent, GUILDS_MESSAGES.

const val GUILDS = 1 // 1 << 0
const val GUILDS_MESSAGES = 512  //1 << 9
val client = Client(json(Pair("intents", arrayOf(GUILDS, GUILDS_MESSAGES))))

Enter fullscreen mode Exit fullscreen mode

Defining Messages

So first we'll quickly define a message, as that's what's passed in. From the docs we can see a message contains a content string, the author, and the channel it was sent in. we're going to use the channel to send the reply to, and use the author to create a personalized message.
To make things quicker I've defined what we need from the documentation below:

external class Message {
    val author: User
    val channel: TextChannel
    val content: String
}

external class User {
    val avatar: String
    val username: String
}

external class TextChannel {
    fun send(content: Any): Promise<Message>
}
Enter fullscreen mode Exit fullscreen mode

Listening for Messages

With that being set we can then go to the Client definition and add a listener for the message:

external class Client(config: Json?) {
    // ...
    fun on(event: String, cb: (item: Message) -> Unit)
}
Enter fullscreen mode Exit fullscreen mode

So now we can listen for "message" events (or rather "messageCreate" events defined in the migration guide).

client.on("messageCreate") { message ->
    print(message.content)
}
Enter fullscreen mode Exit fullscreen mode

To go even further let's implement our desired code. We'll check to see if someone says hello, and if they do respond with their name and the bots computer name.

client.on("messageCreate") { message ->
    if(message.content == "hello")
        message.channel.send("Hello ${message.author.username} From ${computerName()}!")
}
Enter fullscreen mode Exit fullscreen mode

Now build and run the bot, and say hello! You should see a response like so.

Discord Response

Conclusion

Congrats, You've made a working Discord Bot! You now have the basics to expand your bot to respond to lots of different messages in many ways.

After playing around with the bot you may realize that there are some uncaught issues, for example the bot won't respond if there's a space in the message or if "Hello" is capitalized. In the next post of this series, we'll go over how we can write tests to catch these issues and make sure our bot works the way we expect.

Thanks for reading! Let me know in the comments if you have questions. Also, you can reach out to me at @kevinschildhorn on Twitter.

Top comments (0)

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post