DEV Community

Cover image for Cool Discord Bots with BJDA
MONEY
MONEY

Posted on

Cool Discord Bots with BJDA

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>
Enter fullscreen mode Exit fullscreen mode

Now, we can create a Todo discord bot.
First of all, you need to get a JDA instance

fun main() {
    val jda = JDABuilder.createDefault("token here")
        .build()
        .awaitReady()
}
Enter fullscreen mode Exit fullscreen mode

Then install modules with BJDA by doing BJDA.create(jda)

fun main() {
    val jda = JDABuilder.createDefault("token here")
        .build()
        .awaitReady()

    BJDA.create(jda)
        .install(
            SuperCommandModule( //slash commands
                TodoCommands,
            ),
            UIEventModule() //ui event handlers
        )
}
Enter fullscreen mode Exit fullscreen mode

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

val TodoCommands = SuperCommandGroup.create("todo", "Todo Commands",
    CreateTodo()
)
Enter fullscreen mode Exit fullscreen mode

and create the /todo create command

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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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

data class State(
    val todos: ArrayList<String> = ArrayList()
)
Enter fullscreen mode Exit fullscreen mode

Next, we can create our component:

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"
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Click the "Add" button and add some todos.
Add Todo

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

Top comments (0)