EDA.Interaction (EDA v0.3.0)

Copy Markdown View Source

Helpers for working with Discord interactions.

Works directly with the raw interaction maps received from the gateway, providing convenient accessors and response helpers.

Handling Slash Commands

def handle_event({:INTERACTION_CREATE, interaction}) do
  import EDA.Interaction

  case command_name(interaction) do
    "ping" ->
      respond(interaction, "Pong!")

    "greet" ->
      msg = get_option(interaction, "message")
      target = get_option(interaction, "target")
      respond(interaction, content: "<@#{target}> #{msg}", ephemeral: true)

    "role" ->
      case sub_command_name(interaction) do
        "add" ->
          role_id = get_option(interaction, "role")
          respond(interaction, "Added <@&#{role_id}>!")

        "remove" ->
          respond(interaction, "Removed!")
      end
  end
end

Deferred Responses

# Show "thinking..." then edit later
defer(interaction)
# ... do work ...
edit_response(interaction, content: "Done!")

# Ephemeral defer
defer(interaction, ephemeral: true)

Summary

Functions

Responds with autocomplete results.

Returns the channel ID.

Returns the command name from the interaction data.

Returns the command type as an atom.

Returns the component type for a message component interaction.

Returns the custom_id for component interactions and modal submits.

Defers the interaction response (shows "thinking..." indicator).

Defers the interaction, runs the given function, then edits the response.

Deletes the original interaction response.

Deletes the message that triggered a component interaction.

Edits the original interaction response (typically after deferring).

Sends a followup message to the interaction.

Gets an option value by name from the interaction.

Returns all options as a flat map of %{"name" => value}.

Returns the guild ID, or nil in DMs.

Returns the interaction type as an atom.

Returns the guild member map, or nil in DMs.

Returns a resolved object by type and ID.

Sends an immediate response to the interaction.

Responds to an interaction by opening a modal dialog.

Returns the selected values from a select menu interaction.

Returns the sub_command name, or nil if not a sub_command invocation.

Returns the target ID for user/message context menu commands.

Returns the interaction token.

Returns the user who triggered the interaction (works in both guild and DM).

Types

interaction()

@type interaction() :: map()

Functions

autocomplete(interaction, choices)

@spec autocomplete(interaction(), [{String.t(), term()}]) :: :ok | {:error, term()}

Responds with autocomplete results.

Takes a list of {name, value} tuples (max 25).

Example

autocomplete(interaction, [
  {"Option A", "a"},
  {"Option B", "b"}
])

channel_id(arg1)

@spec channel_id(interaction()) :: String.t() | nil

Returns the channel ID.

command_name(arg1)

@spec command_name(interaction()) :: String.t() | nil

Returns the command name from the interaction data.

command_type(arg1)

@spec command_type(interaction()) :: :slash | :user | :message | nil

Returns the command type as an atom.

  • :slash (type 1, CHAT_INPUT)
  • :user (type 2, USER context menu)
  • :message (type 3, MESSAGE context menu)

component_type(arg1)

@spec component_type(interaction()) :: non_neg_integer() | nil

Returns the component type for a message component interaction.

Returns nil if not a component interaction.

Common types: 2 = button, 3 = string select, 5 = user select, 6 = role select, 7 = mentionable select, 8 = channel select.

Examples

case EDA.Interaction.component_type(interaction) do
  2 -> handle_button(interaction)
  3 -> handle_select(interaction)
  _ -> :ignore
end

custom_id(arg1)

@spec custom_id(interaction()) :: String.t() | nil

Returns the custom_id for component interactions and modal submits.

defer(interaction, opts \\ [])

@spec defer(
  interaction(),
  keyword()
) :: :ok | {:error, term()}

Defers the interaction response (shows "thinking..." indicator).

Must be followed by edit_response/2 within 15 minutes.

Options

  • :ephemeral - If true, the thinking indicator and subsequent response are only visible to the invoking user.

defer_and_edit(interaction, fun, opts \\ [])

@spec defer_and_edit(interaction(), (-> String.t() | keyword()), keyword()) ::
  {:ok, map()} | {:error, term()}

Defers the interaction, runs the given function, then edits the response.

Wraps the common defer → do work → edit_response pattern in a single call. The function receives no arguments and should return a string or keyword list suitable for edit_response/2.

Options

  • :ephemeral — if true, the thinking indicator and response are ephemeral

Examples

EDA.Interaction.defer_and_edit(interaction, fn ->
  result = do_heavy_work()
  "Result: #{result}"
end)

EDA.Interaction.defer_and_edit(interaction, fn ->
  data = fetch_data()
  [content: "Here's your data", embeds: [build_embed(data)]]
end, ephemeral: true)

delete_response(interaction)

@spec delete_response(interaction()) :: :ok | {:error, term()}

Deletes the original interaction response.

delete_source(interaction)

@spec delete_source(interaction()) :: :ok | {:error, term()}

Deletes the message that triggered a component interaction.

Works for both ephemeral and non-ephemeral messages. Uses Discord's type 6 (DEFERRED_UPDATE_MESSAGE) to claim ownership of the source message, then deletes it via delete_response.

Important: After calling delete_source/1, the interaction is already acknowledged. Use followup/2 instead of respond/2 for any reply:

# Correct pattern:
delete_source(interaction)
followup(interaction, content: "Done!", ephemeral: true)

# WRONG — will fail because interaction is already acknowledged:
delete_source(interaction)
respond(interaction, "Done!")

Examples

# User clicks "Confirm" button → delete the prompt, show next step
EDA.Interaction.delete_source(interaction)
EDA.Interaction.followup(interaction, content: "Next step...", components: [select_menu])

edit_response(interaction, content)

@spec edit_response(interaction(), String.t() | keyword()) ::
  {:ok, map()} | {:error, term()}

Edits the original interaction response (typically after deferring).

Examples

edit_response(interaction, "Done!")
edit_response(interaction, content: "Updated!", embeds: [embed])

followup(interaction, content)

@spec followup(interaction(), String.t() | keyword()) ::
  {:ok, map()} | {:error, term()}

Sends a followup message to the interaction.

Examples

followup(interaction, "Another message!")
followup(interaction, content: "Followup", ephemeral: true)

get_option(interaction, name, default \\ nil)

@spec get_option(interaction(), String.t(), term()) :: term()

Gets an option value by name from the interaction.

Automatically traverses into sub_commands and sub_command_groups to find the option.

Returns nil if not found, or default if provided.

get_options(interaction)

@spec get_options(interaction()) :: %{required(String.t()) => term()}

Returns all options as a flat map of %{"name" => value}.

Traverses sub_commands and sub_command_groups automatically.

guild_id(arg1)

@spec guild_id(interaction()) :: String.t() | nil

Returns the guild ID, or nil in DMs.

interaction_type(arg1)

@spec interaction_type(interaction()) :: atom() | nil

Returns the interaction type as an atom.

  • :ping (1)
  • :command (2, APPLICATION_COMMAND)
  • :component (3, MESSAGE_COMPONENT)
  • :autocomplete (4, APPLICATION_COMMAND_AUTOCOMPLETE)
  • :modal_submit (5, MODAL_SUBMIT)

member(arg1)

@spec member(interaction()) :: EDA.Member.t() | map() | nil

Returns the guild member map, or nil in DMs.

resolved(arg1, type, id)

@spec resolved(interaction(), String.t(), String.t()) :: map() | nil

Returns a resolved object by type and ID.

Types: "users", "members", "roles", "channels", "messages", "attachments".

respond(interaction, content)

@spec respond(interaction(), String.t() | keyword()) :: :ok | {:error, term()}

Sends an immediate response to the interaction.

Examples

respond(interaction, "Hello!")
respond(interaction, content: "Hello!", ephemeral: true)
respond(interaction, content: "Look!", embeds: [embed])

respond_modal(interaction, modal)

@spec respond_modal(interaction(), map()) :: :ok | {:error, term()}

Responds to an interaction by opening a modal dialog.

Takes a modal map built with EDA.Modal.modal/3+.

Example

import EDA.Modal

modal =
  modal("feedback", "Feedback",
    text_input("subject", "Subject", :short),
    text_input("body", "Details", :paragraph)
  )

respond_modal(interaction, modal)

selected_values(arg1)

@spec selected_values(interaction()) :: [String.t()]

Returns the selected values from a select menu interaction.

Returns an empty list if the interaction is not a select menu.

Examples

values = EDA.Interaction.selected_values(interaction)
# => ["option_1", "option_2"]

sub_command_name(arg1)

@spec sub_command_name(interaction()) :: String.t() | {String.t(), String.t()} | nil

Returns the sub_command name, or nil if not a sub_command invocation.

For sub_command_groups, returns {group_name, sub_command_name}.

target_id(arg1)

@spec target_id(interaction()) :: String.t() | nil

Returns the target ID for user/message context menu commands.

token(arg1)

@spec token(interaction()) :: String.t() | nil

Returns the interaction token.

user(arg1)

@spec user(interaction()) :: EDA.User.t() | map() | nil

Returns the user who triggered the interaction (works in both guild and DM).