Skip to content

Declarative text commands

Declarative commands allow you full control over your commands, start with a service implementing TextCommandProvider.

They must be declared with Kotlin, but the command itself may be in any language.

The implemented method gives you a manager, you can use the textCommand function to create a new top-level text command.

Example

@Command
class TextPing : TextCommandProvider {
    suspend fun onTextPing(
        // Fallback command event since we have no args
        event: CommandEvent
    ) {
        val message = event.reply("Pong!").await()

        val ping = event.jda.getRestPing().await()
        message.editMessage("Pong! $ping ms").queue()
    }

    override fun declareTextCommands(manager: TextCommandManager) {
        manager.textCommand("ping") {
            // Shared top level properties here

            category = "Utility"
            description = "Pong!"

            variation(::onTextPing) {
                // Properties of this variation only
            }
        }
    }
}

Tip

You are free to skip registration of any command/subcommand/group by returning.

Subcommands

With text commands, you're free to have any command structure

Add a subcommand by using subcommand, you'll find text subcommands to be very similar to top-level commands.

Example

@Command
class TextTag : TextCommandProvider {
    fun onTextTagCreate(event: CommandEvent) {
        // ...
    }

    fun onTextTagDelete(event: CommandEvent) {
        // ...
    }

    override fun declareTextCommands(manager: TextCommandManager) {
        manager.textCommand("tag") {
            description = "Manage tags"

            subcommand("create") {
                description = "Creates a tag"

                variation(::onTextTagCreate) {
                    // ...
                }
            }

            subcommand("delete") {
                description = "Deletes a tag"

                variation(::onTextTagDelete) {
                    // ...
                }
            }
        }
    }
}

Adding options

Options can be added with a parameter and declaring it using option in your command builder, where the declaredName is the name of your parameter, the block will let you change properties.

All supported types are documented under TextParameterResolver, and other types can be added.

Example

@Command
class TextSay : TextCommandProvider {
    suspend fun onTextSay(event: BaseCommandEvent, content: String) {
        event.reply(content).await()
    }

    override fun declareTextCommands(manager: TextCommandManager) {
        manager.textCommand("say") {
            // Shared top level properties here

            description = "Says something"

            // The "say" command can only be parsed one way, so, one variation
            variation(::onTextSay) {
                // Properties of this variation only

                // This will show up in the help command, if the prefix is '!' then it will show:
                // !say I like trains
                example = "I like trains"

                // Declare the "content" parameter as a regular option
                option("content")
            }
        }
    }
}

Tip

You can override the option name by setting optionName in the option declaration:

option("content", optionName = "sentence") {
    ...
}

Generated values

Generated values are a command parameter that gets their values computed by the given block everytime the command run.

Contrary to the annotated commands, no checks are required, as this is tied to the currently built command.

Example

@Command
class TextCreateTime : TextCommandProvider {
    suspend fun onTextCreateTime(event: CommandEvent, timestamp: Instant) {
        event.reply("I was created on ${TimeFormat.DATE_TIME_SHORT.format(timestamp)}").await()
    }

    override fun declareTextCommands(manager: TextCommandManager) {
        manager.textCommand("create_time") {
            // Shared top level properties here

            description = "Shows the creation time of this command"

            variation(::onTextCreateTime) {
                // Properties of this variation only

                // Create a snapshot of the instant the command was created
                val now = Instant.now()
                // Declare the "timestamp" parameter as a generated option
                // The closure is executed everytime the command runs
                generatedOption("timestamp") { _: BaseCommandEvent ->
                    // Give back the instant snapshot, as this will be called every time the command runs
                    now
                }
            }
        }
    }
}

Rate limiting

This lets you reject application commands if the user tries to use them too often, see "Using rate limiters in commands".