Nosedrum.ApplicationCommand behaviour (nosedrum v0.6.0) View Source

The application command behaviour specifies the interface that a slash, user, or message command module should implement.

Like regular commands, application command modules are stateless on their own. Implementations of the callbacks defined by this behaviour are invoked from other modules/functions, notably a Nosedrum.Storage.

The types defined in this module reflect the official Application Command docs.

Example Slash Command

This command echos the passed message back to the user.

# In your application command module file, e.g. ./lib/my_app/commands/echo.ex
defmodule MyApp.Commands.Echo do
  @behaviour Nosedrum.ApplicationCommand

  @impl true
  def description() do
    "Echos a message."
  end

  @impl true
  def command(interaction) do
    [%{name: "message", value: message}] = interaction.data.options
    [
      content: message,
      ephemeral?: true
    ]
  end

  @impl true
  def type() do
    :slash
  end

  @impl true
  def options() do
    [
      %{
        type: :string,
        name: "message",
        description: "The message for the bot to echo.",
        required: true
      }
    ]
  end
end
# In your Nostrum.Consumer file, e.g. ./lib/my_app/consumer.ex
defmodule MyApp.Consumer do
  use Nostrum.Consumer

  # ...

  # You may use `:global` instead of a guild id at GUILD_ID_HERE, but note
  # that global commands could take up to an hour to become available.
  def handle_event({:READY, _data, _ws_state}) do
    case Nosedrum.Storage.Dispatcher.add_command("echo", MyApp.Commands.Echo, GUILD_ID_HERE) do
      {:ok, _} -> IO.puts("Registered Echo command.")
      e -> IO.inspect(e, label: "An error occurred registering the Echo command")
    end
  end

  def handle_event({:INTERACTION_CREATE, interaction, _ws_state}) do
    Nosedrum.Storage.Dispatcher.handle_interaction(interaction)
  end
end

You will also need to start the Nosedrum.Storage.Dispatcher as part of your supervision tree, for example, by adding this to your application start function:

# ./lib/my_app/application.ex
defmodule MyApp.Application do
  # ...
  def start(type, args) do
    children = [
      # ...
      {Nosedrum.Storage.Dispatcher, name: Nosedrum.Storage.Dispatcher},
    ]

    options = [strategy: :rest_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, options)
  end
end

Link to this section Summary

Types

Called by Nosedrum.Storage.followup/2 after deferring an interaction response.

A choice for an option.

An option (argument) for an application command.

A keyword list of fields to include in the interaction response, after running the command/1 callback.

A field in a keyword list interaction response.

A value of the :type field in a command/1 return value. See response/0 for more details.

Callbacks

Execute the command invoked by the given Nostrum.Struct.Interaction.t/0. Returns a response/0

Returns a description of the command. Used when registering the command with Discord. This is what the user will see in the autofill command-selection menu.

An optional callback that returns a list of options (arguments) that the command takes. Used when registering the command with Discord. Only valid for CHAT_INPUT application commands, aka slash commands.

Returns one of :slash, :message, or :user, indicating what kind of application command this module represents.

Link to this section Types

Specs

callback() ::
  {(... -> response()), args :: list()}
  | {module(), fn_name :: atom(), args :: list()}

Called by Nosedrum.Storage.followup/2 after deferring an interaction response.

The callback should return a response similar to command/1, excluding the :type, :tts?, and :ephemeral? options.

Specs

choice() :: %{name: String.t(), value: String.t() | number()}

A choice for an option.

See callback options/0 documentation for examples.

Specs

option() :: %{
  optional(:required) => true | false,
  optional(:choices) => [choice()],
  optional(:options) => [option()],
  type:
    :sub_command
    | :sub_command_group
    | :string
    | :integer
    | :boolean
    | :user
    | :channel
    | :role
    | :mentionable
    | :number,
  name: String.t(),
  description: String.t()
}

An option (argument) for an application command.

See callback options/0 documentation for examples.

Specs

response() :: [response_field()]

A keyword list of fields to include in the interaction response, after running the command/1 callback.

If :type is not specified, it will default to :channel_message_with_source, though one of either :embeds or :content must be present.

If you are deferring an interaction response with :deferred_channel_message_with_source or :deferred_update_message, you should also supply a callback under :type in the form of {:deferred_*, callback_tuple} (See the Deferred Response Example below for more details on callback_tuple).

Example

def command(interaction) do
  # Since `:content` is included, Nosedrum will infer `type: :channel_message_with_source`
  [
    content: "Hello, world!",
    ephemeral?: true,
    allowed_mentions: ["users", "roles"]
  ]
end

Deferred Response Example

In order to avoid a potential race condition when deferring, you should supply a callback function for Nosedrum to call only after the initial response succeeds. The callback should take the form of {anonymous_fn, args}, or an MFA (Module, Function, Args) tuple, like {MyCommand, :followup_fn, [interaction, extra_arg]}

@impl Nosedrum.ApplicationCommand
def command(interaction) do
  [
    type: {:deferred_channel_message_with_source, {&expensive_calculation/1, [interaction]}}
  ]
end

defp expensive_calculation(interaction) do
  # ... do expensive things
  [
    content: "Hello, I've been edited in after the original interaction response"
  ]
end

Specs

response_field() ::
  {:type, response_type()}
  | {:content, String.t()}
  | {:embeds, [Nostrum.Struct.Embed.t()]}
  | {:components, [map()]}
  | {:ephemeral?, boolean()}
  | {:tts?, boolean()}
  | {:allowed_mentions, [String.t()] | []}

A field in a keyword list interaction response.

Special notes:

  • :type is required, unless :content or :embeds is present, in which case it defaults to :channel_message_with_source.
  • :allowed_mentions is a list that should contain "users", "roles", and/or "everyone", or be empty.

Specs

response_type() ::
  :channel_message_with_source
  | :deferred_channel_message_with_source
  | :deferred_update_message
  | {:deferred_channel_message_with_source, callback()}
  | {:deferred_update_message, callback()}
  | :pong
  | :update_message

A value of the :type field in a command/1 return value. See response/0 for more details.

Link to this section Callbacks

Specs

command(interaction :: Nostrum.Struct.Interaction.t()) :: response()

Execute the command invoked by the given Nostrum.Struct.Interaction.t/0. Returns a response/0

Example

defmodule MyApp.MyCommand do
  @behaviour Nosedrum.ApplicationCommand

  # ...

  @impl true
  def command(interaction) do
    %{name: opt_name} = List.first(interaction.data.options)
    [content: "Hello World #{opt_name}!"]
  end
end

Specs

description() :: String.t()

Returns a description of the command. Used when registering the command with Discord. This is what the user will see in the autofill command-selection menu.

Example

def description, do: "This is a command description."

Specs

options() :: [option()]

An optional callback that returns a list of options (arguments) that the command takes. Used when registering the command with Discord. Only valid for CHAT_INPUT application commands, aka slash commands.

Read more in the official Application Command documentation.

Example options callback for a "/role" command

def options, do:
  [
    %{
      type: :user,
      name: "user",
      description: "The user to assign the role to.",
      required: true # Defaults to false if not specified.
    },
    %{
      type: :role,
      name: "role",
      description: "The role to be assigned.",
      required: true,
      choices: [
        %{
          name: "Event Notifications",
          value: 123456789123456789 # A role ID, passed to your `command/1` callback via the Interaction struct.
        },
        %{
          name: "Announcements",
          value: 123456789123456790
        }
      ]
    }
  ]

Specs

type() :: :slash | :message | :user

Returns one of :slash, :message, or :user, indicating what kind of application command this module represents.