From 69b27e455433ae3b034b5c91b062ed72e09c5441 Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 13 Dec 2024 10:50:30 +0100 Subject: [PATCH 1/9] add prototype choices implementation --- .../interaction/annotation/AutoComplete.java | 5 ++ .../commands/ChoiceValueProvider.java | 5 ++ .../interaction/commands/EnumChoices.java | 74 +++++++++++++++++++ .../ExecutableSlashCommandDefinition.java | 8 ++ .../annotation/SlashCommandOption.java | 6 ++ .../annotation/SlashCommandOptionChoice.java | 7 ++ .../wrapper/javacord/JavacordWrapper.java | 24 +++++- 7 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/interaction/commands/ChoiceValueProvider.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java b/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java new file mode 100644 index 0000000..69e80e0 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java @@ -0,0 +1,5 @@ +package net.tomatentum.marinara.interaction.annotation; + +public @interface AutoComplete { + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ChoiceValueProvider.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ChoiceValueProvider.java new file mode 100644 index 0000000..b7d13f6 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ChoiceValueProvider.java @@ -0,0 +1,5 @@ +package net.tomatentum.marinara.interaction.commands; + +public interface ChoiceValueProvider { + T getChoiceValue(); +} diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java new file mode 100644 index 0000000..411c5ac --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java @@ -0,0 +1,74 @@ +package net.tomatentum.marinara.interaction.commands; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import io.leangen.geantyref.AnnotationFormatException; +import io.leangen.geantyref.GenericTypeReflector; +import io.leangen.geantyref.TypeFactory; +import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice; + +public record EnumChoices(Class> enumClass, ChoiceType type, SlashCommandOptionChoice[] choices) { + + private static Method method; + + static { + try { + method = ChoiceValueProvider.class.getMethod("getChoiceValue"); + } catch (NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + } + + public static EnumChoices of(Class> enumClass) { + if (!ChoiceValueProvider.class.isAssignableFrom(enumClass)) + throw new IllegalArgumentException("Provided class needs to implement the ChoiceValueProvider interface."); + ChoiceType type = parseChoiceType(enumClass); + SlashCommandOptionChoice[] choices = parseChoices(enumClass, type); + return new EnumChoices(enumClass, type, choices); + } + + private static ChoiceType parseChoiceType(Class> enumClass) { + ParameterizedType type = (ParameterizedType) GenericTypeReflector.getExactSuperType(enumClass, ChoiceValueProvider.class); + Type typeParam = type.getActualTypeArguments()[0]; + + if (!(typeParam instanceof Class)) + throw new IllegalArgumentException("ChoiceValueProvider need either a String or Number type parameter."); + + if (String.class.isAssignableFrom((Class) typeParam)) + return ChoiceType.String; + else if (Number.class.isAssignableFrom((Class) typeParam)) + return ChoiceType.Number; + else + throw new IllegalArgumentException("ChoiceValueProvider need either a String or Number type parameter."); + } + + private static SlashCommandOptionChoice[] parseChoices(Class> enumClass, ChoiceType type) { + Enum>[] constants = enumClass.getEnumConstants(); + List choices = new ArrayList<>(); + for (Enum> enumInstance : constants) { + Object value; + try { + value = method.invoke(enumInstance); + if (type.equals(ChoiceType.String)) + choices.add(TypeFactory.annotation(SlashCommandOptionChoice.class, Map.of("name", enumInstance.name(), "stringValue", value))); + else if (type.equals(ChoiceType.Number)) + choices.add(TypeFactory.annotation(SlashCommandOptionChoice.class, Map.of("name", enumInstance.name(), "longValue", value))); + } catch (IllegalAccessException | InvocationTargetException | AnnotationFormatException e) { + e.printStackTrace(); + return null; + } + } + return choices.toArray(new SlashCommandOptionChoice[0]); + } + + public static enum ChoiceType { + String, + Number + } +} diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java index e110098..543311c 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java @@ -2,6 +2,7 @@ package net.tomatentum.marinara.interaction.commands; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; +import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; @@ -11,6 +12,13 @@ public record ExecutableSlashCommandDefinition( SubCommandGroup subCommandGroup, SlashCommandOption[] options) { + public static SlashCommandOptionChoice[] getActualChoices(SlashCommandOption option) { + SlashCommandOptionChoice[] choices = option.choices(); + if (choices.length <= 0) + choices = EnumChoices.of(option.choiceEnum()).choices(); + return choices; + } + @Override public final boolean equals(Object o) { if (!(o instanceof ExecutableSlashCommandDefinition)) diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java index 09574d3..eea19e3 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java @@ -14,4 +14,10 @@ public @interface SlashCommandOption { public String description() default ""; public SlashCommandOptionType type() default SlashCommandOptionType.STRING; public boolean required() default false; + public SlashCommandOptionChoice[] choices() default {}; + public Class> choiceEnum() default PlaceHolderEnum.class; + + public static enum PlaceHolderEnum { + + } } diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java new file mode 100644 index 0000000..0f5c8a7 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java @@ -0,0 +1,7 @@ +package net.tomatentum.marinara.interaction.commands.annotation; + +public @interface SlashCommandOptionChoice { + public String name(); + public long longValue() default Long.MAX_VALUE; + public String stringValue() default ""; +} diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java index 98988df..a0f7337 100644 --- a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java @@ -22,6 +22,7 @@ import net.tomatentum.marinara.interaction.commands.ExecutableSlashCommandDefini import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; +import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; @@ -121,18 +122,35 @@ public class JavacordWrapper extends LibraryWrapper { private org.javacord.api.interaction.SlashCommandOption convertSubCommandGroupDef(SlashCommandDefinition def, SubCommandGroup subGroup) { SubCommand[] subCommands = def.getSubCommands(subGroup.name()); org.javacord.api.interaction.SlashCommandOption[] convertedSubCommands = (org.javacord.api.interaction.SlashCommandOption[]) Arrays.stream(subCommands).map(this::convertSubCommandDef).toArray(); - return org.javacord.api.interaction.SlashCommandOption.createWithOptions(org.javacord.api.interaction.SlashCommandOptionType.SUB_COMMAND_GROUP, subGroup.name(), subGroup.description(), Arrays.asList(convertedSubCommands)); + return org.javacord.api.interaction.SlashCommandOption.createWithOptions( + org.javacord.api.interaction.SlashCommandOptionType.SUB_COMMAND_GROUP, + subGroup.name(), + subGroup.description(), + Arrays.asList(convertedSubCommands)); } private org.javacord.api.interaction.SlashCommandOption convertSubCommandDef(SubCommand sub) { List convertedOptions = new ArrayList<>(); Arrays.stream(sub.options()).map(this::convertOptionDef).forEach(convertedOptions::add); - return org.javacord.api.interaction.SlashCommandOption.createWithOptions(org.javacord.api.interaction.SlashCommandOptionType.SUB_COMMAND, sub.name(), sub.description(), convertedOptions); + return org.javacord.api.interaction.SlashCommandOption.createWithOptions( + org.javacord.api.interaction.SlashCommandOptionType.SUB_COMMAND, + sub.name(), + sub.description(), + convertedOptions); } private org.javacord.api.interaction.SlashCommandOption convertOptionDef(SlashCommandOption option) { org.javacord.api.interaction.SlashCommandOptionType type = Enum.valueOf(org.javacord.api.interaction.SlashCommandOptionType.class, option.type().toString()); - return org.javacord.api.interaction.SlashCommandOption.create(type, option.name(), option.description(), option.required()); + + List choices = new ArrayList<>(); + for (SlashCommandOptionChoice choice : ExecutableSlashCommandDefinition.getActualChoices(option)) { + if (choice.stringValue().isEmpty()) + choices.add(org.javacord.api.interaction.SlashCommandOptionChoice.create(choice.name(), choice.longValue())); + else + choices.add(org.javacord.api.interaction.SlashCommandOptionChoice.create(choice.name(), choice.stringValue())); + } + + return org.javacord.api.interaction.SlashCommandOption.createWithChoices(type, option.name(), option.description(), option.required(), choices); } private Object getOptionValue(SlashCommandInteractionOption option, SlashCommandOptionType type) { -- 2.45.2 From f32c7045a1d96a7d2e1f131a631f88dd7500338b Mon Sep 17 00:00:00 2001 From: tueem Date: Sat, 14 Dec 2024 12:32:03 +0100 Subject: [PATCH 2/9] remove unnecessary getClass call fixing test --- .../interaction/commands/ExecutableSlashCommandDefinition.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java index 543311c..6505984 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java @@ -5,6 +5,7 @@ import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptio import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; +import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption.PlaceHolderEnum; public record ExecutableSlashCommandDefinition( SlashCommand applicationCommand, @@ -14,7 +15,7 @@ public record ExecutableSlashCommandDefinition( public static SlashCommandOptionChoice[] getActualChoices(SlashCommandOption option) { SlashCommandOptionChoice[] choices = option.choices(); - if (choices.length <= 0) + if (choices.length <= 0 && !option.choiceEnum().equals(PlaceHolderEnum.class)) choices = EnumChoices.of(option.choiceEnum()).choices(); return choices; } -- 2.45.2 From 7a2c15d87753d3300f8fda12066d7e86a0e52097 Mon Sep 17 00:00:00 2001 From: Tueem Date: Sun, 15 Dec 2024 15:08:34 +0100 Subject: [PATCH 3/9] create seperate class for ContextObjectProviders and renamed some context parameters from parameter to context. First parts of AutocompleteInteraction added --- .../methods/ButtonInteractionMethod.java | 5 +- .../SlashCommandInteractionMethod.java | 2 +- .../wrapper/ContextObjectProvider.java | 11 ++ .../marinara/wrapper/LibraryWrapper.java | 7 +- .../JavacordContextObjectProvider.java | 108 ++++++++++++++++++ .../wrapper/javacord/JavacordWrapper.java | 64 ++--------- 6 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 lib/src/main/java/net/tomatentum/marinara/wrapper/ContextObjectProvider.java create mode 100644 wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/ButtonInteractionMethod.java b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/ButtonInteractionMethod.java index f6b2a9e..fe039ac 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/ButtonInteractionMethod.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/ButtonInteractionMethod.java @@ -24,9 +24,9 @@ public class ButtonInteractionMethod extends InteractionMethod { } @Override - public Object getParameter(Object parameter, int index) { + public Object getParameter(Object context, int index) { Class type = getMethod().getParameterTypes()[index+1]; - return marinara.getWrapper().getComponentContextObject(parameter, type); + return marinara.getWrapper().getContextObjectProvider().getComponentContextObject(context, type); } @Override @@ -38,4 +38,5 @@ public class ButtonInteractionMethod extends InteractionMethod { public InteractionType getType() { return InteractionType.BUTTON; } + } diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/SlashCommandInteractionMethod.java b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/SlashCommandInteractionMethod.java index 666dd3c..2f5faf4 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/SlashCommandInteractionMethod.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/SlashCommandInteractionMethod.java @@ -26,7 +26,7 @@ public class SlashCommandInteractionMethod extends InteractionMethod { @Override public Object getParameter(Object context, int index) { - return marinara.getWrapper().convertCommandOption(context, commandDefinition.options()[index].type(), commandDefinition.options()[index].name()); + return marinara.getWrapper().getContextObjectProvider().convertCommandOption(context, commandDefinition.options()[index].name()); } @Override diff --git a/lib/src/main/java/net/tomatentum/marinara/wrapper/ContextObjectProvider.java b/lib/src/main/java/net/tomatentum/marinara/wrapper/ContextObjectProvider.java new file mode 100644 index 0000000..27cbaa4 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/wrapper/ContextObjectProvider.java @@ -0,0 +1,11 @@ +package net.tomatentum.marinara.wrapper; + +public interface ContextObjectProvider { + + public Object convertCommandOption(Object context, String optionName); + + public Object getComponentContextObject(Object context, Class type); + public Object getInteractionContextObject(Object context, Class type); + + public Object getAutocompleteFocusedOption(Object context); +} diff --git a/lib/src/main/java/net/tomatentum/marinara/wrapper/LibraryWrapper.java b/lib/src/main/java/net/tomatentum/marinara/wrapper/LibraryWrapper.java index 4fe8ef9..06e1661 100644 --- a/lib/src/main/java/net/tomatentum/marinara/wrapper/LibraryWrapper.java +++ b/lib/src/main/java/net/tomatentum/marinara/wrapper/LibraryWrapper.java @@ -6,7 +6,6 @@ import java.util.function.Consumer; import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; import net.tomatentum.marinara.interaction.commands.ExecutableSlashCommandDefinition; -import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; import net.tomatentum.marinara.interaction.InteractionType; public abstract class LibraryWrapper { @@ -17,7 +16,6 @@ public abstract class LibraryWrapper { interactionSubscriber = new ArrayList<>(); } - public void handleInteraction(Object context) { interactionSubscriber.forEach((o) -> o.accept(context)); } @@ -32,9 +30,10 @@ public abstract class LibraryWrapper { public abstract InteractionType getInteractionType(Class clazz); public abstract void registerSlashCommands(SlashCommandDefinition[] defs); - public abstract Object convertCommandOption(Object context, SlashCommandOptionType type, String optionName); public abstract ExecutableSlashCommandDefinition getCommandDefinition(Object context); public abstract String getButtonId(Object context); - public abstract Object getComponentContextObject(Object context, Class type); + + public abstract ContextObjectProvider getContextObjectProvider(); + } \ No newline at end of file diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java new file mode 100644 index 0000000..f1b8f49 --- /dev/null +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java @@ -0,0 +1,108 @@ +package net.tomatentum.marinara.wrapper.javacord; + +import org.javacord.api.interaction.AutocompleteInteraction; +import org.javacord.api.interaction.ButtonInteraction; +import org.javacord.api.interaction.SlashCommandInteraction; +import org.javacord.api.interaction.SlashCommandInteractionOption; + +import net.tomatentum.marinara.wrapper.ContextObjectProvider; + +public class JavacordContextObjectProvider implements ContextObjectProvider { + + @Override + public Object convertCommandOption(Object context, String optionName) { + if (!(context instanceof SlashCommandInteraction)) + return null; + SlashCommandInteraction interaction = (SlashCommandInteraction) context; + if (!interaction.getArguments().isEmpty()) + return getOptionValue(interaction.getOptionByName(optionName).get()); + + SlashCommandInteractionOption subCommandOption = interaction.getOptions().getFirst(); + + if (!subCommandOption.getOptions().isEmpty()) + subCommandOption = subCommandOption.getOptions().getFirst(); + + return getOptionValue(subCommandOption.getOptionByName(optionName).get()); + } + + private Object getOptionValue(SlashCommandInteractionOption option) { + switch (getOptionType(option)) { + case ATTACHMENT: + return option.getAttachmentValue().get(); + case BOOLEAN: + return option.getBooleanValue().get(); + case CHANNEL: + return option.getChannelValue().get(); + case DECIMAL: + return option.getDecimalValue().get(); + case LONG: + return option.getLongValue().get(); + case MENTIONABLE: + return option.getMentionableValue().get(); + case ROLE: + return option.getRoleValue().get(); + case STRING: + return option.getStringValue().get(); + case USER: + return option.getUserValue().get(); + default: + return null; + } + } + + + private org.javacord.api.interaction.SlashCommandOptionType getOptionType(SlashCommandInteractionOption option) { + if (option.getAttachmentValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.ATTACHMENT; + if (option.getBooleanValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.BOOLEAN; + if (option.getChannelValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.CHANNEL; + if (option.getDecimalValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.DECIMAL; + if (option.getLongValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.LONG; + if (option.getMentionableValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.MENTIONABLE; + if (option.getRoleValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.ROLE; + if (option.getStringValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.ATTACHMENT; + if (option.getUserValue().isPresent()) + return org.javacord.api.interaction.SlashCommandOptionType.USER; + + return org.javacord.api.interaction.SlashCommandOptionType.UNKNOWN; + } + + @Override + public Object getComponentContextObject(Object context, Class type) { + ButtonInteraction button = (ButtonInteraction) context; + switch (type.getName()) { + case "org.javacord.api.entity.message.Message": + return button.getMessage(); + default: + return getInteractionContextObject(context, type); + } + } + + @Override + public Object getInteractionContextObject(Object context, Class type) { + ButtonInteraction button = (ButtonInteraction) context; + switch (type.getName()) { + case "org.javacord.api.entity.channel.TextChannel": + return button.getChannel().orElse(null); + case "org.javacord.api.entity.server.Server": + return button.getServer().orElse(null); + case "org.javacord.api.entity.user.User": + return button.getUser(); + } + return null; + } + + @Override + public Object getAutocompleteFocusedOption(Object context) { + AutocompleteInteraction interaction = (AutocompleteInteraction) context; + return getOptionValue(interaction.getFocusedOption()); + } + +} diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java index a0f7337..1bb3697 100644 --- a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java @@ -10,6 +10,7 @@ import java.util.Set; import org.javacord.api.DiscordApi; import org.javacord.api.interaction.ApplicationCommandInteraction; +import org.javacord.api.interaction.AutocompleteInteraction; import org.javacord.api.interaction.ButtonInteraction; import org.javacord.api.interaction.SlashCommandBuilder; import org.javacord.api.interaction.SlashCommandInteraction; @@ -25,15 +26,17 @@ import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptio import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; -import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; +import net.tomatentum.marinara.wrapper.ContextObjectProvider; import net.tomatentum.marinara.wrapper.LibraryWrapper; public class JavacordWrapper extends LibraryWrapper { private DiscordApi api; + private JavacordContextObjectProvider contextObjectProvider; public JavacordWrapper(DiscordApi api) { this.api = api; + this.contextObjectProvider = new JavacordContextObjectProvider(); api.addInteractionCreateListener((e) -> handleInteraction(e.getInteraction())); } @@ -43,6 +46,8 @@ public class JavacordWrapper extends LibraryWrapper { return InteractionType.COMMAND; if (ButtonInteraction.class.isAssignableFrom(clazz)) return InteractionType.BUTTON; + if (AutocompleteInteraction.class.isAssignableFrom(clazz)) + return InteractionType.AUTOCOMPLETE; return null; } @@ -68,22 +73,6 @@ public class JavacordWrapper extends LibraryWrapper { api.bulkOverwriteGlobalApplicationCommands(globalCommands); } - @Override - public Object convertCommandOption(Object context, SlashCommandOptionType type, String optionName) { - if (!(context instanceof SlashCommandInteraction)) - return null; - SlashCommandInteraction interaction = (SlashCommandInteraction) context; - if (!interaction.getArguments().isEmpty()) - return getOptionValue(interaction.getOptionByName(optionName).get(), type); - - SlashCommandInteractionOption subCommandOption = interaction.getOptions().getFirst(); - - if (!subCommandOption.getOptions().isEmpty()) - subCommandOption = subCommandOption.getOptions().getFirst(); - - return getOptionValue(subCommandOption.getOptionByName(optionName).get(), type); - } - @Override public ExecutableSlashCommandDefinition getCommandDefinition(Object context) { if (!(context instanceof SlashCommandInteraction)) @@ -153,31 +142,6 @@ public class JavacordWrapper extends LibraryWrapper { return org.javacord.api.interaction.SlashCommandOption.createWithChoices(type, option.name(), option.description(), option.required(), choices); } - private Object getOptionValue(SlashCommandInteractionOption option, SlashCommandOptionType type) { - switch (type) { - case ATTACHMENT: - return option.getAttachmentValue().get(); - case BOOLEAN: - return option.getBooleanValue().get(); - case CHANNEL: - return option.getChannelValue().get(); - case DECIMAL: - return option.getDecimalValue().get(); - case LONG: - return option.getLongValue().get(); - case MENTIONABLE: - return option.getMentionableValue().get(); - case ROLE: - return option.getRoleValue().get(); - case STRING: - return option.getStringValue().get(); - case USER: - return option.getUserValue().get(); - default: - return null; - } - } - @Override public String getButtonId(Object context) { ButtonInteraction button = (ButtonInteraction) context; @@ -185,20 +149,8 @@ public class JavacordWrapper extends LibraryWrapper { } @Override - public Object getComponentContextObject(Object context, Class type) { - ButtonInteraction button = (ButtonInteraction) context; - switch (type.getName()) { - case "org.javacord.api.entity.channel.TextChannel": - return button.getChannel().orElse(null); - case "org.javacord.api.entity.message.Message": - return button.getMessage(); - case "org.javacord.api.entity.server.Server": - return button.getServer().orElse(null); - case "org.javacord.api.entity.user.User": - return button.getUser(); - } - return null; + public ContextObjectProvider getContextObjectProvider() { + return contextObjectProvider; } - } -- 2.45.2 From 432db43bf598017f54642573ea2d96010efc9060 Mon Sep 17 00:00:00 2001 From: Tueem Date: Sun, 15 Dec 2024 15:09:09 +0100 Subject: [PATCH 4/9] add remaining parts of AutocompleteInteraction --- .../AutoCompleteInteractionMethod.java | 51 +++++++++++++++++++ .../methods/InteractionMethod.java | 5 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 lib/src/main/java/net/tomatentum/marinara/interaction/methods/AutoCompleteInteractionMethod.java diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/AutoCompleteInteractionMethod.java b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/AutoCompleteInteractionMethod.java new file mode 100644 index 0000000..ffed507 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/AutoCompleteInteractionMethod.java @@ -0,0 +1,51 @@ +package net.tomatentum.marinara.interaction.methods; + +import java.lang.reflect.Method; + +import net.tomatentum.marinara.Marinara; +import net.tomatentum.marinara.interaction.InteractionHandler; +import net.tomatentum.marinara.interaction.InteractionType; +import net.tomatentum.marinara.interaction.commands.ExecutableSlashCommandDefinition; +import net.tomatentum.marinara.parser.AnnotationParser; +import net.tomatentum.marinara.parser.SlashCommandParser; + +public class AutoCompleteInteractionMethod extends InteractionMethod { + + private ExecutableSlashCommandDefinition commandDefinition; + + public AutoCompleteInteractionMethod(Method method, + InteractionHandler handler, + Marinara marinara + ) { + super(method, handler, marinara); + } + + @Override + public AnnotationParser[] getParsers() { + return new AnnotationParser[] { + new SlashCommandParser(method, (x) -> { this.commandDefinition = x; } ) + }; + } + + @Override + public Object getParameter(Object context, int index) { + Class type = getMethod().getParameterTypes()[index+1]; + Object autocompleteOptionValue = marinara.getWrapper().getContextObjectProvider().getAutocompleteFocusedOption(context); + if (autocompleteOptionValue != null) + return autocompleteOptionValue; + + return marinara.getWrapper().getContextObjectProvider().getComponentContextObject(context, type); + } + + @Override + public boolean canRun(Object context) { + ExecutableSlashCommandDefinition other = marinara.getWrapper().getCommandDefinition(context); + return commandDefinition.equals(other); + } + + @Override + public InteractionType getType() { + return InteractionType.AUTOCOMPLETE; + } + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/InteractionMethod.java b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/InteractionMethod.java index f9c5a1c..20fec5f 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/methods/InteractionMethod.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/methods/InteractionMethod.java @@ -11,6 +11,7 @@ import net.tomatentum.marinara.Marinara; import net.tomatentum.marinara.checks.AppliedCheck; import net.tomatentum.marinara.interaction.InteractionHandler; import net.tomatentum.marinara.interaction.InteractionType; +import net.tomatentum.marinara.interaction.annotation.AutoComplete; import net.tomatentum.marinara.interaction.annotation.Button; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; @@ -20,6 +21,8 @@ import net.tomatentum.marinara.parser.InteractionCheckParser; public abstract class InteractionMethod { public static InteractionMethod create(Method method, InteractionHandler handler, Marinara marinara) { + if (method.isAnnotationPresent(AutoComplete.class)) + return new AutoCompleteInteractionMethod(method, handler, marinara); if (method.isAnnotationPresent(SlashCommand.class) || method.isAnnotationPresent(SubCommand.class)) return new SlashCommandInteractionMethod(method, handler, marinara); if (method.isAnnotationPresent(Button.class)) @@ -53,7 +56,7 @@ public abstract class InteractionMethod { public abstract AnnotationParser[] getParsers(); - public abstract Object getParameter(Object parameter, int index); + public abstract Object getParameter(Object context, int index); public abstract boolean canRun(Object context); -- 2.45.2 From a5e1230fc68678b589e823f58f33d92a23cce325 Mon Sep 17 00:00:00 2001 From: Tueem Date: Sun, 15 Dec 2024 23:13:57 +0100 Subject: [PATCH 5/9] fix issues with ExecutableSlashCommandDefinition equals check --- .../commands/ExecutableSlashCommandDefinition.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java index 6505984..f0b551c 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/ExecutableSlashCommandDefinition.java @@ -27,8 +27,8 @@ public record ExecutableSlashCommandDefinition( ExecutableSlashCommandDefinition other = (ExecutableSlashCommandDefinition) o; boolean equals = false; - if (this.applicationCommand() != null && other.subCommandGroup() != null) - equals = this.applicationCommand.name().equals(other.applicationCommand().name()); + if (this.applicationCommand() != null && other.applicationCommand() != null) + equals = this.applicationCommand().name().equals(other.applicationCommand().name()); if (this.subCommandGroup() != null && other.subCommandGroup() != null) equals = this.subCommandGroup().name().equals(other.subCommandGroup().name()); -- 2.45.2 From 1cb6cd0e0557a4c2813548d100a8dd56f35c7da0 Mon Sep 17 00:00:00 2001 From: Tueem Date: Sun, 15 Dec 2024 23:15:29 +0100 Subject: [PATCH 6/9] clean up code and switch to request instead of getting from cache --- .../interaction/annotation/AutoComplete.java | 7 ++++ .../JavacordContextObjectProvider.java | 34 ++++++++++--------- .../wrapper/javacord/JavacordWrapper.java | 17 +++++----- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java b/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java index 69e80e0..46aa6aa 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/annotation/AutoComplete.java @@ -1,5 +1,12 @@ package net.tomatentum.marinara.interaction.annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) public @interface AutoComplete { } diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java index f1b8f49..0812f0c 100644 --- a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordContextObjectProvider.java @@ -4,6 +4,7 @@ import org.javacord.api.interaction.AutocompleteInteraction; import org.javacord.api.interaction.ButtonInteraction; import org.javacord.api.interaction.SlashCommandInteraction; import org.javacord.api.interaction.SlashCommandInteractionOption; +import org.javacord.api.interaction.SlashCommandOptionType; import net.tomatentum.marinara.wrapper.ContextObjectProvider; @@ -26,7 +27,8 @@ public class JavacordContextObjectProvider implements ContextObjectProvider { } private Object getOptionValue(SlashCommandInteractionOption option) { - switch (getOptionType(option)) { + SlashCommandOptionType type = getOptionType(option); + switch (type) { case ATTACHMENT: return option.getAttachmentValue().get(); case BOOLEAN: @@ -38,40 +40,40 @@ public class JavacordContextObjectProvider implements ContextObjectProvider { case LONG: return option.getLongValue().get(); case MENTIONABLE: - return option.getMentionableValue().get(); + return option.requestMentionableValue().get(); case ROLE: return option.getRoleValue().get(); case STRING: return option.getStringValue().get(); case USER: - return option.getUserValue().get(); + return option.requestUserValue().get(); default: return null; } } - private org.javacord.api.interaction.SlashCommandOptionType getOptionType(SlashCommandInteractionOption option) { + private SlashCommandOptionType getOptionType(SlashCommandInteractionOption option) { if (option.getAttachmentValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.ATTACHMENT; + return SlashCommandOptionType.ATTACHMENT; if (option.getBooleanValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.BOOLEAN; + return SlashCommandOptionType.BOOLEAN; if (option.getChannelValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.CHANNEL; + return SlashCommandOptionType.CHANNEL; if (option.getDecimalValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.DECIMAL; + return SlashCommandOptionType.DECIMAL; if (option.getLongValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.LONG; - if (option.getMentionableValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.MENTIONABLE; + return SlashCommandOptionType.LONG; + if (option.requestMentionableValue().isPresent()) + return SlashCommandOptionType.MENTIONABLE; if (option.getRoleValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.ROLE; + return SlashCommandOptionType.ROLE; if (option.getStringValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.ATTACHMENT; - if (option.getUserValue().isPresent()) - return org.javacord.api.interaction.SlashCommandOptionType.USER; + return SlashCommandOptionType.STRING; + if (option.requestUserValue().isPresent()) + return SlashCommandOptionType.USER; - return org.javacord.api.interaction.SlashCommandOptionType.UNKNOWN; + return SlashCommandOptionType.UNKNOWN; } @Override diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java index 1bb3697..b3a3da4 100644 --- a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java @@ -42,13 +42,12 @@ public class JavacordWrapper extends LibraryWrapper { @Override public InteractionType getInteractionType(Class clazz) { + if (AutocompleteInteraction.class.isAssignableFrom(clazz)) + return InteractionType.AUTOCOMPLETE; if (ApplicationCommandInteraction.class.isAssignableFrom(clazz)) return InteractionType.COMMAND; if (ButtonInteraction.class.isAssignableFrom(clazz)) return InteractionType.BUTTON; - if (AutocompleteInteraction.class.isAssignableFrom(clazz)) - return InteractionType.AUTOCOMPLETE; - return null; } @@ -83,11 +82,13 @@ public class JavacordWrapper extends LibraryWrapper { List options = interaction.getOptions(); try { builder.setApplicationCommand(TypeFactory.annotation(SlashCommand.class, Map.of("name", interaction.getCommandName()))); - if (!options.getFirst().getArguments().isEmpty()) { - builder.setSubCommandGroup(TypeFactory.annotation(SubCommandGroup.class, Map.of("name", options.getFirst().getName()))); - builder.setSubCommand(TypeFactory.annotation(SubCommand.class, Map.of("name", options.getFirst().getOptions().getFirst().getName()))); - }else - builder.setSubCommand(TypeFactory.annotation(SubCommand.class, Map.of("name", options.getFirst().getName()))); + if (!options.isEmpty()) { + if (!options.getFirst().getArguments().isEmpty()) { + builder.setSubCommandGroup(TypeFactory.annotation(SubCommandGroup.class, Map.of("name", options.getFirst().getName()))); + builder.setSubCommand(TypeFactory.annotation(SubCommand.class, Map.of("name", options.getFirst().getOptions().getFirst().getName()))); + }else + builder.setSubCommand(TypeFactory.annotation(SubCommand.class, Map.of("name", options.getFirst().getName()))); + } } catch (AnnotationFormatException e) { e.printStackTrace(); } -- 2.45.2 From 9d3a6b8b8501f311b6d30355d62500287e9b3792 Mon Sep 17 00:00:00 2001 From: Tueem Date: Sun, 15 Dec 2024 23:15:37 +0100 Subject: [PATCH 7/9] add Autocomplete Test --- .../marinara/test/AutoCompleteTest.java | 23 +++ .../marinara/test/TestAutocomplete.java | 23 +++ .../mocks/AutocompleteInteractionMock.java | 179 ++++++++++++++++++ .../SlashCommandInteractionOptionMock.java | 20 +- 4 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 wrapper/javacord/src/test/java/net/tomatentum/marinara/test/AutoCompleteTest.java create mode 100644 wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestAutocomplete.java create mode 100644 wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/AutocompleteInteractionMock.java diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/AutoCompleteTest.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/AutoCompleteTest.java new file mode 100644 index 0000000..443c2b4 --- /dev/null +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/AutoCompleteTest.java @@ -0,0 +1,23 @@ +package net.tomatentum.marinara.test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import net.tomatentum.marinara.Marinara; +import net.tomatentum.marinara.test.mocks.AutocompleteInteractionMock; +import net.tomatentum.marinara.test.mocks.DiscordApiMock; +import net.tomatentum.marinara.wrapper.LibraryWrapper; +import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; + +public class AutoCompleteTest { + + @Test + public void testAutocomplete() { + LibraryWrapper wrapper = new JavacordWrapper(new DiscordApiMock()); //null okay as we don't use the discord API in this test. + Marinara marinara = Marinara.load(wrapper); + marinara.getRegistry().addInteractions(new TestAutocomplete()); + wrapper.handleInteraction(new AutocompleteInteractionMock()); + assertTrue(AutocompleteInteractionMock.didAutocompleteRun); + } +} diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestAutocomplete.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestAutocomplete.java new file mode 100644 index 0000000..c38f4a2 --- /dev/null +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestAutocomplete.java @@ -0,0 +1,23 @@ +package net.tomatentum.marinara.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; + +import org.javacord.api.interaction.AutocompleteInteraction; + +import net.tomatentum.marinara.interaction.InteractionHandler; +import net.tomatentum.marinara.interaction.annotation.AutoComplete; +import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; + +public class TestAutocomplete implements InteractionHandler { + + @SlashCommand(name = "test") + @AutoComplete + public void autocomplete(AutocompleteInteraction context, String value) { + System.out.println("Success!"); + assertEquals(value, "test"); + context.respondWithChoices(Collections.emptyList()); + } + +} diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/AutocompleteInteractionMock.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/AutocompleteInteractionMock.java new file mode 100644 index 0000000..29d64b9 --- /dev/null +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/AutocompleteInteractionMock.java @@ -0,0 +1,179 @@ +package net.tomatentum.marinara.test.mocks; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.component.HighLevelComponent; +import org.javacord.api.entity.permission.PermissionType; +import org.javacord.api.entity.server.Server; +import org.javacord.api.entity.user.User; +import org.javacord.api.interaction.AutocompleteInteraction; +import org.javacord.api.interaction.DiscordLocale; +import org.javacord.api.interaction.InteractionType; +import org.javacord.api.interaction.SlashCommandInteractionOption; +import org.javacord.api.interaction.SlashCommandOptionChoice; +import org.javacord.api.interaction.callback.InteractionFollowupMessageBuilder; +import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder; +import org.javacord.api.interaction.callback.InteractionOriginalResponseUpdater; + +public class AutocompleteInteractionMock implements AutocompleteInteraction { + + public static boolean didAutocompleteRun = false; + + @Override + public String getFullCommandName() { + return "test"; + } + + @Override + public long getCommandId() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getCommandId'"); + } + + @Override + public String getCommandIdAsString() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getCommandIdAsString'"); + } + + @Override + public String getCommandName() { + return "test"; + } + + @Override + public Optional getRegisteredCommandServerId() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getRegisteredCommandServerId'"); + } + + @Override + public long getApplicationId() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getApplicationId'"); + } + + @Override + public InteractionType getType() { + return InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE; + } + + @Override + public InteractionImmediateResponseBuilder createImmediateResponder() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'createImmediateResponder'"); + } + + @Override + public CompletableFuture respondLater() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'respondLater'"); + } + + @Override + public CompletableFuture respondLater(boolean ephemeral) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'respondLater'"); + } + + @Override + public CompletableFuture respondWithModal(String customId, String title, + List components) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'respondWithModal'"); + } + + @Override + public InteractionFollowupMessageBuilder createFollowupMessageBuilder() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'createFollowupMessageBuilder'"); + } + + @Override + public Optional getServer() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getServer'"); + } + + @Override + public Optional getChannel() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getChannel'"); + } + + @Override + public User getUser() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getUser'"); + } + + @Override + public String getToken() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getToken'"); + } + + @Override + public int getVersion() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getVersion'"); + } + + @Override + public DiscordLocale getLocale() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getLocale'"); + } + + @Override + public Optional getServerLocale() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getServerLocale'"); + } + + @Override + public Optional> getBotPermissions() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getBotPermissions'"); + } + + @Override + public DiscordApi getApi() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getApi'"); + } + + @Override + public long getId() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getId'"); + } + + @Override + public List getOptions() { + return new ArrayList<>(); + } + + @Override + public List getArguments() { + return new ArrayList<>(); + } + + @Override + public CompletableFuture respondWithChoices(List choices) { + didAutocompleteRun = true; + return CompletableFuture.completedFuture(null); + } + + @Override + public SlashCommandInteractionOption getFocusedOption() { + return new SlashCommandInteractionOptionMock(); + } + +} diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/SlashCommandInteractionOptionMock.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/SlashCommandInteractionOptionMock.java index 4f077b1..b102a21 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/SlashCommandInteractionOptionMock.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/SlashCommandInteractionOptionMock.java @@ -41,52 +41,52 @@ public class SlashCommandInteractionOptionMock implements SlashCommandInteractio @Override public Optional getLongValue() { - throw new UnsupportedOperationException("Unimplemented method 'getLongValue'"); + return Optional.empty(); } @Override public Optional getBooleanValue() { - throw new UnsupportedOperationException("Unimplemented method 'getBooleanValue'"); + return Optional.empty(); } @Override public Optional getUserValue() { - throw new UnsupportedOperationException("Unimplemented method 'getUserValue'"); + return Optional.empty(); } @Override public Optional> requestUserValue() { - throw new UnsupportedOperationException("Unimplemented method 'requestUserValue'"); + return Optional.empty(); } @Override public Optional getChannelValue() { - throw new UnsupportedOperationException("Unimplemented method 'getChannelValue'"); + return Optional.empty(); } @Override public Optional getAttachmentValue() { - throw new UnsupportedOperationException("Unimplemented method 'getAttachmentValue'"); + return Optional.empty(); } @Override public Optional getRoleValue() { - throw new UnsupportedOperationException("Unimplemented method 'getRoleValue'"); + return Optional.empty(); } @Override public Optional getMentionableValue() { - throw new UnsupportedOperationException("Unimplemented method 'getMentionableValue'"); + return Optional.empty(); } @Override public Optional getDecimalValue() { - throw new UnsupportedOperationException("Unimplemented method 'getDecimalValue'"); + return Optional.empty(); } @Override public Optional> requestMentionableValue() { - throw new UnsupportedOperationException("Unimplemented method 'requestMentionableValue'"); + return Optional.empty(); } @Override -- 2.45.2 From 445190db8961cf866b81bfc0daeaaff190f5e8d7 Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 16 Dec 2024 12:49:51 +0100 Subject: [PATCH 8/9] added Tests for choices --- .../marinara/test/TestChoiceEnum.java | 20 +++++++++++++++++++ .../tomatentum/marinara/test/TestCommand.java | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestChoiceEnum.java diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestChoiceEnum.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestChoiceEnum.java new file mode 100644 index 0000000..1c7fd0a --- /dev/null +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestChoiceEnum.java @@ -0,0 +1,20 @@ +package net.tomatentum.marinara.test; + +import net.tomatentum.marinara.interaction.commands.ChoiceValueProvider; + +public enum TestChoiceEnum implements ChoiceValueProvider { + TestValue("testValue"), + FooBar("fooBar"), + Spongebob("spongebob"); + + private String value; + + private TestChoiceEnum(String value) { + this.value = value; + } + @Override + public String getChoiceValue() { + return value; + } + +} diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestCommand.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestCommand.java index f6710b0..2716fea 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestCommand.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestCommand.java @@ -20,7 +20,8 @@ public class TestCommand implements InteractionHandler { @SlashCommandOption( name = "foo", description = "foo bar is very fooby", - type = SlashCommandOptionType.STRING + type = SlashCommandOptionType.STRING, + choiceEnum = TestChoiceEnum.class ) } ) -- 2.45.2 From aaf4f3297a99409fddf36ef19b7971841db6ef91 Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 16 Dec 2024 13:03:11 +0100 Subject: [PATCH 9/9] add autocomplete option toggle, add double value for non javacord wrappers, rename OptionChoices to match with discords naming and general cleanup --- .../interaction/commands/EnumChoices.java | 18 +++++---- .../annotation/SlashCommandOption.java | 1 + .../annotation/SlashCommandOptionChoice.java | 1 + .../option/SlashCommandOptionType.java | 34 +++++++++++------ .../wrapper/javacord/JavacordWrapper.java | 37 +++++++++++++++---- 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java index 411c5ac..58d82d9 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/EnumChoices.java @@ -40,12 +40,13 @@ public record EnumChoices(Class> enumClass, ChoiceType type, S if (!(typeParam instanceof Class)) throw new IllegalArgumentException("ChoiceValueProvider need either a String or Number type parameter."); + if (Long.class.isAssignableFrom((Class) typeParam)) + return ChoiceType.INTEGER; + if (Double.class.isAssignableFrom((Class) typeParam)) + return ChoiceType.DOUBLE; if (String.class.isAssignableFrom((Class) typeParam)) return ChoiceType.String; - else if (Number.class.isAssignableFrom((Class) typeParam)) - return ChoiceType.Number; - else - throw new IllegalArgumentException("ChoiceValueProvider need either a String or Number type parameter."); + throw new IllegalArgumentException("ChoiceValueProvider need either a String, Number or Decimal type parameter."); } private static SlashCommandOptionChoice[] parseChoices(Class> enumClass, ChoiceType type) { @@ -55,10 +56,12 @@ public record EnumChoices(Class> enumClass, ChoiceType type, S Object value; try { value = method.invoke(enumInstance); + if (type.equals(ChoiceType.INTEGER)) + choices.add(TypeFactory.annotation(SlashCommandOptionChoice.class, Map.of("name", enumInstance.name(), "longValue", value))); + if (type.equals(ChoiceType.DOUBLE)) + choices.add(TypeFactory.annotation(SlashCommandOptionChoice.class, Map.of("name", enumInstance.name(), "doubleValue", value))); if (type.equals(ChoiceType.String)) choices.add(TypeFactory.annotation(SlashCommandOptionChoice.class, Map.of("name", enumInstance.name(), "stringValue", value))); - else if (type.equals(ChoiceType.Number)) - choices.add(TypeFactory.annotation(SlashCommandOptionChoice.class, Map.of("name", enumInstance.name(), "longValue", value))); } catch (IllegalAccessException | InvocationTargetException | AnnotationFormatException e) { e.printStackTrace(); return null; @@ -69,6 +72,7 @@ public record EnumChoices(Class> enumClass, ChoiceType type, S public static enum ChoiceType { String, - Number + INTEGER, + DOUBLE } } diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java index eea19e3..d7b845f 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOption.java @@ -14,6 +14,7 @@ public @interface SlashCommandOption { public String description() default ""; public SlashCommandOptionType type() default SlashCommandOptionType.STRING; public boolean required() default false; + public boolean autocomplete() default false; public SlashCommandOptionChoice[] choices() default {}; public Class> choiceEnum() default PlaceHolderEnum.class; diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java index 0f5c8a7..9ccd3e0 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/annotation/SlashCommandOptionChoice.java @@ -3,5 +3,6 @@ package net.tomatentum.marinara.interaction.commands.annotation; public @interface SlashCommandOptionChoice { public String name(); public long longValue() default Long.MAX_VALUE; + public double doubleValue() default Double.MAX_VALUE; public String stringValue() default ""; } diff --git a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/option/SlashCommandOptionType.java b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/option/SlashCommandOptionType.java index db222c8..ce80c6f 100644 --- a/lib/src/main/java/net/tomatentum/marinara/interaction/commands/option/SlashCommandOptionType.java +++ b/lib/src/main/java/net/tomatentum/marinara/interaction/commands/option/SlashCommandOptionType.java @@ -1,16 +1,26 @@ package net.tomatentum.marinara.interaction.commands.option; public enum SlashCommandOptionType { - ATTACHMENT, - BOOLEAN, - CHANNEL, - DECIMAL, - LONG, - MENTIONABLE, - ROLE, - STRING, - SUB_COMMAND, - SUB_COMMAND_GROUP, - UNKNOW, - USER + SUB_COMMAND(1), + SUB_COMMAND_GROUP(2), + STRING(3), + INTEGER(4), + BOOLEAN(5), + USER(6), + CHANNEL(7), + ROLE(8), + MENTIONABLE(9), + DOUBLE(10), + ATTACHMENT(11), + UNKNOWN(-1); + + private final int value; + + private SlashCommandOptionType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } } diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java index b3a3da4..e07fb40 100644 --- a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/JavacordWrapper.java @@ -15,6 +15,9 @@ import org.javacord.api.interaction.ButtonInteraction; import org.javacord.api.interaction.SlashCommandBuilder; import org.javacord.api.interaction.SlashCommandInteraction; import org.javacord.api.interaction.SlashCommandInteractionOption; +import org.javacord.api.interaction.SlashCommandOptionBuilder; +import org.javacord.api.interaction.SlashCommandOptionChoiceBuilder; +import org.javacord.api.interaction.SlashCommandOptionType; import io.leangen.geantyref.AnnotationFormatException; import io.leangen.geantyref.TypeFactory; @@ -130,17 +133,35 @@ public class JavacordWrapper extends LibraryWrapper { } private org.javacord.api.interaction.SlashCommandOption convertOptionDef(SlashCommandOption option) { - org.javacord.api.interaction.SlashCommandOptionType type = Enum.valueOf(org.javacord.api.interaction.SlashCommandOptionType.class, option.type().toString()); + SlashCommandOptionType type = SlashCommandOptionType.fromValue(option.type().getValue()); + SlashCommandOptionBuilder builder = new SlashCommandOptionBuilder(); + builder + .setType(type) + .setName(option.name()) + .setDescription(option.description()) + .setRequired(option.required()) + .setAutocompletable(option.autocomplete()) + .setChoices(convertChoices(option)); + + return builder.build(); + } - List choices = new ArrayList<>(); + private List convertChoices(SlashCommandOption option) { + List convertedChoices = new ArrayList<>(); for (SlashCommandOptionChoice choice : ExecutableSlashCommandDefinition.getActualChoices(option)) { - if (choice.stringValue().isEmpty()) - choices.add(org.javacord.api.interaction.SlashCommandOptionChoice.create(choice.name(), choice.longValue())); - else - choices.add(org.javacord.api.interaction.SlashCommandOptionChoice.create(choice.name(), choice.stringValue())); + SlashCommandOptionChoiceBuilder builder = new SlashCommandOptionChoiceBuilder(); + builder.setName(choice.name()); + if (choice.longValue() != Long.MAX_VALUE) + builder.setValue(choice.longValue()); + /* + not yet available + if (choice.doubleValue() != Double.MAX_VALUE) + builder.setValue(choice.doubleValue()); + */ + if (!choice.stringValue().isEmpty()) + builder.setValue(choice.stringValue()); } - - return org.javacord.api.interaction.SlashCommandOption.createWithChoices(type, option.name(), option.description(), option.required(), choices); + return convertedChoices; } @Override -- 2.45.2