EDA - Elixir Discord API

A modern Discord library for Elixir, inspired by JDA (Java Discord API).

Quick Start

  1. Add your bot token to config:

    config :eda, token: System.get_env("DISCORD_TOKEN"), intents: [:guilds, :guild_messages, :message_content]

  2. Create a consumer module:

    defmodule MyBot.Consumer do @behaviour EDA.Consumer

    def handle_event({:MESSAGE_CREATE, msg}) do

     if msg.content == "!ping" do
       EDA.API.Message.create(msg.channel_id, "Pong!")
     end

    end

    def handleevent(), do: :ok end

  3. Register your consumer:

    config :eda, consumer: MyBot.Consumer

  4. Start your application and the bot will connect automatically!

Features

  • WebSocket Gateway connection with automatic reconnection
  • REST API client with rate limiting
  • ETS-based caching for guilds, users, and channels
  • Simple consumer behaviour for handling events
  • Telemetry integration for observability

Summary

Functions

Awaits an INTERACTION_CREATE event matching the given filter.

Requests all members and waits for completion.

Awaits a MESSAGE_CREATE event matching the given filter.

Awaits a MESSAGE_REACTION_ADD event matching the given filter.

Blocks until all shards have finished loading their guilds.

Returns the configured consumer module.

Fetches specific members by user IDs (max 100).

Returns the configured gateway encoding.

Returns the configured gateway intents as a bitfield.

Returns the gateway heartbeat latency in milliseconds for the given shard.

Retrieves message history with automatic pagination.

Returns a lazy Stream of messages from a channel.

Purges messages from a channel with auto-chunking.

Returns true if all shards have finished loading their guilds.

Requests all members for a guild (fire-and-forget, caches automatically).

Searches members by username prefix (max 100 results).

Sets the bot's activity on all shards.

Updates the bot's presence on all shards.

Sets the bot's status on all shards without changing the activity.

Returns the configured shard mode.

Returns the configured bot token.

Functions

await_component(filter, opts \\ [])

@spec await_component(
  (term() -> boolean()),
  keyword()
) :: {:ok, term()} | {:ok, [term()]} | {:error, :timeout}

Awaits an INTERACTION_CREATE event matching the given filter.

Useful for collecting button clicks, select menu selections, and modal submissions.

Examples

{:ok, interaction} = EDA.await_component(fn i ->
  EDA.Interaction.custom_id(i) == "confirm_btn"
end, timeout: 30_000)

await_members(guild_id, opts \\ [])

@spec await_members(
  String.t() | integer(),
  keyword()
) :: {:ok, [map()]} | {:error, :timeout}

Requests all members and waits for completion.

await_message(filter, opts \\ [])

@spec await_message(
  (term() -> boolean()),
  keyword()
) :: {:ok, term()} | {:ok, [term()]} | {:error, :timeout}

Awaits a MESSAGE_CREATE event matching the given filter.

Examples

{:ok, msg} = EDA.await_message(fn msg ->
  msg.channel_id == channel_id and msg.author["id"] == user_id
end, timeout: 30_000)

await_reaction(filter, opts \\ [])

@spec await_reaction(
  (term() -> boolean()),
  keyword()
) :: {:ok, term()} | {:ok, [term()]} | {:error, :timeout}

Awaits a MESSAGE_REACTION_ADD event matching the given filter.

Examples

{:ok, reaction} = EDA.await_reaction(fn r ->
  r.message_id == msg_id and r.user_id != bot_id
end, timeout: 60_000)

await_ready(timeout \\ 60000)

@spec await_ready(timeout()) :: :ok | {:error, :timeout}

Blocks until all shards have finished loading their guilds.

Returns :ok when the bot is fully ready, or {:error, :timeout} if the timeout expires. Uses OTP's native GenServer.call suspension — no scheduler is blocked.

If the bot is already ready, returns :ok immediately.

Options

  • timeout — maximum wait in milliseconds (default 60_000)

Examples

EDA.await_ready()
EDA.await_ready(30_000)

consumer()

@spec consumer() :: module() | nil

Returns the configured consumer module.

fetch_members(guild_id, user_ids, opts \\ [])

@spec fetch_members(String.t() | integer(), [String.t() | integer()], keyword()) ::
  {:ok, [map()]} | {:error, :timeout}

Fetches specific members by user IDs (max 100).

gateway_encoding()

@spec gateway_encoding() :: :etf | :json

Returns the configured gateway encoding.

  • :etf (default) — Erlang External Term Format (binary, efficient)
  • :json — JSON via Jason (text, useful for debugging)

Configuration

config :eda, gateway_encoding: :etf   # default
config :eda, gateway_encoding: :json

intents()

@spec intents() :: integer()

Returns the configured gateway intents as a bitfield.

latency(shard_id \\ 0)

@spec latency(non_neg_integer()) :: non_neg_integer() | nil

Returns the gateway heartbeat latency in milliseconds for the given shard.

Defaults to shard 0 (single-shard bots). Returns nil if the shard hasn't received a heartbeat ACK yet or isn't connected.

Examples

EDA.latency()       # => 42
EDA.latency(1)      # => 55  (shard 1)

message_history(channel_id, limit, opts \\ [])

Retrieves message history with automatic pagination.

message_stream(channel_id, opts \\ [])

Returns a lazy Stream of messages from a channel.

purge_messages(channel_id, opts \\ [])

Purges messages from a channel with auto-chunking.

ready?()

@spec ready?() :: boolean()

Returns true if all shards have finished loading their guilds.

Non-blocking — reads from :persistent_term (O(1)).

request_members(guild_id, opts \\ [])

@spec request_members(
  String.t() | integer(),
  keyword()
) :: :ok

Requests all members for a guild (fire-and-forget, caches automatically).

search_members(guild_id, query, opts \\ [])

@spec search_members(String.t() | integer(), String.t(), keyword()) ::
  {:ok, [map()]} | {:error, :timeout}

Searches members by username prefix (max 100 results).

set_activity(name, opts \\ [])

@spec set_activity(
  String.t(),
  keyword()
) :: :ok

Sets the bot's activity on all shards.

Options

  • :type — activity type (default :playing)
  • :url — stream URL (only for :streaming)
  • :status — status to set alongside the activity (default :online)

Examples

EDA.set_activity("Elixir", type: :playing)
EDA.set_activity("on Twitch", type: :streaming, url: "https://twitch.tv/example")

set_presence(presence)

@spec set_presence(EDA.Presence.t() | keyword()) :: :ok

Updates the bot's presence on all shards.

Accepts an %EDA.Presence{} struct or a keyword list of options.

Examples

EDA.set_presence(EDA.Presence.new(status: :dnd, activities: [EDA.Presence.playing("Elixir")]))
EDA.set_presence(status: :idle, activities: [EDA.Presence.watching("you")])

set_status(status)

@spec set_status(EDA.Presence.status()) :: :ok

Sets the bot's status on all shards without changing the activity.

Examples

EDA.set_status(:dnd)
EDA.set_status(:invisible)

shard_config()

@spec shard_config() :: :auto | pos_integer() | {Range.t(), pos_integer()}

Returns the configured shard mode.

  • :auto (default) — use Discord's recommended shard count
  • integer — fixed number of shards (e.g. 4 → shards 0..3)
  • {Range.t(), integer} — specific shards on this node (e.g. {0..1, 4})

token()

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

Returns the configured bot token.