Skip to content

MyraBot/slasher

Repository files navigation

🗡️ Slasher

A simple slash command handler written in Kotlin. Annotation based command mapping with a simple support for subcommands.
Because this handler is for a private Discord wrapper this code won't work. Feel free to port it though. This shouldn't take too long!

Warning

🏗️ Set up

Before registering commands you have to set up the command handler. The handler is based on reflection which makes registering commands easier. At least I feel so.
This means that you are not able to register commands by a function or so. To register an entire package assign the commandPackage variable to the package, separated by dots.
The handler function return the actual Handler class which implements EventListener. The EventListener interface makes it possible to listen for events in my Discord Wrapper. So store the result of the handler function, since we will later register the handler as an event listener. To finish the configuration of the handler load the cogs by using Handler#loadCogs.

To make the handler actually handle commands, we have to register the stored handler variable as an EventListener from diskord#addEventListener.

fun main() {

    val commandHandler = handler {
        commandPackage = "com.example.myApp.commands"
    }.also { it.loadCogs() }

    diskord {
        token = "YOUR_TOKEN"
        addListeners(commandHandler)
        connectGateway()
    }

}

Now you are ready to write your command functions. Let's take a look at 2 examples.
So we want to make function handling the following 2 commands:

  • /help
  • /ban temp [user] [timeInMinutes]

The first command is simple:

package com.example.myApp.commands.General

object General {
    
    @Command("help")
    fun onHelpCommand(ctx: CommandContext) {
        /* Handle help command */
    }
    
}

The second command is a subcommand, and it contains arguments. To handle such case you do the following:

package com.example.myApp.commands.Moderation

object Moderation {
    
    @Command("ban temp")
    fun onBanTempCommand(ctx: CommandContext, user: User, timeInMinutes: Int) {
        /* Handle temp ban command */
    }
    
}

Note that the parameters of onBanTempCommand must match to 100% with the argument names of the command. Read more about arguments here

🧬 Structure

Cogs

Commands are grouped together in kotlin objects. You can't use classes for this! Because the library is annotation based, you shouldn't use an object for a single command function.
For example: You have an info user, info guild and info emoji command then you would put them all in one object (or called Cog).

Commands

As already said commands are annotation based. I prefer the way of having multiple commands in a single file/class/object. With the Command annotation you can make any function which is in a Cog, a command function. Always remember to make the command function public, otherwise it won't work.

Command Arguments

Arguments are handled as command function parameters. Pretty cool, huh?
So let's take a look at an example.
Imagine you have a command like this one: /ban [user] [reason]
So your command function will probably look something like this:

@Command("ban")
fun onBanCommand(ctx: CommandContext) {
    // How do I get the user and reason argument?
}

In order to use the arguments, add their datatype with the exact argument name as a parameter in the function:

Valid ✔️

@Command("ban")
fun onBanCommand(ctx: CommandContext, user: Member, reason: String) {
    /* Handle ban */
}
@Command("ban")
fun onBanCommand(ctx: CommandContext, user: User, reason: String) {
    /* Handle ban */
}

Invalid ❌

@Command("ban")
fun onBanCommand(ctx: CommandContext, user: String, reason: String) {
    /* Handle ban */
}
@Command("ban")
fun onBanCommand(ctx: CommandContext, user: User, reason: Reason) {
    /* Handle ban */
}
@Command("ban")
fun onBanCommand(ctx: CommandContext, member: User, reason: String) {
    /* Handle ban */
}

Though what if the reason argument is optional? To handle optional arguments, make the parameter nullable. There is no need to add a default value. If no reason argument was provided, the parameter would be null automatically.

@Command("ban")
fun onBanCommand(ctx: CommandContext, member: User, reason: String?) {
    if(reason == null) {
        // No reason given
    } else {
        // A reason is given
    }
    
    /* Continue handling ban */
}

Command context

The CommandContext is a replacement for the GuildSlashCommandEvent. Actually it is a GuildSlashCommandEvent, because it extends this class. So this is basically just your event parameter with some extra fields. Use this to get other information than the parameters such as the current guild, the member who executed the command or the channel in which the command got executed.