Compare commits
	
		
			16 Commits
		
	
	
		
			dev
			...
			83b446e6fb
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 83b446e6fb | |||
| ef9384336a | |||
| ebf5600e29 | |||
| 070319853a | |||
| 60ead419e2 | |||
| 0b7b607a23 | |||
| 991d1c047b | |||
| ec17952375 | |||
| 0114cffcbd | |||
| 450f1fdaa1 | |||
| 92540576df | |||
| 8a3cde52fd | |||
| 8495659364 | |||
| 0973016a74 | |||
| 0590789359 | |||
| 2647a1f0b4 | 
| @@ -8,7 +8,19 @@ allprojects { | |||||||
|     group = "net.tomatentum.Marinara" |     group = "net.tomatentum.Marinara" | ||||||
|     version = "1.0.0-RC1" + (if (!project.hasProperty("release")) ("-" + getGitHash()) else "") |     version = "1.0.0-RC1" + (if (!project.hasProperty("release")) ("-" + getGitHash()) else "") | ||||||
|     description = "A simple but powerful, library-agnostic Discord Interaction Wrapper." |     description = "A simple but powerful, library-agnostic Discord Interaction Wrapper." | ||||||
|  |     plugins.withType<JavaPlugin> { | ||||||
|  |         tasks.withType<Jar>().configureEach { | ||||||
|  |             archiveBaseName.set("marinara-" + archiveBaseName.get()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     repositories { | ||||||
|  |         // Use Maven Central for resolving dependencies. | ||||||
|  |         mavenCentral() | ||||||
|  |         maven { | ||||||
|  |             url = uri("https://git.tomatentum.net/api/packages/tueem/maven") | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| subprojects { | subprojects { | ||||||
|   | |||||||
| @@ -8,11 +8,14 @@ javacord = "3.8.0" | |||||||
| discord4j = "3.2.7" | discord4j = "3.2.7" | ||||||
| geantyref = "2.0.0" | geantyref = "2.0.0" | ||||||
| mockito = "5.15.2" | mockito = "5.15.2" | ||||||
|  | cutin = "0.1.1-cad019e" | ||||||
|  |  | ||||||
| [libraries] | [libraries] | ||||||
| junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } | junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } | ||||||
| slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j"} | slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j"} | ||||||
|  | slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j"} | ||||||
| javacord = { module = "org.javacord:javacord", version.ref = "javacord"} | javacord = { module = "org.javacord:javacord", version.ref = "javacord"} | ||||||
| discord4j = { module = "com.discord4j:discord4j-core", version.ref = "discord4j"} | discord4j = { module = "com.discord4j:discord4j-core", version.ref = "discord4j"} | ||||||
| geantyref = { module = "io.leangen.geantyref:geantyref", version.ref = "geantyref"} | geantyref = { module = "io.leangen.geantyref:geantyref", version.ref = "geantyref"} | ||||||
| mockito = {module = "org.mockito:mockito-core", version.ref = "mockito"} | mockito = {module = "org.mockito:mockito-core", version.ref = "mockito"} | ||||||
|  | cutin = {module = "net.tomatentum.cutin:lib-dev", version.ref = "cutin"} | ||||||
|   | |||||||
| @@ -10,11 +10,6 @@ plugins { | |||||||
|     `java-library` |     `java-library` | ||||||
| } | } | ||||||
|  |  | ||||||
| repositories { |  | ||||||
|     // Use Maven Central for resolving dependencies. |  | ||||||
|     mavenCentral() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|     // Use JUnit Jupiter for testing. |     // Use JUnit Jupiter for testing. | ||||||
|     testImplementation(libs.junit.jupiter) |     testImplementation(libs.junit.jupiter) | ||||||
| @@ -22,6 +17,7 @@ dependencies { | |||||||
|     testRuntimeOnly("org.junit.platform:junit-platform-launcher") |     testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||||||
|     implementation(libs.slf4j) |     implementation(libs.slf4j) | ||||||
|     implementation(libs.geantyref) |     implementation(libs.geantyref) | ||||||
|  |     api(libs.cutin) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Apply a specific Java toolchain to ease working on different environments. | // Apply a specific Java toolchain to ease working on different environments. | ||||||
|   | |||||||
| @@ -1,40 +1,85 @@ | |||||||
| package net.tomatentum.marinara; | package net.tomatentum.marinara; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import java.util.Arrays; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.registry.InteractionCheckRegistry; | import org.slf4j.Logger; | ||||||
| import net.tomatentum.marinara.registry.InteractionRegistry; | import org.slf4j.LoggerFactory; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
|  | import net.tomatentum.cutin.MethodExecutor; | ||||||
|  | import net.tomatentum.cutin.ProcessorMethodExecutor; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
|  | import net.tomatentum.marinara.container.InteractionCheckContainer; | ||||||
|  | import net.tomatentum.marinara.container.InteractionMethodContainer; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier; | ||||||
|  | import net.tomatentum.marinara.interaction.processor.AutocompleteInteractionProcessor; | ||||||
|  | import net.tomatentum.marinara.interaction.processor.DirectInteractionProcessor; | ||||||
|  | import net.tomatentum.marinara.util.ObjectAggregator; | ||||||
|  | import net.tomatentum.marinara.wrapper.IdentifierProvider; | ||||||
| import net.tomatentum.marinara.wrapper.LibraryWrapper; | import net.tomatentum.marinara.wrapper.LibraryWrapper; | ||||||
|  |  | ||||||
| public class Marinara { | public class Marinara { | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|      |      | ||||||
|     public static <T extends LibraryWrapper> Marinara load(LibraryWrapper wrapper) { |     public static Marinara load(LibraryWrapper wrapper) { | ||||||
|         return new Marinara(wrapper); |         return new Marinara(wrapper); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private InteractionRegistry registry; |  | ||||||
|     private InteractionCheckRegistry checkRegistry; |  | ||||||
|     private LibraryWrapper wrapper; |     private LibraryWrapper wrapper; | ||||||
|  |     private MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer; | ||||||
|  |     private MethodContainer<InteractionIdentifier, Object> interactionContainer; | ||||||
|  |     private MethodExecutor<Object> interactionExecutor; | ||||||
|  |  | ||||||
|     private Marinara(LibraryWrapper wrapper) { |     private Marinara(LibraryWrapper wrapper) { | ||||||
|         this.wrapper = wrapper; |         this.wrapper = wrapper; | ||||||
|         this.registry = new InteractionRegistry(this); |         this.checkContainer = new InteractionCheckContainer(); | ||||||
|         this.checkRegistry = new InteractionCheckRegistry(); |         this.interactionContainer = new InteractionMethodContainer(getCheckContainer(), getWrapper().getContextObjectProvider()); | ||||||
|  |         IdentifierProvider provider = wrapper.createIdentifierProvider(); | ||||||
|  |         ProcessorMethodExecutor<InteractionIdentifier, Object> exec = new ProcessorMethodExecutor<>(getInteractionContainer()); | ||||||
|  |         exec | ||||||
|  |             .addProcessor(new DirectInteractionProcessor(provider, InteractionType.COMMAND, InteractionType.BUTTON)) | ||||||
|  |             .addProcessor(new AutocompleteInteractionProcessor(getWrapper(), provider)); | ||||||
|  |         this.interactionExecutor = exec; | ||||||
|  |         wrapper.subscribeInteractions(this.interactionExecutor::handle); | ||||||
|         logger.info("Marinara loaded successfully!"); |         logger.info("Marinara loaded successfully!"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public InteractionRegistry getRegistry() { |     //TODO move to future interactionstructure module | ||||||
|         return registry; |     public void registerCommands() { | ||||||
|     } |         List<InteractionIdentifier> slashIdentifiers = getInteractionContainer().identifiers().stream() | ||||||
|  |             .filter(i -> i.type().equals(InteractionType.COMMAND)) | ||||||
|  |             .toList(); | ||||||
|  |  | ||||||
|     public InteractionCheckRegistry getCheckRegistry() { |         SlashCommandDefinition[] defs = new ObjectAggregator<InteractionIdentifier, RootCommandIdentifier, SlashCommandDefinition>( | ||||||
|         return checkRegistry; |             i -> Arrays.asList((RootCommandIdentifier)i.rootNode()), | ||||||
|  |             SlashCommandDefinition::addIdentifier, | ||||||
|  |             SlashCommandDefinition::new) | ||||||
|  |             .aggregate(slashIdentifiers) | ||||||
|  |             .toArray(SlashCommandDefinition[]::new); | ||||||
|  |  | ||||||
|  |         wrapper.getRegisterer().register(defs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public LibraryWrapper getWrapper() { |     public LibraryWrapper getWrapper() { | ||||||
|         return wrapper; |         return this.wrapper; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public MethodContainer<InteractionIdentifier, Object> getInteractionContainer() { | ||||||
|  |         return this.interactionContainer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MethodContainer<CheckMethodIdentifier, CheckExecutionContext> getCheckContainer() { | ||||||
|  |         return this.checkContainer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MethodExecutor<Object> getInteractionExecutor() { | ||||||
|  |         return interactionExecutor; | ||||||
|  |     } | ||||||
|  |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,50 +1,28 @@ | |||||||
| package net.tomatentum.marinara.checks; | package net.tomatentum.marinara.checks; | ||||||
|  |  | ||||||
| import java.lang.annotation.Annotation; | import java.lang.annotation.Annotation; | ||||||
| import java.lang.reflect.InvocationTargetException; |  | ||||||
| import java.lang.reflect.Method; |  | ||||||
| import java.util.Arrays; |  | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
| import net.tomatentum.marinara.util.ReflectionUtil; |  | ||||||
|  |  | ||||||
| public record AppliedCheck(InteractionCheck<?> check, Annotation annotation) { | public record AppliedCheck( | ||||||
|  |         Annotation annotation,  | ||||||
|  |         ReflectedMethod<CheckMethodIdentifier, CheckExecutionContext> preExec, | ||||||
|  |         ReflectedMethod<CheckMethodIdentifier, CheckExecutionContext> postExec | ||||||
|  |     ) { | ||||||
|  |  | ||||||
|     private static Logger logger = LoggerUtil.getLogger(AppliedCheck.class); |     private static Logger logger = LoggerFactory.getLogger(AppliedCheck.class);  | ||||||
|  |  | ||||||
|     public boolean pre(Object context) { |     public boolean pre(Object context) { | ||||||
|         Method[] methods = Arrays.stream(check.getClass().getMethods()) |         logger.debug("Running InteractionCheck preExec {} with annotation {}", preExec(), annotation()); | ||||||
|             .filter(x -> x.getName().equals("preExec")) |         return (boolean) preExec().run(new CheckExecutionContext(annotation, context)); | ||||||
|             .filter(x -> !x.isBridge()) |  | ||||||
|             .toArray(s -> new Method[s]); |  | ||||||
|         Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.annotationType()); |  | ||||||
|         method.setAccessible(true); |  | ||||||
|         try { |  | ||||||
|             logger.debug("Executing pre check {} with context {}", check.getClass().getName(), context.toString()); |  | ||||||
|             boolean result = (boolean) method.invoke(check, context, annotation); |  | ||||||
|             logger.debug("Pre Check {} {} with context {}", check.getClass().getName(), result ? "succeeded" : "failed", context.toString()); |  | ||||||
|             return result; |  | ||||||
|         } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { |  | ||||||
|             logger.error("Failed executing pre-check", e); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void post(Object context) { |     public void post(Object context) { | ||||||
|         Method[] methods = Arrays.stream(check.getClass().getMethods()) |         logger.debug("Running InteractionCheck postExec {} with annotation {}", postExec(), annotation()); | ||||||
|             .filter(x -> x.getName().equals("postExec")) |         postExec().run(new CheckExecutionContext(annotation, context)); | ||||||
|             .filter(x -> !x.isBridge()) |  | ||||||
|             .toArray(s -> new Method[s]); |  | ||||||
|         Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), annotation.annotationType()); |  | ||||||
|         method.setAccessible(true); |  | ||||||
|         try { |  | ||||||
|             logger.debug("Executing post check {} with context {}", check.getClass().getName(), context.toString()); |  | ||||||
|             method.invoke(check, context, annotation); |  | ||||||
|         } catch (IllegalAccessException | InvocationTargetException | SecurityException e) { |  | ||||||
|             logger.error("Failed executing post-check", e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | package net.tomatentum.marinara.checks; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.Annotation; | ||||||
|  |  | ||||||
|  | public record CheckExecutionContext(Annotation annotation, Object originalContext) { | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | package net.tomatentum.marinara.checks; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  |  | ||||||
|  | public record CheckMethodIdentifier(Type annotationType, CheckMethodType type) { | ||||||
|  |  | ||||||
|  |     public enum CheckMethodType { | ||||||
|  |         PRE("preExec"), | ||||||
|  |         POST("postExec"); | ||||||
|  |  | ||||||
|  |         private String methodName; | ||||||
|  |  | ||||||
|  |         private CheckMethodType(String methodName) { | ||||||
|  |             this.methodName = methodName; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public String methodName() { | ||||||
|  |             return this.methodName; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public final String toString() { | ||||||
|  |         return "InteractionCheck(%s, %s)".formatted(annotationType, type); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -0,0 +1,66 @@ | |||||||
|  | package net.tomatentum.marinara.checks; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.method.BestCandidateMethod; | ||||||
|  | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier.CheckMethodType; | ||||||
|  | import net.tomatentum.marinara.parser.InteractionCheckClassParser; | ||||||
|  |  | ||||||
|  | public class InteractionCheckMethod extends BestCandidateMethod<CheckMethodIdentifier, CheckExecutionContext> { | ||||||
|  |  | ||||||
|  |     private CheckMethodIdentifier identifier; | ||||||
|  |  | ||||||
|  |     public InteractionCheckMethod(String methodName, Object containingObject) { | ||||||
|  |         super(methodName, containingObject); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object getParameter(CheckExecutionContext context, int index) { | ||||||
|  |         switch (index) { | ||||||
|  |             case 0: | ||||||
|  |                 return context.originalContext(); | ||||||
|  |             case 1: | ||||||
|  |                 return context.annotation(); | ||||||
|  |             default: | ||||||
|  |                 return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public CheckMethodIdentifier identifier() { | ||||||
|  |         return this.identifier; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class InteractionCheckMethodFactory extends BestCandidateMethod.Factory<CheckMethodIdentifier, CheckExecutionContext> { | ||||||
|  |  | ||||||
|  |         private CheckMethodType type; | ||||||
|  |  | ||||||
|  |         public InteractionCheckMethodFactory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> methodContainer, CheckMethodType type) { | ||||||
|  |             super(methodContainer, type.methodName()); | ||||||
|  |             this.type = type; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @SuppressWarnings("unchecked") | ||||||
|  |         @Override | ||||||
|  |         public void addParser(ReflectedMethod<CheckMethodIdentifier, CheckExecutionContext> method, List<MethodParser> parsers) { | ||||||
|  |             parsers.add( | ||||||
|  |                 new InteractionCheckClassParser((Class<InteractionCheck<?>>) method.containingObject().getClass(), | ||||||
|  |                     a -> ((InteractionCheckMethod) method).identifier = new CheckMethodIdentifier(a, type)) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         protected Optional<BestCandidateMethod<CheckMethodIdentifier, CheckExecutionContext>> bcProduce(String methodName, | ||||||
|  |                 Object containingObject) { | ||||||
|  |             if (!(containingObject instanceof InteractionCheck)) | ||||||
|  |                 return Optional.empty(); | ||||||
|  |             return Optional.of(new InteractionCheckMethod(methodName, containingObject)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | package net.tomatentum.marinara.container; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.ReflectedMethodFactoryImpl; | ||||||
|  | import net.tomatentum.cutin.container.LoneMethodContainer; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier.CheckMethodType; | ||||||
|  | import net.tomatentum.marinara.checks.InteractionCheckMethod.InteractionCheckMethodFactory; | ||||||
|  |  | ||||||
|  | public class InteractionCheckContainer extends LoneMethodContainer<CheckMethodIdentifier, CheckExecutionContext> { | ||||||
|  |      | ||||||
|  |     public InteractionCheckContainer() { | ||||||
|  |         super(new ReflectedMethodFactoryImpl<>()); | ||||||
|  |         super.factory() | ||||||
|  |             .addFactory(new InteractionCheckMethodFactory(this, CheckMethodType.PRE)) | ||||||
|  |             .addFactory(new InteractionCheckMethodFactory(this, CheckMethodType.POST)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | package net.tomatentum.marinara.container; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.ReflectedMethodFactory; | ||||||
|  | import net.tomatentum.cutin.ReflectedMethodFactoryImpl; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.container.MultiMethodContainer; | ||||||
|  | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
|  | import net.tomatentum.marinara.interaction.components.methods.ButtonInteractionMethod; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
|  | import net.tomatentum.marinara.wrapper.ContextObjectProvider; | ||||||
|  | import net.tomatentum.marinara.interaction.methods.AutoCompleteInteractionMethod; | ||||||
|  | import net.tomatentum.marinara.interaction.methods.SlashCommandInteractionMethod; | ||||||
|  |  | ||||||
|  | public class InteractionMethodContainer extends MultiMethodContainer<InteractionIdentifier, Object> { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     private static ReflectedMethodFactory<InteractionIdentifier, Object> createFactory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkRegistry, ContextObjectProvider cop) { | ||||||
|  |         return new ReflectedMethodFactoryImpl<InteractionIdentifier, Object>() | ||||||
|  |             .addFactory(new AutoCompleteInteractionMethod.Factory(checkRegistry, cop)) | ||||||
|  |             .addFactory(new SlashCommandInteractionMethod.Factory(checkRegistry, cop)) | ||||||
|  |             .addFactory(new ButtonInteractionMethod.Factory(checkRegistry, cop)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public InteractionMethodContainer(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkRegistry, ContextObjectProvider cop) { | ||||||
|  |         super(createFactory(checkRegistry, cop)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public MethodContainer<InteractionIdentifier, Object> addMethod(ReflectedMethod<InteractionIdentifier, Object> method) { | ||||||
|  |         super.identifiers().stream() | ||||||
|  |             .filter(method.identifier()::equals) | ||||||
|  |             .forEach(i -> InteractionIdentifier.tryAddDescriptions(i, method.identifier())); | ||||||
|  |         return super.addMethod(method); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -8,5 +8,5 @@ import java.lang.annotation.Target; | |||||||
| @Target({ElementType.METHOD}) | @Target({ElementType.METHOD}) | ||||||
| @Retention(RetentionPolicy.RUNTIME) | @Retention(RetentionPolicy.RUNTIME) | ||||||
| public @interface AutoComplete { | public @interface AutoComplete { | ||||||
|      |     public String value(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import java.util.List; | |||||||
| import java.util.Set; | import java.util.Set; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.CommandChoices; | import net.tomatentum.marinara.interaction.commands.annotation.CommandChoices; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | ||||||
| @@ -14,7 +15,6 @@ import net.tomatentum.marinara.interaction.commands.choice.EnumChoices; | |||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier; | import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
|  |  | ||||||
| public class SlashCommandDefinition { | public class SlashCommandDefinition { | ||||||
|  |  | ||||||
| @@ -30,7 +30,7 @@ public class SlashCommandDefinition { | |||||||
|     private RootCommandIdentifier rootIdentifier; |     private RootCommandIdentifier rootIdentifier; | ||||||
|     private boolean isRootCommand; |     private boolean isRootCommand; | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     public SlashCommandDefinition(RootCommandIdentifier rootIdentifier) { |     public SlashCommandDefinition(RootCommandIdentifier rootIdentifier) { | ||||||
|         this.entries = new HashSet<>(); |         this.entries = new HashSet<>(); | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import java.lang.annotation.Retention; | |||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
| import java.lang.annotation.Target; | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | import net.tomatentum.marinara.interaction.annotation.AutoComplete; | ||||||
| import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | ||||||
|  |  | ||||||
| @Target({ElementType.ANNOTATION_TYPE}) | @Target({ElementType.ANNOTATION_TYPE}) | ||||||
| @@ -14,11 +15,11 @@ public @interface SlashCommandOption { | |||||||
|     public String description() default ""; |     public String description() default ""; | ||||||
|     public SlashCommandOptionType type() default SlashCommandOptionType.STRING; |     public SlashCommandOptionType type() default SlashCommandOptionType.STRING; | ||||||
|     public boolean required() default false; |     public boolean required() default false; | ||||||
|     public boolean autocomplete() default false; |     public AutoComplete[] autocompletes() default {}; | ||||||
|     public Range range() default @Range; |     public Range range() default @Range; | ||||||
|     public CommandChoices choices() default @CommandChoices; |     public CommandChoices choices() default @CommandChoices; | ||||||
|  |  | ||||||
|     public static enum PlaceHolderEnum { |     public enum PlaceHolderEnum { | ||||||
|  |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | package net.tomatentum.marinara.interaction.commands.option; | ||||||
|  |  | ||||||
|  | import java.util.Arrays; | ||||||
|  |  | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | ||||||
|  |  | ||||||
|  | public record AutocompleteOptionData(String name, Object input) { | ||||||
|  |      | ||||||
|  |     public String[] getAutocompleteRefs(SlashCommandOption[] options) { | ||||||
|  |         return Arrays.stream(options) | ||||||
|  |             .filter(o -> o.name().equals(this.name())) | ||||||
|  |             .flatMap(o -> Arrays.stream(o.autocompletes())) | ||||||
|  |             .map(a -> a.value()) | ||||||
|  |             .toArray(String[]::new); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,80 @@ | |||||||
|  | package net.tomatentum.marinara.interaction.components.methods; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Method; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionHandler; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
|  | import net.tomatentum.marinara.interaction.annotation.Button; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
|  | import net.tomatentum.marinara.interaction.methods.InteractionMethod; | ||||||
|  | import net.tomatentum.marinara.parser.ButtonParser; | ||||||
|  | import net.tomatentum.marinara.wrapper.ContextObjectProvider; | ||||||
|  |  | ||||||
|  | public class ButtonInteractionMethod extends InteractionMethod { | ||||||
|  |  | ||||||
|  |     private String customId; | ||||||
|  |     private ContextObjectProvider cop; | ||||||
|  |  | ||||||
|  |     private ButtonInteractionMethod(Method method, InteractionHandler handler, ContextObjectProvider cop) { | ||||||
|  |         super(method, handler); | ||||||
|  |         this.cop = cop; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object getParameter(Object context, int index) { | ||||||
|  |         Class<?> type = method().getParameterTypes()[index]; | ||||||
|  |         Object superResult = super.getParameter(context, index); | ||||||
|  |         if (superResult == null) | ||||||
|  |             return this.cop.getComponentContextObject(context, type); | ||||||
|  |         else | ||||||
|  |             return superResult; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public InteractionIdentifier identifier() { | ||||||
|  |         return InteractionIdentifier.builder() | ||||||
|  |             .name(customId) | ||||||
|  |             .description("Button") | ||||||
|  |             .type(InteractionType.BUTTON) | ||||||
|  |             .build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Factory extends InteractionMethod.Factory { | ||||||
|  |  | ||||||
|  |         private ContextObjectProvider cop; | ||||||
|  |  | ||||||
|  |         public Factory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer, ContextObjectProvider cop) { | ||||||
|  |             super(checkContainer); | ||||||
|  |             this.cop = cop; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public Optional<ReflectedMethod<InteractionIdentifier, Object>> produce(Method method, Object containingObject) { | ||||||
|  |             ButtonInteractionMethod rMethod = null; | ||||||
|  |             if (method.isAnnotationPresent(Button.class) && | ||||||
|  |                 (containingObject instanceof InteractionHandler iHandler) | ||||||
|  |                 ) | ||||||
|  |                 rMethod = new ButtonInteractionMethod(method, iHandler, this.cop); | ||||||
|  |  | ||||||
|  |             return Optional.ofNullable(rMethod); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void addParser(ReflectedMethod<InteractionIdentifier, Object> method, List<MethodParser> parser) { | ||||||
|  |             super.addParser(method, parser); | ||||||
|  |  | ||||||
|  |             parser.add( | ||||||
|  |                 new ButtonParser(method.method(), x -> ((ButtonInteractionMethod) method).customId = x) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -80,14 +80,19 @@ public class InteractionIdentifier { | |||||||
|             return false; |             return false; | ||||||
|         if (!name().equals(ident.name())) |         if (!name().equals(ident.name())) | ||||||
|             return false; |             return false; | ||||||
|         return Objects.equals(ident, obj); |         return Objects.equals(parent(), ident.parent()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(type(), name(), parent()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         if (parent() == null) |         if (parent() == null) | ||||||
|             return name(); |             return name() + " - " + type(); | ||||||
|         return "{}.{}".formatted(name(), parent().toString()); |         return "%s:%s".formatted(name(), parent().toString()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static class Builder { |     public static class Builder { | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ public class RootCommandIdentifier extends SlashCommandIdentifier { | |||||||
|         private String description; |         private String description; | ||||||
|         private SlashCommandOption[] options; |         private SlashCommandOption[] options; | ||||||
|         private long[] serverIds; |         private long[] serverIds; | ||||||
|  |         private String[] autocompleteRef; | ||||||
|  |  | ||||||
|         public InteractionIdentifier parent() { |         public InteractionIdentifier parent() { | ||||||
|             return parent; |             return parent; | ||||||
| @@ -75,12 +75,21 @@ public class RootCommandIdentifier extends SlashCommandIdentifier { | |||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SlashCommandIdentifier build(boolean autocomplete) { |         public String[] autocompleteRef() { | ||||||
|  |             return this.autocompleteRef; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder autocompleteRef(String[] autocompleteRef) { | ||||||
|  |             this.autocompleteRef = autocompleteRef; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public SlashCommandIdentifier build() { | ||||||
|             return new RootCommandIdentifier( |             return new RootCommandIdentifier( | ||||||
|                 parent,  |                 parent,  | ||||||
|                 name,  |                 name,  | ||||||
|                 description,  |                 description,  | ||||||
|                 autocomplete ? InteractionType.AUTOCOMPLETE : InteractionType.COMMAND,  |                 InteractionType.COMMAND,  | ||||||
|                 options,  |                 options,  | ||||||
|                 serverIds); |                 serverIds); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -12,8 +12,7 @@ public class SlashCommandIdentifier extends InteractionIdentifier { | |||||||
|             String name,  |             String name,  | ||||||
|             String description, |             String description, | ||||||
|             InteractionType type, |             InteractionType type, | ||||||
|             SlashCommandOption[] options |             SlashCommandOption[] options) { | ||||||
|             ) { |  | ||||||
|         super(parent, name, description, type); |         super(parent, name, description, type); | ||||||
|         this.options = options; |         this.options = options; | ||||||
|     } |     } | ||||||
| @@ -27,6 +26,7 @@ public class SlashCommandIdentifier extends InteractionIdentifier { | |||||||
|         private String name; |         private String name; | ||||||
|         private String description; |         private String description; | ||||||
|         private SlashCommandOption[] options; |         private SlashCommandOption[] options; | ||||||
|  |         private String[] autocompleteRef; | ||||||
|  |  | ||||||
|         public InteractionIdentifier parent() { |         public InteractionIdentifier parent() { | ||||||
|             return parent; |             return parent; | ||||||
| @@ -64,12 +64,21 @@ public class SlashCommandIdentifier extends InteractionIdentifier { | |||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SlashCommandIdentifier build(boolean autocomplete) { |         public String[] autocompleteRef() { | ||||||
|  |             return this.autocompleteRef; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder autocompleteRef(String[] autocompleteRef) { | ||||||
|  |             this.autocompleteRef = autocompleteRef; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public SlashCommandIdentifier build() { | ||||||
|             return new SlashCommandIdentifier( |             return new SlashCommandIdentifier( | ||||||
|                 parent,  |                 parent,  | ||||||
|                 name,  |                 name,  | ||||||
|                 description,  |                 description,  | ||||||
|                 autocomplete ? InteractionType.AUTOCOMPLETE : InteractionType.COMMAND,  |                 InteractionType.COMMAND,  | ||||||
|                 options); |                 options); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,44 +1,89 @@ | |||||||
| package net.tomatentum.marinara.interaction.methods; | package net.tomatentum.marinara.interaction.methods; | ||||||
|  |  | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.Marinara; | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.InteractionHandler; | import net.tomatentum.marinara.interaction.InteractionHandler; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
|  | import net.tomatentum.marinara.interaction.annotation.AutoComplete; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; | ||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.parser.AnnotationParser; | import net.tomatentum.marinara.parser.AutocompleteParser; | ||||||
| import net.tomatentum.marinara.parser.SlashCommandParser; | import net.tomatentum.marinara.wrapper.ContextObjectProvider; | ||||||
|  |  | ||||||
| public class AutoCompleteInteractionMethod extends InteractionMethod { | public class AutoCompleteInteractionMethod extends InteractionMethod { | ||||||
|  |  | ||||||
|     private InteractionIdentifier interactionIdentifier; |     private String autocompleteRef; | ||||||
|  |     private ContextObjectProvider cop; | ||||||
|  |  | ||||||
|     public AutoCompleteInteractionMethod(Method method,  |     private AutoCompleteInteractionMethod(Method method,  | ||||||
|         InteractionHandler handler,  |         InteractionHandler handler,  | ||||||
|         Marinara marinara |         ContextObjectProvider cop | ||||||
|         ) { |         ) { | ||||||
|         super(method, handler, marinara); |         super(method, handler); | ||||||
|     } |         this.cop = cop; | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public AnnotationParser[] parsers() { |  | ||||||
|         return new AnnotationParser[] {  |  | ||||||
|             new SlashCommandParser(method, true, (x) -> { this.interactionIdentifier = x; } )  |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Object getParameter(Object context, int index) { |     public Object getParameter(Object context, int index) { | ||||||
|         Class<?> type = method().getParameterTypes()[index+1]; |         Class<?> type = method().getParameterTypes()[index]; | ||||||
|         Object autocompleteOptionValue = marinara.getWrapper().getContextObjectProvider().getAutocompleteFocusedOption(context); |         Object contextObject = this.cop.getInteractionContextObject(context, type); | ||||||
|         if (autocompleteOptionValue != null) |         if (contextObject != null) | ||||||
|  |             return contextObject; | ||||||
|  |  | ||||||
|  |         Object autocompleteOptionValue = this.cop.getAutocompleteFocusedOption(context).input(); | ||||||
|  |         if (type.isInstance(autocompleteOptionValue)) | ||||||
|             return autocompleteOptionValue; |             return autocompleteOptionValue; | ||||||
|  |  | ||||||
|         return marinara.getWrapper().getContextObjectProvider().getInteractionContextObject(context, type); |         return super.getParameter(context, index); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public InteractionIdentifier identifier() { |     public InteractionIdentifier identifier() { | ||||||
|         return interactionIdentifier; |         return InteractionIdentifier.builder() | ||||||
|  |             .type(InteractionType.AUTOCOMPLETE) | ||||||
|  |             .name(autocompleteRef) | ||||||
|  |             .description("AUTOCOMPLETE") | ||||||
|  |             .build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Factory extends InteractionMethod.Factory { | ||||||
|  |  | ||||||
|  |         private ContextObjectProvider cop; | ||||||
|  |  | ||||||
|  |         public Factory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer, ContextObjectProvider cop) { | ||||||
|  |             super(checkContainer); | ||||||
|  |             this.cop = cop; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public Optional<ReflectedMethod<InteractionIdentifier, Object>> produce(Method method, Object containingObject) { | ||||||
|  |             AutoCompleteInteractionMethod rMethod = null; | ||||||
|  |             if ((containingObject instanceof InteractionHandler iHandler) && | ||||||
|  |                 method.isAnnotationPresent(AutoComplete.class) && | ||||||
|  |                 !(method.isAnnotationPresent(SlashCommand.class) || | ||||||
|  |                 method.isAnnotationPresent(SubCommand.class))) | ||||||
|  |                 rMethod = new AutoCompleteInteractionMethod(method, iHandler, cop); | ||||||
|  |  | ||||||
|  |             return Optional.ofNullable(rMethod); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void addParser(ReflectedMethod<InteractionIdentifier, Object> method, List<MethodParser> parser) { | ||||||
|  |             super.addParser(method, parser); | ||||||
|  |  | ||||||
|  |             parser.add( | ||||||
|  |                 new AutocompleteParser(method.method(), x -> ((AutoCompleteInteractionMethod) method).autocompleteRef = x[0]) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,42 +0,0 @@ | |||||||
| 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.ident.InteractionIdentifier; |  | ||||||
| import net.tomatentum.marinara.parser.AnnotationParser; |  | ||||||
| import net.tomatentum.marinara.parser.ButtonParser; |  | ||||||
|  |  | ||||||
| public class ButtonInteractionMethod extends InteractionMethod { |  | ||||||
|  |  | ||||||
|     private String customId; |  | ||||||
|  |  | ||||||
|     ButtonInteractionMethod(Method method, InteractionHandler handler, Marinara marinara) { |  | ||||||
|         super(method, handler, marinara); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public AnnotationParser[] parsers() { |  | ||||||
|         return new AnnotationParser[] { |  | ||||||
|             new ButtonParser(method, (x) -> { this.customId = x; } ) |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public Object getParameter(Object context, int index) { |  | ||||||
|         Class<?> type = method().getParameterTypes()[index+1]; |  | ||||||
|         return marinara.getWrapper().getContextObjectProvider().getComponentContextObject(context, type); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public InteractionIdentifier identifier() { |  | ||||||
|         return InteractionIdentifier.builder() |  | ||||||
|             .name(customId) |  | ||||||
|             .description("Button") |  | ||||||
|             .type(InteractionType.BUTTON) |  | ||||||
|             .build(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,105 +1,72 @@ | |||||||
| package net.tomatentum.marinara.interaction.methods; | package net.tomatentum.marinara.interaction.methods; | ||||||
|  |  | ||||||
| import java.lang.reflect.InvocationTargetException; |  | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
| import java.security.InvalidParameterException; |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.ReflectedMethodFactory; | ||||||
| import net.tomatentum.marinara.Marinara; | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
| import net.tomatentum.marinara.checks.AppliedCheck; | import net.tomatentum.marinara.checks.AppliedCheck; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.InteractionHandler; | import net.tomatentum.marinara.interaction.InteractionHandler; | ||||||
| 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; |  | ||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.parser.AnnotationParser; |  | ||||||
| import net.tomatentum.marinara.parser.InteractionCheckParser; | import net.tomatentum.marinara.parser.InteractionCheckParser; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
| import net.tomatentum.marinara.util.ReflectionUtil; |  | ||||||
|  |  | ||||||
| public abstract class InteractionMethod { | public abstract class InteractionMethod extends ReflectedMethod<InteractionIdentifier, Object> { | ||||||
|  |  | ||||||
|     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)) |  | ||||||
|             return new ButtonInteractionMethod(method, handler, marinara); |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected Method method; |  | ||||||
|     protected InteractionHandler handler; |  | ||||||
|     protected Marinara marinara; |  | ||||||
|     protected List<AnnotationParser> parsers; |  | ||||||
|     protected List<AppliedCheck> appliedChecks; |     protected List<AppliedCheck> appliedChecks; | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |  | ||||||
|  |  | ||||||
|     protected InteractionMethod( |     protected InteractionMethod( | ||||||
|         Method method,  |         Method method,  | ||||||
|         InteractionHandler handler,  |         InteractionHandler handler | ||||||
|         Marinara marinara |  | ||||||
|         ) { |         ) { | ||||||
|         if (!Arrays.asList(handler.getClass().getMethods()).contains(method)) |         super(method, handler); | ||||||
|             throw new InvalidParameterException("Method does not apply to specified handler"); |  | ||||||
|  |  | ||||||
|         this.method = method; |  | ||||||
|         this.handler = handler; |  | ||||||
|         this.marinara = marinara; |  | ||||||
|         this.parsers = new ArrayList<>(Arrays.asList(parsers())); |  | ||||||
|         this.appliedChecks = new ArrayList<>(); |         this.appliedChecks = new ArrayList<>(); | ||||||
|  |  | ||||||
|         parsers.add(new InteractionCheckParser(method, appliedChecks::add, marinara.getCheckRegistry())); |  | ||||||
|  |  | ||||||
|         parsers.stream().forEach(AnnotationParser::parse); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void run(Object context) { |     @Override | ||||||
|  |     public Object getParameter(Object context, int index) { | ||||||
|  |         if (index == 0) | ||||||
|  |             return context; | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @Override | ||||||
|  |     public Object run(Object context) { | ||||||
|  |         Object result = null; | ||||||
|         if (this.appliedChecks.stream().filter(x -> !x.pre(context)).count() > 0) |         if (this.appliedChecks.stream().filter(x -> !x.pre(context)).count() > 0) | ||||||
|             return; |             return null; | ||||||
|  |  | ||||||
|         method.setAccessible(true); |         result = super.run(context); | ||||||
|         try { |  | ||||||
|             method.invoke(handler, getParameters(context)); |  | ||||||
|         }catch (IllegalAccessException | InvocationTargetException ex) { |  | ||||||
|             logger.error("InteractionMethod failed to run", ex); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.appliedChecks.forEach(x -> x.post(context)); |         this.appliedChecks.forEach(x -> x.post(context)); | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public abstract AnnotationParser[] parsers(); |     public List<AppliedCheck> appliedChecks() { | ||||||
|  |         return this.appliedChecks; | ||||||
|     public abstract Object getParameter(Object context, int index); |  | ||||||
|  |  | ||||||
|     public abstract InteractionIdentifier identifier(); |  | ||||||
|  |  | ||||||
|     public Method method() { |  | ||||||
|         return method; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Object[] getParameters(Object context) { |     public abstract static class Factory implements ReflectedMethodFactory.Factory<InteractionIdentifier, Object> { | ||||||
|         int parameterCount = method.getParameterCount(); |  | ||||||
|         List<Object> parameters = new ArrayList<>(); |  | ||||||
|  |  | ||||||
|         for (int i = 0; i < parameterCount; i++) { |         private MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer; | ||||||
|             Object parameter; |  | ||||||
|             if (i == 0) { |  | ||||||
|                 parameter = context; |  | ||||||
|             }else |  | ||||||
|                 parameter = getParameter(context, i-1); |  | ||||||
|  |  | ||||||
|             logger.trace("Found parameter {}={} for method {}", parameter != null ? parameter.getClass().toString() : " ", parameter, ReflectionUtil.getFullMethodName(method)); |         protected Factory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer) { | ||||||
|             parameters.add(parameter);    |             this.checkContainer = checkContainer; | ||||||
|         } |         } | ||||||
|         return parameters.toArray(); |  | ||||||
|  |         @Override | ||||||
|  |         public void addParser(ReflectedMethod<InteractionIdentifier, Object> method, List<MethodParser> parser) { | ||||||
|  |             InteractionMethod imethod = (InteractionMethod) method; | ||||||
|  |             parser.add( | ||||||
|  |                 new InteractionCheckParser(method.method(), imethod.appliedChecks::add, this.checkContainer) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,32 +1,39 @@ | |||||||
| package net.tomatentum.marinara.interaction.methods; | package net.tomatentum.marinara.interaction.methods; | ||||||
|  |  | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.Marinara; | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.method.ReflectedMethod; | ||||||
|  | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
|  | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.InteractionHandler; | import net.tomatentum.marinara.interaction.InteractionHandler; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; | ||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | ||||||
| import net.tomatentum.marinara.parser.AnnotationParser; |  | ||||||
| import net.tomatentum.marinara.parser.SlashCommandParser; | import net.tomatentum.marinara.parser.SlashCommandParser; | ||||||
|  | import net.tomatentum.marinara.wrapper.ContextObjectProvider; | ||||||
|  |  | ||||||
| public class SlashCommandInteractionMethod extends InteractionMethod { | public class SlashCommandInteractionMethod extends InteractionMethod { | ||||||
|  |  | ||||||
|     private SlashCommandIdentifier interactionIdentifier; |     private SlashCommandIdentifier interactionIdentifier; | ||||||
|  |     private ContextObjectProvider cop; | ||||||
|  |  | ||||||
|     SlashCommandInteractionMethod(Method method, InteractionHandler handler, Marinara marinara) { |     private SlashCommandInteractionMethod(Method method, InteractionHandler handler, ContextObjectProvider cop) { | ||||||
|         super(method, handler, marinara); |         super(method, handler); | ||||||
|     } |         this.cop = cop; | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public AnnotationParser[] parsers() { |  | ||||||
|         return new AnnotationParser[] {  |  | ||||||
|             new SlashCommandParser(method, false, (x) -> { this.interactionIdentifier = x; } )  |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Object getParameter(Object context, int index) { |     public Object getParameter(Object context, int index) { | ||||||
|         return marinara.getWrapper().getContextObjectProvider().convertCommandOption(context, interactionIdentifier.options()[index].name()); |         Object superResult = super.getParameter(context, index); | ||||||
|  |         if (superResult == null) | ||||||
|  |             return this.cop.convertCommandOption(context, interactionIdentifier.options()[index-1].name()); | ||||||
|  |         else | ||||||
|  |             return superResult; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -34,4 +41,34 @@ public class SlashCommandInteractionMethod extends InteractionMethod { | |||||||
|         return interactionIdentifier; |         return interactionIdentifier; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static class Factory extends InteractionMethod.Factory { | ||||||
|  |  | ||||||
|  |         private ContextObjectProvider cop; | ||||||
|  |  | ||||||
|  |         public Factory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer, ContextObjectProvider cop) { | ||||||
|  |             super(checkContainer); | ||||||
|  |             this.cop = cop; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public Optional<ReflectedMethod<InteractionIdentifier, Object>> produce(Method method, Object containingObject) { | ||||||
|  |             SlashCommandInteractionMethod rMethod = null; | ||||||
|  |             if ((containingObject instanceof InteractionHandler iHandler) &&  | ||||||
|  |                 (method.isAnnotationPresent(SlashCommand.class) || | ||||||
|  |                 method.isAnnotationPresent(SubCommand.class))) | ||||||
|  |                 rMethod = new SlashCommandInteractionMethod(method, iHandler, cop); | ||||||
|  |             return Optional.ofNullable(rMethod); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void addParser(ReflectedMethod<InteractionIdentifier, Object> method, List<MethodParser> parser) { | ||||||
|  |             super.addParser(method, parser); | ||||||
|  |  | ||||||
|  |             parser.add( | ||||||
|  |                 new SlashCommandParser(method.method(), x -> ((SlashCommandInteractionMethod) method).interactionIdentifier = x) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,54 @@ | |||||||
|  | package net.tomatentum.marinara.interaction.processor; | ||||||
|  |  | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Objects; | ||||||
|  | import java.util.Optional; | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | ||||||
|  | import net.tomatentum.marinara.wrapper.IdentifierProvider; | ||||||
|  | import net.tomatentum.marinara.wrapper.LibraryWrapper; | ||||||
|  |  | ||||||
|  | public class AutocompleteInteractionProcessor extends InteractionMethodProcessor { | ||||||
|  |  | ||||||
|  |     private LibraryWrapper wrapper; | ||||||
|  |  | ||||||
|  |     public AutocompleteInteractionProcessor(LibraryWrapper wrapper, IdentifierProvider provider) { | ||||||
|  |         super(provider, Set.of(InteractionType.AUTOCOMPLETE)); | ||||||
|  |         this.wrapper = wrapper; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void processInteraction(Object context, MethodContainer<InteractionIdentifier, Object> container, InteractionIdentifier identifier) { | ||||||
|  |         Optional<InteractionIdentifier> oIdent = container.identifiers().stream() | ||||||
|  |             .filter(i -> convertToCommandIdentifier(identifier).equals(i)) | ||||||
|  |             .findFirst(); | ||||||
|  |         if (oIdent.isPresent() && oIdent.get() instanceof SlashCommandIdentifier sIdent) { | ||||||
|  |             List<String> autocompleteRefs = Arrays.asList(this.wrapper.getContextObjectProvider() | ||||||
|  |                 .getAutocompleteFocusedOption(context).getAutocompleteRefs(sIdent.options())); | ||||||
|  |             List<Object> results = container.methods().stream() | ||||||
|  |                 .filter(m -> m.identifier().type().equals(InteractionType.AUTOCOMPLETE)) | ||||||
|  |                 .filter(m -> autocompleteRefs.contains(m.identifier().name())) | ||||||
|  |                 .map(m -> m.run(context)) | ||||||
|  |                 .filter(Objects::nonNull) | ||||||
|  |                 .toList(); | ||||||
|  |             if (!results.isEmpty()) | ||||||
|  |                 this.wrapper.respondAutocomplete(context, results); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private InteractionIdentifier convertToCommandIdentifier(InteractionIdentifier identifier) { | ||||||
|  |         if (Objects.isNull(identifier)) | ||||||
|  |             return null; | ||||||
|  |         return InteractionIdentifier.builder() | ||||||
|  |             .type(InteractionType.COMMAND) | ||||||
|  |             .name(identifier.name()) | ||||||
|  |             .parent(convertToCommandIdentifier(identifier.parent())) | ||||||
|  |             .build(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | package net.tomatentum.marinara.interaction.processor; | ||||||
|  |  | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
|  | import net.tomatentum.marinara.wrapper.IdentifierProvider; | ||||||
|  |  | ||||||
|  | public class DirectInteractionProcessor extends InteractionMethodProcessor { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public DirectInteractionProcessor(IdentifierProvider provider, InteractionType... types) { | ||||||
|  |         super(provider, Set.of(types)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void processInteraction(Object context, MethodContainer<InteractionIdentifier, Object> container, InteractionIdentifier identifier) { | ||||||
|  |         container.findFor(identifier).forEach(m -> m.run(context)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | package net.tomatentum.marinara.interaction.processor; | ||||||
|  |  | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodProcessor; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
|  | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
|  | import net.tomatentum.marinara.wrapper.IdentifierProvider; | ||||||
|  |  | ||||||
|  | public abstract class InteractionMethodProcessor implements MethodProcessor<InteractionIdentifier, Object> { | ||||||
|  |  | ||||||
|  |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|  |     private IdentifierProvider provider; | ||||||
|  |     private Set<InteractionType> types; | ||||||
|  |  | ||||||
|  |     protected InteractionMethodProcessor(IdentifierProvider provider, Set<InteractionType> types) { | ||||||
|  |         this.provider = provider; | ||||||
|  |         this.types = types; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void process(Object context, MethodContainer<InteractionIdentifier, Object> container) { | ||||||
|  |         InteractionIdentifier identifier = this.provider.provide(context); | ||||||
|  |         if (!this.types.contains(identifier.type())) return; | ||||||
|  |         logger.debug("Processing {} : {} with context {}", identifier, identifier.type(), context); | ||||||
|  |         this.processInteraction(context, container, identifier); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected abstract void processInteraction( | ||||||
|  |         Object context,  | ||||||
|  |         MethodContainer<InteractionIdentifier, Object> container,  | ||||||
|  |         InteractionIdentifier identifier); | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| package net.tomatentum.marinara.parser; |  | ||||||
|  |  | ||||||
| import java.lang.reflect.Method; |  | ||||||
|  |  | ||||||
| public interface AnnotationParser { |  | ||||||
|     void parse(); |  | ||||||
|     Method getMethod(); |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | package net.tomatentum.marinara.parser; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Method; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.util.ReflectionUtil; | ||||||
|  | import net.tomatentum.marinara.interaction.annotation.AutoComplete; | ||||||
|  |  | ||||||
|  | public class AutocompleteParser implements MethodParser { | ||||||
|  |  | ||||||
|  |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|  |     private Method method; | ||||||
|  |     private Consumer<String[]> consumer; | ||||||
|  |  | ||||||
|  |     public AutocompleteParser(Method method, Consumer<String[]> consumer) { | ||||||
|  |         this.method = method; | ||||||
|  |         this.consumer = consumer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void parse() { | ||||||
|  |         String[] autocompletes = Arrays.stream(this.method.getAnnotationsByType(AutoComplete.class)) | ||||||
|  |             .map(AutoComplete::value) | ||||||
|  |             .toArray(String[]::new); | ||||||
|  |         logger.trace("Parsed AutoComplete annotation {} for method {}", autocompletes, ReflectionUtil.getFullMethodName(method)); | ||||||
|  |         this.consumer.accept(autocompletes); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -4,17 +4,18 @@ import java.lang.reflect.Method; | |||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.util.ReflectionUtil; | ||||||
| import net.tomatentum.marinara.interaction.annotation.Button; | import net.tomatentum.marinara.interaction.annotation.Button; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
| import net.tomatentum.marinara.util.ReflectionUtil; |  | ||||||
|  |  | ||||||
| public class ButtonParser implements AnnotationParser { | public class ButtonParser implements MethodParser { | ||||||
|      |      | ||||||
|     private Method method; |     private Method method; | ||||||
|     private Consumer<String> consumer; |     private Consumer<String> consumer; | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     public ButtonParser(Method method, Consumer<String> consumer) { |     public ButtonParser(Method method, Consumer<String> consumer) { | ||||||
|         this.method = method; |         this.method = method; | ||||||
| @@ -23,14 +24,9 @@ public class ButtonParser implements AnnotationParser { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void parse() { |     public void parse() { | ||||||
|         Button button = getMethod().getAnnotation(Button.class); |         Button button = this.method.getAnnotation(Button.class); | ||||||
|         logger.trace("Parsed Button annotation {} for method {}", button.toString(), ReflectionUtil.getFullMethodName(method)); |         logger.trace("Parsed Button annotation {} for method {}", button, ReflectionUtil.getFullMethodName(method)); | ||||||
|         this.consumer.accept(button.value()); |         this.consumer.accept(button.value()); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     @Override |  | ||||||
|     public Method getMethod() { |  | ||||||
|         return this.method; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | package net.tomatentum.marinara.parser; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.ParameterizedType; | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import io.leangen.geantyref.GenericTypeReflector; | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.marinara.checks.InteractionCheck; | ||||||
|  |  | ||||||
|  | public class InteractionCheckClassParser implements MethodParser { | ||||||
|  |  | ||||||
|  |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|  |     private Class<? extends InteractionCheck<?>> interactionCheckType; | ||||||
|  |     private Consumer<Type> annotationTypeConsumer; | ||||||
|  |  | ||||||
|  |     public InteractionCheckClassParser(Class<? extends InteractionCheck<?>> interactionCheckType, Consumer<Type> annotationTypeConsumer) { | ||||||
|  |         this.interactionCheckType = interactionCheckType; | ||||||
|  |         this.annotationTypeConsumer = annotationTypeConsumer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void parse() { | ||||||
|  |         ParameterizedType type = (ParameterizedType) GenericTypeReflector.getExactSuperType(interactionCheckType, InteractionCheck.class); | ||||||
|  |         Type typeParam = type.getActualTypeArguments()[0]; | ||||||
|  |         logger.trace("Parsed InteractionCheck Annotation {}", typeParam); | ||||||
|  |         this.annotationTypeConsumer.accept(typeParam); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
| @@ -3,27 +3,29 @@ package net.tomatentum.marinara.parser; | |||||||
| import java.lang.annotation.Annotation; | import java.lang.annotation.Annotation; | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Optional; |  | ||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.container.MethodContainer; | ||||||
|  | import net.tomatentum.cutin.util.ReflectionUtil; | ||||||
| import net.tomatentum.marinara.checks.AppliedCheck; | import net.tomatentum.marinara.checks.AppliedCheck; | ||||||
| import net.tomatentum.marinara.checks.InteractionCheck; | import net.tomatentum.marinara.checks.CheckExecutionContext; | ||||||
| import net.tomatentum.marinara.registry.InteractionCheckRegistry; | import net.tomatentum.marinara.checks.CheckMethodIdentifier; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; | import net.tomatentum.marinara.checks.CheckMethodIdentifier.CheckMethodType; | ||||||
| import net.tomatentum.marinara.util.ReflectionUtil; |  | ||||||
|  |  | ||||||
| public class InteractionCheckParser implements AnnotationParser { | public class InteractionCheckParser implements MethodParser { | ||||||
|  |  | ||||||
|     private InteractionCheckRegistry checkRegistry; |     private MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer; | ||||||
|     private Method method; |     private Method method; | ||||||
|     private Consumer<AppliedCheck> consumer; |     private Consumer<AppliedCheck> consumer; | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     public InteractionCheckParser(Method method, Consumer<AppliedCheck> consumer, InteractionCheckRegistry checkRegistry) { |     public InteractionCheckParser(Method method, Consumer<AppliedCheck> consumer, MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer) { | ||||||
|         this.checkRegistry = checkRegistry; |         this.checkContainer = checkContainer; | ||||||
|         this.method = method; |         this.method = method; | ||||||
|         this.consumer = consumer; |         this.consumer = consumer; | ||||||
|     } |     } | ||||||
| @@ -35,17 +37,13 @@ public class InteractionCheckParser implements AnnotationParser { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void convertAnnotation(Annotation annotation) { |     private void convertAnnotation(Annotation annotation) { | ||||||
|             Optional<InteractionCheck<?>> check = this.checkRegistry.getCheckFromAnnotation(annotation.annotationType()); |             var preExec = this.checkContainer.findFirstFor(new CheckMethodIdentifier(annotation.annotationType(), CheckMethodType.PRE)); | ||||||
|             if (check.isPresent())  { |             var postExec = this.checkContainer.findFirstFor(new CheckMethodIdentifier(annotation.annotationType(), CheckMethodType.POST)); | ||||||
|                 AppliedCheck appliedCheck = new AppliedCheck(check.get(), annotation); |             if (preExec.isPresent() && postExec.isPresent())  { | ||||||
|                 logger.trace("Parsed InteractionCheck {} for annotation {} for method {}", check.getClass().getName(), annotation.toString(), ReflectionUtil.getFullMethodName(method)); |                 AppliedCheck appliedCheck = new AppliedCheck(annotation, preExec.get(), postExec.get()); | ||||||
|  |                 logger.trace("Parsed InteractionCheck {} for annotation {} for method {}", preExec.get().containingObject(), annotation, ReflectionUtil.getFullMethodName(method)); | ||||||
|                 consumer.accept(appliedCheck); |                 consumer.accept(appliedCheck); | ||||||
|             } |             } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     @Override |  | ||||||
|     public Method getMethod() { |  | ||||||
|         return this.method; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,27 +4,26 @@ import java.lang.reflect.Method; | |||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import net.tomatentum.cutin.MethodParser; | ||||||
|  | import net.tomatentum.cutin.util.ReflectionUtil; | ||||||
| import net.tomatentum.marinara.interaction.InteractionType; | import net.tomatentum.marinara.interaction.InteractionType; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; | import net.tomatentum.marinara.interaction.commands.annotation.SubCommand; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; | import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup; | ||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
| import net.tomatentum.marinara.util.ReflectionUtil; |  | ||||||
|  |  | ||||||
| public class SlashCommandParser implements AnnotationParser { | public class SlashCommandParser implements MethodParser { | ||||||
|  |  | ||||||
|     private Method method; |     private Method method; | ||||||
|     private boolean isAutoComplete; |  | ||||||
|     private Consumer<SlashCommandIdentifier> consumer; |     private Consumer<SlashCommandIdentifier> consumer; | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     public SlashCommandParser(Method method, boolean isAutoComplete, Consumer<SlashCommandIdentifier> consumer) { |     public SlashCommandParser(Method method, Consumer<SlashCommandIdentifier> consumer) { | ||||||
|         this.method = method; |         this.method = method; | ||||||
|         this.isAutoComplete = isAutoComplete; |  | ||||||
|         this.consumer = consumer; |         this.consumer = consumer; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -38,14 +37,14 @@ public class SlashCommandParser implements AnnotationParser { | |||||||
|             .description(cmd.description()) |             .description(cmd.description()) | ||||||
|             .options(cmd.options()) |             .options(cmd.options()) | ||||||
|             .serverIds(cmd.serverIds()) |             .serverIds(cmd.serverIds()) | ||||||
|             .build(isAutoComplete); |             .build(); | ||||||
|  |  | ||||||
|         if (ReflectionUtil.isAnnotationPresent(method, SubCommandGroup.class)) { |         if (ReflectionUtil.isAnnotationPresent(method, SubCommandGroup.class)) { | ||||||
|             SubCommandGroup cmdGroup = ReflectionUtil.getAnnotation(method, SubCommandGroup.class); |             SubCommandGroup cmdGroup = ReflectionUtil.getAnnotation(method, SubCommandGroup.class); | ||||||
|             lastIdentifier = InteractionIdentifier.builder() |             lastIdentifier = InteractionIdentifier.builder() | ||||||
|                 .name(cmdGroup.name()) |                 .name(cmdGroup.name()) | ||||||
|                 .description(cmdGroup.description()) |                 .description(cmdGroup.description()) | ||||||
|                 .type(isAutoComplete ? InteractionType.AUTOCOMPLETE : InteractionType.COMMAND) |                 .type(InteractionType.COMMAND) | ||||||
|                 .parent(lastIdentifier) |                 .parent(lastIdentifier) | ||||||
|                 .build(); |                 .build(); | ||||||
|         } |         } | ||||||
| @@ -56,18 +55,13 @@ public class SlashCommandParser implements AnnotationParser { | |||||||
|                 .name(subCmd.name()) |                 .name(subCmd.name()) | ||||||
|                 .description(subCmd.description()) |                 .description(subCmd.description()) | ||||||
|                 .options(subCmd.options()) |                 .options(subCmd.options()) | ||||||
|                 .build(isAutoComplete); |                 .build(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         logger.trace("Parsed using SlashCommandParser for method {} with the result: {}", ReflectionUtil.getFullMethodName(method), lastIdentifier.toString()); |         logger.trace("Parsed using SlashCommandParser for method {} with the result: {}", ReflectionUtil.getFullMethodName(method), lastIdentifier); | ||||||
|         consumer.accept((SlashCommandIdentifier) lastIdentifier); |         consumer.accept((SlashCommandIdentifier) lastIdentifier); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public Method getMethod() { |  | ||||||
|        return this.method; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void checkValidCommandMethod(Method method) { |     private void checkValidCommandMethod(Method method) { | ||||||
|         if (method.isAnnotationPresent(SlashCommand.class) &&  |         if (method.isAnnotationPresent(SlashCommand.class) &&  | ||||||
|             method.getDeclaringClass().isAnnotationPresent(SlashCommand.class)) { |             method.getDeclaringClass().isAnnotationPresent(SlashCommand.class)) { | ||||||
|   | |||||||
| @@ -1,40 +0,0 @@ | |||||||
| package net.tomatentum.marinara.registry; |  | ||||||
|  |  | ||||||
| import java.lang.reflect.ParameterizedType; |  | ||||||
| import java.lang.reflect.Type; |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Optional; |  | ||||||
|  |  | ||||||
| import org.slf4j.Logger; |  | ||||||
|  |  | ||||||
| import io.leangen.geantyref.GenericTypeReflector; |  | ||||||
| import net.tomatentum.marinara.checks.InteractionCheck; |  | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
|  |  | ||||||
| public class InteractionCheckRegistry { |  | ||||||
|      |  | ||||||
|     private List<InteractionCheck<?>> checks; |  | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |  | ||||||
|  |  | ||||||
|     public InteractionCheckRegistry() { |  | ||||||
|         this.checks = new ArrayList<>(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void addCheck(InteractionCheck<?> check) { |  | ||||||
|         checks.add(check); |  | ||||||
|         logger.info("Registered Check {}", check.getClass().getName()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public Optional<InteractionCheck<?>> getCheckFromAnnotation(Type annotation) { |  | ||||||
|         for (InteractionCheck<?> interactionCheck : checks) { |  | ||||||
|             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(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,65 +0,0 @@ | |||||||
| package net.tomatentum.marinara.registry; |  | ||||||
|  |  | ||||||
| import java.util.HashSet; |  | ||||||
| import java.util.Set; |  | ||||||
|  |  | ||||||
| import org.slf4j.Logger; |  | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.interaction.InteractionType; |  | ||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; |  | ||||||
| import net.tomatentum.marinara.interaction.methods.InteractionMethod; |  | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
|  |  | ||||||
| public class InteractionEntry { |  | ||||||
|     private InteractionIdentifier identifier; |  | ||||||
|     private Set<InteractionMethod> methods; |  | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |  | ||||||
|  |  | ||||||
|     public InteractionEntry(InteractionIdentifier identifier) { |  | ||||||
|         this.identifier = identifier; |  | ||||||
|         this.methods = new HashSet<>(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public InteractionEntry addMethod(InteractionMethod method) { |  | ||||||
|         if (!method.identifier().equals(this.identifier)) |  | ||||||
|             throw new IllegalArgumentException("Method's identifier did not match the entry's identifier"); |  | ||||||
|  |  | ||||||
|         this.methods.add(method); |  | ||||||
|         InteractionIdentifier.tryAddDescriptions(identifier, method.identifier()); |  | ||||||
|         return this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void runAll(Object context) { |  | ||||||
|         this.methods.stream().forEach(x -> { |  | ||||||
|             logger.debug("Running Method {} from {} with context {}", x.toString(), this.toString(), context.toString()); |  | ||||||
|             x.run(context); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean equals(Object obj) { |  | ||||||
|         if (!(obj instanceof InteractionEntry)) |  | ||||||
|             return false; |  | ||||||
|         InteractionEntry other = (InteractionEntry) obj; |  | ||||||
|         return other.identifier().equals(identifier()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public String toString() { |  | ||||||
|         return "InteractionEntry(%s)".formatted(identifier().toString()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public InteractionType type() { |  | ||||||
|         return identifier().type(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public InteractionIdentifier identifier() { |  | ||||||
|         return identifier; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public Set<InteractionMethod> methods() { |  | ||||||
|         return methods; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| package net.tomatentum.marinara.registry; |  | ||||||
|  |  | ||||||
| import java.lang.reflect.Method; |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.HashSet; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Optional; |  | ||||||
| import java.util.Set; |  | ||||||
|  |  | ||||||
| import org.slf4j.Logger; |  | ||||||
|  |  | ||||||
| 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.ident.InteractionIdentifier; |  | ||||||
| import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier; |  | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
| import net.tomatentum.marinara.util.ObjectAggregator; |  | ||||||
| import net.tomatentum.marinara.wrapper.IdentifierProvider; |  | ||||||
| import net.tomatentum.marinara.interaction.methods.InteractionMethod; |  | ||||||
|  |  | ||||||
| public class InteractionRegistry { |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |  | ||||||
|     private Set<InteractionEntry> interactions; |  | ||||||
|     private Marinara marinara; |  | ||||||
|     private IdentifierProvider identifierProvider; |  | ||||||
|  |  | ||||||
|     public InteractionRegistry(Marinara marinara) { |  | ||||||
|         this.interactions = new HashSet<>(); |  | ||||||
|         this.marinara = marinara; |  | ||||||
|         this.identifierProvider = marinara.getWrapper().createIdentifierProvider(); |  | ||||||
|         marinara.getWrapper().subscribeInteractions(this::handle); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * TODO: Maybe relocate InteractionEntry checking to another class with description merging. |  | ||||||
|      */ |  | ||||||
|     public void addInteractions(InteractionHandler interactionHandler) { |  | ||||||
|         for (Method method : interactionHandler.getClass().getMethods()) { |  | ||||||
|             InteractionMethod iMethod = InteractionMethod.create(method, interactionHandler, marinara); |  | ||||||
|             if (iMethod != null) { |  | ||||||
|                 Optional<InteractionEntry> oentry = this.interactions.stream() |  | ||||||
|                     .filter(i -> i.identifier().equals(iMethod.identifier())) |  | ||||||
|                     .findFirst(); |  | ||||||
|  |  | ||||||
|                 InteractionEntry entry = oentry.orElse(new InteractionEntry(iMethod.identifier())).addMethod(iMethod); |  | ||||||
|                 if (oentry.isEmpty()) this.interactions.add(entry); |  | ||||||
|                 logger.debug("Added {} method from {}", iMethod.method().getName(), interactionHandler.getClass().getSimpleName()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         logger.info("Added all Interactions from {}", interactionHandler.getClass().getSimpleName()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void registerCommands() { |  | ||||||
|         List<InteractionIdentifier> slashIdentifiers = interactions.stream() |  | ||||||
|             .filter((x) -> x.type().equals(InteractionType.COMMAND)) |  | ||||||
|             .map((x) -> x.identifier()) |  | ||||||
|             .toList(); |  | ||||||
|  |  | ||||||
|         SlashCommandDefinition[] defs = new ObjectAggregator<InteractionIdentifier, RootCommandIdentifier, SlashCommandDefinition>( |  | ||||||
|             i -> Arrays.asList((RootCommandIdentifier)i.rootNode()), |  | ||||||
|             SlashCommandDefinition::addIdentifier, |  | ||||||
|             SlashCommandDefinition::new) |  | ||||||
|             .aggregate(slashIdentifiers) |  | ||||||
|             .toArray(SlashCommandDefinition[]::new); |  | ||||||
|  |  | ||||||
|         marinara.getWrapper().getRegisterer().register(defs); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void handle(Object context) { |  | ||||||
|         logger.debug("Received {} interaction ", context); |  | ||||||
|         interactions.forEach((e) -> { |  | ||||||
|             if (this.identifierProvider.provide(context).equals(e.identifier())) { |  | ||||||
|                 logger.info("Running {} interaction using {}\ncontext: {}", e.type(), e.toString(), context.toString()); |  | ||||||
|                 e.runAll(context); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| package net.tomatentum.marinara.util; |  | ||||||
|  |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| import org.slf4j.helpers.NOPLoggerFactory; |  | ||||||
|  |  | ||||||
| public class LoggerUtil { |  | ||||||
|     public static Logger getLogger(String name) { |  | ||||||
|         if (LoggerFactory.getILoggerFactory() instanceof NOPLoggerFactory) |  | ||||||
|             return new SimpleLogger(name); |  | ||||||
|         return LoggerFactory.getLogger(name); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static Logger getLogger(Class<?> clazz) { |  | ||||||
|         return getLogger(clazz.getName()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,107 +0,0 @@ | |||||||
| 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 { |  | ||||||
|  |  | ||||||
|     public static <T extends Annotation> boolean isAnnotationPresent(Method method, Class<T> annotationClass) { |  | ||||||
|         if (method.isAnnotationPresent(annotationClass) || method.getDeclaringClass().isAnnotationPresent(annotationClass)) |  | ||||||
|             return true; |  | ||||||
|  |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static <T extends Annotation> T getAnnotation(Method method, Class<T> annotationClass) { |  | ||||||
|         return method.isAnnotationPresent(annotationClass) ?  |  | ||||||
|             method.getAnnotation(annotationClass) :  |  | ||||||
|             method.getDeclaringClass().getAnnotation(annotationClass); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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."); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         int depth = 0; |  | ||||||
|         Class<?> curr = child; |  | ||||||
|         List<Class<?>> 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<Method> 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; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static String getFullMethodName(Method method) { |  | ||||||
|         return method.getDeclaringClass().getName() + "." + method.getName(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| package net.tomatentum.marinara.util; |  | ||||||
|  |  | ||||||
| import org.slf4j.Marker; |  | ||||||
| import org.slf4j.event.Level; |  | ||||||
| import org.slf4j.helpers.LegacyAbstractLogger; |  | ||||||
| import org.slf4j.helpers.MessageFormatter; |  | ||||||
|  |  | ||||||
| public class SimpleLogger extends LegacyAbstractLogger { |  | ||||||
|  |  | ||||||
|     private String name; |  | ||||||
|  |  | ||||||
|     public SimpleLogger(String name) { |  | ||||||
|         this.name = name; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isTraceEnabled() { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isDebugEnabled() { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isInfoEnabled() { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isWarnEnabled() { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isErrorEnabled() { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected String getFullyQualifiedCallerName() { |  | ||||||
|         return this.name; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, |  | ||||||
|             Throwable throwable) { |  | ||||||
|         String formatted = MessageFormatter.basicArrayFormat(messagePattern, arguments); |  | ||||||
|         System.out.println("[%s] %s => %s".formatted(level, this.name, formatted)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|      |  | ||||||
| } |  | ||||||
| @@ -5,6 +5,7 @@ import java.util.Arrays; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; | import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | ||||||
| @@ -12,7 +13,6 @@ import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptio | |||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier; | import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier; | ||||||
| import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
|  |  | ||||||
| public class CommandConverter<A extends Object, O extends Object, C extends Object> { | public class CommandConverter<A extends Object, O extends Object, C extends Object> { | ||||||
|  |  | ||||||
| @@ -20,7 +20,7 @@ public class CommandConverter<A extends Object, O extends Object, C extends Obje | |||||||
|         return new CommandConverter<>(spec); |         return new CommandConverter<>(spec); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     private Spec<A, O, C> spec; |     private Spec<A, O, C> spec; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,9 +5,9 @@ import java.util.Set; | |||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; | import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
| import net.tomatentum.marinara.util.ObjectAggregator; | import net.tomatentum.marinara.util.ObjectAggregator; | ||||||
|  |  | ||||||
| public class CommandRegisterer<A extends Object> { | public class CommandRegisterer<A extends Object> { | ||||||
| @@ -16,7 +16,7 @@ public class CommandRegisterer<A extends Object> { | |||||||
|         return new CommandRegisterer<A>(strategy, converter); |         return new CommandRegisterer<A>(strategy, converter); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     private Strategy<A> strategy; |     private Strategy<A> strategy; | ||||||
|     private CommandConverter<A, ?, ?> converter; |     private CommandConverter<A, ?, ?> converter; | ||||||
| @@ -29,13 +29,17 @@ public class CommandRegisterer<A extends Object> { | |||||||
|     public void register(SlashCommandDefinition[] slashDefs) { |     public void register(SlashCommandDefinition[] slashDefs) { | ||||||
|         Set<ServerCommandList<A>> serverCommands = new ObjectAggregator<SlashCommandDefinition, Long, ServerCommandList<A>>( |         Set<ServerCommandList<A>> serverCommands = new ObjectAggregator<SlashCommandDefinition, Long, ServerCommandList<A>>( | ||||||
|                 def -> Arrays.stream(def.serverIds()).boxed().toList(), |                 def -> Arrays.stream(def.serverIds()).boxed().toList(), | ||||||
|                 (l, o) -> l.add(converter.convert(o)), |                 (l, o) -> { | ||||||
|  |                     logger.debug("Added {} for server ({}) registration.", o.rootIdentifier(), l.serverId()); | ||||||
|  |                     l.add(converter.convert(o)); | ||||||
|  |                 }, | ||||||
|                 ServerCommandList::new) |                 ServerCommandList::new) | ||||||
|             .aggregate(Arrays.asList(slashDefs)).stream() |             .aggregate(Arrays.asList(slashDefs)).stream() | ||||||
|             .collect(Collectors.toSet()); |             .collect(Collectors.toSet()); | ||||||
|  |  | ||||||
|         Set<A> globalCommands = Arrays.stream(slashDefs) |         Set<A> globalCommands = Arrays.stream(slashDefs) | ||||||
|             .filter(x -> x.serverIds().length <= 0) |             .filter(x -> x.serverIds().length <= 0) | ||||||
|  |             .peek(c -> logger.debug("Added {} for global registration.", c.rootIdentifier())) | ||||||
|             .map(converter::convert) |             .map(converter::convert) | ||||||
|             .collect(Collectors.toSet()); |             .collect(Collectors.toSet()); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| package net.tomatentum.marinara.wrapper; | package net.tomatentum.marinara.wrapper; | ||||||
|  |  | ||||||
|  | import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData; | ||||||
|  |  | ||||||
| public interface ContextObjectProvider { | public interface ContextObjectProvider { | ||||||
|  |  | ||||||
|     public Object convertCommandOption(Object context, String optionName); |     public Object convertCommandOption(Object context, String optionName); | ||||||
| @@ -7,5 +9,5 @@ public interface ContextObjectProvider { | |||||||
|     public Object getComponentContextObject(Object context, Class<?> type); |     public Object getComponentContextObject(Object context, Class<?> type); | ||||||
|     public Object getInteractionContextObject(Object context, Class<?> type); |     public Object getInteractionContextObject(Object context, Class<?> type); | ||||||
|  |  | ||||||
|     public Object getAutocompleteFocusedOption(Object context); |     public AutocompleteOptionData getAutocompleteFocusedOption(Object context); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,11 +8,11 @@ import java.util.List; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import io.leangen.geantyref.GenericTypeReflector; | import io.leangen.geantyref.GenericTypeReflector; | ||||||
|  | import net.tomatentum.cutin.util.ReflectionUtil; | ||||||
| import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | import net.tomatentum.marinara.interaction.ident.InteractionIdentifier; | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; |  | ||||||
| import net.tomatentum.marinara.util.ReflectionUtil; |  | ||||||
|  |  | ||||||
| public class IdentifierProvider { | public class IdentifierProvider { | ||||||
|  |  | ||||||
| @@ -20,8 +20,9 @@ public class IdentifierProvider { | |||||||
|         return new IdentifierProvider(Arrays.asList(converter)); |         return new IdentifierProvider(Arrays.asList(converter)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
|  |  | ||||||
|     private Map<Class<?>, Converter<?>> converter; |     private Map<Class<?>, Converter<?>> converter; | ||||||
|     private Logger logger = LoggerUtil.getLogger(getClass()); |  | ||||||
|  |  | ||||||
|     private IdentifierProvider(List<Converter<?>> converter) { |     private IdentifierProvider(List<Converter<?>> converter) { | ||||||
|         this.converter = new HashMap<>(); |         this.converter = new HashMap<>(); | ||||||
| @@ -29,7 +30,7 @@ public class IdentifierProvider { | |||||||
|             if (conv.getClass().getName().contains("$$Lambda")) |             if (conv.getClass().getName().contains("$$Lambda")) | ||||||
|                 throw new IllegalArgumentException("Lambdas cannot be used for IdentifierConverter because of Type erasure."); |                 throw new IllegalArgumentException("Lambdas cannot be used for IdentifierConverter because of Type erasure."); | ||||||
|             Type type = GenericTypeReflector.getExactSuperType(conv.getClass(), Converter.class); |             Type type = GenericTypeReflector.getExactSuperType(conv.getClass(), Converter.class); | ||||||
|             Type parameterType = (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0]; |             Type parameterType = ((ParameterizedType) type).getActualTypeArguments()[0]; | ||||||
|             if (!(parameterType instanceof Class)) |             if (!(parameterType instanceof Class)) | ||||||
|                 throw new IllegalArgumentException("Only full Class types are supported by IdentiferConverters"); |                 throw new IllegalArgumentException("Only full Class types are supported by IdentiferConverters"); | ||||||
|             this.converter.put((Class<?>) parameterType, conv); |             this.converter.put((Class<?>) parameterType, conv); | ||||||
| @@ -42,12 +43,14 @@ public class IdentifierProvider { | |||||||
|             context.getClass()); |             context.getClass()); | ||||||
|  |  | ||||||
|         if (type == null) |         if (type == null) | ||||||
|             logger.debug("No Identifier converter found for context {}", context.getClass().toString()); |             logger.debug("No Identifier converter found for context {}", context.getClass()); | ||||||
|  |  | ||||||
|         @SuppressWarnings("unchecked") |         @SuppressWarnings("unchecked") | ||||||
|         Converter<Object> conv = (Converter<Object>) converter.get(type); |         Converter<Object> conv = (Converter<Object>) converter.get(type); | ||||||
|  |  | ||||||
|         return conv.convert(context); |         InteractionIdentifier result = conv.convert(context); | ||||||
|  |         logger.trace("Converted {} to {} using {}", context, result, conv); | ||||||
|  |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @FunctionalInterface |     @FunctionalInterface | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ public abstract class LibraryWrapper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void handleInteraction(Object context) { |     public void handleInteraction(Object context) { | ||||||
|         interactionSubscriber.forEach((o) -> o.accept(context)); |         interactionSubscriber.forEach(o -> o.accept(context)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void subscribeInteractions(Consumer<Object> consumer) { |     public void subscribeInteractions(Consumer<Object> consumer) { | ||||||
| @@ -25,5 +25,6 @@ public abstract class LibraryWrapper { | |||||||
|     public abstract CommandRegisterer<?> getRegisterer();   |     public abstract CommandRegisterer<?> getRegisterer();   | ||||||
|     public abstract IdentifierProvider createIdentifierProvider(); |     public abstract IdentifierProvider createIdentifierProvider(); | ||||||
|     public abstract ContextObjectProvider getContextObjectProvider(); |     public abstract ContextObjectProvider getContextObjectProvider(); | ||||||
|  |     public abstract void respondAutocomplete(Object context, List<Object> options); | ||||||
|  |  | ||||||
| } | } | ||||||
| @@ -10,11 +10,6 @@ plugins { | |||||||
|     `java-library` |     `java-library` | ||||||
| } | } | ||||||
|  |  | ||||||
| repositories { |  | ||||||
|     // Use Maven Central for resolving dependencies. |  | ||||||
|     mavenCentral() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|     // Use JUnit Jupiter for testing. |     // Use JUnit Jupiter for testing. | ||||||
|     testImplementation(libs.junit.jupiter) |     testImplementation(libs.junit.jupiter) | ||||||
| @@ -22,6 +17,8 @@ dependencies { | |||||||
|     testImplementation(libs.discord4j) |     testImplementation(libs.discord4j) | ||||||
|  |  | ||||||
|     testRuntimeOnly("org.junit.platform:junit-platform-launcher") |     testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||||||
|  |     testImplementation(libs.slf4j.simple) | ||||||
|  |  | ||||||
|     implementation(libs.slf4j) |     implementation(libs.slf4j) | ||||||
|     implementation(libs.discord4j) { |     implementation(libs.discord4j) { | ||||||
|         exclude(module="discord4j-voice") |         exclude(module="discord4j-voice") | ||||||
|   | |||||||
| @@ -5,7 +5,9 @@ import java.util.List; | |||||||
| import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent; | import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent; | ||||||
| import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; | import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; | ||||||
| import discord4j.core.event.domain.interaction.ComponentInteractionEvent; | import discord4j.core.event.domain.interaction.ComponentInteractionEvent; | ||||||
|  | import discord4j.core.event.domain.interaction.InteractionCreateEvent; | ||||||
| import discord4j.core.object.command.ApplicationCommandInteractionOption; | import discord4j.core.object.command.ApplicationCommandInteractionOption; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData; | ||||||
| import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | ||||||
| import net.tomatentum.marinara.wrapper.ContextObjectProvider; | import net.tomatentum.marinara.wrapper.ContextObjectProvider; | ||||||
|  |  | ||||||
| @@ -79,24 +81,25 @@ public class Discord4JContextObjectProvider implements ContextObjectProvider { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Object getInteractionContextObject(Object context, Class<?> type) { |     public Object getInteractionContextObject(Object context, Class<?> type) { | ||||||
|         ComponentInteractionEvent componentInteractionEvent = (ComponentInteractionEvent) context; |         InteractionCreateEvent interactionEvent = (InteractionCreateEvent) context; | ||||||
|         switch (type.getName()) { |         switch (type.getName()) { | ||||||
|             case "discord4j.core.object.entity.channel.MessageChannel": |             case "discord4j.core.object.entity.channel.MessageChannel": | ||||||
|                 return componentInteractionEvent.getInteraction().getChannel().block(); |                 return interactionEvent.getInteraction().getChannel().block(); | ||||||
|             case "discord4j.core.object.entity.Guild": |             case "discord4j.core.object.entity.Guild": | ||||||
|                 return componentInteractionEvent.getInteraction().getGuild().block(); |                 return interactionEvent.getInteraction().getGuild().block(); | ||||||
|             case "discord4j.core.object.entity.Member": |             case "discord4j.core.object.entity.Member": | ||||||
|                 return componentInteractionEvent.getInteraction().getMember().orElse(null); |                 return interactionEvent.getInteraction().getMember().orElse(null); | ||||||
|             case "discord4j.core.object.entity.User": |             case "discord4j.core.object.entity.User": | ||||||
|                 return componentInteractionEvent.getInteraction().getUser(); |                 return interactionEvent.getInteraction().getUser(); | ||||||
|         } |             default: | ||||||
|                 return null; |                 return null; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Object getAutocompleteFocusedOption(Object context) { |     public AutocompleteOptionData getAutocompleteFocusedOption(Object context) { | ||||||
|         ChatInputAutoCompleteEvent interaction = (ChatInputAutoCompleteEvent) context; |         ApplicationCommandInteractionOption option = ((ChatInputAutoCompleteEvent) context).getFocusedOption(); | ||||||
|         return getOptionValue(interaction.getFocusedOption()); |         return new AutocompleteOptionData(option.getName(), getOptionValue(option)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -55,10 +55,10 @@ public class Discord4JConverterSpec implements CommandConverter.Spec<Application | |||||||
|             .name(option.name()) |             .name(option.name()) | ||||||
|             .description(option.description()) |             .description(option.description()) | ||||||
|             .required(option.required()) |             .required(option.required()) | ||||||
|             .autocomplete(option.autocomplete()) |             .autocomplete(option.autocompletes().length > 0) | ||||||
| 			.minLength(Double.valueOf(option.range().min()).intValue()) | 			.minLength((int) option.range().min()) | ||||||
| 			.minValue(option.range().min()) | 			.minValue(option.range().min()) | ||||||
| 			.maxLength(Double.valueOf(option.range().max()).intValue()) | 			.maxLength((int)option.range().max()) | ||||||
| 			.maxValue(option.range().max()) | 			.maxValue(option.range().max()) | ||||||
|             .choices(choices) |             .choices(choices) | ||||||
|             .build(); |             .build(); | ||||||
|   | |||||||
| @@ -1,14 +1,16 @@ | |||||||
| package net.tomatentum.marinara.wrapper.discord4j; | package net.tomatentum.marinara.wrapper.discord4j; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.function.Function; | import java.util.function.UnaryOperator; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  |  | ||||||
| import discord4j.core.GatewayDiscordClient; | import discord4j.core.GatewayDiscordClient; | ||||||
|  | import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent; | ||||||
| import discord4j.core.event.domain.interaction.InteractionCreateEvent; | import discord4j.core.event.domain.interaction.InteractionCreateEvent; | ||||||
| import discord4j.core.object.command.ApplicationCommandInteractionOption; | import discord4j.core.object.command.ApplicationCommandInteractionOption; | ||||||
| import discord4j.core.object.command.ApplicationCommandOption.Type; | import discord4j.core.object.command.ApplicationCommandOption.Type; | ||||||
|  | import discord4j.discordjson.json.ApplicationCommandOptionChoiceData; | ||||||
| import discord4j.discordjson.json.ApplicationCommandRequest; | import discord4j.discordjson.json.ApplicationCommandRequest; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.util.LoggerUtil; | import net.tomatentum.marinara.util.LoggerUtil; | ||||||
| @@ -22,13 +24,12 @@ import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.ButtonIdent | |||||||
| import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.SlashCommandIdentifierConverter; | import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.SlashCommandIdentifierConverter; | ||||||
|  |  | ||||||
| public class Discord4JWrapper extends LibraryWrapper { | public class Discord4JWrapper extends LibraryWrapper { | ||||||
|  |     public static final UnaryOperator<List<ApplicationCommandInteractionOption>> SUB_FILTER = i -> | ||||||
|     public static final Function<List<ApplicationCommandInteractionOption>, List<ApplicationCommandInteractionOption>> SUB_FILTER = (i) -> |  | ||||||
|         i.stream() |         i.stream() | ||||||
|             .filter(o -> o.getType().equals(Type.SUB_COMMAND) || o.getType().equals(Type.SUB_COMMAND_GROUP)) |             .filter(o -> o.getType().equals(Type.SUB_COMMAND) || o.getType().equals(Type.SUB_COMMAND_GROUP)) | ||||||
|             .toList(); |             .toList(); | ||||||
|      |      | ||||||
|     public static final Function<List<ApplicationCommandInteractionOption>, List<ApplicationCommandInteractionOption>> ARG_FILTER = (i) -> |     public static final UnaryOperator<List<ApplicationCommandInteractionOption>> ARG_FILTER = i -> | ||||||
|             i.stream() |             i.stream() | ||||||
|                 .filter(o -> !o.getType().equals(Type.SUB_COMMAND) && !o.getType().equals(Type.SUB_COMMAND_GROUP)) |                 .filter(o -> !o.getType().equals(Type.SUB_COMMAND) && !o.getType().equals(Type.SUB_COMMAND_GROUP)) | ||||||
|                 .toList(); |                 .toList(); | ||||||
| @@ -71,4 +72,15 @@ public class Discord4JWrapper extends LibraryWrapper { | |||||||
|         return this.contextObjectProvider; |         return this.contextObjectProvider; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void respondAutocomplete(Object context, List<Object> options) { | ||||||
|  |         if (context instanceof ChatInputAutoCompleteEvent event) { | ||||||
|  |             List<ApplicationCommandOptionChoiceData> choices = options.stream() | ||||||
|  |                 .filter(ApplicationCommandOptionChoiceData.class::isInstance) | ||||||
|  |                 .map(o -> (ApplicationCommandOptionChoiceData)o) | ||||||
|  |                 .toList(); | ||||||
|  |             event.respondWithSuggestions(choices); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,26 +13,28 @@ public class AutocompleteIdentifierConverter implements IdentifierProvider.Conve | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public InteractionIdentifier convert(ChatInputAutoCompleteEvent context) { |     public InteractionIdentifier convert(ChatInputAutoCompleteEvent context) { | ||||||
|         List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions()); |         InteractionIdentifier last = InteractionIdentifier.builder() | ||||||
|         String commandName = context.getCommandName(); |             .type(InteractionType.AUTOCOMPLETE) | ||||||
|  |             .name(context.getCommandName()) | ||||||
|  |             .build(); | ||||||
|  |  | ||||||
|  |         List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions()); | ||||||
|         if (!options.isEmpty()) { |         if (!options.isEmpty()) { | ||||||
|             List<ApplicationCommandInteractionOption> sub_options = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions()); |             last = InteractionIdentifier.builder() | ||||||
|             if (!sub_options.isEmpty()) |                 .type(InteractionType.AUTOCOMPLETE) | ||||||
|                 return InteractionIdentifier.createHierarchy( |                 .name(options.getFirst().getName()) | ||||||
|                     InteractionType.AUTOCOMPLETE,  |                 .parent(last) | ||||||
|                     commandName,  |                 .build(); | ||||||
|                     options.getFirst().getName(), |  | ||||||
|                     sub_options.getFirst().getName()); |             List<ApplicationCommandInteractionOption> subOptions = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions()); | ||||||
|             else |             if (!subOptions.isEmpty()) | ||||||
|                 return InteractionIdentifier.createHierarchy( |                 last = InteractionIdentifier.builder() | ||||||
|                     InteractionType.AUTOCOMPLETE,  |                     .type(InteractionType.AUTOCOMPLETE) | ||||||
|                     commandName,  |                     .name(subOptions.getFirst().getName()) | ||||||
|                     options.getFirst().getName()); |                     .parent(last) | ||||||
|         }else |                     .build(); | ||||||
|             return InteractionIdentifier.createHierarchy( |         } | ||||||
|                 InteractionType.AUTOCOMPLETE,  |         return last; | ||||||
|                 commandName);  |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,27 +13,28 @@ public class SlashCommandIdentifierConverter implements IdentifierProvider.Conve | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public InteractionIdentifier convert(ChatInputInteractionEvent context) { |     public InteractionIdentifier convert(ChatInputInteractionEvent context) { | ||||||
|  |         InteractionIdentifier last = InteractionIdentifier.builder() | ||||||
|  |             .type(InteractionType.COMMAND) | ||||||
|  |             .name(context.getCommandName()) | ||||||
|  |             .build(); | ||||||
|  |  | ||||||
|         List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions()); |         List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions()); | ||||||
|         String commandName = context.getCommandName(); |  | ||||||
|  |  | ||||||
|         if (!options.isEmpty()) { |         if (!options.isEmpty()) { | ||||||
|             List<ApplicationCommandInteractionOption> sub_options = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions()); |             last = InteractionIdentifier.builder() | ||||||
|             if (!sub_options.isEmpty()) |                 .type(InteractionType.COMMAND) | ||||||
|                 return InteractionIdentifier.createHierarchy( |                 .name(options.getFirst().getName()) | ||||||
|                     InteractionType.COMMAND,  |                 .parent(last) | ||||||
|                     commandName,  |                 .build(); | ||||||
|                     options.getFirst().getName(), |  | ||||||
|                     sub_options.getFirst().getName()); |  | ||||||
|             else |  | ||||||
|                 return InteractionIdentifier.createHierarchy( |  | ||||||
|                     InteractionType.COMMAND,  |  | ||||||
|                     commandName,  |  | ||||||
|                     options.getFirst().getName()); |  | ||||||
|         }else |  | ||||||
|             return InteractionIdentifier.createHierarchy( |  | ||||||
|                 InteractionType.COMMAND,  |  | ||||||
|                 commandName);  |  | ||||||
|  |  | ||||||
|  |             List<ApplicationCommandInteractionOption> subOptions = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions()); | ||||||
|  |             if (!subOptions.isEmpty()) | ||||||
|  |                 last = InteractionIdentifier.builder() | ||||||
|  |                     .type(InteractionType.COMMAND) | ||||||
|  |                     .name(subOptions.getFirst().getName()) | ||||||
|  |                     .parent(last) | ||||||
|  |                     .build(); | ||||||
|  |         } | ||||||
|  |         return last; | ||||||
|     } |     } | ||||||
|      |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,10 +18,10 @@ import net.tomatentum.marinara.Marinara; | |||||||
| import net.tomatentum.marinara.wrapper.LibraryWrapper; | import net.tomatentum.marinara.wrapper.LibraryWrapper; | ||||||
| import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper; | import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper; | ||||||
|  |  | ||||||
| public class AutoCompleteTest { | class AutoCompleteTest { | ||||||
|      |      | ||||||
|     @Test |     @Test | ||||||
|     public void testAutocomplete() { |     void testAutocomplete() { | ||||||
|         ApplicationCommandInteractionOption optionMock = mock(); |         ApplicationCommandInteractionOption optionMock = mock(); | ||||||
|         ChatInputAutoCompleteEvent autoCompleteEventMock = mock(); |         ChatInputAutoCompleteEvent autoCompleteEventMock = mock(); | ||||||
|  |  | ||||||
| @@ -38,7 +38,7 @@ public class AutoCompleteTest { | |||||||
|  |  | ||||||
|         LibraryWrapper wrapper = new Discord4JWrapper(null); //null okay as we don't use the discord API in this test. |         LibraryWrapper wrapper = new Discord4JWrapper(null); //null okay as we don't use the discord API in this test. | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getRegistry().addInteractions(new TestAutocomplete()); |         marinara.getInteractionContainer().addAllMethods(new TestAutocomplete()); | ||||||
|         wrapper.handleInteraction(autoCompleteEventMock); |         wrapper.handleInteraction(autoCompleteEventMock); | ||||||
|         verify(autoCompleteEventMock).respondWithSuggestions(any()); |         verify(autoCompleteEventMock).respondWithSuggestions(any()); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,15 +13,15 @@ import net.tomatentum.marinara.wrapper.LibraryWrapper; | |||||||
| import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper; | import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper; | ||||||
|  |  | ||||||
| @TestInstance(Lifecycle.PER_CLASS) | @TestInstance(Lifecycle.PER_CLASS) | ||||||
| public class ButtonTest { | class ButtonTest { | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testButtonExecution() { |     void testButtonExecution() { | ||||||
|         ButtonInteractionEvent buttonEventMock = CommonMocks.getButtonEventMock("test"); |         ButtonInteractionEvent buttonEventMock = CommonMocks.getButtonEventMock("test"); | ||||||
|  |  | ||||||
|         LibraryWrapper wrapper = new Discord4JWrapper(null); //null okay as we don't use the discord API in this test. |         LibraryWrapper wrapper = new Discord4JWrapper(null); //null okay as we don't use the discord API in this test. | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getRegistry().addInteractions(new TestButton()); |         marinara.getInteractionContainer().addAllMethods(new TestButton()); | ||||||
|         wrapper.handleInteraction(buttonEventMock); |         wrapper.handleInteraction(buttonEventMock); | ||||||
|         assertTrue(TestButton.didRun); |         assertTrue(TestButton.didRun); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -24,16 +24,16 @@ import net.tomatentum.marinara.wrapper.discord4j.checks.PermissionCheck; | |||||||
| import reactor.core.publisher.Mono; | import reactor.core.publisher.Mono; | ||||||
|  |  | ||||||
| @TestInstance(Lifecycle.PER_CLASS) | @TestInstance(Lifecycle.PER_CLASS) | ||||||
| public class InteractionCheckTest { | class InteractionCheckTest { | ||||||
|      |      | ||||||
|     @Test |     @Test | ||||||
|     public void testInteractionCheck() { |     void testInteractionCheck() { | ||||||
|         ButtonInteractionEvent buttonEventMock = CommonMocks.getButtonEventMock("test"); |         ButtonInteractionEvent buttonEventMock = CommonMocks.getButtonEventMock("test"); | ||||||
|  |  | ||||||
|         LibraryWrapper wrapper = new Discord4JWrapper(null); |         LibraryWrapper wrapper = new Discord4JWrapper(null); | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getCheckRegistry().addCheck(new TestInteractionCheck()); |         marinara.getCheckContainer().addAllMethods(new TestInteractionCheck()); | ||||||
|         marinara.getRegistry().addInteractions(new TestButton()); |         marinara.getInteractionContainer().addAllMethods(new TestButton()); | ||||||
|         wrapper.handleInteraction(buttonEventMock); |         wrapper.handleInteraction(buttonEventMock); | ||||||
|  |  | ||||||
|         assertTrue(TestInteractionCheck.preExecuted); |         assertTrue(TestInteractionCheck.preExecuted); | ||||||
| @@ -42,7 +42,7 @@ public class InteractionCheckTest { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testPermissionCheck() { |     void testPermissionCheck() { | ||||||
|         Member memberMock = mock(); |         Member memberMock = mock(); | ||||||
|         Interaction interactionMock = mock(); |         Interaction interactionMock = mock(); | ||||||
|  |  | ||||||
| @@ -54,8 +54,8 @@ public class InteractionCheckTest { | |||||||
|  |  | ||||||
|         LibraryWrapper wrapper = new Discord4JWrapper(null); |         LibraryWrapper wrapper = new Discord4JWrapper(null); | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getCheckRegistry().addCheck(new PermissionCheck()); |         marinara.getCheckContainer().addAllMethods(new PermissionCheck()); | ||||||
|         marinara.getRegistry().addInteractions(new TestButton()); |         marinara.getInteractionContainer().addAllMethods(new TestButton()); | ||||||
|  |  | ||||||
|         wrapper.handleInteraction(buttonEventMock); |         wrapper.handleInteraction(buttonEventMock); | ||||||
|         assertFalse(TestButton.didPermRun); |         assertFalse(TestButton.didPermRun); | ||||||
|   | |||||||
| @@ -22,9 +22,9 @@ import net.tomatentum.marinara.Marinara; | |||||||
| import net.tomatentum.marinara.wrapper.LibraryWrapper; | import net.tomatentum.marinara.wrapper.LibraryWrapper; | ||||||
| import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper; | import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper; | ||||||
| @TestInstance(Lifecycle.PER_CLASS) | @TestInstance(Lifecycle.PER_CLASS) | ||||||
| public class SlashCommandTest { | class SlashCommandTest { | ||||||
|  |  | ||||||
|     String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN"); |     private static String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN"); | ||||||
|     GatewayDiscordClient client; |     GatewayDiscordClient client; | ||||||
|  |  | ||||||
|     @BeforeAll |     @BeforeAll | ||||||
| @@ -41,8 +41,8 @@ public class SlashCommandTest { | |||||||
|     @Test |     @Test | ||||||
|     void testSlashCommand() { |     void testSlashCommand() { | ||||||
|         Marinara marinara = Marinara.load(new Discord4JWrapper(client)); |         Marinara marinara = Marinara.load(new Discord4JWrapper(client)); | ||||||
|         marinara.getRegistry().addInteractions(new TestCommand()); |         marinara.getInteractionContainer().addAllMethods(new TestCommand()); | ||||||
|         marinara.getRegistry().registerCommands(); |         marinara.registerCommands(); | ||||||
|         System.out.println("Success!"); |         System.out.println("Success!"); | ||||||
|     } |     } | ||||||
|      |      | ||||||
| @@ -64,7 +64,7 @@ public class SlashCommandTest { | |||||||
|          |          | ||||||
|         LibraryWrapper wrapper = new Discord4JWrapper(client); |         LibraryWrapper wrapper = new Discord4JWrapper(client); | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getRegistry().addInteractions(new TestCommand()); |         marinara.getInteractionContainer().addAllMethods(new TestCommand()); | ||||||
|  |  | ||||||
|         wrapper.handleInteraction(eventMock); |         wrapper.handleInteraction(eventMock); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -2,21 +2,36 @@ package net.tomatentum.marinara.test.discord4j; | |||||||
|  |  | ||||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  |  | ||||||
| import java.util.Collections; |  | ||||||
|  |  | ||||||
| import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent; | import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent; | ||||||
|  | import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; | ||||||
|  | import discord4j.discordjson.json.ApplicationCommandOptionChoiceData; | ||||||
| import net.tomatentum.marinara.interaction.InteractionHandler; | import net.tomatentum.marinara.interaction.InteractionHandler; | ||||||
| import net.tomatentum.marinara.interaction.annotation.AutoComplete; | import net.tomatentum.marinara.interaction.annotation.AutoComplete; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | ||||||
|  |  | ||||||
| public class TestAutocomplete implements InteractionHandler { | public class TestAutocomplete implements InteractionHandler { | ||||||
|  |  | ||||||
|     @SlashCommand(name = "test") |     @SlashCommand( | ||||||
|     @AutoComplete |         name = "test", | ||||||
|     public void autocomplete(ChatInputAutoCompleteEvent context, String value) { |         options = @SlashCommandOption( | ||||||
|  |                 name = "foo", | ||||||
|  |                 type = SlashCommandOptionType.STRING, | ||||||
|  |                 autocompletes = @AutoComplete("testAuto") | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     public void exec(ChatInputInteractionEvent context) { | ||||||
|  |         // Not executed just there for autocomplete to work | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @AutoComplete("testAuto") | ||||||
|  |     public ApplicationCommandOptionChoiceData[] autocomplete(ChatInputAutoCompleteEvent context, String value) { | ||||||
|         System.out.println("Success!"); |         System.out.println("Success!"); | ||||||
|         assertEquals(value, "test"); |         assertEquals("test", value); | ||||||
|         context.respondWithSuggestions(Collections.emptyList()); |         return new ApplicationCommandOptionChoiceData[]{ | ||||||
|  |             ApplicationCommandOptionChoiceData.builder().name("TestValue").value("test").build() | ||||||
|  |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ public class TestCommand implements InteractionHandler { | |||||||
|         } |         } | ||||||
|         ) |         ) | ||||||
|     public void exec(ChatInputInteractionEvent event, String test) { |     public void exec(ChatInputInteractionEvent event, String test) { | ||||||
|         assertEquals(test, "test"); |         assertEquals("test", test); | ||||||
|         System.out.println("Success!"); |         System.out.println("Success!"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | # SLF4J's SimpleLogger configuration file | ||||||
|  | # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. | ||||||
|  |  | ||||||
|  | # Default logging detail level for all instances of SimpleLogger. | ||||||
|  | # Must be one of ("trace", "debug", "info", "warn", or "error"). | ||||||
|  | # If not specified, defaults to "info". | ||||||
|  | org.slf4j.simpleLogger.defaultLogLevel=trace | ||||||
| @@ -10,17 +10,14 @@ plugins { | |||||||
|     `java-library` |     `java-library` | ||||||
| } | } | ||||||
|  |  | ||||||
| repositories { |  | ||||||
|     // Use Maven Central for resolving dependencies. |  | ||||||
|     mavenCentral() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|     // Use JUnit Jupiter for testing. |     // Use JUnit Jupiter for testing. | ||||||
|     testImplementation(libs.junit.jupiter) |     testImplementation(libs.junit.jupiter) | ||||||
|     testImplementation(libs.mockito) |     testImplementation(libs.mockito) | ||||||
|  |  | ||||||
|     testRuntimeOnly("org.junit.platform:junit-platform-launcher") |     testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||||||
|  |     testImplementation(libs.slf4j.simple) | ||||||
|  |  | ||||||
|     implementation(libs.slf4j) |     implementation(libs.slf4j) | ||||||
|     implementation(libs.javacord) |     implementation(libs.javacord) | ||||||
|     implementation(libs.geantyref) |     implementation(libs.geantyref) | ||||||
|   | |||||||
| @@ -2,10 +2,12 @@ package net.tomatentum.marinara.wrapper.javacord; | |||||||
|  |  | ||||||
| import org.javacord.api.interaction.AutocompleteInteraction; | import org.javacord.api.interaction.AutocompleteInteraction; | ||||||
| import org.javacord.api.interaction.ButtonInteraction; | import org.javacord.api.interaction.ButtonInteraction; | ||||||
|  | import org.javacord.api.interaction.InteractionBase; | ||||||
| import org.javacord.api.interaction.SlashCommandInteraction; | import org.javacord.api.interaction.SlashCommandInteraction; | ||||||
| import org.javacord.api.interaction.SlashCommandInteractionOption; | import org.javacord.api.interaction.SlashCommandInteractionOption; | ||||||
| import org.javacord.api.interaction.SlashCommandOptionType; | import org.javacord.api.interaction.SlashCommandOptionType; | ||||||
|  |  | ||||||
|  | import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData; | ||||||
| import net.tomatentum.marinara.wrapper.ContextObjectProvider; | import net.tomatentum.marinara.wrapper.ContextObjectProvider; | ||||||
|  |  | ||||||
| public class JavacordContextObjectProvider implements ContextObjectProvider { | public class JavacordContextObjectProvider implements ContextObjectProvider { | ||||||
| @@ -16,14 +18,14 @@ public class JavacordContextObjectProvider implements ContextObjectProvider { | |||||||
|             return null; |             return null; | ||||||
|         SlashCommandInteraction interaction = (SlashCommandInteraction) context; |         SlashCommandInteraction interaction = (SlashCommandInteraction) context; | ||||||
|         if (!interaction.getArguments().isEmpty()) |         if (!interaction.getArguments().isEmpty()) | ||||||
|             return getOptionValue(interaction.getOptionByName(optionName).get()); |             return getOptionValue(interaction.getOptionByName(optionName).orElse(null)); | ||||||
|  |  | ||||||
|         SlashCommandInteractionOption subCommandOption = interaction.getOptions().getFirst(); |         SlashCommandInteractionOption subCommandOption = interaction.getOptions().getFirst(); | ||||||
|  |  | ||||||
|         if (!subCommandOption.getOptions().isEmpty()) |         if (!subCommandOption.getOptions().isEmpty()) | ||||||
|             subCommandOption = subCommandOption.getOptions().getFirst(); |             subCommandOption = subCommandOption.getOptions().getFirst(); | ||||||
|  |  | ||||||
|         return getOptionValue(subCommandOption.getOptionByName(optionName).get()); |         return getOptionValue(subCommandOption.getOptionByName(optionName).orElse(null)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Object getOptionValue(SlashCommandInteractionOption option) { |     private Object getOptionValue(SlashCommandInteractionOption option) { | ||||||
| @@ -89,7 +91,7 @@ public class JavacordContextObjectProvider implements ContextObjectProvider { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Object getInteractionContextObject(Object context, Class<?> type) { |     public Object getInteractionContextObject(Object context, Class<?> type) { | ||||||
|         ButtonInteraction button = (ButtonInteraction) context; |         InteractionBase button = (InteractionBase) context; | ||||||
|         switch (type.getName()) { |         switch (type.getName()) { | ||||||
|             case "org.javacord.api.entity.channel.TextChannel": |             case "org.javacord.api.entity.channel.TextChannel": | ||||||
|                 return button.getChannel().orElse(null); |                 return button.getChannel().orElse(null); | ||||||
| @@ -102,9 +104,9 @@ public class JavacordContextObjectProvider implements ContextObjectProvider { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Object getAutocompleteFocusedOption(Object context) { |     public AutocompleteOptionData getAutocompleteFocusedOption(Object context) { | ||||||
|         AutocompleteInteraction interaction = (AutocompleteInteraction) context; |         SlashCommandInteractionOption option = ((AutocompleteInteraction) context).getFocusedOption(); | ||||||
|         return getOptionValue(interaction.getFocusedOption()); |         return new AutocompleteOptionData(option.getName(), getOptionValue(option)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -50,10 +50,10 @@ public class JavacordConverterSpec implements CommandConverter.Spec<SlashCommand | |||||||
|             .setName(option.name()) |             .setName(option.name()) | ||||||
|             .setDescription(option.description()) |             .setDescription(option.description()) | ||||||
|             .setRequired(option.required()) |             .setRequired(option.required()) | ||||||
|             .setAutocompletable(option.autocomplete()) |             .setAutocompletable(option.autocompletes().length > 0) | ||||||
| 			.setMinLength(Double.valueOf(option.range().min()).longValue()) | 			.setMinLength((long) option.range().min()) | ||||||
| 			.setDecimalMinValue(option.range().min()) | 			.setDecimalMinValue(option.range().min()) | ||||||
| 			.setMaxLength(Double.valueOf(option.range().max()).longValue()) | 			.setMaxLength((long) option.range().max()) | ||||||
| 			.setDecimalMaxValue(option.range().max()) | 			.setDecimalMaxValue(option.range().max()) | ||||||
|             .setChoices(choices) |             .setChoices(choices) | ||||||
| 			.build(); | 			.build(); | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| package net.tomatentum.marinara.wrapper.javacord; | package net.tomatentum.marinara.wrapper.javacord; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| import org.javacord.api.DiscordApi; | import org.javacord.api.DiscordApi; | ||||||
|  | import org.javacord.api.interaction.AutocompleteInteraction; | ||||||
| import org.javacord.api.interaction.SlashCommandBuilder; | import org.javacord.api.interaction.SlashCommandBuilder; | ||||||
|  | import org.javacord.api.interaction.SlashCommandOptionChoice; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.wrapper.CommandConverter; | import net.tomatentum.marinara.wrapper.CommandConverter; | ||||||
| @@ -27,7 +31,7 @@ public class JavacordWrapper extends LibraryWrapper { | |||||||
|  |  | ||||||
|         if (api != null) { |         if (api != null) { | ||||||
|             this.commandRegisterer = CommandRegisterer.of(new JavacordRegistererStrategy(api), converter); |             this.commandRegisterer = CommandRegisterer.of(new JavacordRegistererStrategy(api), converter); | ||||||
|             api.addInteractionCreateListener((e) -> handleInteraction(e.getInteraction())); |             api.addInteractionCreateListener(e -> handleInteraction(e.getInteraction())); | ||||||
|         }else |         }else | ||||||
|             logger.warn("DiscordApi was null so no Events were subscribed to."); |             logger.warn("DiscordApi was null so no Events were subscribed to."); | ||||||
|         logger.info("Javacord wrapper loaded!"); |         logger.info("Javacord wrapper loaded!"); | ||||||
| @@ -52,4 +56,15 @@ public class JavacordWrapper extends LibraryWrapper { | |||||||
|         return contextObjectProvider; |         return contextObjectProvider; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void respondAutocomplete(Object context, List<Object> options) { | ||||||
|  |         if (context instanceof AutocompleteInteraction interaction) { | ||||||
|  |             List<SlashCommandOptionChoice> choices = options.stream() | ||||||
|  |                 .filter(SlashCommandOptionChoice.class::isInstance) | ||||||
|  |                 .map(o -> (SlashCommandOptionChoice)o) | ||||||
|  |                 .toList(); | ||||||
|  |             interaction.respondWithChoices(choices); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,25 +13,28 @@ public class AutocompleteIdentifierConverter implements IdentifierProvider.Conve | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public InteractionIdentifier convert(AutocompleteInteraction context) { |     public InteractionIdentifier convert(AutocompleteInteraction context) { | ||||||
|  |         InteractionIdentifier last = InteractionIdentifier.builder() | ||||||
|  |             .type(InteractionType.AUTOCOMPLETE) | ||||||
|  |             .name(context.getCommandName()) | ||||||
|  |             .build(); | ||||||
|  |  | ||||||
|         List<SlashCommandInteractionOption> options = context.getOptions(); |         List<SlashCommandInteractionOption> options = context.getOptions(); | ||||||
|         String commandName = context.getCommandName(); |  | ||||||
|         if (!options.isEmpty()) { |         if (!options.isEmpty()) { | ||||||
|             List<SlashCommandInteractionOption> sub_options = context.getOptions().getFirst().getOptions(); |             last = InteractionIdentifier.builder() | ||||||
|             if (!sub_options.isEmpty()) |                 .type(InteractionType.AUTOCOMPLETE) | ||||||
|                 return InteractionIdentifier.createHierarchy( |                 .name(options.getFirst().getName()) | ||||||
|                     InteractionType.AUTOCOMPLETE,  |                 .parent(last) | ||||||
|                     commandName,  |                 .build(); | ||||||
|                     options.getFirst().getName(), |  | ||||||
|                     sub_options.getFirst().getName()); |             List<SlashCommandInteractionOption> subOptions = context.getOptions().getFirst().getOptions(); | ||||||
|             else |             if (!subOptions.isEmpty()) | ||||||
|                 return InteractionIdentifier.createHierarchy( |                 last = InteractionIdentifier.builder() | ||||||
|                     InteractionType.AUTOCOMPLETE,  |                     .type(InteractionType.AUTOCOMPLETE) | ||||||
|                     commandName,  |                     .name(subOptions.getFirst().getName()) | ||||||
|                     options.getFirst().getName()); |                     .parent(last) | ||||||
|         }else |                     .build(); | ||||||
|             return InteractionIdentifier.createHierarchy( |         } | ||||||
|                 InteractionType.AUTOCOMPLETE,  |         return last; | ||||||
|                 commandName);  |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,26 +13,28 @@ public class SlashCommandIdentifierConverter implements IdentifierProvider.Conve | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public InteractionIdentifier convert(SlashCommandInteraction context) { |     public InteractionIdentifier convert(SlashCommandInteraction context) { | ||||||
|         List<SlashCommandInteractionOption> options = context.getOptions(); |         InteractionIdentifier last = InteractionIdentifier.builder() | ||||||
|         String commandName = context.getCommandName(); |             .type(InteractionType.COMMAND) | ||||||
|         if (!options.isEmpty()) { |             .name(context.getCommandName()) | ||||||
|             List<SlashCommandInteractionOption> sub_options = context.getOptions().getFirst().getOptions(); |             .build(); | ||||||
|             if (!sub_options.isEmpty()) |  | ||||||
|                 return InteractionIdentifier.createHierarchy( |  | ||||||
|                     InteractionType.COMMAND,  |  | ||||||
|                     commandName,  |  | ||||||
|                     options.getFirst().getName(), |  | ||||||
|                     sub_options.getFirst().getName()); |  | ||||||
|             else |  | ||||||
|                 return InteractionIdentifier.createHierarchy( |  | ||||||
|                     InteractionType.COMMAND,  |  | ||||||
|                     commandName,  |  | ||||||
|                     options.getFirst().getName()); |  | ||||||
|         }else |  | ||||||
|             return InteractionIdentifier.createHierarchy( |  | ||||||
|                 InteractionType.COMMAND,  |  | ||||||
|                 commandName);  |  | ||||||
|  |  | ||||||
|  |         List<SlashCommandInteractionOption> options = context.getOptions(); | ||||||
|  |         if (!options.isEmpty()) { | ||||||
|  |             last = InteractionIdentifier.builder() | ||||||
|  |                 .type(InteractionType.COMMAND) | ||||||
|  |                 .name(options.getFirst().getName()) | ||||||
|  |                 .parent(last) | ||||||
|  |                 .build(); | ||||||
|  |                  | ||||||
|  |             List<SlashCommandInteractionOption> subOptions = context.getOptions().getFirst().getOptions(); | ||||||
|  |             if (!subOptions.isEmpty()) | ||||||
|  |                 last = InteractionIdentifier.builder() | ||||||
|  |                     .type(InteractionType.COMMAND) | ||||||
|  |                     .name(subOptions.getFirst().getName()) | ||||||
|  |                     .parent(last) | ||||||
|  |                     .build(); | ||||||
|  |         } | ||||||
|  |         return last; | ||||||
|     } |     } | ||||||
|      |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,10 +16,10 @@ import net.tomatentum.marinara.Marinara; | |||||||
| import net.tomatentum.marinara.wrapper.LibraryWrapper; | import net.tomatentum.marinara.wrapper.LibraryWrapper; | ||||||
| import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | ||||||
|  |  | ||||||
| public class AutoCompleteTest { | class AutoCompleteTest { | ||||||
|      |      | ||||||
|     @Test |     @Test | ||||||
|     public void testAutocomplete() { |     void testAutocomplete() { | ||||||
|  |  | ||||||
|         SlashCommandInteractionOption optionMock = mock(); |         SlashCommandInteractionOption optionMock = mock(); | ||||||
|         AutocompleteInteraction autocompleteInteractionMock = mock(); |         AutocompleteInteraction autocompleteInteractionMock = mock(); | ||||||
| @@ -33,7 +33,7 @@ public class AutoCompleteTest { | |||||||
|  |  | ||||||
|         LibraryWrapper wrapper = new JavacordWrapper(null); //null okay as we don't use the discord API in this test. |         LibraryWrapper wrapper = new JavacordWrapper(null); //null okay as we don't use the discord API in this test. | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getRegistry().addInteractions(new TestAutocomplete()); |         marinara.getInteractionContainer().addAllMethods(new TestAutocomplete()); | ||||||
|         wrapper.handleInteraction(autocompleteInteractionMock); |         wrapper.handleInteraction(autocompleteInteractionMock); | ||||||
|         verify(autocompleteInteractionMock).respondWithChoices(any()); |         verify(autocompleteInteractionMock).respondWithChoices(any()); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -12,13 +12,13 @@ import net.tomatentum.marinara.wrapper.LibraryWrapper; | |||||||
| import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | ||||||
|  |  | ||||||
| @TestInstance(Lifecycle.PER_CLASS) | @TestInstance(Lifecycle.PER_CLASS) | ||||||
| public class ButtonTest { | class ButtonTest { | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testButtonExecution() { |     void testButtonExecution() { | ||||||
|         LibraryWrapper wrapper = new JavacordWrapper(null); //null okay as we don't use the discord API in this test. |         LibraryWrapper wrapper = new JavacordWrapper(null); //null okay as we don't use the discord API in this test. | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getRegistry().addInteractions(new TestButton()); |         marinara.getInteractionContainer().addAllMethods(new TestButton()); | ||||||
|         wrapper.handleInteraction(CommonMocks.getButtonInteractionMock("test")); |         wrapper.handleInteraction(CommonMocks.getButtonInteractionMock("test")); | ||||||
|         assertTrue(TestButton.didRun); |         assertTrue(TestButton.didRun); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -19,25 +19,25 @@ import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | |||||||
| import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck; | import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck; | ||||||
|  |  | ||||||
| @TestInstance(Lifecycle.PER_CLASS) | @TestInstance(Lifecycle.PER_CLASS) | ||||||
| public class InteractionCheckTest { | class InteractionCheckTest { | ||||||
|      |      | ||||||
|     @Test |     @Test | ||||||
|     public void testInteractionCheck() { |     void testInteractionCheck() { | ||||||
|         LibraryWrapper wrapper = new JavacordWrapper(null); |         LibraryWrapper wrapper = new JavacordWrapper(null); | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getCheckRegistry().addCheck(new TestInteractionCheck()); |         marinara.getCheckContainer().addAllMethods(new TestInteractionCheck()); | ||||||
|         marinara.getRegistry().addInteractions(new TestButton()); |         marinara.getInteractionContainer().addAllMethods(new TestButton()); | ||||||
|         wrapper.handleInteraction(CommonMocks.getButtonInteractionMock("test")); |         wrapper.handleInteraction(CommonMocks.getButtonInteractionMock("test")); | ||||||
|         assertTrue(TestInteractionCheck.preExecuted); |         assertTrue(TestInteractionCheck.preExecuted); | ||||||
|         assertTrue(TestInteractionCheck.postExecuted); |         assertTrue(TestInteractionCheck.postExecuted); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testPermissionCheck() { |     void testPermissionCheck() { | ||||||
|         LibraryWrapper wrapper = new JavacordWrapper(null); |         LibraryWrapper wrapper = new JavacordWrapper(null); | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getCheckRegistry().addCheck(new PermissionCheck()); |         marinara.getCheckContainer().addAllMethods(new PermissionCheck()); | ||||||
|         marinara.getRegistry().addInteractions(new TestButton()); |         marinara.getInteractionContainer().addAllMethods(new TestButton()); | ||||||
|  |  | ||||||
|         Server serverMock = mock(); |         Server serverMock = mock(); | ||||||
|         ButtonInteraction buttonInteractionMock = CommonMocks.getButtonInteractionMock("permissionCheck", serverMock); |         ButtonInteraction buttonInteractionMock = CommonMocks.getButtonInteractionMock("permissionCheck", serverMock); | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ import net.tomatentum.marinara.Marinara; | |||||||
| import net.tomatentum.marinara.wrapper.LibraryWrapper; | import net.tomatentum.marinara.wrapper.LibraryWrapper; | ||||||
| import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper; | ||||||
| @TestInstance(Lifecycle.PER_CLASS) | @TestInstance(Lifecycle.PER_CLASS) | ||||||
| public class SlashCommandTest { | class SlashCommandTest { | ||||||
|  |  | ||||||
|     String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN"); |     String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN"); | ||||||
|     DiscordApi api; |     DiscordApi api; | ||||||
| @@ -41,8 +41,8 @@ public class SlashCommandTest { | |||||||
|     @Test |     @Test | ||||||
|     void testSlashCommand() { |     void testSlashCommand() { | ||||||
|         Marinara marinara = Marinara.load(new JavacordWrapper(api)); |         Marinara marinara = Marinara.load(new JavacordWrapper(api)); | ||||||
|         marinara.getRegistry().addInteractions(new TestCommand()); |         marinara.getInteractionContainer().addAllMethods(new TestCommand()); | ||||||
|         marinara.getRegistry().registerCommands(); |         marinara.registerCommands(); | ||||||
|         System.out.println("Success!"); |         System.out.println("Success!"); | ||||||
|     } |     } | ||||||
|      |      | ||||||
| @@ -50,7 +50,7 @@ public class SlashCommandTest { | |||||||
|     void testSlashCommandExecution() { |     void testSlashCommandExecution() { | ||||||
|         LibraryWrapper wrapper = new JavacordWrapper(api); |         LibraryWrapper wrapper = new JavacordWrapper(api); | ||||||
|         Marinara marinara = Marinara.load(wrapper); |         Marinara marinara = Marinara.load(wrapper); | ||||||
|         marinara.getRegistry().addInteractions(new TestCommand()); |         marinara.getInteractionContainer().addAllMethods(new TestCommand()); | ||||||
|  |  | ||||||
|         SlashCommandInteractionOption optionMock = mock(); |         SlashCommandInteractionOption optionMock = mock(); | ||||||
|         SlashCommandInteraction interactionMock = mock(); |         SlashCommandInteraction interactionMock = mock(); | ||||||
|   | |||||||
| @@ -5,18 +5,32 @@ import static org.junit.jupiter.api.Assertions.assertEquals; | |||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
|  |  | ||||||
| import org.javacord.api.interaction.AutocompleteInteraction; | import org.javacord.api.interaction.AutocompleteInteraction; | ||||||
|  | import org.javacord.api.interaction.SlashCommandInteraction; | ||||||
|  |  | ||||||
| import net.tomatentum.marinara.interaction.InteractionHandler; | import net.tomatentum.marinara.interaction.InteractionHandler; | ||||||
| import net.tomatentum.marinara.interaction.annotation.AutoComplete; | import net.tomatentum.marinara.interaction.annotation.AutoComplete; | ||||||
| import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption; | ||||||
|  | import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | ||||||
|  |  | ||||||
| public class TestAutocomplete implements InteractionHandler { | public class TestAutocomplete implements InteractionHandler { | ||||||
|      |      | ||||||
|     @SlashCommand(name = "test") |     @SlashCommand( | ||||||
|     @AutoComplete |         name = "test", | ||||||
|  |         options = @SlashCommandOption( | ||||||
|  |                 name = "foo", | ||||||
|  |                 type = SlashCommandOptionType.STRING, | ||||||
|  |                 autocompletes = @AutoComplete("testAuto") | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     public void exec(SlashCommandInteraction context) { | ||||||
|  |         //only here for command definition | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @AutoComplete("testAuto") | ||||||
|     public void autocomplete(AutocompleteInteraction context, String value) { |     public void autocomplete(AutocompleteInteraction context, String value) { | ||||||
|         System.out.println("Success!"); |         System.out.println("Success!"); | ||||||
|         assertEquals(value, "test"); |         assertEquals("test", value); | ||||||
|         context.respondWithChoices(Collections.emptyList()); |         context.respondWithChoices(Collections.emptyList()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,8 +16,8 @@ import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck.HasPermis | |||||||
|  |  | ||||||
| public class TestButton implements InteractionHandler { | public class TestButton implements InteractionHandler { | ||||||
|  |  | ||||||
|  |  | ||||||
|     public static boolean didRun = false; |     public static boolean didRun = false; | ||||||
|  |  | ||||||
|     @Button("test") |     @Button("test") | ||||||
|     @TestCheck |     @TestCheck | ||||||
|     public void exec(ButtonInteraction interaction, TextChannel channel, Message message, User member, Server server) { |     public void exec(ButtonInteraction interaction, TextChannel channel, Message message, User member, Server server) { | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptio | |||||||
| import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType; | ||||||
|  |  | ||||||
| public class TestCommand implements InteractionHandler { | public class TestCommand implements InteractionHandler { | ||||||
|  |  | ||||||
|     @SlashCommand( |     @SlashCommand( | ||||||
|         name = "test",  |         name = "test",  | ||||||
|         description = "testingen", |         description = "testingen", | ||||||
| @@ -27,7 +28,8 @@ public class TestCommand implements InteractionHandler { | |||||||
|         } |         } | ||||||
|         ) |         ) | ||||||
|     public void exec(SlashCommandInteraction interaction, String test) { |     public void exec(SlashCommandInteraction interaction, String test) { | ||||||
|         assertEquals(test, "test"); |         assertEquals("test", test); | ||||||
|         System.out.println("Success!"); |         System.out.println("Success!"); | ||||||
|     } |     } | ||||||
|  |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | # SLF4J's SimpleLogger configuration file | ||||||
|  | # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. | ||||||
|  |  | ||||||
|  | # Default logging detail level for all instances of SimpleLogger. | ||||||
|  | # Must be one of ("trace", "debug", "info", "warn", or "error"). | ||||||
|  | # If not specified, defaults to "info". | ||||||
|  | org.slf4j.simpleLogger.defaultLogLevel=trace | ||||||
		Reference in New Issue
	
	Block a user