api-surface #1
2 changed files with 213 additions and 0 deletions
feat(model): ApplicationCommand family with recursive options
commit
490ad53a54
80
lib/dexcord/model/application_command.ex
Normal file
80
lib/dexcord/model/application_command.ex
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
defmodule Dexcord.ApplicationCommand do
|
||||
@moduledoc "An application command. https://docs.discord.com/developers/interactions/application-commands"
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :id, :snowflake
|
||||
field :type, {:enum, Dexcord.ApplicationCommandType}, default: :chat_input
|
||||
field :application_id, :snowflake
|
||||
field :guild_id, :snowflake
|
||||
field :name, :string
|
||||
field :name_localizations, :raw
|
||||
field :description, :string
|
||||
field :description_localizations, :raw
|
||||
field :options, {:list, {:struct, Dexcord.ApplicationCommand.Option}}, default: []
|
||||
field :default_member_permissions, {:flags, Dexcord.Permissions}, wire_string: true
|
||||
field :dm_permission, :boolean
|
||||
field :default_permission, :boolean
|
||||
field :nsfw, :boolean, default: false
|
||||
field :integration_types, {:list, {:enum, Dexcord.ApplicationIntegrationType}}
|
||||
field :contexts, {:list, {:enum, Dexcord.InteractionContextType}}
|
||||
field :version, :snowflake
|
||||
field :handler, {:enum, Dexcord.EntryPointHandlerType}
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.ApplicationCommand.Option do
|
||||
@moduledoc "A command option (recursive — sub-command groups nest options)."
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :type, {:enum, Dexcord.ApplicationCommandOptionType}
|
||||
field :name, :string
|
||||
field :name_localizations, :raw
|
||||
field :description, :string
|
||||
field :description_localizations, :raw
|
||||
field :required, :boolean, default: false
|
||||
field :choices, {:list, {:struct, Dexcord.ApplicationCommand.OptionChoice}}, default: []
|
||||
field :options, {:list, {:struct, __MODULE__}}, default: []
|
||||
field :channel_types, {:list, :integer}
|
||||
field :min_value, :number
|
||||
field :max_value, :number
|
||||
field :min_length, :integer
|
||||
field :max_length, :integer
|
||||
field :autocomplete, :boolean
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.ApplicationCommand.OptionChoice do
|
||||
@moduledoc "A choice for a command option."
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :name, :string
|
||||
field :name_localizations, :raw
|
||||
field :value, :raw
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.GuildApplicationCommandPermissions do
|
||||
@moduledoc "The permissions for a command in a guild."
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :id, :snowflake
|
||||
field :application_id, :snowflake
|
||||
field :guild_id, :snowflake
|
||||
field :permissions, {:list, {:struct, Dexcord.ApplicationCommandPermission}}, default: []
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Dexcord.ApplicationCommandPermission do
|
||||
@moduledoc "A single command permission override (role/user/channel target)."
|
||||
use Dexcord.Struct
|
||||
|
||||
discord_struct do
|
||||
field :id, :snowflake
|
||||
field :type, {:enum, Dexcord.ApplicationCommandPermissionType}
|
||||
field :permission, :boolean
|
||||
end
|
||||
end
|
||||
133
test/dexcord/model_application_command_test.exs
Normal file
133
test/dexcord/model_application_command_test.exs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
defmodule Dexcord.ModelApplicationCommandTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Dexcord.ApplicationCommand
|
||||
alias Dexcord.ApplicationCommand.{Option, OptionChoice}
|
||||
alias Dexcord.{ApplicationCommandPermission, GuildApplicationCommandPermissions}
|
||||
|
||||
describe "ApplicationCommand recursive options" do
|
||||
setup do
|
||||
map = %{
|
||||
"id" => "1000000000000000001",
|
||||
"type" => 1,
|
||||
"application_id" => "2000000000000000002",
|
||||
"guild_id" => "3000000000000000003",
|
||||
"name" => "config",
|
||||
"description" => "Configure the bot",
|
||||
"default_member_permissions" => "8",
|
||||
"nsfw" => false,
|
||||
"integration_types" => [0, 1],
|
||||
"contexts" => [0, 1, 2],
|
||||
"version" => "9000000000000000009",
|
||||
"options" => [
|
||||
%{
|
||||
"type" => 2,
|
||||
"name" => "channels",
|
||||
"description" => "Channel settings",
|
||||
"options" => [
|
||||
%{
|
||||
"type" => 1,
|
||||
"name" => "set",
|
||||
"description" => "Set a channel",
|
||||
"options" => [
|
||||
%{
|
||||
"type" => 3,
|
||||
"name" => "kind",
|
||||
"description" => "Which kind",
|
||||
"required" => true,
|
||||
"choices" => [
|
||||
%{"name" => "Logs", "value" => "logs"},
|
||||
%{"name" => "Welcome", "value" => "welcome"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
%{command: ApplicationCommand.from_map(map)}
|
||||
end
|
||||
|
||||
test "decodes the top-level command with typed enums", %{command: cmd} do
|
||||
assert %ApplicationCommand{
|
||||
id: 1_000_000_000_000_000_001,
|
||||
type: :chat_input,
|
||||
application_id: 2_000_000_000_000_000_002,
|
||||
guild_id: 3_000_000_000_000_000_003,
|
||||
name: "config",
|
||||
nsfw: false,
|
||||
integration_types: [:guild_install, :user_install],
|
||||
contexts: [:guild, :bot_dm, :private_channel],
|
||||
version: 9_000_000_000_000_000_009
|
||||
} = cmd
|
||||
end
|
||||
|
||||
test "default_member_permissions decodes the wire string to an integer bitfield", %{
|
||||
command: cmd
|
||||
} do
|
||||
assert cmd.default_member_permissions == 8
|
||||
end
|
||||
|
||||
test "decodes the sub_command_group -> sub_command -> string option tree recursively", %{
|
||||
command: cmd
|
||||
} do
|
||||
assert [%Option{type: :sub_command_group, name: "channels"} = group] = cmd.options
|
||||
assert [%Option{type: :sub_command, name: "set"} = sub] = group.options
|
||||
|
||||
assert [
|
||||
%Option{
|
||||
type: :string,
|
||||
name: "kind",
|
||||
required: true,
|
||||
choices: [
|
||||
%OptionChoice{name: "Logs", value: "logs"},
|
||||
%OptionChoice{name: "Welcome", value: "welcome"}
|
||||
]
|
||||
}
|
||||
] = sub.options
|
||||
end
|
||||
|
||||
test "to_map re-encodes default_member_permissions as a wire string", %{command: cmd} do
|
||||
assert %{"default_member_permissions" => "8"} = ApplicationCommand.to_map(cmd)
|
||||
end
|
||||
|
||||
test "type defaults to :chat_input when the wire omits it" do
|
||||
cmd = ApplicationCommand.from_map(%{"name" => "ping", "description" => "pong"})
|
||||
assert cmd.type == :chat_input
|
||||
end
|
||||
end
|
||||
|
||||
describe "GuildApplicationCommandPermissions" do
|
||||
test "decodes permission overrides with typed permission targets" do
|
||||
map = %{
|
||||
"id" => "1000000000000000001",
|
||||
"application_id" => "2000000000000000002",
|
||||
"guild_id" => "3000000000000000003",
|
||||
"permissions" => [
|
||||
%{"id" => "4000000000000000004", "type" => 1, "permission" => true},
|
||||
%{"id" => "5000000000000000005", "type" => 2, "permission" => false}
|
||||
]
|
||||
}
|
||||
|
||||
assert %GuildApplicationCommandPermissions{
|
||||
id: 1_000_000_000_000_000_001,
|
||||
application_id: 2_000_000_000_000_000_002,
|
||||
guild_id: 3_000_000_000_000_000_003,
|
||||
permissions: [
|
||||
%ApplicationCommandPermission{
|
||||
id: 4_000_000_000_000_000_004,
|
||||
type: :role,
|
||||
permission: true
|
||||
},
|
||||
%ApplicationCommandPermission{
|
||||
id: 5_000_000_000_000_000_005,
|
||||
type: :user,
|
||||
permission: false
|
||||
}
|
||||
]
|
||||
} = GuildApplicationCommandPermissions.from_map(map)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue