Using KotlinJS and NPM to create a Discord Bot - Discord Bot Series (Part 2)
Tags: kotlin, javascript, discord, kotlinjs
Published: 2022-04-26 18:37:31

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](https://dev.to/kevinschildhorn/how-to-use-npm-modules-in-kotlinjs-discord-bot-series-part-1-51ni).

### 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](https://discord.com/developers/applications). Choose `New Application`, add a name and click `create`. 

> You can find more documentation on this process [here](https://discord.com/developers/docs/intro).

After you create your bot you should see this screen.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m4g9pza8odqttlvklqab.png)

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](https://discordjs.guide/preparations/adding-your-bot-to-servers.html#bot-invite-links).

### Initializing DiscordJS

For our KotlinJS project we'll be using [DiscordJs](https://discord.js.org/#/), 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"))
}
```

Then we can reference the [Creating the main file documents](https://discordjs.guide/creating-your-bot/#creating-the-main-file) 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](https://discord.js.org/#/docs/discord.js/v13/class/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.

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

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

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](https://discord.com/developers/docs/topics/gateway#list-of-intents). 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.

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

client.once("ready") {
    println("Hello From ${computerName()}!")
}
```

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.

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

//Main.kt
client.login("YOUR_TOKEN")
```
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:
```kotlin
const val GUILDS = 1 // 1 << 0
val client = Client(json(Pair("intents", arrayOf(GUILDS))))
client.once("ready") {
    println("Hello From ${computerName()}!")
}
client.login(token)
```

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))))

```
### Defining Messages

So first we'll quickly define a message, as that's what's passed in. From [the docs](https://discord.js.org/#/docs/discord.js/v13/class/Message) 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:

```kotlin
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>
}
```

### Listening for Messages

With that being set we can then go to the `Client` definition and add a listener for the message:
```kotlin
external class Client(config: Json?) {
    // ...
    fun on(event: String, cb: (item: Message) -> Unit)
}
```
So now we can listen for "message" events (or rather "messageCreate" events defined in the [migration guide](https://discordjs.guide/additional-info/changes-in-v13.html#naming-conventions)).
```kotlin
client.on("messageCreate") { message ->
    print(message.content)
}
```

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.

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

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

![Discord Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vuv1uevzgl7psl86lqf.png)

## 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](https://twitter.com/KevinSchildhorn) on Twitter.