Slash Commands

Slash Commands are the new way of interacting with Discord bots. AckCord comes with a built-in slash commands framework.

Commands

The simplest command you can make is a /ping command.

import ackcord.interactions._
import ackcord.interactions.commands._

class MyPingCommands(requests: Requests) extends CacheApplicationCommandController(requests) {

  val pongCommand = SlashCommand.command("ping", "Check if the bot is alive") { _ =>
    sendMessage("Pong")
  }
}

Arguments

class MyParameterCommands(requests: Requests) extends CacheApplicationCommandController(requests) {

  // Single argument
  val echoCommand = SlashCommand
    .withParams(string("message", "The message to send back"))
    .command("echo", "Echoes a message you send") { implicit i =>
      sendMessage(s"ECHO: ${i.args}")
    }

  // Multiple arguments
  val multiArgsCommand = SlashCommand
    .withParams(string("message", "The message to send back") ~ string("intro", "The start of the message"))
    .command("echoWithPrefix", "Echoes a message you send") { implicit i =>
      sendMessage(s"${i.args._1}: ${i.args._2}")
    }

  // Optional arguments
  val optArgsCommand = SlashCommand
    .withParams(string("message", "The message to send back").notRequired)
    .command("echoOptional", "Echoes an optional message you send") { implicit i =>
      sendMessage(s"ECHO: ${i.args.getOrElse("No message")}")
    }
}

Autocomplete

Discord allows you to autocomplete arguments to allow the user to enter better arguments.

This command will bring a small menu up with suggestions for the arguments, it will show 3 options, each a differently multiplied version of the number you have initially typed.

class MyAutocompleteCommands(requests: Requests) extends CacheApplicationCommandController(requests) {

  val autocompleteCommand = SlashCommand
    .withParams(string("auto", "An autocomplete parameter").withAutocomplete(s => Seq(s * 2, s * 3, s * 4)))
    .command("simple-autocomplete", "A simple autocomplete command") { i =>
      sendMessage(s"Res: ${i.args}")
    }
}

Async

Async commands allow you to inform the user there will be a slight delay before the bot responds, useful for commands that fetch external resources or do a lot of work.

class MyAsyncCommands(requests: Requests) extends CacheApplicationCommandController(requests) {

  // This command will very quickly show a loading message in discord.
  val asyncCommand = SlashCommand.command("async", "An async test command") { implicit i =>
    async(implicit token => sendAsyncMessage("Async message"))
  }
}

This will edit the message you’ve sent, after it has been received in Discord but will not show the user a loading message.

class MyAsyncEditCommands(requests: Requests) extends CacheApplicationCommandController(requests) {

  // This command will show a loading message in discord, send a message and edit the original message (which would mark the end of the async interaction).
  val asyncEditCommand = SlashCommand
    .withParams(string("par1", "The first parameter") ~ string("par2", "The second parameter"))
    .command("asyncEdit", "An async edit test command") { implicit i =>
      sendMessage("An instant message").doAsync { implicit token =>
        editOriginalMessage(content = JsonSome("An instant message (with an edit)"))
      }
    }
}

Command groups

Discord allow you to group commands together, this is useful for when you have a lot of commands that are similar.

This example will register the commands: /group foo and /group bar

class MyGroupedCommands(requests: Requests) extends CacheApplicationCommandController(requests) {

  val groupCommand = SlashCommand.group("group", "Group test")(
    SlashCommand.command("foo", "Sends foo")(_ => sendMessage("Foo")),
    SlashCommand.command("bar", "Sends bar")(_ => sendMessage("Bar"))
  )
}

Registering commands with Discord

You need to register slash commands with discord for them to appear, it can be done like this.

Commands that are globally registered can take up to an hour to propagate to all servers so using guild commands in development is recommended.

val myPingCommands = new MyPingCommands(client.requests)
val myParameterCommands = new MyParameterCommands(client.requests)

val myAutocompleteCommands = new MyAutocompleteCommands(client.requests)
val myAsyncCommands = new MyAsyncCommands(client.requests)
val myAsyncEditCommands = new MyAsyncEditCommands(client.requests)

client.onEventSideEffectsIgnore {
  case msg: APIMessage.Ready =>
    
    // Create the commands globally in all discords.
    InteractionsRegistrar.createGlobalCommands(
      msg.applicationId, // Client ID
      client.requests,
      replaceAll = true, // Boolean whether to replace all existing
      // CreatedGuildCommand*
      myPingCommands.pongCommand,
      myParameterCommands.echoCommand,
      myParameterCommands.multiArgsCommand,
      myParameterCommands.optArgsCommand
    )
    
    val myGuildId = GuildId("<some id here>")
    
    // Create the commands in a specific discord.
    InteractionsRegistrar.createGuildCommands(
      msg.applicationId, // Client ID
      myGuildId, // Guild ID
      client.requests,
      replaceAll = true, // Boolean whether to replace all existing
      // CreatedGuildCommand*
      myAutocompleteCommands.autocompleteCommand,
      myAsyncCommands.asyncCommand,
      myAsyncEditCommands.asyncEditCommand
    )
}

Registering commands with Ackcord

When you have created all your slash commands and registered them with discord you can register them with ackcord.

client.onEventSideEffectsIgnore { case msg: APIMessage.Ready =>
  client.runGatewayCommands(msg.applicationId.asString)(
    myParameterCommands.echoCommand,
    myParameterCommands.multiArgsCommand,
    myParameterCommands.optArgsCommand,
    myAutocompleteCommands.autocompleteCommand,
    myAsyncCommands.asyncCommand,
    myAsyncEditCommands.asyncEditCommand
  )
}