Nostrum.Bot (nostrum v0.11.0-dev)

View Source

Supervisor for a bot along with all its associated components.

Usage

In your supervisor tree, write:

bot_options = %{
  consumer: MyBot.Consumer,
  # This is an example and includes privileged intents, but
  # should provide a useful set of intents for bots. Feel
  # free to adjust this as you see fit.
  intents: [
    :direct_messages,
    :guild_bans,
    :guild_members,
    :guild_message_reactions,
    :guild_messages,
    :guilds,
    :message_content
  ],
  wrapped_token: fn -> System.fetch_env!("BOT_TOKEN") end,
}
children = [
  {Nostrum.Bot, bot_options}
]

See bot_options/0 for which options you can configure here. You may also configure supervisor_options/0, which allow you to customize the options we start the Supervisor with by defining your child in the form {Nostrum.Bot, {bot_options, supervisor_options}}, but the default supervisor_options/0 should suit most needs.

Migration guide

If you are still using the old way of deploying nostrum, please do the following:

  1. Remove the :nostrum, :token configuration option. nostrum will then assume that you are starting it as part of your own supervision tree, and automatic startup via the application is disabled.

  2. Remove your consumer from your application supervisor tree.

  3. In your supervisor tree, add the bot_options described above, and add Nostrum.Bot to your supervisor tree as described above. For the :intents setting, cut the value you previously had in your config.exs and use it here. Do the same with :num_shards, renaming it to :shards on the bot_options map. You can then remove the old config values.

  4. In your consumer, change use Nostrum.Consumer to @behaviour Nostrum.Consumer.

  5. In your consumer, add a default event handling clause for unmatched events at the end of the module, such as:

  def handle_event(_), do: :ok

Multiple Bots

Nostrum allows you to start multiple bots under your supervision tree. If you are calling a function that interacts with an API specific to a bot - Rest, Cache, Voice, etc. - nostrum needs to determine which bot to call it for. It does this with a bot Registry and process dictionaries so that you don't have to pass an additional argument to every function.

Inside the handle_event/1 callback in your consumer, the calling process already has the context of the bot that generated the event for any API calls that follow.

If you have a single bot running and need to call an API asynchronously, i.e. not as a response to a gateway event in your consumer, nostrum will automatically determine the bot.

However, if you have multiple bots running and you need to make unprompted bot calls, you will need to assist nostrum by setting the process's context in one of three ways:

  • Nostrum.Bot.with_bot(MyBot, fn -> ... end) to wrap the API function calls
  • use Nostrum.Bot, name: MyBot at the top of the module where the API-calling functions are defined
  • Nostrum.Bot.set_bot_name(MyBot) dynamically before each API call

You are free to call API functions from newly-spawned tasks as nostrum will search the process dictionaries of a few generations of calling processes to find the bot context.

Note that, unless explicitly configured otherwise (see bot_options/0), nostrum uses the user ID from the bot's token as the bot name. If you wish to dynamically determine the user ID from the token, use Nostrum.Token.decode_token!/1.

Summary

Types

Options to start a bot with.

Which gateway intents to request.

Unique name for the bot.

Options to pass to the bot's supervisor.

Functions

Returns all running bots

Manually set the bot name in the calling process's context

Call a function with the context of the provided bot

Types

bot_options()

(since 0.11.0)
@type bot_options() :: %{
  :consumer => module(),
  :intents => :all | :nonprivileged | [atom()],
  :wrapped_token => (-> String.t()),
  optional(:name) => name(),
  optional(:shards) =>
    :auto
    | :manual
    | (num_shards :: pos_integer())
    | {lowest :: pos_integer(), highest :: pos_integer(),
       total :: pos_integer()},
  optional(:request_guild_members) => boolean(),
  optional(:ffmpeg) => String.t(),
  optional(:youtubedl) => String.t(),
  optional(:streamlink) => String.t(),
  optional(:audio_timeout) => pos_integer(),
  optional(:audio_frames_per_burst) => pos_integer(),
  optional(:voice_auto_connect) => boolean(),
  optional(:voice_encryption_mode) => Nostrum.Voice.Crypto.cipher(),
  optional(:log_full_events) => boolean(),
  optional(:log_dispatch_events) => boolean(),
  optional(:force_http1) => boolean()
}

Options to start a bot with.

Required fields

  • :consumer: A module implementing the Nostrum.Consumer behaviour that should be called for each event.

  • :intents: Which gateway intents to request. See the Gateway Intents documentation for more details.

  • :wrapped_token: A function that takes no arguments and returns the bot token to use. This is wrapped to prevent exposure of the token in stacktraces.

Optional fields

  • :name: Unique name for your bot. Defaults to the integer bot id encoded into the token.

  • :shards: Shards that should be started with this bot. Possible values:

    • :auto uses the suggested amount of shards as provided by Discord. This value is the default.

    • :manual do not automatically spawn shards. In this case, it is your responsibility to spawn shards manually, see the manual sharding documentation.

    • pos_integer(): A number of shards to run. nostrum will warn if this is not the recommended amount.

    • {lowest, highest, total} starts shards lowest to highest. total should contain the total amount of shards that your bot is expected to have. Useful for splitting a single bot across multiple nodes, see the multi-node documentation for further information.

Global config override fields

The following fields will override global config values if provided. More information on globally configuring these options and their defaults may be found here.

  • :request_guild_members
  • :ffmpeg
  • :youtubedl
  • :streamlink
  • :audio_timeout
  • :audio_frames_per_burst
  • :voice_auto_connect
  • :voice_encryption_mode
  • :log_full_events
  • :log_dispatch_events
  • :force_http1

intents()

(since 0.11.0)
@type intents() :: :all | :nonprivileged | [atom()]

Which gateway intents to request.

A full reference can be found either on Discord or on our Gateway Intents documentation.

name()

(since 0.11.0)
@type name() :: atom() | integer() | String.t()

Unique name for the bot.

supervisor_options()

(since 0.11.0)
@type supervisor_options() :: [Supervisor.init_option()]

Options to pass to the bot's supervisor.

By default, we set the strategy: :one_for_one. No other options are set by nostrum.

Functions

child_spec(nostrum_options)

(since 0.11.0)
@spec child_spec(options()) :: Supervisor.child_spec()

fetch_all_bots()

(since 0.11.0)
@spec fetch_all_bots() :: [%{name: name(), pid: pid(), bot_options: bot_options()}]

Returns all running bots

set_bot_name(name)

(since 0.11.0)
@spec set_bot_name(name()) :: name() | nil

Manually set the bot name in the calling process's context

spawn_task(task_func, bot_name \\ fetch_bot_name(), task_key \\ :erlang.unique_integer())

(since 0.11.0)

with_bot(name, function, next \\ :reset)

(since 0.11.0)
@spec with_bot(name(), (-> any()), :reset | :clear | :keep) :: any()

Call a function with the context of the provided bot

Nostrum.Bot.with_bot(ReticentBot, fn ->
  Nostrum.Api.Message.create(channel_id, "Hi...")
end)

Nostrum.Bot.with_bot(SillyGoofyBot, fn ->
  Nostrum.Api.Message.create(channel_id, "Heyyy :)")
end, :keep)

# Context is still set to SillyGoofyBot
Nostrum.Api.Message.create(channel_id, "Me again :)")

Parameters

  • name - Name of the bot to run the function as
  • function - Zero arity function to execute
  • next - What to do with the process's bot context after the function
    • :reset - Reset the values to what they were before (default)
    • :clear - Set the values to nil
    • :keep - Keep the provided bot's context in this process's dictionary