From 582e0f0bae80d46d54a5ea6b4113346ba88d76cc Mon Sep 17 00:00:00 2001 From: tueem Date: Sun, 24 Nov 2024 00:02:19 +0100 Subject: [PATCH 01/16] implement AnnotationParser system --- .../methods/ButtonInteractionMethod.java | 17 ++--- .../methods/InteractionMethod.java | 6 ++ .../SlashCommandInteractionMethod.java | 34 ++++------ .../marinara/parser/AnnotationParser.java | 8 +++ .../marinara/parser/ButtonParser.java | 29 +++++++++ .../marinara/parser/SlashCommandParser.java | 63 +++++++++++++++++++ .../marinara/util/ReflectionUtil.java | 18 ------ 7 files changed, 126 insertions(+), 49 deletions(-) create mode 100644 lib/src/main/java/net/tomatentum/marinara/parser/AnnotationParser.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/parser/ButtonParser.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/parser/SlashCommandParser.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 baf8f4e..c366b48 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 @@ -4,7 +4,8 @@ import java.lang.reflect.Method; import net.tomatentum.marinara.interaction.InteractionHandler; import net.tomatentum.marinara.interaction.InteractionType; -import net.tomatentum.marinara.interaction.annotation.Button; +import net.tomatentum.marinara.parser.AnnotationParser; +import net.tomatentum.marinara.parser.ButtonParser; import net.tomatentum.marinara.wrapper.LibraryWrapper; public class ButtonInteractionMethod extends InteractionMethod { @@ -13,7 +14,13 @@ public class ButtonInteractionMethod extends InteractionMethod { ButtonInteractionMethod(Method method, InteractionHandler handler, LibraryWrapper wrapper) { super(method, handler, wrapper); - parseMethod(); + } + + @Override + public AnnotationParser[] getParsers() { + return new AnnotationParser[] { + new ButtonParser(method, (x) -> { this.customId = x; } ) + }; } @Override @@ -31,10 +38,4 @@ public class ButtonInteractionMethod extends InteractionMethod { public InteractionType getType() { return InteractionType.BUTTON; } - - private void parseMethod() { - Button button = getMethod().getAnnotation(Button.class); - this.customId = button.value(); - } - } 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 0f6b09e..ca8d263 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 @@ -12,6 +12,7 @@ import net.tomatentum.marinara.interaction.InteractionType; import net.tomatentum.marinara.interaction.annotation.Button; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; +import net.tomatentum.marinara.parser.AnnotationParser; import net.tomatentum.marinara.wrapper.LibraryWrapper; public abstract class InteractionMethod { @@ -27,6 +28,7 @@ public abstract class InteractionMethod { protected Method method; protected InteractionHandler handler; protected LibraryWrapper wrapper; + protected AnnotationParser[] parsers; protected InteractionMethod(Method method, InteractionHandler handler, LibraryWrapper wrapper) { if (!Arrays.asList(handler.getClass().getMethods()).contains(method)) @@ -34,8 +36,12 @@ public abstract class InteractionMethod { this.method = method; this.handler = handler; this.wrapper = wrapper; + this.parsers = getParsers(); + Arrays.stream(parsers).forEach(AnnotationParser::parse); } + public abstract AnnotationParser[] getParsers(); + public abstract Object getParameter(Object parameter, int index); public abstract boolean canRun(Object context); 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 6498078..cd63d69 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 @@ -5,10 +5,8 @@ import java.lang.reflect.Method; import net.tomatentum.marinara.interaction.InteractionHandler; import net.tomatentum.marinara.interaction.InteractionType; import net.tomatentum.marinara.interaction.commands.ExecutableSlashCommandDefinition; -import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; -import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; -import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; -import net.tomatentum.marinara.util.ReflectionUtil; +import net.tomatentum.marinara.parser.AnnotationParser; +import net.tomatentum.marinara.parser.SlashCommandParser; import net.tomatentum.marinara.wrapper.LibraryWrapper; public class SlashCommandInteractionMethod extends InteractionMethod { @@ -17,7 +15,13 @@ public class SlashCommandInteractionMethod extends InteractionMethod { SlashCommandInteractionMethod(Method method, InteractionHandler handler, LibraryWrapper wrapper) { super(method, handler, wrapper); - parseMethod(); + } + + @Override + public AnnotationParser[] getParsers() { + return new AnnotationParser[] { + new SlashCommandParser(method, (x) -> { this.commandDefinition = x; } ) + }; } @Override @@ -40,24 +44,8 @@ public class SlashCommandInteractionMethod extends InteractionMethod { return commandDefinition; } - private void parseMethod() { - ReflectionUtil.checkValidCommandMethod(method); - - SlashCommand cmd = ReflectionUtil.getAnnotation(method, SlashCommand.class); - ExecutableSlashCommandDefinition.Builder builder = new ExecutableSlashCommandDefinition.Builder(); - builder.setApplicationCommand(cmd); - - if (ReflectionUtil.isAnnotationPresent(method, SubCommandGroup.class)) { - SubCommandGroup cmdGroup = ReflectionUtil.getAnnotation(method, SubCommandGroup.class); - builder.setSubCommandGroup(cmdGroup); - } - - if (ReflectionUtil.isAnnotationPresent(method, SubCommand.class)) { - SubCommand subCmd = ReflectionUtil.getAnnotation(method, SubCommand.class); - builder.setSubCommand(subCmd); - } - - this.commandDefinition = builder.build(); + public void setCommandDefinition(ExecutableSlashCommandDefinition commandDefinition) { + this.commandDefinition = commandDefinition; } } diff --git a/lib/src/main/java/net/tomatentum/marinara/parser/AnnotationParser.java b/lib/src/main/java/net/tomatentum/marinara/parser/AnnotationParser.java new file mode 100644 index 0000000..187a4a4 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/parser/AnnotationParser.java @@ -0,0 +1,8 @@ +package net.tomatentum.marinara.parser; + +import java.lang.reflect.Method; + +public interface AnnotationParser { + void parse(); + Method getMethod(); +} diff --git a/lib/src/main/java/net/tomatentum/marinara/parser/ButtonParser.java b/lib/src/main/java/net/tomatentum/marinara/parser/ButtonParser.java new file mode 100644 index 0000000..1b7dcbb --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/parser/ButtonParser.java @@ -0,0 +1,29 @@ +package net.tomatentum.marinara.parser; + +import java.lang.reflect.Method; +import java.util.function.Consumer; + +import net.tomatentum.marinara.interaction.annotation.Button; + +public class ButtonParser implements AnnotationParser { + + private Method method; + private Consumer consumer; + + public ButtonParser(Method method, Consumer consumer) { + this.method = method; + this.consumer = consumer; + } + + @Override + public void parse() { + Button button = getMethod().getAnnotation(Button.class); + this.consumer.accept(button.value()); + } + + @Override + public Method getMethod() { + return this.method; + } + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/parser/SlashCommandParser.java b/lib/src/main/java/net/tomatentum/marinara/parser/SlashCommandParser.java new file mode 100644 index 0000000..8997d9f --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/parser/SlashCommandParser.java @@ -0,0 +1,63 @@ +package net.tomatentum.marinara.parser; + +import java.lang.reflect.Method; +import java.util.function.Consumer; + +import net.tomatentum.marinara.interaction.commands.ExecutableSlashCommandDefinition; +import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; +import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; +import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; +import net.tomatentum.marinara.util.ReflectionUtil; + +public class SlashCommandParser implements AnnotationParser { + + private Method method; + private Consumer consumer; + + public SlashCommandParser(Method method, Consumer consumer) { + this.method = method; + this.consumer = consumer; + } + + @Override + public void parse() { + this.checkValidCommandMethod(method); + + SlashCommand cmd = ReflectionUtil.getAnnotation(method, SlashCommand.class); + ExecutableSlashCommandDefinition.Builder builder = new ExecutableSlashCommandDefinition.Builder(); + builder.setApplicationCommand(cmd); + + if (ReflectionUtil.isAnnotationPresent(method, SubCommandGroup.class)) { + SubCommandGroup cmdGroup = ReflectionUtil.getAnnotation(method, SubCommandGroup.class); + builder.setSubCommandGroup(cmdGroup); + } + + if (ReflectionUtil.isAnnotationPresent(method, SubCommand.class)) { + SubCommand subCmd = ReflectionUtil.getAnnotation(method, SubCommand.class); + builder.setSubCommand(subCmd); + } + + consumer.accept(builder.build()); + } + + @Override + public Method getMethod() { + return this.method; + } + + private void checkValidCommandMethod(Method method) { + if (method.isAnnotationPresent(SlashCommand.class) && + method.getDeclaringClass().isAnnotationPresent(SlashCommand.class)) { + throw new RuntimeException(method.getName() + ": Can't have ApplicationCommand Annotation on Class and Method"); + } + + if (!ReflectionUtil.isAnnotationPresent(method, SlashCommand.class)) + throw new RuntimeException(method.getName() + ": Missing ApplicationCommand Annotation on either Class or Method"); + + if ((method.isAnnotationPresent(SubCommand.class) && + !ReflectionUtil.isAnnotationPresent(method, SlashCommand.class))) { + throw new RuntimeException(method.getName() + ": Missing ApplicationCommand Annotation on either Method or Class"); + } + } + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java index 63105bf..0c7f96e 100644 --- a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java +++ b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java @@ -3,9 +3,6 @@ package net.tomatentum.marinara.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; -import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; - public final class ReflectionUtil { public static boolean isAnnotationPresent(Method method, Class annotationClass) { @@ -20,20 +17,5 @@ public final class ReflectionUtil { method.getAnnotation(annotationClass) : method.getDeclaringClass().getAnnotation(annotationClass); } - - public static void checkValidCommandMethod(Method method) { - if (method.isAnnotationPresent(SlashCommand.class) && - method.getDeclaringClass().isAnnotationPresent(SlashCommand.class)) { - throw new RuntimeException(method.getName() + ": Can't have ApplicationCommand Annotation on Class and Method"); - } - - if (!isAnnotationPresent(method, SlashCommand.class)) - throw new RuntimeException(method.getName() + ": Missing ApplicationCommand Annotation on either Class or Method"); - - if ((method.isAnnotationPresent(SubCommand.class) && - !isAnnotationPresent(method, SlashCommand.class))) { - throw new RuntimeException(method.getName() + ": Missing ApplicationCommand Annotation on either Method or Class"); - } - } } -- 2.45.2 From f89ae5e42540614f3166587ae8d3dc94b3636191 Mon Sep 17 00:00:00 2001 From: tueem Date: Thu, 28 Nov 2024 10:32:48 +0100 Subject: [PATCH 02/16] - add prototype Interactioncheck impementation. - refactor dependency injection to have all widely used dependencies in the Marinara class. --- .../net/tomatentum/marinara/Marinara.java | 20 +++++-- .../marinara/checks/AppliedCheck.java | 26 ++++++++ .../marinara/checks/InteractionCheck.java | 10 ++++ .../methods/ButtonInteractionMethod.java | 10 ++-- .../methods/InteractionMethod.java | 59 ++++++++++++------- .../SlashCommandInteractionMethod.java | 10 ++-- .../parser/InteractionCheckParser.java | 42 +++++++++++++ .../registry/InteractionCheckRegistry.java | 32 ++++++++++ .../registry/InteractionRegistry.java | 16 ++--- 9 files changed, 183 insertions(+), 42 deletions(-) create mode 100644 lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java create mode 100644 lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java diff --git a/lib/src/main/java/net/tomatentum/marinara/Marinara.java b/lib/src/main/java/net/tomatentum/marinara/Marinara.java index cbed606..0896858 100644 --- a/lib/src/main/java/net/tomatentum/marinara/Marinara.java +++ b/lib/src/main/java/net/tomatentum/marinara/Marinara.java @@ -1,22 +1,34 @@ package net.tomatentum.marinara; +import net.tomatentum.marinara.registry.InteractionCheckRegistry; import net.tomatentum.marinara.registry.InteractionRegistry; import net.tomatentum.marinara.wrapper.LibraryWrapper; public class Marinara { public static Marinara load(LibraryWrapper wrapper) { - InteractionRegistry registry = new InteractionRegistry(wrapper); - return new Marinara(registry); + return new Marinara(wrapper); } private InteractionRegistry registry; + private InteractionCheckRegistry checkRegistry; + private LibraryWrapper wrapper; - private Marinara(InteractionRegistry registry) { - this.registry = registry; + private Marinara(LibraryWrapper wrapper) { + this.wrapper = wrapper; + this.registry = new InteractionRegistry(this); + this.checkRegistry = new InteractionCheckRegistry(); } public InteractionRegistry getRegistry() { return registry; } + + public InteractionCheckRegistry getCheckRegistry() { + return checkRegistry; + } + + public LibraryWrapper getWrapper() { + return wrapper; + } } diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java new file mode 100644 index 0000000..266c57d --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java @@ -0,0 +1,26 @@ +package net.tomatentum.marinara.checks; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; + +public record AppliedCheck(InteractionCheck check, Annotation annotation) { + + public boolean pre() { + try { + return (boolean) check.getClass().getMethod("preExec", annotation.getClass()).invoke(check, annotation); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + return false; + } + } + + public boolean post() { + try { + return (boolean) check.getClass().getMethod("postExec", annotation.getClass()).invoke(check, annotation); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + return false; + } + } + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java new file mode 100644 index 0000000..f6e450c --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java @@ -0,0 +1,10 @@ +package net.tomatentum.marinara.checks; + +import java.lang.annotation.Annotation; + +public interface InteractionCheck { + + public boolean preExec(A annotation); + public boolean postExec(A annotation); + +} 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 c366b48..f6b2a9e 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 @@ -2,18 +2,18 @@ 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.parser.AnnotationParser; import net.tomatentum.marinara.parser.ButtonParser; -import net.tomatentum.marinara.wrapper.LibraryWrapper; public class ButtonInteractionMethod extends InteractionMethod { private String customId; - ButtonInteractionMethod(Method method, InteractionHandler handler, LibraryWrapper wrapper) { - super(method, handler, wrapper); + ButtonInteractionMethod(Method method, InteractionHandler handler, Marinara marinara) { + super(method, handler, marinara); } @Override @@ -26,12 +26,12 @@ public class ButtonInteractionMethod extends InteractionMethod { @Override public Object getParameter(Object parameter, int index) { Class type = getMethod().getParameterTypes()[index+1]; - return wrapper.getComponentContextObject(parameter, type); + return marinara.getWrapper().getComponentContextObject(parameter, type); } @Override public boolean canRun(Object context) { - return wrapper.getButtonId(context).equals(customId); + return marinara.getWrapper().getButtonId(context).equals(customId); } @Override 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 ca8d263..2a55f9d 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 @@ -7,37 +7,48 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +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.Button; import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; import net.tomatentum.marinara.parser.AnnotationParser; -import net.tomatentum.marinara.wrapper.LibraryWrapper; +import net.tomatentum.marinara.parser.InteractionCheckParser; public abstract class InteractionMethod { - public static InteractionMethod create(Method method, InteractionHandler handler, LibraryWrapper wrapper) { + public static InteractionMethod create(Method method, InteractionHandler handler, Marinara marinara) { if (method.isAnnotationPresent(SlashCommand.class) || method.isAnnotationPresent(SubCommand.class)) - return new SlashCommandInteractionMethod(method, handler, wrapper); + return new SlashCommandInteractionMethod(method, handler, marinara); if (method.isAnnotationPresent(Button.class)) - return new ButtonInteractionMethod(method, handler, wrapper); + return new ButtonInteractionMethod(method, handler, marinara); return null; } protected Method method; protected InteractionHandler handler; - protected LibraryWrapper wrapper; - protected AnnotationParser[] parsers; + protected Marinara marinara; + protected List parsers; + protected List appliedChecks; - protected InteractionMethod(Method method, InteractionHandler handler, LibraryWrapper wrapper) { + protected InteractionMethod(Method method, + InteractionHandler handler, + Marinara marinara + ) { if (!Arrays.asList(handler.getClass().getMethods()).contains(method)) throw new InvalidParameterException("Method does not apply to specified handler"); + this.method = method; this.handler = handler; - this.wrapper = wrapper; - this.parsers = getParsers(); - Arrays.stream(parsers).forEach(AnnotationParser::parse); + this.marinara = marinara; + this.parsers = new ArrayList<>(Arrays.asList(getParsers())); + this.appliedChecks = new ArrayList<>(); + + parsers.add(new InteractionCheckParser(method, appliedChecks::add, marinara.getCheckRegistry())); + + parsers.stream().forEach(AnnotationParser::parse); } public abstract AnnotationParser[] getParsers(); @@ -49,6 +60,23 @@ public abstract class InteractionMethod { public abstract InteractionType getType(); public void run(Object context) { + this.appliedChecks.forEach(AppliedCheck::pre); + + method.setAccessible(true); + try { + method.invoke(handler, getParameters(context)); + }catch (IllegalAccessException | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + this.appliedChecks.forEach(AppliedCheck::post); + } + + public Method getMethod() { + return method; + } + + private Object[] getParameters(Object context) { int parameterCount = method.getParameterCount(); List parameters = new ArrayList<>(); @@ -59,16 +87,7 @@ public abstract class InteractionMethod { } parameters.add(getParameter(context, i-1)); } - method.setAccessible(true); - try { - method.invoke(handler, parameters.toArray()); - }catch (IllegalAccessException | InvocationTargetException ex) { - throw new RuntimeException(ex); - } - } - - public Method getMethod() { - return method; + return parameters.toArray(); } } 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 cd63d69..666dd3c 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 @@ -2,19 +2,19 @@ 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; -import net.tomatentum.marinara.wrapper.LibraryWrapper; public class SlashCommandInteractionMethod extends InteractionMethod { private ExecutableSlashCommandDefinition commandDefinition; - SlashCommandInteractionMethod(Method method, InteractionHandler handler, LibraryWrapper wrapper) { - super(method, handler, wrapper); + SlashCommandInteractionMethod(Method method, InteractionHandler handler, Marinara marinara) { + super(method, handler, marinara); } @Override @@ -26,12 +26,12 @@ public class SlashCommandInteractionMethod extends InteractionMethod { @Override public Object getParameter(Object context, int index) { - return wrapper.convertCommandOption(context, commandDefinition.options()[index].type(), commandDefinition.options()[index].name()); + return marinara.getWrapper().convertCommandOption(context, commandDefinition.options()[index].type(), commandDefinition.options()[index].name()); } @Override public boolean canRun(Object context) { - ExecutableSlashCommandDefinition other = wrapper.getCommandDefinition(context); + ExecutableSlashCommandDefinition other = marinara.getWrapper().getCommandDefinition(context); return commandDefinition.equals(other); } diff --git a/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java b/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java new file mode 100644 index 0000000..2722456 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java @@ -0,0 +1,42 @@ +package net.tomatentum.marinara.parser; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Consumer; + +import net.tomatentum.marinara.checks.AppliedCheck; +import net.tomatentum.marinara.checks.InteractionCheck; +import net.tomatentum.marinara.registry.InteractionCheckRegistry; + +public class InteractionCheckParser implements AnnotationParser { + + private InteractionCheckRegistry checkRegistry; + private Method method; + private Consumer consumer; + + public InteractionCheckParser(Method method, Consumer consumer, InteractionCheckRegistry checkRegistry) { + this.checkRegistry = checkRegistry; + this.method = method; + this.consumer = consumer; + } + + @Override + public void parse() { + Annotation[] annotations = method.getAnnotations(); + Arrays.stream(annotations).forEach(this::convertAnnotation); + } + + private void convertAnnotation(Annotation annotation) { + Optional> check = this.checkRegistry.getCheckFromAnnotation(annotation.getClass()); + if (check.isPresent()) + consumer.accept(new AppliedCheck(check.get(), annotation)); + } + + @Override + public Method getMethod() { + return this.method; + } + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java new file mode 100644 index 0000000..59a643e --- /dev/null +++ b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java @@ -0,0 +1,32 @@ +package net.tomatentum.marinara.registry; + +import java.lang.annotation.Annotation; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import net.tomatentum.marinara.checks.InteractionCheck; + +public class InteractionCheckRegistry { + + private List> checks; + + public InteractionCheckRegistry() { + this.checks = new ArrayList<>(); + } + + public void addCheck(InteractionCheck check) { + checks.add(check); + } + + public Optional> getCheckFromAnnotation(Class annotation) { + for (InteractionCheck interactionCheck : checks) { + TypeVariable type = interactionCheck.getClass().getTypeParameters()[0]; + if (type.getClass().equals(annotation.getClass())) + return Optional.of(interactionCheck); + } + return Optional.empty(); + } + +} diff --git a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java index 31e3ad1..443ea39 100644 --- a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java +++ b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java @@ -5,27 +5,27 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import net.tomatentum.marinara.Marinara; import net.tomatentum.marinara.interaction.InteractionHandler; import net.tomatentum.marinara.interaction.InteractionType; import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; import net.tomatentum.marinara.interaction.commands.ExecutableSlashCommandDefinition; import net.tomatentum.marinara.interaction.methods.SlashCommandInteractionMethod; import net.tomatentum.marinara.interaction.methods.InteractionMethod; -import net.tomatentum.marinara.wrapper.LibraryWrapper; public class InteractionRegistry { private List interactionMethods; - private LibraryWrapper wrapper; + private Marinara marinara; - public InteractionRegistry(LibraryWrapper wrapper) { + public InteractionRegistry(Marinara marinara) { this.interactionMethods = new ArrayList<>(); - this.wrapper = wrapper; - wrapper.subscribeInteractions(this::handle); + this.marinara = marinara; + marinara.getWrapper().subscribeInteractions(this::handle); } public void addInteractions(InteractionHandler interactionHandler) { for (Method method : interactionHandler.getClass().getMethods()) { - InteractionMethod iMethod = InteractionMethod.create(method, interactionHandler, wrapper); + InteractionMethod iMethod = InteractionMethod.create(method, interactionHandler, marinara); if (iMethod != null) this.interactionMethods.add(iMethod); } @@ -48,12 +48,12 @@ public class InteractionRegistry { defs.add(new SlashCommandDefinition(def.applicationCommand()).addExecutableCommand(def)); }); - wrapper.registerSlashCommands(defs.toArray(new SlashCommandDefinition[0])); + marinara.getWrapper().registerSlashCommands(defs.toArray(new SlashCommandDefinition[0])); } public void handle(Object context) { interactionMethods.forEach((m) -> { - InteractionType type = wrapper.getInteractionType(context.getClass()); + InteractionType type = marinara.getWrapper().getInteractionType(context.getClass()); if (m.getType().equals(type)) m.run(context); }); -- 2.45.2 From 019ba8f55266d464ad0235d45ad7398c0ce918ed Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 29 Nov 2024 18:15:47 +0100 Subject: [PATCH 03/16] add getMostSpecificMethod method to simplify Check method parsing. This Method searches the The method that has the best matching parameters with the fewest inheritance levels as possible. Left sided priority --- .../marinara/util/ReflectionUtil.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java index 0c7f96e..4f80cff 100644 --- a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java +++ b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java @@ -2,6 +2,10 @@ package net.tomatentum.marinara.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; public final class ReflectionUtil { @@ -17,5 +21,79 @@ public final class ReflectionUtil { method.getAnnotation(annotationClass) : method.getDeclaringClass().getAnnotation(annotationClass); } + + public static int getCastDepth(Class child, Class parent) { + if (!parent.isAssignableFrom(child)) { + throw new IllegalArgumentException("The specified class is not a child class of the specified parent."); + } + + int depth = 0; + Class curr = child; + List> parents = new ArrayList<>(); + + while (!curr.equals(parent)) { + depth++; + parents.add(curr.getSuperclass()); + parents.addAll(Arrays.asList(curr.getInterfaces())); + + for (Class currParent : parents) { + if (currParent != null && parent.isAssignableFrom(currParent)) { + curr = currParent; + break; + } + } + parents.clear(); + } + + return depth; + } + public static Method getMostSpecificMethod(Method[] methods, Class... parameters) { + List compatibleMethods = Arrays.stream(methods) + .filter(x -> isMethodCallable(x, parameters)) + .toList(); + + if (compatibleMethods.size() == 0) + throw new IllegalArgumentException("There are no compatible Methods provided"); + + for (int i = 0; i < parameters.length; i++) { + final int currI = i; + Class[] parameterTypes = compatibleMethods.stream() + .map(x -> x.getParameterTypes()[currI]) + .toArray(x -> new Class[x]); + + Class mostSpecific = getMostSpecificClass(parameterTypes, parameters[i]); + + compatibleMethods = compatibleMethods.stream() + .filter(x -> Objects.equals(x.getParameterTypes()[currI], mostSpecific)) + .toList(); + } + + return compatibleMethods.getFirst(); + } + + public static Class getMostSpecificClass(Class[] classes, Class base) { + int min = Integer.MAX_VALUE; + Class currMostSpecific = null; + for (Class currClass : classes) { + int currCastDepth = getCastDepth(base, currClass); + if (currCastDepth < min) { + min = currCastDepth; + currMostSpecific = currClass; + } + } + return currMostSpecific; + } + + public static boolean isMethodCallable(Method method, Class... parameters) { + if (!Objects.equals(method.getParameterCount(), parameters.length)) + return false; + + Class[] methodParams = method.getParameterTypes(); + for (int i = 0; i < parameters.length; i++) { + if (!methodParams[i].isAssignableFrom(parameters[i])) + return false; + } + return true; + } } -- 2.45.2 From 659218682e545871fd15f342637e3d1cbbb77853 Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 29 Nov 2024 18:17:33 +0100 Subject: [PATCH 04/16] add context Object to check methods and create the ability to have a specific method for each type of context or one for all by using the superclass and casting yourself --- .../marinara/checks/AppliedCheck.java | 26 ++++++++++++++----- .../marinara/checks/InteractionCheck.java | 4 +-- .../methods/InteractionMethod.java | 4 +-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java index 266c57d..28415b0 100644 --- a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java +++ b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java @@ -2,22 +2,36 @@ package net.tomatentum.marinara.checks; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import net.tomatentum.marinara.util.ReflectionUtil; public record AppliedCheck(InteractionCheck check, Annotation annotation) { - public boolean pre() { + public boolean pre(Object context) { + Method[] methods = Arrays.stream(check.getClass().getMethods()) + .filter(x -> x.getName().equals("preExec")) + .toArray(s -> new Method[s]); + Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass()); + method.setAccessible(true); try { - return (boolean) check.getClass().getMethod("preExec", annotation.getClass()).invoke(check, annotation); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + return (boolean) method.invoke(check, annotation); + } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { e.printStackTrace(); return false; } } - public boolean post() { + public boolean post(Object context) { + Method[] methods = Arrays.stream(check.getClass().getMethods()) + .filter(x -> x.getName().equals("postExec")) + .toArray(s -> new Method[s]); + Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass()); + method.setAccessible(true); try { - return (boolean) check.getClass().getMethod("postExec", annotation.getClass()).invoke(check, annotation); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + return (boolean) method.invoke(check, annotation); + } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { e.printStackTrace(); return false; } diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java index f6e450c..a7c3217 100644 --- a/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java +++ b/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java @@ -4,7 +4,7 @@ import java.lang.annotation.Annotation; public interface InteractionCheck { - public boolean preExec(A annotation); - public boolean postExec(A annotation); + public boolean preExec(Object context, A annotation); + public boolean postExec(Object context, A annotation); } 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 2a55f9d..aa06f0e 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 @@ -60,7 +60,7 @@ public abstract class InteractionMethod { public abstract InteractionType getType(); public void run(Object context) { - this.appliedChecks.forEach(AppliedCheck::pre); + this.appliedChecks.forEach(x -> x.pre(context)); method.setAccessible(true); try { @@ -69,7 +69,7 @@ public abstract class InteractionMethod { throw new RuntimeException(ex); } - this.appliedChecks.forEach(AppliedCheck::post); + this.appliedChecks.forEach(x -> x.post(context)); } public Method getMethod() { -- 2.45.2 From 6eb7fb723f5d3bf204e6237da70a263de3bce3a7 Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 29 Nov 2024 21:36:02 +0100 Subject: [PATCH 05/16] add geantyref to fix and simplify generic Type parsing. Also switch to java 23 to avoid conflicts and issues. --- lib/build.gradle.kts | 4 ++-- .../marinara/registry/InteractionCheckRegistry.java | 12 +++++++----- wrapper/javacord/build.gradle.kts | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index adf8f9b..12ef189 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -21,13 +21,13 @@ dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher") implementation(libs.log4j) - + implementation(libs.geantyref) } // Apply a specific Java toolchain to ease working on different environments. java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(23) } } diff --git a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java index 59a643e..370419e 100644 --- a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java +++ b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionCheckRegistry.java @@ -1,11 +1,12 @@ package net.tomatentum.marinara.registry; -import java.lang.annotation.Annotation; -import java.lang.reflect.TypeVariable; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import io.leangen.geantyref.GenericTypeReflector; import net.tomatentum.marinara.checks.InteractionCheck; public class InteractionCheckRegistry { @@ -20,10 +21,11 @@ public class InteractionCheckRegistry { checks.add(check); } - public Optional> getCheckFromAnnotation(Class annotation) { + public Optional> getCheckFromAnnotation(Type annotation) { for (InteractionCheck interactionCheck : checks) { - TypeVariable type = interactionCheck.getClass().getTypeParameters()[0]; - if (type.getClass().equals(annotation.getClass())) + ParameterizedType type = (ParameterizedType) GenericTypeReflector.getExactSuperType(interactionCheck.getClass(), InteractionCheck.class); + Type typeParam = type.getActualTypeArguments()[0]; + if (typeParam.equals(annotation)) return Optional.of(interactionCheck); } return Optional.empty(); diff --git a/wrapper/javacord/build.gradle.kts b/wrapper/javacord/build.gradle.kts index 9ec7322..2d1e7c7 100644 --- a/wrapper/javacord/build.gradle.kts +++ b/wrapper/javacord/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { // Apply a specific Java toolchain to ease working on different environments. java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(23) } } -- 2.45.2 From 239e921e6f41c35ac15afad5d6cb35d6f896bd74 Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 29 Nov 2024 21:37:13 +0100 Subject: [PATCH 06/16] change Annotation#getClass to Annotation#annotationType because it was not working as expected --- .../net/tomatentum/marinara/parser/InteractionCheckParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java b/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java index 2722456..abba9e9 100644 --- a/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java +++ b/lib/src/main/java/net/tomatentum/marinara/parser/InteractionCheckParser.java @@ -29,7 +29,7 @@ public class InteractionCheckParser implements AnnotationParser { } private void convertAnnotation(Annotation annotation) { - Optional> check = this.checkRegistry.getCheckFromAnnotation(annotation.getClass()); + Optional> check = this.checkRegistry.getCheckFromAnnotation(annotation.annotationType()); if (check.isPresent()) consumer.accept(new AppliedCheck(check.get(), annotation)); } -- 2.45.2 From b7333c2e5e92790fae201349aec1b59cd45e7cd7 Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 29 Nov 2024 21:41:18 +0100 Subject: [PATCH 07/16] fix problem with multiple method overrides in generic types. --- .../net/tomatentum/marinara/checks/AppliedCheck.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java index 28415b0..4f969f2 100644 --- a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java +++ b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java @@ -12,11 +12,12 @@ public record AppliedCheck(InteractionCheck check, Annotation annotation) { public boolean pre(Object context) { Method[] methods = Arrays.stream(check.getClass().getMethods()) .filter(x -> x.getName().equals("preExec")) + .filter(x -> !x.isBridge()) .toArray(s -> new Method[s]); - Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass()); + Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.getClass()); method.setAccessible(true); try { - return (boolean) method.invoke(check, annotation); + return (boolean) method.invoke(check, context, annotation); } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { e.printStackTrace(); return false; @@ -26,11 +27,12 @@ public record AppliedCheck(InteractionCheck check, Annotation annotation) { public boolean post(Object context) { Method[] methods = Arrays.stream(check.getClass().getMethods()) .filter(x -> x.getName().equals("postExec")) + .filter(x -> !x.isBridge()) .toArray(s -> new Method[s]); - Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass()); + Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.getClass()); method.setAccessible(true); try { - return (boolean) method.invoke(check, annotation); + return (boolean) method.invoke(check, context, annotation); } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { e.printStackTrace(); return false; -- 2.45.2 From 33392b02fba732c94779e4375b2940fb7fa8c92c Mon Sep 17 00:00:00 2001 From: tueem Date: Fri, 29 Nov 2024 21:41:46 +0100 Subject: [PATCH 08/16] add InteractionCheck test --- .../marinara/test/InteractionCheckTest.java | 29 ++++++++++++++ .../tomatentum/marinara/test/TestButton.java | 2 + .../marinara/test/TestInteractionCheck.java | 38 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java create mode 100644 wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java new file mode 100644 index 0000000..95773f3 --- /dev/null +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java @@ -0,0 +1,29 @@ +package net.tomatentum.marinara.test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import net.tomatentum.marinara.Marinara; +import net.tomatentum.marinara.test.mocks.ButtonInteractionMock; +import net.tomatentum.marinara.test.mocks.DiscordApiMock; +import net.tomatentum.marinara.wrapper.LibraryWrapper; +import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; + +@TestInstance(Lifecycle.PER_CLASS) +public class InteractionCheckTest { + + @Test + public void testInteractionCheck() { + LibraryWrapper wrapper = new JavacordWrapper(new DiscordApiMock()); + Marinara marinara = Marinara.load(wrapper); + marinara.getCheckRegistry().addCheck(new TestInteractionCheck()); + marinara.getRegistry().addInteractions(new TestButton()); + wrapper.handleInteraction(new ButtonInteractionMock()); + assertTrue(TestInteractionCheck.preExecuted); + assertTrue(TestInteractionCheck.postExecuted); + } + +} diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java index c72b8f9..eb9c9c8 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java @@ -10,12 +10,14 @@ import org.javacord.api.interaction.ButtonInteraction; import net.tomatentum.marinara.interaction.InteractionHandler; import net.tomatentum.marinara.interaction.annotation.Button; +import net.tomatentum.marinara.test.TestInteractionCheck.TestCheck; public class TestButton implements InteractionHandler { public static boolean didRun = false; @Button("test") + @TestCheck public void exec(ButtonInteraction interaction, TextChannel channel, Message message, User member, Server server) { assertNotNull(interaction); assertNotNull(channel); diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java new file mode 100644 index 0000000..b52efea --- /dev/null +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java @@ -0,0 +1,38 @@ +package net.tomatentum.marinara.test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import net.tomatentum.marinara.checks.InteractionCheck; + +public class TestInteractionCheck implements InteractionCheck { + + public static boolean preExecuted = false; + public static boolean postExecuted = false; + + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public static @interface TestCheck { + } + + @Override + public boolean preExec(Object context, TestCheck annotation) { + assertNotNull(annotation); + assertNotNull(context); + preExecuted = true; + return true; + } + + @Override + public boolean postExec(Object context, TestCheck annotation) { + assertNotNull(annotation); + assertNotNull(context); + postExecuted = true; + return true; + } + +} -- 2.45.2 From c363ab97442914c4f62061f57a700f459c863435 Mon Sep 17 00:00:00 2001 From: Tueem Date: Sun, 1 Dec 2024 13:06:42 +0100 Subject: [PATCH 09/16] add ability to return false to cancel execution in pre Checks and remove return type on post checks --- .../java/net/tomatentum/marinara/checks/AppliedCheck.java | 5 ++--- .../net/tomatentum/marinara/checks/InteractionCheck.java | 2 +- .../marinara/interaction/methods/InteractionMethod.java | 3 ++- .../net/tomatentum/marinara/test/TestInteractionCheck.java | 3 +-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java index 4f969f2..44e4c8a 100644 --- a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java +++ b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java @@ -24,7 +24,7 @@ public record AppliedCheck(InteractionCheck check, Annotation annotation) { } } - public boolean post(Object context) { + public void post(Object context) { Method[] methods = Arrays.stream(check.getClass().getMethods()) .filter(x -> x.getName().equals("postExec")) .filter(x -> !x.isBridge()) @@ -32,10 +32,9 @@ public record AppliedCheck(InteractionCheck check, Annotation annotation) { Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.getClass()); method.setAccessible(true); try { - return (boolean) method.invoke(check, context, annotation); + method.invoke(check, context, annotation); } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { e.printStackTrace(); - return false; } } diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java index a7c3217..4b38aa4 100644 --- a/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java +++ b/lib/src/main/java/net/tomatentum/marinara/checks/InteractionCheck.java @@ -5,6 +5,6 @@ import java.lang.annotation.Annotation; public interface InteractionCheck { public boolean preExec(Object context, A annotation); - public boolean postExec(Object context, A annotation); + public void postExec(Object context, A annotation); } 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 aa06f0e..f9c5a1c 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 @@ -60,7 +60,8 @@ public abstract class InteractionMethod { public abstract InteractionType getType(); public void run(Object context) { - this.appliedChecks.forEach(x -> x.pre(context)); + if (this.appliedChecks.stream().filter(x -> !x.pre(context)).count() > 0) + return; method.setAccessible(true); try { diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java index b52efea..ed1339b 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestInteractionCheck.java @@ -28,11 +28,10 @@ public class TestInteractionCheck implements InteractionCheck Date: Mon, 2 Dec 2024 13:16:28 +0100 Subject: [PATCH 10/16] add Javacord PermissionCheck --- .../javacord/checks/PermissionCheck.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java new file mode 100644 index 0000000..8996ce4 --- /dev/null +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java @@ -0,0 +1,35 @@ +package net.tomatentum.marinara.wrapper.javacord.checks; + +import java.util.Optional; + +import org.javacord.api.entity.permission.PermissionType; +import org.javacord.api.entity.server.Server; +import org.javacord.api.interaction.InteractionBase; + +import net.tomatentum.marinara.checks.InteractionCheck; + +public class PermissionCheck implements InteractionCheck { + + public static @interface HasPermission { + public PermissionType[] value(); + } + + @Override + public boolean preExec(Object context, HasPermission annotation) { + throw new UnsupportedOperationException("Unimplemented method 'preExec'"); + } + + public boolean preExec(InteractionBase context, HasPermission annotation) { + Optional server = context.getServer(); + if (!server.isPresent()) + return false; + + return server.get().hasPermissions(context.getUser(), annotation.value()); + } + + @Override + public void postExec(Object context, HasPermission annotation) { + + } + +} -- 2.45.2 From aefd8a51a084fd937eee881a9dca69eb3aec5ef6 Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 2 Dec 2024 21:19:42 +0100 Subject: [PATCH 11/16] change wrong annotation class usage --- .../java/net/tomatentum/marinara/checks/AppliedCheck.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java index 44e4c8a..0383ac6 100644 --- a/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java +++ b/lib/src/main/java/net/tomatentum/marinara/checks/AppliedCheck.java @@ -14,7 +14,7 @@ public record AppliedCheck(InteractionCheck check, Annotation annotation) { .filter(x -> x.getName().equals("preExec")) .filter(x -> !x.isBridge()) .toArray(s -> new Method[s]); - Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.getClass()); + Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.annotationType()); method.setAccessible(true); try { return (boolean) method.invoke(check, context, annotation); @@ -29,7 +29,7 @@ public record AppliedCheck(InteractionCheck check, Annotation annotation) { .filter(x -> x.getName().equals("postExec")) .filter(x -> !x.isBridge()) .toArray(s -> new Method[s]); - Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.getClass()); + Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.annotationType()); method.setAccessible(true); try { method.invoke(check, context, annotation); -- 2.45.2 From 83a3efd4b8135242b7b5fe5655d7b504293b0019 Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 2 Dec 2024 21:19:58 +0100 Subject: [PATCH 12/16] add canRun check which i forgor --- .../net/tomatentum/marinara/registry/InteractionRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java index 443ea39..d1640d6 100644 --- a/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java +++ b/lib/src/main/java/net/tomatentum/marinara/registry/InteractionRegistry.java @@ -54,7 +54,7 @@ public class InteractionRegistry { public void handle(Object context) { interactionMethods.forEach((m) -> { InteractionType type = marinara.getWrapper().getInteractionType(context.getClass()); - if (m.getType().equals(type)) + if (m.getType().equals(type) && m.canRun(context)) m.run(context); }); } -- 2.45.2 From 7f471304614cdf012e45f31af0f8f0e775a4acb5 Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 2 Dec 2024 21:20:26 +0100 Subject: [PATCH 13/16] add missing annotation annotations --- .../marinara/wrapper/javacord/checks/PermissionCheck.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java index 8996ce4..0355541 100644 --- a/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java +++ b/wrapper/javacord/src/main/java/net/tomatentum/marinara/wrapper/javacord/checks/PermissionCheck.java @@ -1,5 +1,9 @@ package net.tomatentum.marinara.wrapper.javacord.checks; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Optional; import org.javacord.api.entity.permission.PermissionType; @@ -10,6 +14,8 @@ import net.tomatentum.marinara.checks.InteractionCheck; public class PermissionCheck implements InteractionCheck { + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) public static @interface HasPermission { public PermissionType[] value(); } -- 2.45.2 From 94da2a0e3caf6ff5092d96c2048d97e854032dce Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 2 Dec 2024 21:20:49 +0100 Subject: [PATCH 14/16] change logic to always give objects the highest value --- .../java/net/tomatentum/marinara/util/ReflectionUtil.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java index 4f80cff..2a65282 100644 --- a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java +++ b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java @@ -23,6 +23,10 @@ public final class ReflectionUtil { } public static int getCastDepth(Class child, Class parent) { + + if (parent.equals(Object.class)) + return Integer.MAX_VALUE; + if (!parent.isAssignableFrom(child)) { throw new IllegalArgumentException("The specified class is not a child class of the specified parent."); } @@ -77,7 +81,7 @@ public final class ReflectionUtil { Class currMostSpecific = null; for (Class currClass : classes) { int currCastDepth = getCastDepth(base, currClass); - if (currCastDepth < min) { + if (currCastDepth <= min) { min = currCastDepth; currMostSpecific = currClass; } -- 2.45.2 From 29bb7e667e22969e2dcb3db9b396af18ba2d6fe4 Mon Sep 17 00:00:00 2001 From: tueem Date: Mon, 2 Dec 2024 21:21:00 +0100 Subject: [PATCH 15/16] add PermissionCHeck test --- .../tomatentum/marinara/test/ButtonTest.java | 2 +- .../marinara/test/InteractionCheckTest.java | 20 ++++++++++++++++++- .../tomatentum/marinara/test/TestButton.java | 11 ++++++++++ .../test/mocks/ButtonInteractionMock.java | 8 +++++++- .../marinara/test/mocks/ServerMock.java | 10 ++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/ButtonTest.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/ButtonTest.java index b8ffa0f..db08352 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/ButtonTest.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/ButtonTest.java @@ -20,7 +20,7 @@ public class ButtonTest { 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 TestButton()); - wrapper.handleInteraction(new ButtonInteractionMock()); + wrapper.handleInteraction(new ButtonInteractionMock("test")); assertTrue(TestButton.didRun); } diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java index 95773f3..aed9f5a 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/InteractionCheckTest.java @@ -1,7 +1,9 @@ package net.tomatentum.marinara.test; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.javacord.api.entity.permission.PermissionType; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; @@ -9,8 +11,10 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import net.tomatentum.marinara.Marinara; import net.tomatentum.marinara.test.mocks.ButtonInteractionMock; import net.tomatentum.marinara.test.mocks.DiscordApiMock; +import net.tomatentum.marinara.test.mocks.ServerMock; import net.tomatentum.marinara.wrapper.LibraryWrapper; import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; +import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck; @TestInstance(Lifecycle.PER_CLASS) public class InteractionCheckTest { @@ -21,9 +25,23 @@ public class InteractionCheckTest { Marinara marinara = Marinara.load(wrapper); marinara.getCheckRegistry().addCheck(new TestInteractionCheck()); marinara.getRegistry().addInteractions(new TestButton()); - wrapper.handleInteraction(new ButtonInteractionMock()); + wrapper.handleInteraction(new ButtonInteractionMock("test")); assertTrue(TestInteractionCheck.preExecuted); assertTrue(TestInteractionCheck.postExecuted); } + @Test + public void testPermissionCheck() { + LibraryWrapper wrapper = new JavacordWrapper(new DiscordApiMock()); + Marinara marinara = Marinara.load(wrapper); + marinara.getCheckRegistry().addCheck(new PermissionCheck()); + marinara.getRegistry().addInteractions(new TestButton()); + wrapper.handleInteraction(new ButtonInteractionMock("permissionCheck")); + assertTrue(TestButton.didPermRun); + TestButton.didPermRun = false; + ServerMock.TESTPERMISSION = PermissionType.ATTACH_FILE; + wrapper.handleInteraction(new ButtonInteractionMock("permissionCheck")); + assertFalse(TestButton.didPermRun); + } + } diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java index eb9c9c8..23a7dbf 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/TestButton.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import org.javacord.api.entity.channel.TextChannel; import org.javacord.api.entity.message.Message; +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.ButtonInteraction; @@ -11,6 +12,7 @@ import org.javacord.api.interaction.ButtonInteraction; import net.tomatentum.marinara.interaction.InteractionHandler; import net.tomatentum.marinara.interaction.annotation.Button; import net.tomatentum.marinara.test.TestInteractionCheck.TestCheck; +import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck.HasPermission; public class TestButton implements InteractionHandler { @@ -27,5 +29,14 @@ public class TestButton implements InteractionHandler { didRun = true; System.out.println("Success!"); } + + public static boolean didPermRun = false; + + @Button("permissionCheck") + @HasPermission({PermissionType.ADMINISTRATOR}) + public void exec(ButtonInteraction interaction) { + didPermRun = true; + System.out.println("It worked!"); + } } diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ButtonInteractionMock.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ButtonInteractionMock.java index c9ee36b..3e3e8fd 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ButtonInteractionMock.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ButtonInteractionMock.java @@ -23,6 +23,12 @@ import org.javacord.api.interaction.callback.InteractionOriginalResponseUpdater; public class ButtonInteractionMock implements ButtonInteraction { + private String customId; + + public ButtonInteractionMock(String customId) { + this.customId = customId; + } + @Override public Message getMessage() { return new MessageMock(); @@ -30,7 +36,7 @@ public class ButtonInteractionMock implements ButtonInteraction { @Override public String getCustomId() { - return "test"; + return this.customId; } @Override diff --git a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ServerMock.java b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ServerMock.java index 5a0daaa..275c8ab 100644 --- a/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ServerMock.java +++ b/wrapper/javacord/src/test/java/net/tomatentum/marinara/test/mocks/ServerMock.java @@ -1,5 +1,7 @@ package net.tomatentum.marinara.test.mocks; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import java.time.Instant; import java.util.Collection; import java.util.EnumSet; @@ -27,6 +29,7 @@ import org.javacord.api.entity.channel.ServerTextChannel; import org.javacord.api.entity.channel.ServerThreadChannel; import org.javacord.api.entity.channel.ServerVoiceChannel; import org.javacord.api.entity.emoji.KnownCustomEmoji; +import org.javacord.api.entity.permission.PermissionType; import org.javacord.api.entity.permission.Role; import org.javacord.api.entity.server.ActiveThreads; import org.javacord.api.entity.server.Ban; @@ -2259,5 +2262,12 @@ public class ServerMock implements Server { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'getSystemChannelFlags'"); } + public static PermissionType TESTPERMISSION = PermissionType.ADMINISTRATOR; + @Override + public boolean hasPermissions(User user, PermissionType... type) { + assertNotNull(user); + assertNotNull(type); + return TESTPERMISSION.equals(type[0]); + } } -- 2.45.2 From bef34ee54849dcdb4e1012c642ad9983b98250f5 Mon Sep 17 00:00:00 2001 From: Tueem Date: Tue, 3 Dec 2024 20:20:57 +0100 Subject: [PATCH 16/16] fix null issue --- .../ExecutableSlashCommandDefinition.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 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 933a4a0..e110098 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 @@ -16,9 +16,18 @@ public record ExecutableSlashCommandDefinition( if (!(o instanceof ExecutableSlashCommandDefinition)) return false; ExecutableSlashCommandDefinition other = (ExecutableSlashCommandDefinition) o; - return other.applicationCommand.name().equals(this.applicationCommand.name()) && - other.subCommandGroup.name().equals(this.subCommandGroup.name()) && - other.subCommand.name().equals(this.subCommand.name()); + boolean equals = false; + + if (this.applicationCommand() != null && other.subCommandGroup() != null) + equals = this.applicationCommand.name().equals(other.applicationCommand().name()); + + if (this.subCommandGroup() != null && other.subCommandGroup() != null) + equals = this.subCommandGroup().name().equals(other.subCommandGroup().name()); + + if (this.subCommand() != null && other.subCommand() != null) + equals = this.subCommand().name().equals(other.subCommand().name()); + + return equals; } @Override -- 2.45.2