### Bot Setup with Nostrum.Bot and Nostrum.Consumer Source: https://context7.com/kraigie/nostrum/llms.txt Demonstrates how to set up a Discord bot using Nostrum.Bot as a supervisor and Nostrum.Consumer to handle gateway events. Includes example of handling message creation events and configuring bot options like intents and token. ```APIDOC ## Bot Setup — `Nostrum.Bot` and `Nostrum.Consumer` The entry point for any Nostrum bot. `Nostrum.Bot` is an OTP supervisor that starts all bot subsystems (shards, caches, ratelimiter, voice). `Nostrum.Consumer` is a behaviour module that receives gateway events. Add `Nostrum.Bot` to your application's supervision tree with a map of options including `:consumer`, `:intents`, and `:wrapped_token`. ```elixir # lib/my_bot/consumer.ex defmodule MyBot.Consumer do @behaviour Nostrum.Consumer alias Nostrum.Api.Message def handle_event({:MESSAGE_CREATE, msg, _ws_state}) do case msg.content do "!hello" -> # Returns {:ok, %Nostrum.Struct.Message{}} on success {:ok, _msg} = Message.create(msg.channel_id, "Hello, world!") "!echo " <> text -> Message.create(msg.channel_id, text) _ -> :ignore end end # Always add a catch-all to avoid unhandled event warnings def handle_event(_event), do: :ok end # lib/my_bot/application.ex defmodule MyBot.Application do use Application @impl true def start(_type, _args) do bot_options = %{ name: MyBot, consumer: MyBot.Consumer, # Request only the intents you need; :message_content is privileged intents: [:direct_messages, :guild_messages, :message_content, :guilds], wrapped_token: fn -> System.fetch_env!("BOT_TOKEN") end } children = [{Nostrum.Bot, bot_options}] Supervisor.start_link(children, strategy: :one_for_one) end end ``` ``` -------------------------------- ### Start Bot Dynamically in IEx Source: https://github.com/kraigie/nostrum/blob/master/README.md Start a Nostrum bot dynamically at runtime from an `iex` session. This is useful for testing or quick setups. Ensure the necessary bot options, including intents and a token retrieval mechanism, are provided. ```elixir iex()> bot_options = %{ ...()> consumer: ExampleConsumer, ...()> intents: [:direct_messages, :guild_messages, :message_content], ...()> wrapped_token: fn -> System.fetch_env!("BOT_TOKEN") end ...()> } iex()> Supervisor.start_link([{Nostrum.Bot, bot_options}], strategy: :one_for_one) {:ok, #PID<0.208.0>} ``` -------------------------------- ### Start Bot Node Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Starts an Elixir node with the bot and Nostrum configured for mnesia caching and manual sharding. ```sh iex --cookie mybot --sname joe -S mix ``` -------------------------------- ### Start Bot in Application Supervision Tree Source: https://github.com/kraigie/nostrum/blob/master/README.md Configure and start a Nostrum bot within your Elixir application's supervision tree. This example includes essential bot options like the consumer module, intents, and a wrapped token function to fetch the bot token from environment variables. ```elixir defmodule MyApp.Application do use Application def start(_type, _args) do bot_options = %{ consumer: ExampleConsumer, intents: [:direct_messages, :guild_messages, :message_content], wrapped_token: fn -> System.fetch_env!("BOT_TOKEN") end } children = [{Nostrum.Bot, bot_options}] Supervisor.start_link(children, strategy: :one_for_one) end end ``` -------------------------------- ### Configure and Start Nostrum Bot Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/intro.md Add Nostrum.Bot to your application's supervisor tree in `lib/my_bot/application.ex`. Ensure the `BOT_TOKEN` environment variable is set for authentication. ```elixir defmodule MyBot.Application do use Application @impl true def start(_type, _args) do bot_options = %{ name: MyBot, consumer: MyBot.Consumer, intents: [:direct_messages, :guild_messages, :message_content], wrapped_token: fn -> System.fetch_env!("BOT_TOKEN") end } children = [ {Nostrum.Bot, bot_options} ] Supervisor.start_link(children, strategy: :one_for_one) end end ``` -------------------------------- ### Initialize Mnesia with Remote Nodes Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Starts mnesia on the secondary node, including the primary node in its database configuration. ```elixir :mnesia.start(extra_db_nodes: [:joe@host]) ``` -------------------------------- ### Install Nostrum with Hex Source: https://github.com/kraigie/nostrum/blob/master/README.md Add Nostrum as a dependency using a stable release version from Hex.pm. ```elixir def deps do [{:nostrum, "~> 0.10"}] end ``` -------------------------------- ### Example Event Consumer Source: https://github.com/kraigie/nostrum/blob/master/README.md Define a module that behaves as a Nostrum consumer to handle incoming Discord events. This example shows how to respond to 'ping!' messages. Ensure the 'Message Content Intent' is enabled in your bot's application settings and specified in the bot options. ```elixir defmodule ExampleConsumer do @behaviour Nostrum.Consumer alias Nostrum.Api.Message def handle_event({:MESSAGE_CREATE, msg, _ws_state}) do case msg.content do "ping!" -> Message.create(msg.channel_id, "I copy and pasted this code") _ -> :ignore end end end ``` -------------------------------- ### Start Secondary Bot Node Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Starts a secondary Elixir node for migration purposes, preventing automatic startup of services. ```sh iex --cookie mybot --sname mike -S mix run --no-start ``` -------------------------------- ### Install Nostrum from GitHub Source: https://github.com/kraigie/nostrum/blob/master/README.md Add Nostrum as a dependency directly from its GitHub repository for the latest changes. This is recommended for testing pre-release versions. ```elixir def deps do [{:nostrum, github: "Kraigie/nostrum"}] end ``` -------------------------------- ### Starting Nodes for Multi-node Testing Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Commands to launch three separate IEx sessions, each configured as a distinct node ('joe', 'robert', 'mike') in a distributed Erlang cluster. Ensure to use the same cookie and appropriate Erlang configuration files. ```bash iex --sname joe --cookie foo --erl-config myapp_joe.config -S mix ``` ```bash iex --sname robert --cookie foo --erl-config myapp_robert.config -S mix ``` ```bash iex --sname mike --cookie foo --erl-config myapp_mike.config -S mix ``` -------------------------------- ### Basic Bot Setup with Nostrum.Consumer Source: https://context7.com/kraigie/nostrum/llms.txt Defines a bot consumer to handle message events and sets up the Nostrum bot supervisor. Ensure the `:message_content` intent is enabled if needed, as it's a privileged intent. ```elixir defmodule MyBot.Consumer do @behaviour Nostrum.Consumer alias Nostrum.Api.Message def handle_event({:MESSAGE_CREATE, msg, _ws_state}) do case msg.content do "!hello" -> # Returns {:ok, %Nostrum.Struct.Message{}} on success {:ok, _msg} = Message.create(msg.channel_id, "Hello, world!") "!echo " <> text -> Message.create(msg.channel_id, text) _ -> :ignore end end # Always add a catch-all to avoid unhandled event warnings def handle_event(_event), do: :ok end # lib/my_bot/application.ex defmodule MyBot.Application do use Application @impl true def start(_type, _args) do bot_options = %{ name: MyBot, consumer: MyBot.Consumer, # Request only the intents you need; :message_content is privileged intents: [:direct_messages, :guild_messages, :message_content, :guilds], wrapped_token: fn -> System.fetch_env!("BOT_TOKEN") end } children = [{Nostrum.Bot, bot_options}] Supervisor.start_link(children, strategy: :one_for_one) end end ``` -------------------------------- ### Erlang Configuration for Distributed App (Joe Node) Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Configure OTP to recognize ':mybot' as a distributed application and specify its hosts. This example sets up the 'joe' node, defining its distributed peers and mandatory synchronization nodes. ```erlang % mybot_joe.config [{kernel, [{distributed, [{mybot, 5000, [joe@HOSTNAME, {mike@HOSTNAME, robert@HOSTNAME}]}]}, {sync_nodes_mandatory, [mike@HOSTNAME, robert@HOSTNAME]}, {sync_nodes_timeout, 30000}]}]. ``` -------------------------------- ### Configure youtube-dl Executable Source: https://github.com/kraigie/nostrum/blob/master/guides/functionality/voice.md Specify the path to the youtube-dl executable if it is not in the system's PATH. This is useful for custom installations or when using forks like yt-dlp. ```elixir config :nostrum, :youtubedl, "yt-dlp" ``` -------------------------------- ### Basic Nostrum Consumer Implementation Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/intro.md Define a module that behaves as a Nostrum Consumer to handle Discord events. This example shows how to respond to a '!hello' message. ```elixir defmodule MyBot.Consumer do @behaviour Nostrum.Consumer alias Nostrum.Api.Message def handle_event({:MESSAGE_CREATE, msg, _ws_state}) do case msg.content do "!hello" -> {:ok, _message} = Message.create(msg.channel_id, "Hello, world!") _ -> :ignore end end # Ignore any other events def handle_event(_), do: :ok end ``` -------------------------------- ### Erlang Configuration for Distributed App (Robert Node) Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Configure OTP for the 'robert' node in a distributed setup. This configuration specifies the application, its peers, and the nodes it must synchronize with upon startup. ```erlang % mybot_robert.config [{kernel, [{distributed, [{mybot, 5000, [joe@HOSTNAME, {mike@HOSTNAME, robert@HOSTNAME}]}]}, {sync_nodes_mandatory, [joe@HOSTNAME, mike@HOSTNAME]}, {sync_nodes_timeout, 30000}]}]. ``` -------------------------------- ### Start Async Voice Listening Source: https://context7.com/kraigie/nostrum/llms.txt Initiates asynchronous listening for voice-related events within a specific guild. This function is part of the voice subsystem. ```elixir Voice.start_listen_async(guild_id) ``` -------------------------------- ### Get All Guild Roles Source: https://context7.com/kraigie/nostrum/llms.txt Retrieve a list of all roles currently present in a guild. ```elixir alias Nostrum.Api.Guild # Get all roles {:ok, roles} = Guild.roles(guild_id) ``` -------------------------------- ### Connect, Disconnect, and Reconnect Shard Manually Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/manual_sharding.md Use `Nostrum.Shard.Supervisor.connect/3` to manually start shards when `shards` is set to `:manual`. `Nostrum.Shard.Supervisor.disconnect/1` attempts to save resume information, which can then be used with `Nostrum.Shard.Supervisor.reconnect/1` to resume a session. Resumption is not guaranteed; Nostrum will reconnect from scratch if it fails. ```elixir iex> bot_options = %{ ...> consumer: MyBot.Consumer, ...> shards: :manual, ...> wrapped_token: fn -> System.get_env!("BOT_TOKEN") end ...> } iex> Nostrum.Shard.Supervisor.connect(0, 1, bot_options) iex> resume_info = Nostrum.Shard.Supervisor.disconnect(0) %{shard_num: 0, ...} # On another node iex> Nostrum.Shard.Supervisor.reconnect(resume_info) ``` -------------------------------- ### Respond to Interaction with Message Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/application_commands.md Creates a response to a user's interaction, sending a message back to the channel. This example demonstrates sending a confirmation message after assigning a role. ```elixir alias Nostrum.Api alias Nostrum.Struct.Interaction defp manage_role(%Interaction{data: %{options: [%{value: role_id}, %{value: "assign"}]}} = interaction) do Guild.add_member_role(interaction.guild_id, interaction.member.user_id, role_id) response = %{ type: 4, # ChannelMessageWithSource data: %{ content: "role assigned" } } Api.Interaction.create_response(interaction, response) end ``` -------------------------------- ### Register voice channel commands Source: https://context7.com/kraigie/nostrum/llms.txt Registers slash commands for voice interactions when the bot is ready. This example shows command creation within a consumer's READY event handler. ```elixir Enum.each(guilds, fn g -> Nostrum.Api.ApplicationCommand.create_guild_command(g.id, %{ name: "play", description: "Play audio" }) end) ``` -------------------------------- ### Running Multiple Bots with Nostrum Source: https://context7.com/kraigie/nostrum/llms.txt Demonstrates how to start and manage multiple Discord bots within a single Elixir application. Explicitly specify the bot context for API calls made outside of a consumer's event handler. ```elixir # Start two bots in the same supervision tree bot_a = %{name: BotA, consumer: MyBot.Consumer, intents: [:guilds], wrapped_token: fn -> System.fetch_env!("TOKEN_A") end} bot_b = %{name: BotB, consumer: MyBot.Consumer, intents: [:guilds], wrapped_token: fn -> System.fetch_env!("TOKEN_B") end} children = [{Nostrum.Bot, bot_a}, {Nostrum.Bot, bot_b}] Supervisor.start_link(children, strategy: :one_for_one) # Make an API call as BotA from outside a consumer Nostrum.Bot.with_bot(BotA, fn -> Nostrum.Api.Message.create(channel_id, "Sent by BotA") end) # Keep context after the block finishes (for chaining calls) Nostrum.Bot.with_bot(BotB, fn -> Nostrum.Api.Message.create(channel_id, "Sent by BotB") end, :keep) # Context is still BotB here Nostrum.Api.Message.create(channel_id, "Also BotB") # Or set context dynamically per-call Nostrum.Bot.set_bot_name(BotA) Nostrum.Api.Message.create(channel_id, "BotA again") # List all running bots Nostrum.Bot.fetch_all_bots() # => [%{name: BotA, pid: #PID<0.208.0>, bot_options: %{...}}, ...] ``` -------------------------------- ### Receive Interaction Payload Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/application_commands.md Example of an interaction payload received when a user invokes an application command. It includes details like channel ID, command data, and options provided by the user. ```elixir %Nostrum.Struct.Interaction{ channel_id: 474025345243414539, data: %{ id: 793152718839087135, name: "role", options: [ %{name: "name", value: "458692275199803406"}, %{name: "action", value: "assign"} ] }, # ... } ``` -------------------------------- ### Get Users Who Reacted Source: https://context7.com/kraigie/nostrum/llms.txt Retrieve a list of users who reacted to a message with a specific emoji. Supports pagination with a limit. ```elixir {:ok, users} = Message.reactions(channel_id, msg_id, "👍", limit: 10) ``` -------------------------------- ### Retry Playing Audio Source: https://github.com/kraigie/nostrum/blob/master/guides/functionality/voice.md This function retries playing audio if an error occurs, with a short delay between attempts. It's useful for ensuring audio playback starts after the voice channel is ready. ```elixir def try_play(guild_id, url, type, opts \ \ []) do case Nostrum.Voice.play(guild_id, url, type, opts) do {:error, _msg} -> Process.sleep(100) try_play(guild_id, url, type, opts) _ -> :ok end end ``` -------------------------------- ### Message API — `Nostrum.Api.Message` Source: https://context7.com/kraigie/nostrum/llms.txt Provides functions for interacting with Discord messages, including creating, editing, deleting, and reacting to them. Shows examples of sending plain text messages, rich embeds, and replying to specific messages. ```APIDOC ## Message API — `Nostrum.Api.Message` Functions for creating, editing, deleting, and reacting to messages. `create/2` accepts a string shorthand or a keyword/map of options including embeds, file attachments, polls, and mention control. All functions return `{:ok, result}` or `{:error, %Nostrum.Error.ApiError{}}`. ```elixir alias Nostrum.Api.Message alias Nostrum.Struct.Embed import Nostrum.Struct.Embed channel_id = 43189401384091 # Send a plain text message {:ok, %Nostrum.Struct.Message{id: msg_id}} = Message.create(channel_id, "Hello!") # Send a message with a rich embed embed = %Embed{} |> put_title("Server Status") |> put_description("All systems operational") |> put_color(0x2ECC71) |> put_footer("Updated just now") Message.create(channel_id, embeds: [embed]) # Reply to a specific message (requires VIEW_MESSAGE_HISTORY) Message.create(channel_id, content: "Got it!", message_reference: %{message_id: msg_id} ) ``` ``` -------------------------------- ### Manual Bot Takeover Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Demonstrates how to manually initiate a takeover of an application from one node to another using Erlang's `:application.takeover` function. The `:permanent` option suggests a persistent move. ```erlang :application.takeover(:mybot, :permanent) ``` -------------------------------- ### Configure youtube-dl Arguments Source: https://github.com/kraigie/nostrum/blob/master/guides/functionality/voice.md Customize the default arguments used when running youtube-dl for streaming audio. This allows for fine-tuning of audio quality and output. ```elixir config :nostrum, :youtubedl_args, ["-f", "bestaudio", "-o", "-", "-q", "--no-warnings"] ``` -------------------------------- ### Application Commands (Slash Commands) API Source: https://context7.com/kraigie/nostrum/llms.txt Create and manage Discord slash commands globally or per-guild. Guild commands become available instantly; global commands propagate within ~1 hour. ```APIDOC ## Application Commands (Slash Commands) — `Nostrum.Api.ApplicationCommand` Create and manage Discord slash commands globally or per-guild. Guild commands become available instantly; global commands propagate within ~1 hour. ```elixir alias Nostrum.Api.ApplicationCommand guild_id = 81384788765712384 # Define a slash command with typed options command = %{ name: "role", description: "Assign or remove a role", options: [ %{type: 8, name: "name", description: "Role to assign or remove", required: true}, %{type: 3, name: "action", description: "assign or remove", required: true, choices: [%{name: "assign", value: "assign"}, %{name: "remove", value: "remove"}]} ] } ``` ``` -------------------------------- ### Create New Elixir Project Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/intro.md Use this command to generate a new Elixir project with a supervisor tree, which is necessary for running the Nostrum bot. ```bash mix new my_bot --sup ``` -------------------------------- ### Running Project Checks Source: https://github.com/kraigie/nostrum/blob/master/CONTRIBUTING.md Execute these commands to ensure code quality and correctness before submitting changes. `mix check` runs all checks except unit tests. ```shell mix compile --force mix format mix credo --strict --ignore 'TagTODO' mix dialyzer mix test ``` ```shell mix check ``` -------------------------------- ### Create a Text Channel Source: https://context7.com/kraigie/nostrum/llms.txt Create a new text channel within a guild. Requires the MANAGE_CHANNELS permission. ```elixir alias Nostrum.Api.Channel # Create a text channel in a guild (requires MANAGE_CHANNELS) {:ok, new_chan} = Channel.create(guild_id, name: "announcements", type: 0, # 0 = GUILD_TEXT topic: "Official announcements", nsfw: false ) ``` -------------------------------- ### Connect Nodes Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Establishes a connection between the secondary node ('mike') and the primary node ('joe'). ```elixir Node.connect(:joe@host) ``` -------------------------------- ### Register Commands with Nostrum Source: https://context7.com/kraigie/nostrum/llms.txt Register application commands either globally or on a specific guild. Global commands may take up to an hour to propagate. Bulk overwrites replace the entire list atomically. ```elixir alias Nostrum.Api.ApplicationCommand # Register command instantly on a single guild (great for testing) {:ok, cmd} = ApplicationCommand.create_guild_command(guild_id, command) # Register global command (available on all guilds after ~1 hour) {:ok, global_cmd} = ApplicationCommand.create_global_command(command) # List all global commands {:ok, cmds} = ApplicationCommand.global_commands() # => [{%{id: "789...", name: "role", ...}}] # List guild-specific commands {:ok, guild_cmds} = ApplicationCommand.guild_commands(guild_id) # Bulk-overwrite all global commands (replaces the entire list atomically) {:ok, _} = ApplicationCommand.bulk_overwrite_global_commands([ %{name: "ping", description: "Pong!"}, %{name: "help", description: "Show help menu"} ]) # Edit an existing command {:ok, _} = ApplicationCommand.edit_guild_command(guild_id, cmd.id, %{description: "Updated description"}) # Delete a command :ok = ApplicationCommand.delete_guild_command(guild_id, cmd.id) ``` -------------------------------- ### Running Multiple Bots with Nostrum.Bot Source: https://context7.com/kraigie/nostrum/llms.txt Explains how to run multiple Discord bots concurrently within a single Elixir application using Nostrum. Demonstrates using `Nostrum.Bot.with_bot/3`, `Nostrum.Bot.set_bot_name/1`, and fetching all running bots. ```APIDOC ## Multiple Bots — `Nostrum.Bot.with_bot/3` and `Nostrum.Bot.set_bot_name/1` Nostrum supports running multiple bots within a single BEAM node. When making API calls outside of a consumer's `handle_event/1` (where bot context is set automatically), you must explicitly specify which bot to use via `with_bot/3`, `use Nostrum.Bot, name: MyBot`, or `set_bot_name/1`. ```elixir # Start two bots in the same supervision tree bot_a = %{name: BotA, consumer: MyBot.Consumer, intents: [:guilds], wrapped_token: fn -> System.fetch_env!("TOKEN_A") end} bot_b = %{name: BotB, consumer: MyBot.Consumer, intents: [:guilds], wrapped_token: fn -> System.fetch_env!("TOKEN_B") end} children = [{Nostrum.Bot, bot_a}, {Nostrum.Bot, bot_b}] Supervisor.start_link(children, strategy: :one_for_one) # Make an API call as BotA from outside a consumer Nostrum.Bot.with_bot(BotA, fn -> Nostrum.Api.Message.create(channel_id, "Sent by BotA") end) # Keep context after the block finishes (for chaining calls) Nostrum.Bot.with_bot(BotB, fn -> Nostrum.Api.Message.create(channel_id, "Sent by BotB") end, :keep) # Context is still BotB here Nostrum.Api.Message.create(channel_id, "Also BotB") # Or set context dynamically per-call Nostrum.Bot.set_bot_name(BotA) Nostrum.Api.Message.create(channel_id, "BotA again") # List all running bots Nostrum.Bot.fetch_all_bots() # => [%{name: BotA, pid: #PID<0.208.0>, bot_options: %{...}}, ...] ``` ``` -------------------------------- ### Application Commands Source: https://context7.com/kraigie/nostrum/llms.txt Manage Discord application commands, including creating, listing, editing, and deleting them globally or per guild. ```APIDOC ## ApplicationCommand API ### Description Functions for managing Discord application commands. ### Create Guild Command Register a command on a single guild. ```elixir ApplicationCommand.create_guild_command(guild_id, command) ``` ### Create Global Command Register a command globally. It will be available on all guilds after a delay. ```elixir ApplicationCommand.create_global_command(command) ``` ### List Global Commands Retrieve a list of all global commands. ```elixir ApplicationCommand.global_commands() ``` ### List Guild Commands Retrieve a list of commands for a specific guild. ```elixir ApplicationCommand.guild_commands(guild_id) ``` ### Bulk Overwrite Global Commands Atomically replace all existing global commands with a new list. ```elixir ApplicationCommand.bulk_overwrite_global_commands([ %{name: "ping", description: "Pong!"}, %{name: "help", description: "Show help menu"} ]) ``` ### Edit Guild Command Modify an existing command on a specific guild. ```elixir ApplicationCommand.edit_guild_command(guild_id, cmd.id, %{description: "Updated description"}) ``` ### Delete Guild Command Remove a command from a specific guild. ```elixir ApplicationCommand.delete_guild_command(guild_id, cmd.id) ``` ``` -------------------------------- ### Manage Discord Webhooks Source: https://context7.com/kraigie/nostrum/llms.txt Create and execute Discord webhooks using `Nostrum.Api.Webhook` for posting messages without a bot gateway connection. Requires the `MANAGE_WEBHOOKS` permission. ```elixir alias Nostrum.Api.Webhook channel_id = 381889573426429952 # Create a webhook (requires MANAGE_WEBHOOKS permission) {:ok, wh} = Webhook.create(channel_id, %{name: "Alerts", avatar: nil}) ``` -------------------------------- ### Configure Streamlink Executable Source: https://github.com/kraigie/nostrum/blob/master/guides/functionality/voice.md Set the path to the streamlink executable if it's not found in the system's PATH. This is necessary for Nostrum to pipe live streams to FFmpeg. ```elixir config :nostrum, :streamlink, "/path/to/streamlink" ``` -------------------------------- ### Connect Shard Supervisor Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Connects the shard supervisor to the bot, specifying the shard count and bot options. ```elixir Nostrum.Shard.Supervisor.connect(0, 1, MyBot.bot_options()) ``` -------------------------------- ### Handle voice channel interaction Source: https://context7.com/kraigie/nostrum/llms.txt Handles user interactions for voice commands, such as 'play'. Joins the user's voice channel and initiates audio playback. ```elixir channel_id = guild_id |> GuildCache.get!() |> Map.get(:voice_states) |> Enum.find(%{}, &(&1.user_id == user_id)) |> Map.get(:channel_id) case interaction.data.name do "play" -> # Join the user's voice channel first Voice.join_channel(guild_id, channel_id) # Wait until voice is ready, then play wait_and_play(guild_id, "https://example.com/audio.mp3", :url) Interaction.create_response(interaction, %{type: 4, data: %{content: "Playing! 🎵"}}) end ``` -------------------------------- ### Define Slash Command Source: https://context7.com/kraigie/nostrum/llms.txt Define the structure of a slash command, including its name, description, and options with types and choices. ```elixir alias Nostrum.Api.ApplicationCommand # Define a slash command with typed options command = %{ name: "role", description: "Assign or remove a role", options: [ %{type: 8, name: "name", description: "Role to assign or remove", required: true}, %{type: 3, name: "action", description: "assign or remove", required: true, choices: [%{name: "assign", value: "assign"}, %{name: "remove", value: "remove"}]} ] } ``` -------------------------------- ### Documenting New Elixir Code Source: https://github.com/kraigie/nostrum/blob/master/CONTRIBUTING.md When adding new modules, types, or functions in Elixir, use the `since: "NEXTVERSION"` attribute. Maintainers will replace `NEXTVERSION` with the actual release version. ```elixir defmodule NewModule do @moduledoc since: "NEXTVERSION" end ``` ```elixir defmodule Nostrum.Api do @typedoc since: "NEXTVERSION" @type new_type :: :ok @doc since: "NEXTVERSION" def new_function, do: :ok end ``` -------------------------------- ### Register a Guild Command Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/application_commands.md Registers a defined application command on a specific guild. This function should be called with the guild ID and the command definition. ```elixir Nostrum.Api.ApplicationCommand.create_guild_command(guild_id, command) ``` -------------------------------- ### Receive Resume Info and Reconnect Shard Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Receives resume information on the target node ('mike') and uses it to reconnect the shard. ```elixir resume_info = receive do info -> info end ``` ```elixir Nostrum.Shard.Supervisor.reconnect(resume_info) ``` -------------------------------- ### Erlang Configuration for Distributed App (Mike Node) Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Configuration for the 'mike' node, defining it as part of a distributed application. It lists the other nodes in the cluster and specifies which nodes must be available for synchronization. ```erlang % mybot_mike.config [{kernel, [{distributed, [{mybot, 5000, [joe@HOSTNAME, {mike@HOSTNAME, robert@HOSTNAME}]}]}, {sync_nodes_mandatory, [joe@HOSTNAME, robert@HOSTNAME]}, {sync_nodes_timeout, 30000}]}]. ``` -------------------------------- ### Query voice playback state Source: https://context7.com/kraigie/nostrum/llms.txt Checks if audio is currently playing or if the voice client is ready in a given channel. ```elixir true = Voice.playing?(guild_id) ``` ```elixir true = Voice.ready?(guild_id) ``` -------------------------------- ### Add Rustler for DAVE Build Source: https://github.com/kraigie/nostrum/blob/master/guides/functionality/voice.md Include Rustler as an optional dependency to enable building the DAVE package from source. Set FORCE_DAVE_BUILD=true when compiling if you have a Rust toolchain and prefer to build from source. ```elixir defp deps do [ ... {:rustler, "~> 0.37", optional: true, runtime: false} ] end ``` -------------------------------- ### Configure NoOp Presence Cache Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/pluggable_caching.md Disable presence caching by configuring the :caches option with Nostrum.Cache.PresenceCache.NoOp. This is useful when bot presence status is not required. ```elixir config :nostrum, caches: %{ presences: Nostrum.Cache.PresenceCache.NoOp } ``` -------------------------------- ### Register Shell Process Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Registers the current shell process globally on the target node ('mike') to receive messages. ```elixir :global.register_name(:target, self()) ``` -------------------------------- ### Send a File Attachment Source: https://context7.com/kraigie/nostrum/llms.txt Send a file from disk or memory as an attachment to a message. Can also include inline content. ```elixir Message.create(channel_id, file: "/path/to/report.pdf") ``` ```elixir Message.create(channel_id, content: "Here's the log", files: [%{name: "app.log", body: "error: something went wrong\n"}]) ``` -------------------------------- ### Add :ezstd Dependency to mix.exs Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/gateway_compression.md Add the :ezstd dependency to your project's mix.exs file to enable zstd compression. Ensure you use a compatible version. ```elixir defp deps do [ {:nostrum, ...}, {:ezstd, "~> 1.1"} # new dependency ] end ``` -------------------------------- ### Create, Modify, Delete Roles Source: https://context7.com/kraigie/nostrum/llms.txt Manage roles within a guild, including creation with specific permissions, modification, and deletion. ```elixir alias Nostrum.Api.Guild # Create, modify, delete roles {:ok, role} = Guild.create_role(guild_id, %{name: "Moderator", color: 0xE74C3C, hoist: true}) {:ok, _} = Guild.modify_role(guild_id, role.id, %{name: "Senior Moderator"}) :ok = Guild.delete_role(guild_id, role.id, "Role restructuring") ``` -------------------------------- ### Interactions API Source: https://context7.com/kraigie/nostrum/llms.txt Handle and respond to user interactions such as slash commands and component interactions received via the gateway. ```APIDOC ## Interactions — `Nostrum.Api.Interaction` Respond to slash command and component interactions received via the `:INTERACTION_CREATE` gateway event. ### Create Response Send an initial response to an interaction. ```elixir Interaction.create_response(interaction, %{ type: 4, # Type 4 = CHANNEL_MESSAGE_WITH_SOURCE data: %{content: "Pong! 🏓"} }) ``` ### Edit Response Modify the original response message after it has been sent. ```elixir Interaction.edit_response(interaction, %{content: "Updated response!"}) ``` ### Original Response Retrieve the original response message. ```elixir Interaction.original_response(interaction) ``` ### Create Followup Message Send a followup message, which appears as a new message from the bot. ```elixir Interaction.create_followup_message(interaction.application_id, interaction.token, %{ content: "Here's some extra info..." }) ``` ### Delete Followup Message Remove a previously sent followup message. ```elixir Interaction.delete_followup_message(interaction.application_id, interaction.token, followup.id) ``` ### Delete Response Remove the original interaction response. ```elixir Interaction.delete_response(interaction) ``` ``` -------------------------------- ### Fetch Guild Info Source: https://context7.com/kraigie/nostrum/llms.txt Retrieve detailed information about a specific guild using its ID. ```elixir alias Nostrum.Api.Guild # Fetch guild info from the API {:ok, guild} = Guild.get(guild_id) ``` -------------------------------- ### Fetch a Channel Source: https://context7.com/kraigie/nostrum/llms.txt Retrieve channel information using its ID. ```elixir alias Nostrum.Api.Channel # Fetch a channel {:ok, %Nostrum.Struct.Channel{} = chan} = Channel.get(381889573426429952) ``` -------------------------------- ### Configure Logger Metadata Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/intro.md Configure the Elixir logger to include metadata like bot name, shard ID, guild, and channel for voice connection events. This helps in debugging and understanding event origins. ```elixir config :logger, :console, metadata: [:bot, :shard, :guild, :channel] ``` -------------------------------- ### Handle Discord Interactions Source: https://context7.com/kraigie/nostrum/llms.txt Respond to slash command and component interactions using the `Nostrum.Api.Interaction` module. Use `create_response` for immediate replies and `edit_response` or `create_followup_message` for subsequent messages. Ephemeral responses are only visible to the invoking user. ```elixir alias Nostrum.Api.Interaction alias Nostrum.Api.Guild defmodule MyBot.Consumer do @behaviour Nostrum.Consumer def handle_event({:INTERACTION_CREATE, interaction, _ws_state}) do case interaction.data.name do "ping" -> # Type 4 = CHANNEL_MESSAGE_WITH_SOURCE (immediate visible response) Interaction.create_response(interaction, %{ type: 4, data: %{content: "Pong! 🏓"} }) "role" -> [%{value: role_id}, %{value: action}] = interaction.data.options handle_role(interaction, role_id, action) _ -> # Type 4, ephemeral (only the invoking user sees it) Interaction.create_response(interaction, %{ type: 4, data: %{content: "Unknown command", flags: 64} }) end end def handle_event(_), do: :ok defp handle_role(interaction, role_id, "assign") do Guild.add_member_role(interaction.guild_id, interaction.member.user.id, role_id) Interaction.create_response(interaction, %{type: 4, data: %{content: "Role assigned!"}}) end defp handle_role(interaction, role_id, "remove") do Guild.remove_member_role(interaction.guild_id, interaction.member.user.id, role_id) Interaction.create_response(interaction, %{type: 4, data: %{content: "Role removed!"}}) end end # Edit the original response after the fact (up to 15 minutes) Interaction.edit_response(interaction, %{content: "Updated response!"}) # Get the original response message {:ok, original_msg} = Interaction.original_response(interaction) # Send a followup message (appears as a new message from the bot) {:ok, followup} = Interaction.create_followup_message(interaction.application_id, interaction.token, %{ content: "Here's some extra info..." }) # Delete the followup :ok = Interaction.delete_followup_message(interaction.application_id, interaction.token, followup.id) # Delete the original response :ok = Interaction.delete_response(interaction) ``` -------------------------------- ### Disconnect Shard and Send Resume Info Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Disconnects a shard from its current node ('joe') and sends its resume information to a registered target process. ```elixir resume_info = Nostrum.Shard.Supervisor.disconnect(0) ``` ```elixir :global.send(:target, resume_info) ``` -------------------------------- ### Define a Guild Command Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/application_commands.md Defines the structure for a guild-specific slash command, including its name, description, and options. This structure is used to register commands with Discord. ```elixir command = %{ name: "role", description: "assign or remove a role", options: [ %{ # ApplicationCommandType::ROLE type: 8, name: "name", description: "role to assign or remove", required: true }, %{ # ApplicationCommandType::STRING type: 3, name: "action", description: "whether to assign or remove the role", required: true, choices: [ %{name: "assign", value: "assign"}, %{name: "remove", value: "remove"} ] } ] } ``` -------------------------------- ### Restart Bot Node Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Restarts the Elixir node, allowing mnesia to automatically connect to the existing cluster. ```sh iex --cookie mybot --sname mike -S mix ``` -------------------------------- ### Handle Role Assignment/Removal Source: https://github.com/kraigie/nostrum/blob/master/guides/intro/application_commands.md Handles the logic for assigning or removing a role based on the interaction options. It uses pattern matching to differentiate between 'assign' and 'remove' actions. ```elixir alias Nostrum.Api alias Nostrum.Struct.Interaction defp manage_role(%Interaction{data: %{options: [%{value: role_id}, %{value: "assign"}]}} = interaction) do Guild.add_member_role(interaction.guild_id, interaction.member.user_id, role_id) end defp manage_role(%Interaction{data: %{options: [%{value: role_id}, %{value: "remove"}]}} = interaction) do Guild.remove_member_role(interaction.guild_id, interaction.member.user_id, role_id) end def handle_event({:INTERACTION_CREATE, %Interaction{data: %{name: "role"}} = interaction, _ws_state}) do manage_role(interaction) end ``` -------------------------------- ### List Connected Nodes Source: https://github.com/kraigie/nostrum/blob/master/guides/advanced/multi_node.md Verifies the list of connected nodes in the Erlang cluster, confirming auto-connection. ```elixir Node.list() ``` -------------------------------- ### Fetch guild data from cache Source: https://context7.com/kraigie/nostrum/llms.txt Retrieves a full guild struct from the cache. Uses ETS by default. Raises an error if the guild is not found. ```elixir case GuildCache.get(guild_id) do {:ok, guild} -> IO.inspect(guild.name) # "My Server" IO.inspect(length(guild.members)) # member count IO.inspect(guild.voice_states) # current voice state map {:error, reason} -> IO.puts("Guild not in cache: #{inspect(reason)}") end ``` ```elixir guild = GuildCache.get!(guild_id) ``` -------------------------------- ### Listen to incoming audio Source: https://context7.com/kraigie/nostrum/llms.txt Listens for incoming RTP packets in a voice channel, blocking until a specified number of packets are received. This is useful for receiving audio streams. ```elixir {:ok, rtp_packets} = Voice.listen(guild_id, 50) # 50 RTP packets ≈ 1 second ``` -------------------------------- ### List Guild Bans Source: https://context7.com/kraigie/nostrum/llms.txt Retrieve a list of all users banned from a guild. ```elixir alias Nostrum.Api.Guild # List all bans {:ok, bans} = Guild.bans(guild_id) ``` -------------------------------- ### Fetch All Channel Messages Source: https://context7.com/kraigie/nostrum/llms.txt Retrieve all messages in a channel. Use with caution on channels with a large message history. ```elixir alias Nostrum.Api.Channel # Fetch ALL messages in a channel (use carefully on large channels) {:ok, all_messages} = Channel.messages(channel_id, :infinity, {}) ``` -------------------------------- ### Webhooks API Source: https://context7.com/kraigie/nostrum/llms.txt Create and manage Discord webhooks for posting messages to channels without requiring a bot gateway connection. ```APIDOC ## Webhooks — `Nostrum.Api.Webhook` Create and execute Discord webhooks for posting messages to channels without a bot gateway connection. ### Create Webhook Create a new webhook in a specified channel. Requires the `MANAGE_WEBHOOKS` permission. ```elixir Webhook.create(channel_id, %{name: "Alerts", avatar: nil}) ``` ``` -------------------------------- ### Execute a Discord webhook message Source: https://context7.com/kraigie/nostrum/llms.txt Posts a message using a Discord webhook. Optionally waits for the message object to be returned. ```elixir {:ok, msg} = Webhook.execute(wh.id, wh.token, %{ content: "Deploy succeeded! 🚀", username: "CI Bot", embeds: [%{title: "v1.2.3 deployed", color: 0x2ECC71}] }) ``` ```elixir Webhook.execute(wh.id, wh.token, %{content: "Hello!", wait: true}) ``` -------------------------------- ### Webhook Operations Source: https://context7.com/kraigie/nostrum/llms.txt Functions for executing, editing, fetching, modifying, and deleting webhooks. ```APIDOC ## Webhook Operations ### Execute Webhook Posts a message as the webhook. ```elixir Webhook.execute(wh.id, wh.token, %{ content: "Deploy succeeded! 🚀", username: "CI Bot", embeds: [%{title: "v1.2.3 deployed", color: 0x2ECC71}] }) ``` ### Execute and Wait Executes a webhook and waits for the message object. ```elixir Webhook.execute(wh.id, wh.token, %{content: "Hello!", wait: true}) ``` ### Edit Message Edits a message previously posted by the webhook. ```elixir Webhook.edit_message(wh.id, wh.token, msg.id, %{content: "Deploy succeeded! ✅"}) ``` ### Fetch Webhook Fetches an existing webhook by its ID. ```elixir Webhook.get(wh.id) ``` ### Modify Webhook Modifies the webhook's name and/or avatar. ```elixir Webhook.modify(wh.id, %{name: "Deploy Bot"}) ``` ### Delete Webhook Deletes the specified webhook. ```elixir Webhook.delete(wh.id) ``` ``` -------------------------------- ### Guild API Source: https://context7.com/kraigie/nostrum/llms.txt Functions for managing guilds, members, roles, bans, emojis, and more. Most write operations require specific Discord permissions. ```APIDOC ## Guild API — `Nostrum.Api.Guild` Manage guilds, members, roles, bans, emojis, and more. Most write operations require specific Discord permissions. ```elixir alias Nostrum.Api.Guild guild_id = 81384788765712384 user_id = 172150183260323840 # Fetch guild info from the API {:ok, guild} = Guild.get(guild_id) # Modify guild settings (requires MANAGE_GUILD) {:ok, _} = Guild.modify(guild_id, name: "My Updated Server", description: "A great place") # List all guild members (up to 1000 per request) {:ok, members} = Guild.members(guild_id, %{limit: 100}) # Modify a member (requires MANAGE_NICKNAMES / MANAGE_ROLES) :ok = Guild.modify_member(guild_id, user_id, %{nick: "coolnick"}) # Add / remove roles :ok = Guild.add_member_role(guild_id, user_id, role_id) :ok = Guild.remove_member_role(guild_id, user_id, role_id) # Kick a member (requires KICK_MEMBERS) :ok = Guild.kick_member(guild_id, user_id, "Violated rules") # Ban / unban (requires BAN_MEMBERS) :ok = Guild.ban_member(guild_id, user_id, 7, "Spamming") # delete 7 days of messages :ok = Guild.unban_member(guild_id, user_id) # List all bans {:ok, bans} = Guild.bans(guild_id) # Create, modify, delete roles {:ok, role} = Guild.create_role(guild_id, %{name: "Moderator", color: 0xE74C3C, hoist: true}) {:ok, _} = Guild.modify_role(guild_id, role.id, %{name: "Senior Moderator"}) :ok = Guild.delete_role(guild_id, role.id, "Role restructuring") # Get all roles {:ok, roles} = Guild.roles(guild_id) # Guild prune (removes members inactive for N days) {:ok, %{pruned: count}} = Guild.estimate_prune_count(guild_id, 30) {:ok, %{pruned: _}} = Guild.begin_prune(guild_id, 30, "Inactive member cleanup") # Leave a guild the bot is in :ok = Guild.leave(guild_id) ``` ``` -------------------------------- ### Control audio playback Source: https://context7.com/kraigie/nostrum/llms.txt Provides controls to pause, resume, and stop audio playback in a voice channel. ```elixir Voice.pause(guild_id) ``` ```elixir Voice.resume(guild_id) ``` ```elixir Voice.stop(guild_id) ``` -------------------------------- ### Extract Raw Stream URL with youtube-dl Source: https://github.com/kraigie/nostrum/blob/master/guides/functionality/voice.md Use youtube-dl to extract the raw stream URL (e.g., .m3u8 or .hls) from a given URL. This is helpful when streamlink cannot directly process a human-readable URL. ```elixir {raw_url, 0} = System.cmd("youtube-dl", ["-f", "best", "-g", url]) raw_url = raw_url |> String.trim() ``` -------------------------------- ### Modify Guild Settings Source: https://context7.com/kraigie/nostrum/llms.txt Update the settings of a guild, such as its name or description. Requires the MANAGE_GUILD permission. ```elixir alias Nostrum.Api.Guild # Modify guild settings (requires MANAGE_GUILD) {:ok, _} = Guild.modify(guild_id, name: "My Updated Server", description: "A great place") ``` -------------------------------- ### Find user's voice channel from cache Source: https://context7.com/kraigie/nostrum/llms.txt Looks up which voice channel a user is currently in by accessing guild voice states from the cache. Requires the guild to be cached. ```elixir voice_channel_id = guild_id |> GuildCache.get!() |> Map.get(:voice_states) |> Enum.find(%{}, fn vs -> vs.user_id == user_id end) |> Map.get(:channel_id) ```