Cool Discord Bots with BJDA
Tags: discord, kotlin, java
Published: 2022-07-27 15:25:00

## How is it started?
A few months ago, I started to create a discord bot for task management which is built for big communities.

It is painful for developers to set up an event handler system for component interaction events, specially for developers who care about their code quality.

Therefore, I wrote a discord bot framework in **Kotlin**, a new solution for creating a cool discord bot. 
_It is still in an experimental stage, things might be broken when updating to a newer version._

## Why BJDA?
BJDA is based on **JDA (Java Discord API)**.

It provides those things:
1. A better way for creating and listening to Slash Commands
2. A UI module for creating powerful high-quality UI 
3. Dynamically updating for messages, and state management similar to React.js

With those cool features, you can create a cool bot easier!

_Actually, for Kotlin, we have Kord which is also in an experimental stage. BJDA will also be moved to Kord soon_

## Getting Started

### Install
```
<dependency>
    <groupId>io.github.sonmoosans</groupId>
    <artifactId>bjda</artifactId>
    <version>4.3.1</version>
</dependency>
```

Now, we can create a Todo discord bot.
First of all, you need to get a JDA instance
```kotlin
fun main() {
    val jda = JDABuilder.createDefault("token here")
        .build()
        .awaitReady()
}
```
Then install modules with BJDA by doing `BJDA.create(jda)`
```kotlin
fun main() {
    val jda = JDABuilder.createDefault("token here")
        .build()
        .awaitReady()

    BJDA.create(jda)
        .install(
            SuperCommandModule( //slash commands
                TodoCommands,
            ),
            UIEventModule() //ui event handlers
        )
}
```
**Notes:** SuperCommand means Slash Command.
UIEventModule should always be installed if your bot uses UI listeners provided by BJDA.

After that, we are going to create the `TodoCommands` variable
```kotlin
val TodoCommands = SuperCommandGroup.create("todo", "Todo Commands",
    CreateTodo()
)
```
and create the `/todo create` command
```kotlin
val users = UIStore<User>()

private class CreateTodo: SuperCommand(name = "create", description = "Create a Todo List") {
    override val run: CommandHandler = {
        val ui = users.getUI(event.user) {
            UI(
                TodoApp {}
            )
        }

        ui.reply(event) {
            ui.listen(it)
        }
    }
}
```
`UIStore` is util for storing UI instances, similar to `HashMap<*, UI>`.

The `run` function is fired when the Slash command is executed.
`users.getUI(event.user)` will return a UI instance or default value.

Notice that `ui.listen(it)` means that it supports real-time updating. When the ui is updated, all messages will be edited.

After declaring the command, we can start designing our UI!
**Imports:**
```kotlin
import bjda.plugins.ui.hook.ButtonClick.Companion.onClick
import bjda.plugins.ui.hook.MenuSelect.Companion.onSelect
import bjda.plugins.ui.modal.Form.Companion.form
import bjda.ui.component.RowLayout
import bjda.ui.component.Text
import bjda.ui.component.TextType
import bjda.ui.component.action.Button
import bjda.ui.component.action.Menu
import bjda.ui.component.action.TextField
import bjda.ui.core.*
import bjda.ui.core.FComponent.Companion.component
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle
import net.dv8tion.jda.api.interactions.components.selections.SelectOption
import net.dv8tion.jda.api.interactions.components.text.TextInputStyle
```

**State**
Same as React, you can define a `prop` and `state`.
In this example, we are not going to define a prop for it.

```kotlin
data class State(
    val todos: ArrayList<String> = ArrayList()
)
```

Next, we can create our component:
```kotlin
val TodoApp = component {
    val state = useState(State())

    val addForm by form {
        title = "Add Todo"

        onSubmit = { event ->

            state.update(event) {
                todos += event.value("todo")
            }
        }
        render = {
            + row (
                TextField("todo") {
                    label = "TODO"
                    style = TextInputStyle.PARAGRAPH
                }
            )
        }
    }

    val onAddItem by onClick { event ->
        event.replyModal(addForm).queue()
    };

    {
        val (todos) = state.get()

        + Text()..{
            content = "**TODO List**"
            type = TextType.LINE
        }

        + on (todos.isEmpty()) {
            Text()..{
                content = "No Todos"
                type = TextType.CODE_BLOCK
            }
        }

        //Elements Collections will be converted to Fragment
        + todos.mapIndexed { i, todo ->
            TodoItem(i, todo)
        }

        + RowLayout() -{
            + Button(onAddItem) {
                label = "Add"
            }
        }
    }
}
```
In the above code, we did:
1. Modal to Add a todo
2. a Button to Open the Modal
3. Code blocks to list all the todos

You can try to understand those codes by running the application and modifying the example.

### Test your bot
Now run the main function and start testing!

Try to run the `/todo create` command, you should see a panel.
![Todo Create](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tm673ugd6grgbungxiei.png)

Click the "Add" button and add some todos.
![Add Todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h15thd45p0us4gc7fy9n.png)

**Keep improving**
You can add a button to close the panel. Therefore, you can destroy the UI and remove it from UIStore `users`, to avoid memory leaks.

Now, you just created a cool todo bot with BJDA!
You can take a look at the Github repo: https://github.com/SonMooSans/bjda