From 4094424c4f640d01500e84a2d81b330bb0679269 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 01:08:16 -0400 Subject: [PATCH 01/27] Refactored optional field logic, added nsfw, started work on contexts --- lib/nosedrum/application_command.ex | 28 ++++++++++++++++- lib/nosedrum/storage/dispatcher.ex | 48 ++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index d454389..129273c 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -292,6 +292,32 @@ defmodule Nosedrum.ApplicationCommand do """ @callback default_member_permissions() :: String.t() + @doc """ + An optional callback that returns a boolean, determining whether a command is + [age-restricted](https://discord.com/developers/docs/interactions/application-commands#agerestricted-commands). + + Defaults to `false`. + + ## Example + ```elixir + def nsfw, do: true + ``` + """ + @callback nsfw() :: boolean() + + @doc """ + An optional callback that returns a list of interaction context types, which + dictate where a command may be used (servers, DMs, or DMs directly with the bot user0). + + Only applies to globally-scoped commands. + + # Example + ```elixir + def contexts, do: [:guild, :bot_dm, :private_channel] + ``` + """ + @callback contexts() :: [:guild | :bot_dm | :private_channel] + @doc """ Execute the command invoked by the given `t:Nostrum.Struct.Interaction.t/0`. Returns a `t:response/0` @@ -320,5 +346,5 @@ defmodule Nosedrum.ApplicationCommand do @callback update_command_payload(map) :: map - @optional_callbacks [options: 0, default_member_permissions: 0, update_command_payload: 1] + @optional_callbacks [options: 0, default_member_permissions: 0, nsfw: 0, update_command_payload: 1] end diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 18755e5..9b961b8 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -1,6 +1,8 @@ defmodule Nosedrum.Storage.Dispatcher do @moduledoc """ An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. + + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -11,7 +13,7 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction - @option_type_map %{ + @option_type_mapping %{ sub_command: 1, sub_command_group: 2, string: 3, @@ -25,6 +27,17 @@ defmodule Nosedrum.Storage.Dispatcher do attachment: 11 } + @context_type_mapping %{ + guild: 0, + bot_dm: 1, + private_channel: 1 + } + + @optional_fields %{ + nsfw: 0, + default_member_permissions: 0 + } + ## Api def start_link(opts) do GenServer.start_link(__MODULE__, %{}, name: Keyword.get(opts, :name, __MODULE__)) @@ -227,19 +240,13 @@ defmodule Nosedrum.Storage.Dispatcher do [] end - payload = - %{ - type: parse_type(command.type()), - name: name - } - |> put_type_specific_fields(command, options) - |> apply_payload_updates(command) - - if function_exported?(command, :default_member_permissions, 0) do - Map.put(payload, :default_member_permissions, command.default_member_permissions()) - else - payload - end + %{ + type: parse_type(command.type()), + name: name + } + |> put_type_specific_fields(command, options) + |> add_optional_fields(command) + |> apply_payload_updates(command) end # This seems like a hacky way to unwrap the outer list... @@ -288,7 +295,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_option_types(options) do Enum.map(options, fn map when is_map_key(map, :type) -> - updated_map = Map.update!(map, :type, &Map.fetch!(@option_type_map, &1)) + updated_map = Map.update!(map, :type, &Map.fetch!(@option_type_mapping, &1)) if is_map_key(updated_map, :options) do parsed_options = parse_option_types(updated_map[:options]) @@ -336,4 +343,15 @@ defmodule Nosedrum.Storage.Dispatcher do payload end end + + defp add_optional_fields(payload, command) do + Enum.reduce(@optional_fields, payload, fn + {callback_name, callback_arity}, working_payload -> + if function_exported?(command, callback_name, callback_arity) do + Map.put(working_payload, callback_name, apply(command, callback_name, [])) + else + working_payload + end + end) + end end From 7b11f1cc1bd9dea289a73c07bb67593862eda3ae Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 03:20:35 -0400 Subject: [PATCH 02/27] Added some global-only handling (hopefully?) --- lib/nosedrum/storage/dispatcher.ex | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 9b961b8..0604a75 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -3,6 +3,7 @@ defmodule Nosedrum.Storage.Dispatcher do An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -33,6 +34,10 @@ defmodule Nosedrum.Storage.Dispatcher do private_channel: 1 } + @global_only_fields [ + :contexts + ] + @optional_fields %{ nsfw: 0, default_member_permissions: 0 @@ -114,7 +119,7 @@ defmodule Nosedrum.Storage.Dispatcher do def handle_call({:process_queue, :global}, _from, commands) do command_list = Enum.map(commands, fn {p, c} -> - build_payload(p, c) + build_payload(p, c, :global) end) case ApplicationCommand.bulk_overwrite_global_commands(command_list) do @@ -129,7 +134,7 @@ defmodule Nosedrum.Storage.Dispatcher do def handle_call({:process_queue, guild_id}, _from, commands) do command_list = Enum.map(commands, fn {p, c} -> - build_payload(p, c) + build_payload(p, c, guild_id) end) case ApplicationCommand.bulk_overwrite_guild_commands(guild_id, command_list) do @@ -229,7 +234,7 @@ defmodule Nosedrum.Storage.Dispatcher do {:reply, Map.fetch(commands, name), commands} end - defp build_payload(name, command) when is_binary(name) do + defp build_payload(name, command, scope) when is_binary(name) do Code.ensure_loaded(command) options = @@ -247,6 +252,7 @@ defmodule Nosedrum.Storage.Dispatcher do |> put_type_specific_fields(command, options) |> add_optional_fields(command) |> apply_payload_updates(command) + |> handle_global_fields(scope) end # This seems like a hacky way to unwrap the outer list... @@ -344,13 +350,21 @@ defmodule Nosedrum.Storage.Dispatcher do end end + defp handle_global_fields(payload, scope) do + case scope do + :global -> Map.drop(payload, @global_only_fields) + _ -> payload + end + + end + defp add_optional_fields(payload, command) do Enum.reduce(@optional_fields, payload, fn - {callback_name, callback_arity}, working_payload -> + {callback_name, callback_arity}, new_payload -> if function_exported?(command, callback_name, callback_arity) do - Map.put(working_payload, callback_name, apply(command, callback_name, [])) + Map.put(new_payload, callback_name, apply(command, callback_name, [])) else - working_payload + new_payload end end) end From 5d99a18401ea0ca36dfd87edbc07ea0fd9b06e9f Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 03:47:14 -0400 Subject: [PATCH 03/27] Refactor mappings. Screw it, this is a refactor. --- lib/nosedrum/storage/dispatcher.ex | 65 ++++++++++++++++++------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 0604a75..b39b278 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -4,6 +4,7 @@ defmodule Nosedrum.Storage.Dispatcher do + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -14,24 +15,34 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction - @option_type_mapping %{ - sub_command: 1, - sub_command_group: 2, - string: 3, - integer: 4, - boolean: 5, - user: 6, - channel: 7, - role: 8, - mentionable: 9, - number: 10, - attachment: 11 - } - - @context_type_mapping %{ - guild: 0, - bot_dm: 1, - private_channel: 1 + @type_mappings %{ + options: %{ + sub_command: 1, + sub_command_group: 2, + string: 3, + integer: 4, + boolean: 5, + user: 6, + channel: 7, + role: 8, + mentionable: 9, + number: 10, + attachment: 11 + }, + contexts: %{ + guild: 0, + bot_dm: 1, + private_channel: 2 + }, + integration_types: %{ + guild_install: 0, + user_install: 1 + }, + commands: %{ + slash: 1, + user: 2, + message: 3 + } } @global_only_fields [ @@ -245,9 +256,12 @@ defmodule Nosedrum.Storage.Dispatcher do [] end + contexts = if function_exported?(command, :contexts, 0), do: command.contexts(), else: [] + %{ type: parse_type(command.type()), - name: name + name: name, + contexts: contexts } |> put_type_specific_fields(command, options) |> add_optional_fields(command) @@ -289,11 +303,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_type(type) do Map.fetch!( - %{ - slash: 1, - user: 2, - message: 3 - }, + @type_mappings.commands, type ) end @@ -301,7 +311,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_option_types(options) do Enum.map(options, fn map when is_map_key(map, :type) -> - updated_map = Map.update!(map, :type, &Map.fetch!(@option_type_mapping, &1)) + updated_map = Map.update!(map, :type, &Map.fetch!(@type_mappings.options, &1)) if is_map_key(updated_map, :options) do parsed_options = parse_option_types(updated_map[:options]) @@ -355,7 +365,6 @@ defmodule Nosedrum.Storage.Dispatcher do :global -> Map.drop(payload, @global_only_fields) _ -> payload end - end defp add_optional_fields(payload, command) do @@ -368,4 +377,8 @@ defmodule Nosedrum.Storage.Dispatcher do end end) end + + defp parse_field(payload, command, :options) do + # TODO I'll add something that parses all parsing needing stuff. + end end From f1a80d7ca56ad7b0c0ce92080b8134302db92115 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 04:09:26 -0400 Subject: [PATCH 04/27] More refactoring of the adding fields --- lib/nosedrum/storage/dispatcher.ex | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index b39b278..64d7549 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -5,6 +5,7 @@ defmodule Nosedrum.Storage.Dispatcher do + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -256,12 +257,9 @@ defmodule Nosedrum.Storage.Dispatcher do [] end - contexts = if function_exported?(command, :contexts, 0), do: command.contexts(), else: [] - %{ type: parse_type(command.type()), - name: name, - contexts: contexts + name: name } |> put_type_specific_fields(command, options) |> add_optional_fields(command) @@ -371,14 +369,30 @@ defmodule Nosedrum.Storage.Dispatcher do Enum.reduce(@optional_fields, payload, fn {callback_name, callback_arity}, new_payload -> if function_exported?(command, callback_name, callback_arity) do - Map.put(new_payload, callback_name, apply(command, callback_name, [])) + add_field(new_payload, command, callback_name) else new_payload end end) end - defp parse_field(payload, command, :options) do - # TODO I'll add something that parses all parsing needing stuff. + defp add_field(payload, command, :default_member_permissions) do + Map.put( + payload, + :default_member_permissions, + command |> apply(:default_member_permissions, []) |> Nostrum.Permission.to_bitset() + ) + end + + defp add_field(payload, command, :contexts) do + Map.put( + payload, + :contexts, + command |> apply(:contexts, []) |> Enum.map(&@type_mappings.contexts[&1]) + ) + end + + defp add_field(payload, command, name) do + Map.put(payload, name, command |> apply(name, [])) end end From 714d84307c58025cdb0891e7d455bbfeb797abd1 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 29 Aug 2025 20:32:45 -0400 Subject: [PATCH 05/27] Changed types in ApplicationCommand --- lib/nosedrum/application_command.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index 129273c..c30562c 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -94,6 +94,7 @@ defmodule Nosedrum.ApplicationCommand do @moduledoc since: "0.4.0" alias Nostrum.Struct.{Embed, Interaction} + alias Nostrum.Permission @typedoc """ Called by `Nosedrum.Storage.followup/2` after deferring an interaction response. @@ -290,7 +291,7 @@ defmodule Nosedrum.ApplicationCommand do Nostrum.Permission.to_bitset([:ban_members]) ``` """ - @callback default_member_permissions() :: String.t() + @callback default_member_permissions() :: [Permission.t()] @doc """ An optional callback that returns a boolean, determining whether a command is From 8c3e18e35302b7c9efbaf36eb7f2946c4c7dcc19 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 29 Aug 2025 20:34:40 -0400 Subject: [PATCH 06/27] Changed hexdocs --- lib/nosedrum/application_command.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index c30562c..3280a8b 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -282,13 +282,13 @@ defmodule Nosedrum.ApplicationCommand do @callback options() :: [option] @doc """ - An optional callback that returns a bitset for the required default permissions to run this command. + An optional callback that returns a list of atoms for the required default permissions to run this command. - Example callback that requires that the user has the permission to ban members to be able to see and execute this command + ## Example ```elixir def default_member_permissions, do: - Nostrum.Permission.to_bitset([:ban_members]) + [:ban_members, :kick_members, :manage_roles] ``` """ @callback default_member_permissions() :: [Permission.t()] From a53abdf71662b15a12141359231b43e917f2eaef Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 01:08:16 -0400 Subject: [PATCH 07/27] Refactored optional field logic, added nsfw, started work on contexts --- lib/nosedrum/application_command.ex | 28 ++++++++++++++++- lib/nosedrum/storage/dispatcher.ex | 48 ++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index d454389..129273c 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -292,6 +292,32 @@ defmodule Nosedrum.ApplicationCommand do """ @callback default_member_permissions() :: String.t() + @doc """ + An optional callback that returns a boolean, determining whether a command is + [age-restricted](https://discord.com/developers/docs/interactions/application-commands#agerestricted-commands). + + Defaults to `false`. + + ## Example + ```elixir + def nsfw, do: true + ``` + """ + @callback nsfw() :: boolean() + + @doc """ + An optional callback that returns a list of interaction context types, which + dictate where a command may be used (servers, DMs, or DMs directly with the bot user0). + + Only applies to globally-scoped commands. + + # Example + ```elixir + def contexts, do: [:guild, :bot_dm, :private_channel] + ``` + """ + @callback contexts() :: [:guild | :bot_dm | :private_channel] + @doc """ Execute the command invoked by the given `t:Nostrum.Struct.Interaction.t/0`. Returns a `t:response/0` @@ -320,5 +346,5 @@ defmodule Nosedrum.ApplicationCommand do @callback update_command_payload(map) :: map - @optional_callbacks [options: 0, default_member_permissions: 0, update_command_payload: 1] + @optional_callbacks [options: 0, default_member_permissions: 0, nsfw: 0, update_command_payload: 1] end diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 18755e5..9b961b8 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -1,6 +1,8 @@ defmodule Nosedrum.Storage.Dispatcher do @moduledoc """ An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. + + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -11,7 +13,7 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction - @option_type_map %{ + @option_type_mapping %{ sub_command: 1, sub_command_group: 2, string: 3, @@ -25,6 +27,17 @@ defmodule Nosedrum.Storage.Dispatcher do attachment: 11 } + @context_type_mapping %{ + guild: 0, + bot_dm: 1, + private_channel: 1 + } + + @optional_fields %{ + nsfw: 0, + default_member_permissions: 0 + } + ## Api def start_link(opts) do GenServer.start_link(__MODULE__, %{}, name: Keyword.get(opts, :name, __MODULE__)) @@ -227,19 +240,13 @@ defmodule Nosedrum.Storage.Dispatcher do [] end - payload = - %{ - type: parse_type(command.type()), - name: name - } - |> put_type_specific_fields(command, options) - |> apply_payload_updates(command) - - if function_exported?(command, :default_member_permissions, 0) do - Map.put(payload, :default_member_permissions, command.default_member_permissions()) - else - payload - end + %{ + type: parse_type(command.type()), + name: name + } + |> put_type_specific_fields(command, options) + |> add_optional_fields(command) + |> apply_payload_updates(command) end # This seems like a hacky way to unwrap the outer list... @@ -288,7 +295,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_option_types(options) do Enum.map(options, fn map when is_map_key(map, :type) -> - updated_map = Map.update!(map, :type, &Map.fetch!(@option_type_map, &1)) + updated_map = Map.update!(map, :type, &Map.fetch!(@option_type_mapping, &1)) if is_map_key(updated_map, :options) do parsed_options = parse_option_types(updated_map[:options]) @@ -336,4 +343,15 @@ defmodule Nosedrum.Storage.Dispatcher do payload end end + + defp add_optional_fields(payload, command) do + Enum.reduce(@optional_fields, payload, fn + {callback_name, callback_arity}, working_payload -> + if function_exported?(command, callback_name, callback_arity) do + Map.put(working_payload, callback_name, apply(command, callback_name, [])) + else + working_payload + end + end) + end end From 07b3638f8c7e57672a68e9d11a819ac1316a85d3 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 03:20:35 -0400 Subject: [PATCH 08/27] Added some global-only handling (hopefully?) --- lib/nosedrum/storage/dispatcher.ex | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 9b961b8..0604a75 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -3,6 +3,7 @@ defmodule Nosedrum.Storage.Dispatcher do An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -33,6 +34,10 @@ defmodule Nosedrum.Storage.Dispatcher do private_channel: 1 } + @global_only_fields [ + :contexts + ] + @optional_fields %{ nsfw: 0, default_member_permissions: 0 @@ -114,7 +119,7 @@ defmodule Nosedrum.Storage.Dispatcher do def handle_call({:process_queue, :global}, _from, commands) do command_list = Enum.map(commands, fn {p, c} -> - build_payload(p, c) + build_payload(p, c, :global) end) case ApplicationCommand.bulk_overwrite_global_commands(command_list) do @@ -129,7 +134,7 @@ defmodule Nosedrum.Storage.Dispatcher do def handle_call({:process_queue, guild_id}, _from, commands) do command_list = Enum.map(commands, fn {p, c} -> - build_payload(p, c) + build_payload(p, c, guild_id) end) case ApplicationCommand.bulk_overwrite_guild_commands(guild_id, command_list) do @@ -229,7 +234,7 @@ defmodule Nosedrum.Storage.Dispatcher do {:reply, Map.fetch(commands, name), commands} end - defp build_payload(name, command) when is_binary(name) do + defp build_payload(name, command, scope) when is_binary(name) do Code.ensure_loaded(command) options = @@ -247,6 +252,7 @@ defmodule Nosedrum.Storage.Dispatcher do |> put_type_specific_fields(command, options) |> add_optional_fields(command) |> apply_payload_updates(command) + |> handle_global_fields(scope) end # This seems like a hacky way to unwrap the outer list... @@ -344,13 +350,21 @@ defmodule Nosedrum.Storage.Dispatcher do end end + defp handle_global_fields(payload, scope) do + case scope do + :global -> Map.drop(payload, @global_only_fields) + _ -> payload + end + + end + defp add_optional_fields(payload, command) do Enum.reduce(@optional_fields, payload, fn - {callback_name, callback_arity}, working_payload -> + {callback_name, callback_arity}, new_payload -> if function_exported?(command, callback_name, callback_arity) do - Map.put(working_payload, callback_name, apply(command, callback_name, [])) + Map.put(new_payload, callback_name, apply(command, callback_name, [])) else - working_payload + new_payload end end) end From 15348ee4c8593a7035b135f98548ad54e92943f2 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 03:47:14 -0400 Subject: [PATCH 09/27] Refactor mappings. Screw it, this is a refactor. --- lib/nosedrum/storage/dispatcher.ex | 65 ++++++++++++++++++------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 0604a75..b39b278 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -4,6 +4,7 @@ defmodule Nosedrum.Storage.Dispatcher do + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -14,24 +15,34 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction - @option_type_mapping %{ - sub_command: 1, - sub_command_group: 2, - string: 3, - integer: 4, - boolean: 5, - user: 6, - channel: 7, - role: 8, - mentionable: 9, - number: 10, - attachment: 11 - } - - @context_type_mapping %{ - guild: 0, - bot_dm: 1, - private_channel: 1 + @type_mappings %{ + options: %{ + sub_command: 1, + sub_command_group: 2, + string: 3, + integer: 4, + boolean: 5, + user: 6, + channel: 7, + role: 8, + mentionable: 9, + number: 10, + attachment: 11 + }, + contexts: %{ + guild: 0, + bot_dm: 1, + private_channel: 2 + }, + integration_types: %{ + guild_install: 0, + user_install: 1 + }, + commands: %{ + slash: 1, + user: 2, + message: 3 + } } @global_only_fields [ @@ -245,9 +256,12 @@ defmodule Nosedrum.Storage.Dispatcher do [] end + contexts = if function_exported?(command, :contexts, 0), do: command.contexts(), else: [] + %{ type: parse_type(command.type()), - name: name + name: name, + contexts: contexts } |> put_type_specific_fields(command, options) |> add_optional_fields(command) @@ -289,11 +303,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_type(type) do Map.fetch!( - %{ - slash: 1, - user: 2, - message: 3 - }, + @type_mappings.commands, type ) end @@ -301,7 +311,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_option_types(options) do Enum.map(options, fn map when is_map_key(map, :type) -> - updated_map = Map.update!(map, :type, &Map.fetch!(@option_type_mapping, &1)) + updated_map = Map.update!(map, :type, &Map.fetch!(@type_mappings.options, &1)) if is_map_key(updated_map, :options) do parsed_options = parse_option_types(updated_map[:options]) @@ -355,7 +365,6 @@ defmodule Nosedrum.Storage.Dispatcher do :global -> Map.drop(payload, @global_only_fields) _ -> payload end - end defp add_optional_fields(payload, command) do @@ -368,4 +377,8 @@ defmodule Nosedrum.Storage.Dispatcher do end end) end + + defp parse_field(payload, command, :options) do + # TODO I'll add something that parses all parsing needing stuff. + end end From 4be32d770099703ba9f6052d031c1388367dda7c Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 22 Aug 2025 04:09:26 -0400 Subject: [PATCH 10/27] More refactoring of the adding fields --- lib/nosedrum/storage/dispatcher.ex | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index b39b278..64d7549 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -5,6 +5,7 @@ defmodule Nosedrum.Storage.Dispatcher do + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -256,12 +257,9 @@ defmodule Nosedrum.Storage.Dispatcher do [] end - contexts = if function_exported?(command, :contexts, 0), do: command.contexts(), else: [] - %{ type: parse_type(command.type()), - name: name, - contexts: contexts + name: name } |> put_type_specific_fields(command, options) |> add_optional_fields(command) @@ -371,14 +369,30 @@ defmodule Nosedrum.Storage.Dispatcher do Enum.reduce(@optional_fields, payload, fn {callback_name, callback_arity}, new_payload -> if function_exported?(command, callback_name, callback_arity) do - Map.put(new_payload, callback_name, apply(command, callback_name, [])) + add_field(new_payload, command, callback_name) else new_payload end end) end - defp parse_field(payload, command, :options) do - # TODO I'll add something that parses all parsing needing stuff. + defp add_field(payload, command, :default_member_permissions) do + Map.put( + payload, + :default_member_permissions, + command |> apply(:default_member_permissions, []) |> Nostrum.Permission.to_bitset() + ) + end + + defp add_field(payload, command, :contexts) do + Map.put( + payload, + :contexts, + command |> apply(:contexts, []) |> Enum.map(&@type_mappings.contexts[&1]) + ) + end + + defp add_field(payload, command, name) do + Map.put(payload, name, command |> apply(name, [])) end end From f6ba2a84ca8c09ad687b933603ed7b650b44b73c Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 29 Aug 2025 20:32:45 -0400 Subject: [PATCH 11/27] Changed types in ApplicationCommand --- lib/nosedrum/application_command.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index 129273c..c30562c 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -94,6 +94,7 @@ defmodule Nosedrum.ApplicationCommand do @moduledoc since: "0.4.0" alias Nostrum.Struct.{Embed, Interaction} + alias Nostrum.Permission @typedoc """ Called by `Nosedrum.Storage.followup/2` after deferring an interaction response. @@ -290,7 +291,7 @@ defmodule Nosedrum.ApplicationCommand do Nostrum.Permission.to_bitset([:ban_members]) ``` """ - @callback default_member_permissions() :: String.t() + @callback default_member_permissions() :: [Permission.t()] @doc """ An optional callback that returns a boolean, determining whether a command is From 9a0883ac3e54207c6cff375837ff2988a7fbb369 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Fri, 29 Aug 2025 20:34:40 -0400 Subject: [PATCH 12/27] Changed hexdocs --- lib/nosedrum/application_command.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index c30562c..3280a8b 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -282,13 +282,13 @@ defmodule Nosedrum.ApplicationCommand do @callback options() :: [option] @doc """ - An optional callback that returns a bitset for the required default permissions to run this command. + An optional callback that returns a list of atoms for the required default permissions to run this command. - Example callback that requires that the user has the permission to ban members to be able to see and execute this command + ## Example ```elixir def default_member_permissions, do: - Nostrum.Permission.to_bitset([:ban_members]) + [:ban_members, :kick_members, :manage_roles] ``` """ @callback default_member_permissions() :: [Permission.t()] From a18efb835ddf333e9b1b20a8713186ee361dabcb Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 02:12:32 -0400 Subject: [PATCH 13/27] Readability improvements --- lib/nosedrum/storage/dispatcher.ex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 64d7549..35f21ee 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -6,6 +6,7 @@ defmodule Nosedrum.Storage.Dispatcher do + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -367,11 +368,11 @@ defmodule Nosedrum.Storage.Dispatcher do defp add_optional_fields(payload, command) do Enum.reduce(@optional_fields, payload, fn - {callback_name, callback_arity}, new_payload -> - if function_exported?(command, callback_name, callback_arity) do - add_field(new_payload, command, callback_name) + {callback_name, callback_arity}, acc -> + if command |> function_exported?(callback_name, callback_arity) do + acc |> add_field(command, callback_name) else - new_payload + acc end end) end From 919222a48dc7b23f64da8b21596a55e4022e5d35 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 03:58:35 -0400 Subject: [PATCH 14/27] Removed global handling (not needed), fixed oversights with optional fields --- lib/nosedrum/application_command.ex | 25 ++++++++++++++++++---- lib/nosedrum/storage/dispatcher.ex | 33 +++++++++++++++-------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index 3280a8b..bca1bdf 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -308,16 +308,26 @@ defmodule Nosedrum.ApplicationCommand do @doc """ An optional callback that returns a list of interaction context types, which - dictate where a command may be used (servers, DMs, or DMs directly with the bot user0). + dictate where a command may be used (servers, DMs directly with the bot user, or all other DMS). Only applies to globally-scoped commands. + If not set, this will default to guild and DMs directly with the bot user. + # Example ```elixir - def contexts, do: [:guild, :bot_dm, :private_channel] + def contexts, do: [:guild, :private_channel, :bot_dms] ``` """ - @callback contexts() :: [:guild | :bot_dm | :private_channel] + @callback contexts() :: [:guild | :bot_dms | :private_channel] + + @doc """ + An optional callback that determines which installation context (user or guild) the command + may be used in. + + If not set, this will default to all installation contexts. + """ + @callback integration_types() :: [:guild_install | :user_install] @doc """ Execute the command invoked by the given `t:Nostrum.Struct.Interaction.t/0`. Returns a `t:response/0` @@ -347,5 +357,12 @@ defmodule Nosedrum.ApplicationCommand do @callback update_command_payload(map) :: map - @optional_callbacks [options: 0, default_member_permissions: 0, nsfw: 0, update_command_payload: 1] + @optional_callbacks [ + options: 0, + default_member_permissions: 0, + nsfw: 0, + contexts: 0, + integration_types: 0, + update_command_payload: 1 + ] end diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 35f21ee..43b7056 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -17,6 +17,8 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction + require Logger + @type_mappings %{ options: %{ sub_command: 1, @@ -33,7 +35,7 @@ defmodule Nosedrum.Storage.Dispatcher do }, contexts: %{ guild: 0, - bot_dm: 1, + bot_dms: 1, private_channel: 2 }, integration_types: %{ @@ -47,13 +49,11 @@ defmodule Nosedrum.Storage.Dispatcher do } } - @global_only_fields [ - :contexts - ] - @optional_fields %{ nsfw: 0, - default_member_permissions: 0 + default_member_permissions: 0, + contexts: 0, + integration_types: 0 } ## Api @@ -247,7 +247,7 @@ defmodule Nosedrum.Storage.Dispatcher do {:reply, Map.fetch(commands, name), commands} end - defp build_payload(name, command, scope) when is_binary(name) do + defp build_payload(name, command) when is_binary(name) do Code.ensure_loaded(command) options = @@ -265,7 +265,6 @@ defmodule Nosedrum.Storage.Dispatcher do |> put_type_specific_fields(command, options) |> add_optional_fields(command) |> apply_payload_updates(command) - |> handle_global_fields(scope) end # This seems like a hacky way to unwrap the outer list... @@ -359,14 +358,7 @@ defmodule Nosedrum.Storage.Dispatcher do end end - defp handle_global_fields(payload, scope) do - case scope do - :global -> Map.drop(payload, @global_only_fields) - _ -> payload - end - end - - defp add_optional_fields(payload, command) do + def add_optional_fields(payload, command) do Enum.reduce(@optional_fields, payload, fn {callback_name, callback_arity}, acc -> if command |> function_exported?(callback_name, callback_arity) do @@ -393,7 +385,16 @@ defmodule Nosedrum.Storage.Dispatcher do ) end + defp add_field(payload, command, :integration_types) do + Map.put( + payload, + :integration_types, + command |> apply(:integration_types, []) |> Enum.map(&@type_mappings.integration_types[&1]) + ) + end + defp add_field(payload, command, name) do Map.put(payload, name, command |> apply(name, [])) + end end From f361228d5b37f13b0e0a956afeef3087d79e02c8 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 04:00:35 -0400 Subject: [PATCH 15/27] Re-privated add_optional_fields (unprivated for testing) --- lib/nosedrum/storage/dispatcher.ex | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 43b7056..6016b79 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -7,6 +7,7 @@ defmodule Nosedrum.Storage.Dispatcher do + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -358,7 +359,7 @@ defmodule Nosedrum.Storage.Dispatcher do end end - def add_optional_fields(payload, command) do + defp add_optional_fields(payload, command) do Enum.reduce(@optional_fields, payload, fn {callback_name, callback_arity}, acc -> if command |> function_exported?(callback_name, callback_arity) do @@ -393,8 +394,5 @@ defmodule Nosedrum.Storage.Dispatcher do ) end - defp add_field(payload, command, name) do - Map.put(payload, name, command |> apply(name, [])) - - end + defp add_field(payload, command, name), do: Map.put(payload, name, command |> apply(name, [])) end From 1dad529eab116ff5e0f540d290a71386106e1983 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 04:03:58 -0400 Subject: [PATCH 16/27] Added missing documentation for integration_types --- lib/nosedrum/application_command.ex | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index bca1bdf..da64b09 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -282,14 +282,13 @@ defmodule Nosedrum.ApplicationCommand do @callback options() :: [option] @doc """ - An optional callback that returns a list of atoms for the required default permissions to run this command. + An optional callback that returns a list of atoms for the required default permissions to run this command. - ## Example + ## Example - ```elixir - def default_member_permissions, do: - [:ban_members, :kick_members, :manage_roles] - ``` + ```elixir + def default_member_permissions, do: [:ban_members, :kick_members, :manage_roles] + ``` """ @callback default_member_permissions() :: [Permission.t()] @@ -326,6 +325,11 @@ defmodule Nosedrum.ApplicationCommand do may be used in. If not set, this will default to all installation contexts. + + # Example + ```elixir + def integration_types, do: [:guild_install, :user_install] + ``` """ @callback integration_types() :: [:guild_install | :user_install] @@ -354,7 +358,6 @@ defmodule Nosedrum.ApplicationCommand do `Nostrum.Api.create_global_application_command/2` or `Nostrum.Api.create_guild_application_command/3` """ - @callback update_command_payload(map) :: map @optional_callbacks [ From 7b12e674c3af3e7c8d0795166e30c91cb17d2a27 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 04:13:50 -0400 Subject: [PATCH 17/27] Fixes for global handling removal --- lib/nosedrum/storage/dispatcher.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 6016b79..343d2b1 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -133,7 +133,7 @@ defmodule Nosedrum.Storage.Dispatcher do def handle_call({:process_queue, :global}, _from, commands) do command_list = Enum.map(commands, fn {p, c} -> - build_payload(p, c, :global) + build_payload(p, c) end) case ApplicationCommand.bulk_overwrite_global_commands(command_list) do @@ -148,7 +148,7 @@ defmodule Nosedrum.Storage.Dispatcher do def handle_call({:process_queue, guild_id}, _from, commands) do command_list = Enum.map(commands, fn {p, c} -> - build_payload(p, c, guild_id) + build_payload(p, c) end) case ApplicationCommand.bulk_overwrite_guild_commands(guild_id, command_list) do From 195e74acc068bcd31430697156bf563069ce75e9 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 04:16:31 -0400 Subject: [PATCH 18/27] Removed logger requirement - unintentional debugging leftover. --- lib/nosedrum/storage/dispatcher.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 343d2b1..1cfc0d8 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -18,8 +18,6 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction - require Logger - @type_mappings %{ options: %{ sub_command: 1, From d76af21d3a4cd5566bfc612222bfed59ac495cc2 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 04:17:37 -0400 Subject: [PATCH 19/27] I have no idea why the formatter did that to the moduledoc. --- lib/nosedrum/storage/dispatcher.ex | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 1cfc0d8..3d6ea8d 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -1,13 +1,6 @@ defmodule Nosedrum.Storage.Dispatcher do @moduledoc """ An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. - - - - - - - """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage From b0b8956b8cb965cea59d585236a0c80b6fffaa80 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 05:51:55 -0400 Subject: [PATCH 20/27] Don't use apply unless it's necessary --- lib/nosedrum/storage/dispatcher.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 3d6ea8d..e449318 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -365,7 +365,7 @@ defmodule Nosedrum.Storage.Dispatcher do Map.put( payload, :default_member_permissions, - command |> apply(:default_member_permissions, []) |> Nostrum.Permission.to_bitset() + command.default_members_permissions() |> Nostrum.Permission.to_bitset() ) end @@ -373,7 +373,7 @@ defmodule Nosedrum.Storage.Dispatcher do Map.put( payload, :contexts, - command |> apply(:contexts, []) |> Enum.map(&@type_mappings.contexts[&1]) + command.contexts() |> Enum.map(&@type_mappings.contexts[&1]) ) end @@ -381,9 +381,9 @@ defmodule Nosedrum.Storage.Dispatcher do Map.put( payload, :integration_types, - command |> apply(:integration_types, []) |> Enum.map(&@type_mappings.integration_types[&1]) + command.integration_types() |> Enum.map(&@type_mappings.integration_types[&1]) ) end - defp add_field(payload, command, name), do: Map.put(payload, name, command |> apply(name, [])) + defp add_field(payload, command, name), do: Map.put(payload, name, apply(command, name, [])) end From ebbe59cb0cfdaa6d170a18dc74db34024e393f89 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 05:53:50 -0400 Subject: [PATCH 21/27] Putting `alias Nosedrum.Storage` on its own line to hopefully make the linter happy --- lib/nosedrum/storage/dispatcher.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index e449318..04b564e 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -8,6 +8,7 @@ defmodule Nosedrum.Storage.Dispatcher do use GenServer alias Nosedrum.Storage + alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction From cf6679589715adfe5dae97cc6898f4ac74f29298 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sat, 30 Aug 2025 05:54:52 -0400 Subject: [PATCH 22/27] Correcting the right alias... --- lib/nosedrum/application_command.ex | 2 +- lib/nosedrum/storage/dispatcher.ex | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index da64b09..a2c2427 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -93,8 +93,8 @@ defmodule Nosedrum.ApplicationCommand do """ @moduledoc since: "0.4.0" - alias Nostrum.Struct.{Embed, Interaction} alias Nostrum.Permission + alias Nostrum.Struct.{Embed, Interaction} @typedoc """ Called by `Nosedrum.Storage.followup/2` after deferring an interaction response. diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 04b564e..e449318 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -8,7 +8,6 @@ defmodule Nosedrum.Storage.Dispatcher do use GenServer alias Nosedrum.Storage - alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction From 7adb8c5ace555006325fced06be756a14c145209 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sun, 31 Aug 2025 04:52:56 -0400 Subject: [PATCH 23/27] Hopefully made add_optional_fields easier to read --- lib/nosedrum/storage/dispatcher.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index e449318..5662fba 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -351,14 +351,16 @@ defmodule Nosedrum.Storage.Dispatcher do end defp add_optional_fields(payload, command) do - Enum.reduce(@optional_fields, payload, fn + fun = fn {callback_name, callback_arity}, acc -> if command |> function_exported?(callback_name, callback_arity) do acc |> add_field(command, callback_name) else acc end - end) + end + + Enum.reduce(@optional_fields, payload, fun) end defp add_field(payload, command, :default_member_permissions) do From 413506d35ddf75fb9fcd5f9ca4500258cbc3f5ad Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sun, 31 Aug 2025 04:54:46 -0400 Subject: [PATCH 24/27] More readability improvements --- lib/nosedrum/storage/dispatcher.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 5662fba..1558cb1 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -1,6 +1,8 @@ defmodule Nosedrum.Storage.Dispatcher do @moduledoc """ An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. + + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -352,9 +354,9 @@ defmodule Nosedrum.Storage.Dispatcher do defp add_optional_fields(payload, command) do fun = fn - {callback_name, callback_arity}, acc -> - if command |> function_exported?(callback_name, callback_arity) do - acc |> add_field(command, callback_name) + {name, arity}, acc -> + if command |> function_exported?(name, arity) do + acc |> add_field(command, name) else acc end From 5abe7c01311ac5bb585f5eab3a51af53459f11a5 Mon Sep 17 00:00:00 2001 From: polyjitter Date: Sun, 31 Aug 2025 04:56:35 -0400 Subject: [PATCH 25/27] Final readability improvements --- lib/nosedrum/storage/dispatcher.ex | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index 1558cb1..c6c5bd4 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -353,13 +353,12 @@ defmodule Nosedrum.Storage.Dispatcher do end defp add_optional_fields(payload, command) do - fun = fn - {name, arity}, acc -> - if command |> function_exported?(name, arity) do - acc |> add_field(command, name) - else - acc - end + fun = fn {name, arity}, acc -> + if command |> function_exported?(name, arity) do + acc |> add_field(command, name) + else + acc + end end Enum.reduce(@optional_fields, payload, fun) From 8e08b23c1846c7e5e24682a3b732c37d4d79b04a Mon Sep 17 00:00:00 2001 From: ashe Date: Mon, 8 Sep 2025 17:35:34 +0000 Subject: [PATCH 26/27] Consistency to align with map names across codebase. --- lib/nosedrum/storage/dispatcher.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index c6c5bd4..5955e36 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -13,7 +13,7 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction - @type_mappings %{ + @type_map %{ options: %{ sub_command: 1, sub_command_group: 2, @@ -295,7 +295,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_type(type) do Map.fetch!( - @type_mappings.commands, + @type_map.commands, type ) end @@ -303,7 +303,7 @@ defmodule Nosedrum.Storage.Dispatcher do defp parse_option_types(options) do Enum.map(options, fn map when is_map_key(map, :type) -> - updated_map = Map.update!(map, :type, &Map.fetch!(@type_mappings.options, &1)) + updated_map = Map.update!(map, :type, &Map.fetch!(@type_map.options, &1)) if is_map_key(updated_map, :options) do parsed_options = parse_option_types(updated_map[:options]) @@ -376,7 +376,7 @@ defmodule Nosedrum.Storage.Dispatcher do Map.put( payload, :contexts, - command.contexts() |> Enum.map(&@type_mappings.contexts[&1]) + command.contexts() |> Enum.map(&@type_map.contexts[&1]) ) end @@ -384,7 +384,7 @@ defmodule Nosedrum.Storage.Dispatcher do Map.put( payload, :integration_types, - command.integration_types() |> Enum.map(&@type_mappings.integration_types[&1]) + command.integration_types() |> Enum.map(&@type_map.integration_types[&1]) ) end From b9586110e6a89928ad65582c4a7b8df3f2260c8d Mon Sep 17 00:00:00 2001 From: polyjitter Date: Tue, 16 Sep 2025 17:48:33 -0400 Subject: [PATCH 27/27] Fixing comments, backwards compatibility for permissions, minor doc fix. --- lib/nosedrum/application_command.ex | 25 +++++++++++++++++++------ lib/nosedrum/component_interaction.ex | 2 +- lib/nosedrum/storage/dispatcher.ex | 21 ++++++++++++++++++++- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/lib/nosedrum/application_command.ex b/lib/nosedrum/application_command.ex index a2c2427..b2a48f5 100644 --- a/lib/nosedrum/application_command.ex +++ b/lib/nosedrum/application_command.ex @@ -226,6 +226,21 @@ defmodule Nosedrum.ApplicationCommand do value: String.t() | number() } + @typedoc """ + An interaction context, which dictates where a command may be used (servers, DMs directly with + the bot user, or all other DMS). + + See callback `c:contexts/0` for more examples. + """ + @type context :: :guild | :bot_dms | :private_channel + + @typedoc """ + An installation context for a command (guild or user install). + + See callback `c:integration_types/0` for more examples. + """ + @type installation_context :: :guild_install | :user_install + @doc """ Returns one of `:slash`, `:message`, or `:user`, indicating what kind of application command this module represents. """ @@ -306,8 +321,7 @@ defmodule Nosedrum.ApplicationCommand do @callback nsfw() :: boolean() @doc """ - An optional callback that returns a list of interaction context types, which - dictate where a command may be used (servers, DMs directly with the bot user, or all other DMS). + An optional callback that returns a list of interaction contexts. Only applies to globally-scoped commands. @@ -318,11 +332,10 @@ defmodule Nosedrum.ApplicationCommand do def contexts, do: [:guild, :private_channel, :bot_dms] ``` """ - @callback contexts() :: [:guild | :bot_dms | :private_channel] + @callback contexts() :: [context] @doc """ - An optional callback that determines which installation context (user or guild) the command - may be used in. + An optional callback that determines which installation context the command may be used in. If not set, this will default to all installation contexts. @@ -331,7 +344,7 @@ defmodule Nosedrum.ApplicationCommand do def integration_types, do: [:guild_install, :user_install] ``` """ - @callback integration_types() :: [:guild_install | :user_install] + @callback integration_types() :: [installation_context] @doc """ Execute the command invoked by the given `t:Nostrum.Struct.Interaction.t/0`. Returns a `t:response/0` diff --git a/lib/nosedrum/component_interaction.ex b/lib/nosedrum/component_interaction.ex index c1290f1..1902f3f 100644 --- a/lib/nosedrum/component_interaction.ex +++ b/lib/nosedrum/component_interaction.ex @@ -10,7 +10,7 @@ defmodule Nosedrum.ComponentInteraction do @doc """ Handle message component interactions. - Behaves the same way as the `Nosedrum.ApplicationCommand.command/1` callback. + Behaves the same way as the `c:Nosedrum.ApplicationCommand.command/1` callback. """ @callback message_component_interaction( interaction :: Nostrum.Struct.Interaction.t(), diff --git a/lib/nosedrum/storage/dispatcher.ex b/lib/nosedrum/storage/dispatcher.ex index c6c5bd4..2e59fb0 100644 --- a/lib/nosedrum/storage/dispatcher.ex +++ b/lib/nosedrum/storage/dispatcher.ex @@ -3,6 +3,7 @@ defmodule Nosedrum.Storage.Dispatcher do An implementation of `Nosedrum.Storage`, dispatching Application Command Interactions to the appropriate modules. + """ @moduledoc since: "0.4.0" @behaviour Nosedrum.Storage @@ -13,6 +14,8 @@ defmodule Nosedrum.Storage.Dispatcher do alias Nostrum.Api.ApplicationCommand alias Nostrum.Struct.Interaction + require Logger + @type_mappings %{ options: %{ sub_command: 1, @@ -364,11 +367,27 @@ defmodule Nosedrum.Storage.Dispatcher do Enum.reduce(@optional_fields, payload, fun) end + defp normalize_permissions(permissions) when is_list(permissions) do + Nostrum.Permission.to_bitset(permissions) + end + + defp normalize_permissions(permissions) when is_integer(permissions) do + Logger.warning(""" + DEPRECATION: Returning a bitset integer from default_member_permissions is deprecated. + Please return a list of Nostrum.Permission.t() atoms instead. + + For compatibility, integer bitsets will still be accepted and translated internally, + but this may be removed in a future release. + """) + + permissions + end + defp add_field(payload, command, :default_member_permissions) do Map.put( payload, :default_member_permissions, - command.default_members_permissions() |> Nostrum.Permission.to_bitset() + command.default_members_permissions() |> normalize_permissions() ) end