From 5c24ff24ada164d59822aab18d2dbf3d7c535228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=83=B3=E3=83=89?= Date: Sat, 19 Feb 2022 06:52:40 +0900 Subject: [PATCH] Add support for select options with select! --- src/client/handlers.jl | 34 +++++++++++++++++++++++++++++++++- src/types/exception.jl | 8 +++++++- src/types/interaction.jl | 8 ++++++++ src/utils/helpers.jl | 11 ++++++++++- test/fulltest.jl | 14 ++++++++++---- 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/client/handlers.jl b/src/client/handlers.jl index 810e1ea..664a1ee 100644 --- a/src/client/handlers.jl +++ b/src/client/handlers.jl @@ -1,6 +1,7 @@ export command!, component!, - modal! + modal!, + select! """ command!( @@ -72,6 +73,13 @@ function generate_command_func(f::Function) g end +function generate_select_command_func(f::Function) + g = (ctx::Context) -> begin + f(ctx, ctx.interaction.data.values) + end + g +end + function makeargs(ctx::Context, args) v = [] args = args[2:end] @@ -127,6 +135,30 @@ function component!(f::Function, c::Client, custom_id::AbstractString; auto_ack: if auto_update_ack push!(c.auto_update_ack, custom_id) end return Component(custom_id=custom_id; kwargs...) end + +""" + select!( + f::Function + c::Client + custom_id::AbstractString + args::Vector{Tuple} + kwargs... + ) + +Adds a handler for INTERACTION CREATE gateway events where the InteractionData's `custom_id` field matches `custom_id`. +The `f` parameter signature should be: +``` +(ctx::Context, choices::Vector{String}) -> Any +``` +""" +function select!(f::Function, c::Client, custom_id::AbstractString, args...; auto_ack::Bool=true, auto_update_ack::Bool=true, kwargs...) + add_handler!(c, OnInteractionCreate(generate_select_command_func(f); custom_id=custom_id)) + if !auto_ack push!(c.no_auto_ack, custom_id) end + if auto_update_ack push!(c.auto_update_ack, custom_id) end + if length(args) == 0 throw(FieldRequired("options", "SelectOption")) end + return Component(custom_id=custom_id; options=[SelectOption(a...) for a in args], type=3, kwargs...) +end + """ handle( c::Client diff --git a/src/types/exception.jl b/src/types/exception.jl index 9e78aa3..9aec315 100644 --- a/src/types/exception.jl +++ b/src/types/exception.jl @@ -4,6 +4,12 @@ struct NamingError <: Exception reason::String end +struct FieldRequired <: Exception + field::String + structname::String +end + NamingError(a::AbstractString, nameof::String) = NamingError(a, nameof, "it may be too long or contain invalid characters.") -Base.showerror(io::IO, e::NamingError) = print(io, "Name $(e.invalid_name) is an invalid name for `$(e.name_of)`` because `$(e.reason)`.") +Base.showerror(io::IO, e::NamingError) = print(io, "Name $(e.invalid_name) is an invalid name for `$(e.name_of)` because `$(e.reason)`.") +Base.showerror(io::IO, e::FieldRequired) = print(io, "Field `$(e.field)` is required to construct a `$(e.structname)`.") diff --git a/src/types/interaction.jl b/src/types/interaction.jl index 3c79471..d54748e 100644 --- a/src/types/interaction.jl +++ b/src/types/interaction.jl @@ -77,6 +77,14 @@ struct SelectOption <: DiscordObject end @boilerplate SelectOption :constructors :docs :lower :merge :mock +function SelectOption(label, value, args...) + if length(args)>0 + r = [:description, :emoji, :default] + t = Dict([(r[x+1], args[x+1]) for x in 0:length(args)]) + SelectOption(; label=label, value=value, t...) + else SelectOption(; label=label, value=value) end +end + """ An interactable component. More details [here](https://discord.com/developers/docs/interactions/message-components). diff --git a/src/utils/helpers.jl b/src/utils/helpers.jl index 55a2d04..aceb33c 100644 --- a/src/utils/helpers.jl +++ b/src/utils/helpers.jl @@ -650,7 +650,13 @@ end Helper function that is equivalent to calling `extops(ctx.interaction.data.options)` """ -opt(ctx::Context) = ismissing(ctx.interaction.data.components) ? extops(ctx.interaction.data.options) : Dict([(comp.custom_id, comp.value) for comp in vcat([c.components for c in ctx.interaction.data.components]...)]) +function opt(ctx::Context) + if ismissing(ctx.interaction.data.components) + extops(ctx.interaction.data.options) + else + extops(ctx.interaction.data.components, :custom_id) + end +end """ extops(ops::Vector) @@ -658,6 +664,9 @@ Creates a Dict of `option name` -> `option value` for the given vector of [`Appl If the option is of `Subcommand` type, creates a dict for all its subcommands. """ extops(ops::Vector) = Dict([(op.name, Int(op.type) < 3 ? extops(op.options) : op.value) for op in ops]) +extops(ops::Vector, kf::Symbol) = extops(ops, kf, :value) +extops(ops::Vector, kf::Symbol, kv::Symbol) = Dict([(getproperty(comp, kf), getproperty(comp, kv)) for comp in vcat([c.components for c in ops]...)]) + """ Return an empty `Dict` if the list of options used is missing. """ diff --git a/test/fulltest.jl b/test/fulltest.jl index 3242ee4..70ef47b 100644 --- a/test/fulltest.jl +++ b/test/fulltest.jl @@ -47,11 +47,17 @@ command!(client, TESTGUILD, "water", "Water a plant", legacy=false, options=[ reply(client, ctx, components=[cm], content="$(mention(ctx)) watered their plant for $(howmuch) hours. So much that the plant grew taller than them!") end -command!(client, TESTGUILD, "test", "Test something", legacy=false, auto_ack=false) do ctx - cm = Component(; type=4, custom_id="name", label="Name", style=1) - modal!(client, "ttest", ctx, components=[cm], title="test") do context, name - reply(client, context, raw=true, content="Your name is $(name)") +command!(client, TESTGUILD, "test", "Test something", legacy=false) do ctx + cm = select!( + client, + "class", + ("Rogue", "rogue"), + ("Mage", "mage"); + max_values=1 + ) do context, choices + reply(client, context, content="You chose $(choices[1])") end + reply(client, ctx, components=[cm], content="What class you want noob?") end command!(client, TESTGUILD, "quit", "Ends the bot process!") do (ctx)