Compare commits
	
		
			41 Commits
		
	
	
		
			432cf78a2e
			...
			feat/messa
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						272225ac07
	
				 | 
					
					
						|||
| 
						
						
							
						
						42a675dc96
	
				 | 
					
					
						|||
| 
						
						
							
						
						eea1597b15
	
				 | 
					
					
						|||
| 
						
						
							
						
						83b446e6fb
	
				 | 
					
					
						|||
| 
						
						
							
						
						ef9384336a
	
				 | 
					
					
						|||
| 
						
						
							
						
						ebf5600e29
	
				 | 
					
					
						|||
| 
						
						
							
						
						070319853a
	
				 | 
					
					
						|||
| 
						
						
							
						
						60ead419e2
	
				 | 
					
					
						|||
| 
						
						
							
						
						0b7b607a23
	
				 | 
					
					
						|||
| 
						
						
							
						
						991d1c047b
	
				 | 
					
					
						|||
| 
						
						
							
						
						ec17952375
	
				 | 
					
					
						|||
| 
						
						
							
						
						0114cffcbd
	
				 | 
					
					
						|||
| 
						
						
							
						
						450f1fdaa1
	
				 | 
					
					
						|||
| 
						
						
							
						
						92540576df
	
				 | 
					
					
						|||
| 
						
						
							
						
						8a3cde52fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						8495659364
	
				 | 
					
					
						|||
| 
						
						
							
						
						0973016a74
	
				 | 
					
					
						|||
| 
						
						
							
						
						0590789359
	
				 | 
					
					
						|||
| 
						
						
							
						
						2647a1f0b4
	
				 | 
					
					
						|||
| a3c5eb62ac | |||
| 
						
						
							
						
						996f854ff7
	
				 | 
					
					
						|||
| 
						
						
							
						
						d2eec8b07c
	
				 | 
					
					
						|||
| 
						
						
							
						
						caa2ee7089
	
				 | 
					
					
						|||
| 2e5979e6e4 | |||
| 
						
						
							
						
						ab1eb74e85
	
				 | 
					
					
						|||
| 
						
						
							
						
						a5737b9eaa
	
				 | 
					
					
						|||
| 
						
						
							
						
						faca21724c
	
				 | 
					
					
						|||
| 
						
						
							
						
						4c5e28b679
	
				 | 
					
					
						|||
| 33f355e6ea | |||
| 
						
						
							
						
						d32ac62b4a
	
				 | 
					
					
						|||
| 
						
						
							
						
						e7c35d9308
	
				 | 
					
					
						|||
| 
						
						
							
						
						d4a91f3251
	
				 | 
					
					
						|||
| 
						
						
							
						
						bce4ce7812
	
				 | 
					
					
						|||
| 
						
						
							
						
						bae077654e
	
				 | 
					
					
						|||
| 
						
						
							
						
						203498de68
	
				 | 
					
					
						|||
| 
						
						
							
						
						24df1731da
	
				 | 
					
					
						|||
| 
						
						
							
						
						e3fc10a1ce
	
				 | 
					
					
						|||
| 
						
						
							
						
						78cacb7eb6
	
				 | 
					
					
						|||
| 
						
						
							
						
						7287d44645
	
				 | 
					
					
						|||
| 
						
						
							
						
						630c8ddee5
	
				 | 
					
					
						|||
| 
						
						
							
						
						4e27e6ce56
	
				 | 
					
					
						
@@ -8,7 +8,19 @@ allprojects {
 | 
			
		||||
    group = "net.tomatentum.Marinara"
 | 
			
		||||
    version = "1.0.0-RC1" + (if (!project.hasProperty("release")) ("-" + getGitHash()) else "")
 | 
			
		||||
    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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,19 @@
 | 
			
		||||
 | 
			
		||||
[versions]
 | 
			
		||||
junit-jupiter = "5.10.2"
 | 
			
		||||
log4j = "2.24.1"
 | 
			
		||||
slf4j = "2.0.17"
 | 
			
		||||
javacord = "3.8.0"
 | 
			
		||||
discord4j = "3.2.7"
 | 
			
		||||
geantyref = "2.0.0"
 | 
			
		||||
mockito = "5.15.2"
 | 
			
		||||
cutin = "0.2.0"
 | 
			
		||||
 | 
			
		||||
[libraries]
 | 
			
		||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
 | 
			
		||||
log4j = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j"}
 | 
			
		||||
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"}
 | 
			
		||||
discord4j = { module = "com.discord4j:discord4j-core", version.ref = "discord4j"}
 | 
			
		||||
geantyref = { module = "io.leangen.geantyref:geantyref", version.ref = "geantyref"}
 | 
			
		||||
mockito = {module = "org.mockito:mockito-core", version.ref = "mockito"}
 | 
			
		||||
cutin = {module = "net.tomatentum.cutin:lib", version.ref = "cutin"}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,18 +10,14 @@ plugins {
 | 
			
		||||
    `java-library`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    // Use Maven Central for resolving dependencies.
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    // Use JUnit Jupiter for testing.
 | 
			
		||||
    testImplementation(libs.junit.jupiter)
 | 
			
		||||
 | 
			
		||||
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 | 
			
		||||
    implementation(libs.log4j)
 | 
			
		||||
    implementation(libs.slf4j)
 | 
			
		||||
    implementation(libs.geantyref)
 | 
			
		||||
    api(libs.cutin)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Apply a specific Java toolchain to ease working on different environments.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,40 +1,85 @@
 | 
			
		||||
package net.tomatentum.marinara;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.registry.InteractionCheckRegistry;
 | 
			
		||||
import net.tomatentum.marinara.registry.InteractionRegistry;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private InteractionRegistry registry;
 | 
			
		||||
    private InteractionCheckRegistry checkRegistry;
 | 
			
		||||
    private LibraryWrapper wrapper;
 | 
			
		||||
    private MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer;
 | 
			
		||||
    private MethodContainer<InteractionIdentifier, Object> interactionContainer;
 | 
			
		||||
    private MethodExecutor<Object> interactionExecutor;
 | 
			
		||||
 | 
			
		||||
    private Marinara(LibraryWrapper wrapper) {
 | 
			
		||||
        this.wrapper = wrapper;
 | 
			
		||||
        this.registry = new InteractionRegistry(this);
 | 
			
		||||
        this.checkRegistry = new InteractionCheckRegistry();
 | 
			
		||||
        this.checkContainer = new InteractionCheckContainer();
 | 
			
		||||
        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!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public InteractionRegistry getRegistry() {
 | 
			
		||||
        return registry;
 | 
			
		||||
    }
 | 
			
		||||
    //TODO move to future interactionstructure module
 | 
			
		||||
    public void registerCommands() {
 | 
			
		||||
        List<InteractionIdentifier> slashIdentifiers = getInteractionContainer().identifiers().stream()
 | 
			
		||||
            .filter(i -> i.type().equals(InteractionType.COMMAND))
 | 
			
		||||
            .toList();
 | 
			
		||||
 | 
			
		||||
    public InteractionCheckRegistry getCheckRegistry() {
 | 
			
		||||
        return checkRegistry;
 | 
			
		||||
        SlashCommandDefinition[] defs = new ObjectAggregator<InteractionIdentifier, RootCommandIdentifier, SlashCommandDefinition>(
 | 
			
		||||
            i -> Arrays.asList((RootCommandIdentifier)i.rootNode()),
 | 
			
		||||
            SlashCommandDefinition::addIdentifier,
 | 
			
		||||
            SlashCommandDefinition::new)
 | 
			
		||||
            .aggregate(slashIdentifiers)
 | 
			
		||||
            .toArray(SlashCommandDefinition[]::new);
 | 
			
		||||
 | 
			
		||||
        wrapper.getRegisterer().register(defs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.Annotation;
 | 
			
		||||
import java.lang.reflect.InvocationTargetException;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
import net.tomatentum.marinara.util.ReflectionUtil;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        Method[] methods = Arrays.stream(check.getClass().getMethods())
 | 
			
		||||
            .filter(x -> x.getName().equals("preExec"))
 | 
			
		||||
            .filter(x -> !x.isBridge())
 | 
			
		||||
            .toArray(s -> new Method[s]);
 | 
			
		||||
        Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), 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.fatal(e);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        logger.debug("Running InteractionCheck preExec {} with annotation {}", preExec(), annotation());
 | 
			
		||||
        return (boolean) preExec().run(new CheckExecutionContext(annotation, context));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void post(Object context) {
 | 
			
		||||
        Method[] methods = Arrays.stream(check.getClass().getMethods())
 | 
			
		||||
            .filter(x -> x.getName().equals("postExec"))
 | 
			
		||||
            .filter(x -> !x.isBridge())
 | 
			
		||||
            .toArray(s -> new Method[s]);
 | 
			
		||||
        Method method = ReflectionUtil.getMostSpecificMethod(methods, context.getClass(), 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.fatal(e);
 | 
			
		||||
        }
 | 
			
		||||
        logger.debug("Running InteractionCheck postExec {} with annotation {}", postExec(), annotation());
 | 
			
		||||
        postExec().run(new CheckExecutionContext(annotation, context));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,74 @@
 | 
			
		||||
package net.tomatentum.marinara.checks;
 | 
			
		||||
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.cutin.MethodParser;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory.ParserResults;
 | 
			
		||||
import net.tomatentum.cutin.container.MethodContainer;
 | 
			
		||||
import net.tomatentum.cutin.method.BestCandidateMethod;
 | 
			
		||||
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,
 | 
			
		||||
            CheckMethodIdentifier identifier
 | 
			
		||||
        ) {
 | 
			
		||||
        super(methodName, containingObject);
 | 
			
		||||
        this.identifier = identifier;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void addParser(Set<MethodParser> parsers) {
 | 
			
		||||
            parsers.add(
 | 
			
		||||
                new InteractionCheckClassParser()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Optional<BestCandidateMethod<CheckMethodIdentifier, CheckExecutionContext>> bcProduce(
 | 
			
		||||
                    String methodName,
 | 
			
		||||
                    Object containingObject,
 | 
			
		||||
                    ParserResults parserResults
 | 
			
		||||
                ) {
 | 
			
		||||
 | 
			
		||||
            CheckMethodIdentifier identifier = new CheckMethodIdentifier(parserResults.get(InteractionCheckClassParser.class), type);
 | 
			
		||||
            if (identifier.annotationType() == null)
 | 
			
		||||
                return null;
 | 
			
		||||
            return Optional.of(new InteractionCheckMethod(methodName, containingObject, identifier));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -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})
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
public @interface AutoComplete {
 | 
			
		||||
    
 | 
			
		||||
    public String value();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,15 @@ import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.structure.data.ButtonStructureData.ButtonStyle;
 | 
			
		||||
 | 
			
		||||
@Target({ElementType.METHOD})
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
public @interface Button {
 | 
			
		||||
    public String value(); //aka customId
 | 
			
		||||
    public String label() default "default_button";
 | 
			
		||||
    public ButtonStyle style() default ButtonStyle.PRIMARY;
 | 
			
		||||
    public String url() default "";
 | 
			
		||||
    public boolean disabled() default false;
 | 
			
		||||
    public String emoji() default "";
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,10 @@ import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.CommandChoices;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption.PlaceHolderEnum;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice;
 | 
			
		||||
@@ -13,22 +15,22 @@ import net.tomatentum.marinara.interaction.commands.choice.EnumChoices;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.RootCommandIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
 | 
			
		||||
public class SlashCommandDefinition {
 | 
			
		||||
 | 
			
		||||
    public static SlashCommandOptionChoice[] getActualChoices(SlashCommandOption option) {
 | 
			
		||||
        SlashCommandOptionChoice[] choices = option.choices();
 | 
			
		||||
        if (choices.length <= 0 && !option.choiceEnum().equals(PlaceHolderEnum.class))
 | 
			
		||||
            choices = EnumChoices.of(option.choiceEnum()).choices();
 | 
			
		||||
        return choices;
 | 
			
		||||
        CommandChoices choices = option.choices();
 | 
			
		||||
        SlashCommandOptionChoice[] actualChoices = choices.value();
 | 
			
		||||
        if (choices.value().length <= 0 && !choices.cenum().equals(PlaceHolderEnum.class))
 | 
			
		||||
            actualChoices = EnumChoices.of(choices.cenum()).choices();
 | 
			
		||||
        return actualChoices;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Set<InteractionIdentifier> entries;
 | 
			
		||||
    private RootCommandIdentifier rootIdentifier;
 | 
			
		||||
    private boolean isRootCommand;
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    public SlashCommandDefinition(RootCommandIdentifier rootIdentifier) {
 | 
			
		||||
        this.entries = new HashSet<>();
 | 
			
		||||
@@ -66,13 +68,13 @@ public class SlashCommandDefinition {
 | 
			
		||||
            .map(x -> x.parent())
 | 
			
		||||
            .toList();
 | 
			
		||||
 | 
			
		||||
        return InteractionIdentifier.distinct(subCommandGroups).toArray(SlashCommandIdentifier[]::new);
 | 
			
		||||
        return subCommandGroups.toArray(SlashCommandIdentifier[]::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SlashCommandIdentifier[] getSubCommands() {
 | 
			
		||||
        if (isRootCommand)
 | 
			
		||||
            return null;
 | 
			
		||||
        return InteractionIdentifier.distinct(entries.stream().filter(x -> x.parent() instanceof RootCommandIdentifier).toList()).toArray(SlashCommandIdentifier[]::new);
 | 
			
		||||
        return entries.stream().filter(x -> x.parent() instanceof RootCommandIdentifier).toArray(SlashCommandIdentifier[]::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SlashCommandIdentifier[] getSubCommands(String groupName) {
 | 
			
		||||
@@ -84,7 +86,7 @@ public class SlashCommandDefinition {
 | 
			
		||||
            .map(x -> x.parent().parent())
 | 
			
		||||
            .toList();
 | 
			
		||||
 | 
			
		||||
        return InteractionIdentifier.distinct(subCommands).toArray(SlashCommandIdentifier[]::new);
 | 
			
		||||
        return subCommands.toArray(SlashCommandIdentifier[]::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -95,6 +97,10 @@ public class SlashCommandDefinition {
 | 
			
		||||
        return this.rootIdentifier().equals(other.rootIdentifier());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long[] serverIds() {
 | 
			
		||||
        return rootIdentifier().serverIds();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Set<InteractionIdentifier> entries() {
 | 
			
		||||
        return this.entries;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
package net.tomatentum.marinara.interaction.commands.annotation;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption.PlaceHolderEnum;
 | 
			
		||||
 | 
			
		||||
public @interface CommandChoices {
 | 
			
		||||
    public SlashCommandOptionChoice[] value() default {};
 | 
			
		||||
    public Class<? extends Enum<?>> cenum() default PlaceHolderEnum.class;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
package net.tomatentum.marinara.interaction.commands.annotation;
 | 
			
		||||
 | 
			
		||||
public @interface Range {
 | 
			
		||||
    public double min() default Double.MIN_VALUE;
 | 
			
		||||
    public double max() default Double.MAX_VALUE;
 | 
			
		||||
}
 | 
			
		||||
@@ -5,6 +5,7 @@ import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.annotation.AutoComplete;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType;
 | 
			
		||||
 | 
			
		||||
@Target({ElementType.ANNOTATION_TYPE})
 | 
			
		||||
@@ -14,11 +15,11 @@ public @interface SlashCommandOption {
 | 
			
		||||
    public String description() default "";
 | 
			
		||||
    public SlashCommandOptionType type() default SlashCommandOptionType.STRING;
 | 
			
		||||
    public boolean required() default false;
 | 
			
		||||
    public boolean autocomplete() default false;
 | 
			
		||||
    public SlashCommandOptionChoice[] choices() default {};
 | 
			
		||||
    public Class<? extends Enum<?>> choiceEnum() default PlaceHolderEnum.class;
 | 
			
		||||
    public AutoComplete[] autocompletes() default {};
 | 
			
		||||
    public Range range() default @Range;
 | 
			
		||||
    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,93 @@
 | 
			
		||||
package net.tomatentum.marinara.interaction.components.methods;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.cutin.MethodParser;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory.ParserResults;
 | 
			
		||||
import net.tomatentum.cutin.container.MethodContainer;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
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.InteractionType;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.interaction.methods.InteractionMethod;
 | 
			
		||||
import net.tomatentum.marinara.parser.ButtonParser;
 | 
			
		||||
import net.tomatentum.marinara.parser.InteractionCheckParser;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
 | 
			
		||||
 | 
			
		||||
public class ButtonInteractionMethod extends InteractionMethod {
 | 
			
		||||
 | 
			
		||||
    private String customId;
 | 
			
		||||
    private ContextObjectProvider cop;
 | 
			
		||||
 | 
			
		||||
    private ButtonInteractionMethod(
 | 
			
		||||
            Method method, 
 | 
			
		||||
            InteractionHandler handler,
 | 
			
		||||
            List<AppliedCheck> appliedChecks,
 | 
			
		||||
            String customId, 
 | 
			
		||||
            ContextObjectProvider cop
 | 
			
		||||
        ) {
 | 
			
		||||
        super(method, handler, appliedChecks);
 | 
			
		||||
        this.customId = customId;
 | 
			
		||||
        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, ParserResults parserResults) {
 | 
			
		||||
            if (!(containingObject instanceof InteractionHandler)) return Optional.empty();
 | 
			
		||||
            String customId = parserResults.get(ButtonParser.class);
 | 
			
		||||
            if (customId == null) return Optional.empty();
 | 
			
		||||
            return Optional.of(new ButtonInteractionMethod(
 | 
			
		||||
                method, 
 | 
			
		||||
                (InteractionHandler) containingObject,
 | 
			
		||||
                parserResults.get(InteractionCheckParser.class),
 | 
			
		||||
                customId,
 | 
			
		||||
                this.cop
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void addParser(Set<MethodParser> parser) {
 | 
			
		||||
            super.addParser(parser);
 | 
			
		||||
 | 
			
		||||
            parser.add(
 | 
			
		||||
                new ButtonParser()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
package net.tomatentum.marinara.interaction.ident;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.InteractionType;
 | 
			
		||||
@@ -38,20 +35,6 @@ public class InteractionIdentifier {
 | 
			
		||||
        tryAddDescriptions(receiver.parent(), provider.parent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * TODO: Might not be the best solution. Propagating to future
 | 
			
		||||
     * returns only one Identifier per name and takes the first present description
 | 
			
		||||
     */
 | 
			
		||||
    public static Collection<InteractionIdentifier> distinct(List<InteractionIdentifier> identifiers) {
 | 
			
		||||
        HashMap<String, InteractionIdentifier> distinctIdentifiers = new HashMap<>();
 | 
			
		||||
        identifiers.forEach((x) -> {
 | 
			
		||||
            InteractionIdentifier current = distinctIdentifiers.get(x.name());
 | 
			
		||||
            if (current == null || (current.description().isBlank() && !x.description().isBlank()))
 | 
			
		||||
                distinctIdentifiers.put(x.name(), x);
 | 
			
		||||
        });
 | 
			
		||||
        return distinctIdentifiers.values();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private InteractionIdentifier parent;
 | 
			
		||||
    private String name;
 | 
			
		||||
    private String description;
 | 
			
		||||
@@ -97,14 +80,19 @@ public class InteractionIdentifier {
 | 
			
		||||
            return false;
 | 
			
		||||
        if (!name().equals(ident.name()))
 | 
			
		||||
            return false;
 | 
			
		||||
        return Objects.equals(ident, obj);
 | 
			
		||||
        return Objects.equals(parent(), ident.parent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        return Objects.hash(type(), name(), parent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        if (parent() == null)
 | 
			
		||||
            return name();
 | 
			
		||||
        return "{}.{}".formatted(name(), parent().toString());
 | 
			
		||||
            return name() + " - " + type();
 | 
			
		||||
        return "%s:%s".formatted(name(), parent().toString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Builder {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ public class RootCommandIdentifier extends SlashCommandIdentifier {
 | 
			
		||||
        private String description;
 | 
			
		||||
        private SlashCommandOption[] options;
 | 
			
		||||
        private long[] serverIds;
 | 
			
		||||
 | 
			
		||||
        private String[] autocompleteRef;
 | 
			
		||||
 | 
			
		||||
        public InteractionIdentifier parent() {
 | 
			
		||||
            return parent;
 | 
			
		||||
@@ -75,12 +75,21 @@ public class RootCommandIdentifier extends SlashCommandIdentifier {
 | 
			
		||||
            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(
 | 
			
		||||
                parent, 
 | 
			
		||||
                name, 
 | 
			
		||||
                description, 
 | 
			
		||||
                autocomplete ? InteractionType.AUTOCOMPLETE : InteractionType.COMMAND, 
 | 
			
		||||
                InteractionType.COMMAND, 
 | 
			
		||||
                options, 
 | 
			
		||||
                serverIds);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,7 @@ public class SlashCommandIdentifier extends InteractionIdentifier {
 | 
			
		||||
            String name, 
 | 
			
		||||
            String description,
 | 
			
		||||
            InteractionType type,
 | 
			
		||||
            SlashCommandOption[] options
 | 
			
		||||
            ) {
 | 
			
		||||
            SlashCommandOption[] options) {
 | 
			
		||||
        super(parent, name, description, type);
 | 
			
		||||
        this.options = options;
 | 
			
		||||
    }
 | 
			
		||||
@@ -27,6 +26,7 @@ public class SlashCommandIdentifier extends InteractionIdentifier {
 | 
			
		||||
        private String name;
 | 
			
		||||
        private String description;
 | 
			
		||||
        private SlashCommandOption[] options;
 | 
			
		||||
        private String[] autocompleteRef;
 | 
			
		||||
 | 
			
		||||
        public InteractionIdentifier parent() {
 | 
			
		||||
            return parent;
 | 
			
		||||
@@ -64,12 +64,21 @@ public class SlashCommandIdentifier extends InteractionIdentifier {
 | 
			
		||||
            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(
 | 
			
		||||
                parent, 
 | 
			
		||||
                name, 
 | 
			
		||||
                description, 
 | 
			
		||||
                autocomplete ? InteractionType.AUTOCOMPLETE : InteractionType.COMMAND, 
 | 
			
		||||
                InteractionType.COMMAND, 
 | 
			
		||||
                options);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +1,98 @@
 | 
			
		||||
package net.tomatentum.marinara.interaction.methods;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.Marinara;
 | 
			
		||||
import net.tomatentum.cutin.MethodParser;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory.ParserResults;
 | 
			
		||||
import net.tomatentum.cutin.container.MethodContainer;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
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.InteractionType;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.parser.AnnotationParser;
 | 
			
		||||
import net.tomatentum.marinara.parser.SlashCommandParser;
 | 
			
		||||
import net.tomatentum.marinara.parser.AutocompleteParser;
 | 
			
		||||
import net.tomatentum.marinara.parser.InteractionCheckParser;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
 | 
			
		||||
 | 
			
		||||
public class AutoCompleteInteractionMethod extends InteractionMethod {
 | 
			
		||||
 | 
			
		||||
    private InteractionIdentifier interactionIdentifier;
 | 
			
		||||
    private String autocompleteRef;
 | 
			
		||||
    private ContextObjectProvider cop;
 | 
			
		||||
 | 
			
		||||
    public AutoCompleteInteractionMethod(Method method, 
 | 
			
		||||
    private AutoCompleteInteractionMethod(
 | 
			
		||||
            Method method, 
 | 
			
		||||
            InteractionHandler handler,
 | 
			
		||||
        Marinara marinara
 | 
			
		||||
            List<AppliedCheck> appliedChecks,
 | 
			
		||||
            String autocompleteRef,
 | 
			
		||||
            ContextObjectProvider cop
 | 
			
		||||
        ) {
 | 
			
		||||
        super(method, handler, marinara);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AnnotationParser[] parsers() {
 | 
			
		||||
        return new AnnotationParser[] { 
 | 
			
		||||
            new SlashCommandParser(method, true, (x) -> { this.interactionIdentifier = x; } ) 
 | 
			
		||||
        };
 | 
			
		||||
        super(method, handler, appliedChecks);
 | 
			
		||||
        this.autocompleteRef = autocompleteRef;
 | 
			
		||||
        this.cop = cop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getParameter(Object context, int index) {
 | 
			
		||||
        Class<?> type = method().getParameterTypes()[index+1];
 | 
			
		||||
        Object autocompleteOptionValue = marinara.getWrapper().getContextObjectProvider().getAutocompleteFocusedOption(context);
 | 
			
		||||
        if (autocompleteOptionValue != null)
 | 
			
		||||
        Class<?> type = method().getParameterTypes()[index];
 | 
			
		||||
        Object contextObject = this.cop.getInteractionContextObject(context, type);
 | 
			
		||||
        if (contextObject != null)
 | 
			
		||||
            return contextObject;
 | 
			
		||||
 | 
			
		||||
        Object autocompleteOptionValue = this.cop.getAutocompleteFocusedOption(context).input();
 | 
			
		||||
        if (type.isInstance(autocompleteOptionValue))
 | 
			
		||||
            return autocompleteOptionValue;
 | 
			
		||||
 | 
			
		||||
        return marinara.getWrapper().getContextObjectProvider().getComponentContextObject(context, type);
 | 
			
		||||
        return super.getParameter(context, index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    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, ParserResults parserResults) {
 | 
			
		||||
            if (!(containingObject instanceof InteractionHandler)) return Optional.empty();
 | 
			
		||||
            String[] autocompletes = parserResults.get(AutocompleteParser.class);
 | 
			
		||||
            if (autocompletes.length <= 0) return Optional.empty();
 | 
			
		||||
            
 | 
			
		||||
            return Optional.of(new AutoCompleteInteractionMethod(
 | 
			
		||||
                method, 
 | 
			
		||||
                (InteractionHandler) containingObject,
 | 
			
		||||
                parserResults.get(InteractionCheckParser.class),
 | 
			
		||||
                autocompletes[0],
 | 
			
		||||
                cop
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void addParser(Set<MethodParser> parser) {
 | 
			
		||||
            super.addParser(parser);
 | 
			
		||||
 | 
			
		||||
            parser.add(
 | 
			
		||||
                new AutocompleteParser()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.InvocationTargetException;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.security.InvalidParameterException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.Marinara;
 | 
			
		||||
import net.tomatentum.cutin.MethodParser;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory;
 | 
			
		||||
import net.tomatentum.cutin.container.MethodContainer;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
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.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.parser.AnnotationParser;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    protected InteractionMethod(
 | 
			
		||||
        Method method, 
 | 
			
		||||
        InteractionHandler handler,
 | 
			
		||||
        Marinara marinara
 | 
			
		||||
        List<AppliedCheck> appliedChecks
 | 
			
		||||
        ) {
 | 
			
		||||
        if (!Arrays.asList(handler.getClass().getMethods()).contains(method))
 | 
			
		||||
            throw new InvalidParameterException("Method does not apply to specified handler");
 | 
			
		||||
 | 
			
		||||
        this.method = method;
 | 
			
		||||
        this.handler = handler;
 | 
			
		||||
        this.marinara = marinara;
 | 
			
		||||
        this.parsers = new ArrayList<>(Arrays.asList(parsers()));
 | 
			
		||||
        this.appliedChecks = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        parsers.add(new InteractionCheckParser(method, appliedChecks::add, marinara.getCheckRegistry()));
 | 
			
		||||
 | 
			
		||||
        parsers.stream().forEach(AnnotationParser::parse);
 | 
			
		||||
        super(method, handler);
 | 
			
		||||
        this.appliedChecks = appliedChecks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
            return;
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        method.setAccessible(true);
 | 
			
		||||
        try {
 | 
			
		||||
            method.invoke(handler, getParameters(context));
 | 
			
		||||
        }catch (IllegalAccessException | InvocationTargetException ex) {
 | 
			
		||||
            logger.fatal(ex);
 | 
			
		||||
        }
 | 
			
		||||
        result = super.run(context);
 | 
			
		||||
 | 
			
		||||
        this.appliedChecks.forEach(x -> x.post(context));
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract AnnotationParser[] parsers();
 | 
			
		||||
 | 
			
		||||
    public abstract Object getParameter(Object context, int index);
 | 
			
		||||
 | 
			
		||||
    public abstract InteractionIdentifier identifier();
 | 
			
		||||
 | 
			
		||||
    public Method method() {
 | 
			
		||||
        return method;
 | 
			
		||||
    public List<AppliedCheck> appliedChecks() {
 | 
			
		||||
        return this.appliedChecks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Object[] getParameters(Object context) {
 | 
			
		||||
        int parameterCount = method.getParameterCount();
 | 
			
		||||
        List<Object> parameters = new ArrayList<>();
 | 
			
		||||
    public abstract static class Factory implements ReflectedMethodFactory.Factory<InteractionIdentifier, Object> {
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < parameterCount; i++) {
 | 
			
		||||
            Object parameter;
 | 
			
		||||
            if (i == 0) {
 | 
			
		||||
                parameter = context;
 | 
			
		||||
            }else
 | 
			
		||||
                parameter = getParameter(context, i-1);
 | 
			
		||||
        private MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer;
 | 
			
		||||
 | 
			
		||||
            logger.trace("Found parameter {}={} for method {}", parameter != null ? parameter.getClass().toString() : " ", parameter, ReflectionUtil.getFullMethodName(method));
 | 
			
		||||
            parameters.add(parameter);   
 | 
			
		||||
        protected Factory(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer) {
 | 
			
		||||
            this.checkContainer = checkContainer;
 | 
			
		||||
        }
 | 
			
		||||
        return parameters.toArray();
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void addParser(Set<MethodParser> parser) {
 | 
			
		||||
            parser.add(
 | 
			
		||||
                new InteractionCheckParser(this.checkContainer)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +1,87 @@
 | 
			
		||||
package net.tomatentum.marinara.interaction.methods;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.Marinara;
 | 
			
		||||
import net.tomatentum.cutin.MethodParser;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory.ParserResults;
 | 
			
		||||
import net.tomatentum.cutin.container.MethodContainer;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
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.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.parser.AnnotationParser;
 | 
			
		||||
import net.tomatentum.marinara.parser.InteractionCheckParser;
 | 
			
		||||
import net.tomatentum.marinara.parser.SlashCommandParser;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
 | 
			
		||||
 | 
			
		||||
public class SlashCommandInteractionMethod extends InteractionMethod {
 | 
			
		||||
 | 
			
		||||
    private SlashCommandIdentifier interactionIdentifier;
 | 
			
		||||
    private SlashCommandIdentifier identifier;
 | 
			
		||||
    private ContextObjectProvider cop;
 | 
			
		||||
 | 
			
		||||
    SlashCommandInteractionMethod(Method method, InteractionHandler handler, Marinara marinara) {
 | 
			
		||||
        super(method, handler, marinara);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AnnotationParser[] parsers() {
 | 
			
		||||
        return new AnnotationParser[] { 
 | 
			
		||||
            new SlashCommandParser(method, false, (x) -> { this.interactionIdentifier = x; } ) 
 | 
			
		||||
        };
 | 
			
		||||
    private SlashCommandInteractionMethod(
 | 
			
		||||
            Method method, 
 | 
			
		||||
            InteractionHandler handler,
 | 
			
		||||
            List<AppliedCheck> appliedChecks,
 | 
			
		||||
            SlashCommandIdentifier identifier,
 | 
			
		||||
            ContextObjectProvider cop
 | 
			
		||||
        ) {
 | 
			
		||||
        super(method, handler, appliedChecks);
 | 
			
		||||
        this.identifier = identifier;
 | 
			
		||||
        this.cop = cop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    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, identifier.options()[index-1].name());
 | 
			
		||||
        else
 | 
			
		||||
            return superResult;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public InteractionIdentifier identifier() {
 | 
			
		||||
        return interactionIdentifier;
 | 
			
		||||
        return identifier;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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, ParserResults parserResults) {
 | 
			
		||||
            if (!(containingObject instanceof InteractionHandler)) return Optional.empty();
 | 
			
		||||
            SlashCommandIdentifier ident = parserResults.get(SlashCommandParser.class);
 | 
			
		||||
            if (ident == null) return Optional.empty();
 | 
			
		||||
            return Optional.of(new SlashCommandInteractionMethod(
 | 
			
		||||
                method, 
 | 
			
		||||
                (InteractionHandler) containingObject,
 | 
			
		||||
                parserResults.get(InteractionCheckParser.class),
 | 
			
		||||
                ident,
 | 
			
		||||
                cop
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void addParser(Set<MethodParser> parser) {
 | 
			
		||||
            super.addParser(parser);
 | 
			
		||||
 | 
			
		||||
            parser.add(
 | 
			
		||||
                new SlashCommandParser()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,27 @@
 | 
			
		||||
package net.tomatentum.marinara.parser;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
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());
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object parse(Method method, Object containingObject) {
 | 
			
		||||
        String[] autoCompletes = Arrays.stream(method.getAnnotationsByType(AutoComplete.class))
 | 
			
		||||
            .map(AutoComplete::value)
 | 
			
		||||
            .toArray(String[]::new);
 | 
			
		||||
        if (autoCompletes.length > 0)
 | 
			
		||||
            logger.trace("Parsed AutoComplete annotations {} for method {}", autoCompletes, ReflectionUtil.getFullMethodName(method));
 | 
			
		||||
        return autoCompletes;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -1,36 +1,27 @@
 | 
			
		||||
package net.tomatentum.marinara.parser;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.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.util.LoggerUtil;
 | 
			
		||||
import net.tomatentum.marinara.util.ReflectionUtil;
 | 
			
		||||
 | 
			
		||||
public class ButtonParser implements AnnotationParser {
 | 
			
		||||
public class ButtonParser implements MethodParser {
 | 
			
		||||
    
 | 
			
		||||
    private Method method;
 | 
			
		||||
    private Consumer<String> consumer;
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    public ButtonParser(Method method, Consumer<String> consumer) {
 | 
			
		||||
        this.method = method;
 | 
			
		||||
        this.consumer = consumer;
 | 
			
		||||
    public ButtonParser() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void parse() {
 | 
			
		||||
        Button button = getMethod().getAnnotation(Button.class);
 | 
			
		||||
        logger.trace("Parsed Button annotation {} for method {}", button.toString(), ReflectionUtil.getFullMethodName(method));
 | 
			
		||||
        this.consumer.accept(button.value());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Method getMethod() {
 | 
			
		||||
        return this.method;
 | 
			
		||||
    public Object parse(Method method, Object containingObject) {
 | 
			
		||||
        Button button = method.getAnnotation(Button.class);
 | 
			
		||||
        if (button == null) return null;
 | 
			
		||||
        logger.trace("Parsed Button annotation {} for method {}", button, ReflectionUtil.getFullMethodName(method));
 | 
			
		||||
        return button.value();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
package net.tomatentum.marinara.parser;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.lang.reflect.ParameterizedType;
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
 | 
			
		||||
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());
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object parse(Method method, Object containingObject) {
 | 
			
		||||
        ParameterizedType type = (ParameterizedType) GenericTypeReflector.getExactSuperType(containingObject.getClass(), InteractionCheck.class);
 | 
			
		||||
        if (type == null) return null;
 | 
			
		||||
        Type typeParam = type.getActualTypeArguments().length == 1 ? type.getActualTypeArguments()[0] : null;
 | 
			
		||||
        if (typeParam != null)
 | 
			
		||||
            logger.trace("Parsed InteractionCheck Annotation {}", typeParam);
 | 
			
		||||
        return typeParam;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -3,49 +3,47 @@ package net.tomatentum.marinara.parser;
 | 
			
		||||
import java.lang.annotation.Annotation;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.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.InteractionCheck;
 | 
			
		||||
import net.tomatentum.marinara.registry.InteractionCheckRegistry;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
import net.tomatentum.marinara.util.ReflectionUtil;
 | 
			
		||||
import net.tomatentum.marinara.checks.CheckExecutionContext;
 | 
			
		||||
import net.tomatentum.marinara.checks.CheckMethodIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.checks.CheckMethodIdentifier.CheckMethodType;
 | 
			
		||||
 | 
			
		||||
public class InteractionCheckParser implements AnnotationParser {
 | 
			
		||||
public class InteractionCheckParser implements MethodParser {
 | 
			
		||||
 | 
			
		||||
    private InteractionCheckRegistry checkRegistry;
 | 
			
		||||
    private Method method;
 | 
			
		||||
    private Consumer<AppliedCheck> consumer;
 | 
			
		||||
    private MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer;
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    public InteractionCheckParser(Method method, Consumer<AppliedCheck> consumer, InteractionCheckRegistry checkRegistry) {
 | 
			
		||||
        this.checkRegistry = checkRegistry;
 | 
			
		||||
        this.method = method;
 | 
			
		||||
        this.consumer = consumer;
 | 
			
		||||
    public InteractionCheckParser(MethodContainer<CheckMethodIdentifier, CheckExecutionContext> checkContainer) {
 | 
			
		||||
        this.checkContainer = checkContainer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void parse() {
 | 
			
		||||
    public Object parse(Method method, Object containingObject) {
 | 
			
		||||
        Annotation[] annotations = method.getAnnotations();
 | 
			
		||||
        Arrays.stream(annotations).forEach(this::convertAnnotation);
 | 
			
		||||
        return Arrays.stream(annotations)
 | 
			
		||||
            .map(a -> convertAnnotation(a, method))
 | 
			
		||||
            .filter(Objects::nonNull)
 | 
			
		||||
            .toList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void convertAnnotation(Annotation annotation) {
 | 
			
		||||
            Optional<InteractionCheck<?>> check = this.checkRegistry.getCheckFromAnnotation(annotation.annotationType());
 | 
			
		||||
            if (check.isPresent())  {
 | 
			
		||||
                AppliedCheck appliedCheck = new AppliedCheck(check.get(), annotation);
 | 
			
		||||
                logger.trace("Parsed InteractionCheck {} for annotation {} for method {}", check.getClass().getName(), annotation.toString(), ReflectionUtil.getFullMethodName(method));
 | 
			
		||||
                consumer.accept(appliedCheck);
 | 
			
		||||
    private AppliedCheck convertAnnotation(Annotation annotation, Method method) {
 | 
			
		||||
            var preExec = this.checkContainer.findFirstFor(new CheckMethodIdentifier(annotation.annotationType(), CheckMethodType.PRE));
 | 
			
		||||
            var postExec = this.checkContainer.findFirstFor(new CheckMethodIdentifier(annotation.annotationType(), CheckMethodType.POST));
 | 
			
		||||
            if (preExec.isPresent() && postExec.isPresent())  {
 | 
			
		||||
                AppliedCheck appliedCheck = new AppliedCheck(annotation, preExec.get(), postExec.get());
 | 
			
		||||
                logger.trace("Parsed InteractionCheck {} for annotation {} for method {}", preExec.get().containingObject(), annotation, ReflectionUtil.getFullMethodName(method));
 | 
			
		||||
                return appliedCheck;
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Method getMethod() {
 | 
			
		||||
        return this.method;
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,35 +1,28 @@
 | 
			
		||||
package net.tomatentum.marinara.parser;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.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.commands.annotation.SlashCommand;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SubCommand;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SubCommandGroup;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
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 boolean isAutoComplete;
 | 
			
		||||
    private Consumer<SlashCommandIdentifier> consumer;
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    public SlashCommandParser(Method method, boolean isAutoComplete, Consumer<SlashCommandIdentifier> consumer) {
 | 
			
		||||
        this.method = method;
 | 
			
		||||
        this.isAutoComplete = isAutoComplete;
 | 
			
		||||
        this.consumer = consumer;
 | 
			
		||||
    public SlashCommandParser() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void parse() {
 | 
			
		||||
    public Object parse(Method method, Object containingObject) {
 | 
			
		||||
        if (!method.isAnnotationPresent(SlashCommand.class) && !method.isAnnotationPresent(SubCommand.class)) return null;
 | 
			
		||||
        this.checkValidCommandMethod(method);
 | 
			
		||||
 | 
			
		||||
        SlashCommand cmd = ReflectionUtil.getAnnotation(method, SlashCommand.class);
 | 
			
		||||
@@ -38,14 +31,14 @@ public class SlashCommandParser implements AnnotationParser {
 | 
			
		||||
            .description(cmd.description())
 | 
			
		||||
            .options(cmd.options())
 | 
			
		||||
            .serverIds(cmd.serverIds())
 | 
			
		||||
            .build(isAutoComplete);
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        if (ReflectionUtil.isAnnotationPresent(method, SubCommandGroup.class)) {
 | 
			
		||||
            SubCommandGroup cmdGroup = ReflectionUtil.getAnnotation(method, SubCommandGroup.class);
 | 
			
		||||
            lastIdentifier = InteractionIdentifier.builder()
 | 
			
		||||
                .name(cmdGroup.name())
 | 
			
		||||
                .description(cmdGroup.description())
 | 
			
		||||
                .type(isAutoComplete ? InteractionType.AUTOCOMPLETE : InteractionType.COMMAND)
 | 
			
		||||
                .type(InteractionType.COMMAND)
 | 
			
		||||
                .parent(lastIdentifier)
 | 
			
		||||
                .build();
 | 
			
		||||
        }
 | 
			
		||||
@@ -56,30 +49,25 @@ public class SlashCommandParser implements AnnotationParser {
 | 
			
		||||
                .name(subCmd.name())
 | 
			
		||||
                .description(subCmd.description())
 | 
			
		||||
                .options(subCmd.options())
 | 
			
		||||
                .build(isAutoComplete);
 | 
			
		||||
                .build();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.trace("Parsed using SlashCommandParser for method {} with the result:\n{}", ReflectionUtil.getFullMethodName(method), lastIdentifier.toString());
 | 
			
		||||
        consumer.accept((SlashCommandIdentifier) lastIdentifier);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Method getMethod() {
 | 
			
		||||
       return this.method;
 | 
			
		||||
        logger.trace("Parsed using SlashCommandParser for method {} with the result: {}", ReflectionUtil.getFullMethodName(method), lastIdentifier);
 | 
			
		||||
        return lastIdentifier;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void checkValidCommandMethod(Method method) {
 | 
			
		||||
        if (method.isAnnotationPresent(SlashCommand.class) && 
 | 
			
		||||
            method.getDeclaringClass().isAnnotationPresent(SlashCommand.class)) {
 | 
			
		||||
            throw new RuntimeException(method.getName() + ": Can't have ApplicationCommand Annotation on Class and Method");
 | 
			
		||||
            throw new RuntimeException(method.getName() + ": Can't have SlashCommand Annotation on Class and Method");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!ReflectionUtil.isAnnotationPresent(method, SlashCommand.class))
 | 
			
		||||
            throw new RuntimeException(method.getName() + ": Missing ApplicationCommand Annotation on either Class or Method");
 | 
			
		||||
            throw new RuntimeException(method.getName() + ": Missing SlashCommand Annotation on either Class or Method");
 | 
			
		||||
 | 
			
		||||
        if ((method.isAnnotationPresent(SubCommand.class) && 
 | 
			
		||||
            !ReflectionUtil.isAnnotationPresent(method, SlashCommand.class))) {
 | 
			
		||||
            throw new RuntimeException(method.getName() + ": Missing ApplicationCommand Annotation on either Method or Class");
 | 
			
		||||
            throw new RuntimeException(method.getName() + ": Missing SlashCommand Annotation on either Method or 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.apache.logging.log4j.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.apache.logging.log4j.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,86 +0,0 @@
 | 
			
		||||
package net.tomatentum.marinara.registry;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.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.RootCommandIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.SlashCommandIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
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> entry = this.interactions.stream().filter(iMethod::equals).findFirst();
 | 
			
		||||
                if (entry.isEmpty()) {
 | 
			
		||||
                    interactions.add(new InteractionEntry(iMethod.identifier()).addMethod(iMethod));
 | 
			
		||||
                }else
 | 
			
		||||
                    entry.get().addMethod(iMethod);
 | 
			
		||||
                logger.debug("Added {} method from {}", iMethod.method().getName(), interactionHandler.getClass().getSimpleName());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        logger.info("Added all Interactions from {}", interactionHandler.getClass().getSimpleName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void registerCommands() {
 | 
			
		||||
        List<SlashCommandDefinition> defs = new ArrayList<>();
 | 
			
		||||
        List<SlashCommandIdentifier> slashIdentifiers = interactions.stream()
 | 
			
		||||
            .filter((x) -> x.type().equals(InteractionType.COMMAND))
 | 
			
		||||
            .map((x) -> (SlashCommandIdentifier)x.identifier())
 | 
			
		||||
            .toList();
 | 
			
		||||
 | 
			
		||||
        slashIdentifiers.forEach((ident) -> {
 | 
			
		||||
            Optional<SlashCommandDefinition> appDef = defs.stream()
 | 
			
		||||
                .filter((x) -> x.rootIdentifier().equals(ident.rootNode()))
 | 
			
		||||
                .findFirst();
 | 
			
		||||
 | 
			
		||||
            if (appDef.isPresent())
 | 
			
		||||
                appDef.get().addIdentifier(ident);
 | 
			
		||||
            else
 | 
			
		||||
                defs.add(
 | 
			
		||||
                    new SlashCommandDefinition((RootCommandIdentifier) ident.rootNode())
 | 
			
		||||
                        .addIdentifier(ident));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        marinara.getWrapper().registerSlashCommands(defs.toArray(SlashCommandDefinition[]::new));
 | 
			
		||||
        logger.info("Registered all SlashCommands");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,64 @@
 | 
			
		||||
package net.tomatentum.marinara.structure;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.cutin.MethodParser;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory;
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactory.ParserResults;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.structure.parser.ComponentStructureParser;
 | 
			
		||||
 | 
			
		||||
public class ComponentStructureMethod extends ReflectedMethod<InteractionIdentifier, Void> {
 | 
			
		||||
 | 
			
		||||
    private InteractionIdentifier identifier;
 | 
			
		||||
 | 
			
		||||
    private ComponentStructureMethod(
 | 
			
		||||
            Method method, 
 | 
			
		||||
            Object containingObject,
 | 
			
		||||
            InteractionIdentifier identifier
 | 
			
		||||
        ) {
 | 
			
		||||
        super(method, containingObject);
 | 
			
		||||
        this.identifier = identifier;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getParameter(Void context, int index) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public InteractionIdentifier identifier() {
 | 
			
		||||
        return this.identifier;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Factory implements ReflectedMethodFactory.Factory<InteractionIdentifier, Void> {
 | 
			
		||||
 | 
			
		||||
        private Class<? extends Object> buttonClass;
 | 
			
		||||
 | 
			
		||||
        public Factory(Class<? extends Object> buttonClass) {
 | 
			
		||||
            this.buttonClass = buttonClass;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void addParser(Set<MethodParser> parser) {
 | 
			
		||||
            parser.add(new ComponentStructureParser(buttonClass));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public Optional<ReflectedMethod<InteractionIdentifier, Void>> produce(
 | 
			
		||||
                Method method, 
 | 
			
		||||
                Object containingObject,
 | 
			
		||||
                ParserResults parserResults) {
 | 
			
		||||
            InteractionIdentifier identifier = parserResults.get(ComponentStructureParser.class);
 | 
			
		||||
            if (identifier == null)
 | 
			
		||||
                return Optional.empty();
 | 
			
		||||
            
 | 
			
		||||
            return Optional.of(new ComponentStructureMethod(method, containingObject, identifier));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
package net.tomatentum.marinara.structure;
 | 
			
		||||
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.cutin.ReflectedMethodFactoryImpl;
 | 
			
		||||
import net.tomatentum.cutin.container.LoneMethodContainer;
 | 
			
		||||
import net.tomatentum.cutin.method.ReflectedMethod;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
 | 
			
		||||
public class MethodStructureProvider<B extends Object> 
 | 
			
		||||
    extends LoneMethodContainer<InteractionIdentifier, Void>
 | 
			
		||||
    implements StructureProvider<B> {
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Class<? extends B> buttonClass;
 | 
			
		||||
 | 
			
		||||
    public MethodStructureProvider(Class<? extends B> buttonClass) {
 | 
			
		||||
        super(new ReflectedMethodFactoryImpl<>());
 | 
			
		||||
        super.factory().addFactory(new ComponentStructureMethod.Factory(buttonClass));
 | 
			
		||||
        this.buttonClass = buttonClass;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public B button(InteractionIdentifier identifier) {
 | 
			
		||||
        Optional<ReflectedMethod<InteractionIdentifier, Void>> method = super.findFirstFor(identifier);
 | 
			
		||||
        if (method.isEmpty()) return null;
 | 
			
		||||
        try {
 | 
			
		||||
            return buttonClass.cast(method.get().run(null));
 | 
			
		||||
        }catch (ClassCastException ex) {
 | 
			
		||||
            logger.warn("Structure Method {} return type did not match expected {}", method.get(), buttonClass);
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
package net.tomatentum.marinara.structure;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.TreeMap;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
 | 
			
		||||
public class PriorityStructureProvider<B extends Object> implements StructureProvider<B> {
 | 
			
		||||
 | 
			
		||||
    private Map<Short, StructureProvider<B>> providerMap;
 | 
			
		||||
 | 
			
		||||
    public PriorityStructureProvider() {
 | 
			
		||||
        this.providerMap = new TreeMap<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public PriorityStructureProvider<B> addProvider(StructureProvider<B> provider, short priority) {
 | 
			
		||||
        this.providerMap.put(priority, provider);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public B button(InteractionIdentifier identifier) {
 | 
			
		||||
        for (short priority : this.providerMap.keySet()) {
 | 
			
		||||
            B result = this.providerMap.get(priority).button(identifier);
 | 
			
		||||
            if (result != null)
 | 
			
		||||
                return result;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
package net.tomatentum.marinara.structure;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.structure.data.ButtonStructureData;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ComponentStructureConverter;
 | 
			
		||||
 | 
			
		||||
public class StaticStructureProvider<B extends Object> implements StructureProvider<B> {
 | 
			
		||||
 | 
			
		||||
    private ComponentStructureConverter<B> converter;
 | 
			
		||||
 | 
			
		||||
    private Map<InteractionIdentifier, B> buttonMap;
 | 
			
		||||
 | 
			
		||||
    public StaticStructureProvider(ComponentStructureConverter<B> converter) {
 | 
			
		||||
        this.converter = converter;
 | 
			
		||||
        this.buttonMap = new HashMap<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StaticStructureProvider<B> addButton(InteractionIdentifier identifier, ButtonStructureData data) {
 | 
			
		||||
        this.buttonMap.put(identifier, this.converter.convertButton(data));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public B button(InteractionIdentifier identifier) {
 | 
			
		||||
        return this.buttonMap.get(identifier);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
package net.tomatentum.marinara.structure;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
 | 
			
		||||
public interface StructureProvider<B extends Object> {
 | 
			
		||||
    B button(InteractionIdentifier identifier);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
package net.tomatentum.marinara.structure.annotation;
 | 
			
		||||
 | 
			
		||||
public @interface ComponentStructure {
 | 
			
		||||
    public String customId();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
package net.tomatentum.marinara.structure.data;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.annotation.Button;
 | 
			
		||||
 | 
			
		||||
public record ButtonStructureData(
 | 
			
		||||
    String customId, 
 | 
			
		||||
    String label,
 | 
			
		||||
    ButtonStyle style,
 | 
			
		||||
    String url,
 | 
			
		||||
    boolean disabled,
 | 
			
		||||
    String emoji
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
    public ButtonStructureData(Button button) {
 | 
			
		||||
        this(button.value(), button.label(), button.style(), button.url(), button.disabled(), button.emoji());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum ButtonStyle {
 | 
			
		||||
        PRIMARY,
 | 
			
		||||
        SECONDARY,
 | 
			
		||||
        SUCCESS,
 | 
			
		||||
        DANGER,
 | 
			
		||||
        LINK;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
package net.tomatentum.marinara.structure.parser;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
 | 
			
		||||
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.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.structure.annotation.ComponentStructure;
 | 
			
		||||
 | 
			
		||||
public class ComponentStructureParser implements MethodParser {
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Class<? extends Object> buttonClass;
 | 
			
		||||
 | 
			
		||||
    public ComponentStructureParser(Class<? extends Object> buttonClass) {
 | 
			
		||||
        this.buttonClass = buttonClass;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object parse(Method method, Object containingObject) {
 | 
			
		||||
        if (!method.isAnnotationPresent(ComponentStructure.class)) 
 | 
			
		||||
            return null;
 | 
			
		||||
        InteractionIdentifier.Builder builder = InteractionIdentifier.builder()
 | 
			
		||||
            .name(method.getAnnotation(ComponentStructure.class).customId());
 | 
			
		||||
 | 
			
		||||
        if (buttonClass.isAssignableFrom(method.getReturnType()))
 | 
			
		||||
            builder.type(InteractionType.BUTTON);
 | 
			
		||||
 | 
			
		||||
        if (builder.type() == null) {
 | 
			
		||||
            logger.error("Structure Method {} return type did not match any of the required {}", String.join(","), buttonClass);
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.trace("Parsed Structure Method of {} with result {}", ReflectionUtil.getFullMethodName(method), builder.build());
 | 
			
		||||
        return builder.build();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
package net.tomatentum.marinara.util;
 | 
			
		||||
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Level;
 | 
			
		||||
import org.apache.logging.log4j.LogManager;
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import org.apache.logging.log4j.simple.SimpleLogger;
 | 
			
		||||
import org.apache.logging.log4j.util.PropertiesUtil;
 | 
			
		||||
import org.apache.logging.log4j.util.ProviderUtil;
 | 
			
		||||
 | 
			
		||||
public class LoggerUtil {
 | 
			
		||||
    public static Logger getLogger(String name) {
 | 
			
		||||
        if (ProviderUtil.hasProviders()) {
 | 
			
		||||
            return LogManager.getLogger(name);
 | 
			
		||||
        }else
 | 
			
		||||
            return new SimpleLogger(name, Level.DEBUG, true, false, true, true, "yyyy-MM-dd HH:mm:ss.SSSZ", null,
 | 
			
		||||
                                                 new PropertiesUtil(new Properties()), System.out);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Logger getLogger(Class<?> clazz) {
 | 
			
		||||
        return getLogger(clazz.getName());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,46 @@
 | 
			
		||||
package net.tomatentum.marinara.util;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.BiConsumer;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
public class ObjectAggregator<O, K, V> {
 | 
			
		||||
    private Function<O, Iterable<K>> keySupplier;
 | 
			
		||||
    private BiConsumer<V, O> valueConsumer;
 | 
			
		||||
    private Function<K, V> defaultGenerator;
 | 
			
		||||
 | 
			
		||||
    public ObjectAggregator(
 | 
			
		||||
            Function<O, Iterable<K>> keySupplier, 
 | 
			
		||||
            BiConsumer<V, O> valueConsumer, 
 | 
			
		||||
            Function<K, V> defaultGenerator) {
 | 
			
		||||
        this.keySupplier = keySupplier;
 | 
			
		||||
        this.valueConsumer = valueConsumer;
 | 
			
		||||
        this.defaultGenerator = defaultGenerator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ObjectAggregator(
 | 
			
		||||
            Function<O, Iterable<K>> keySupplier, 
 | 
			
		||||
            BiConsumer<V, O> valueConsumer, 
 | 
			
		||||
            Supplier<V> defaultGenerator) {
 | 
			
		||||
        this.keySupplier = keySupplier;
 | 
			
		||||
        this.valueConsumer = valueConsumer;
 | 
			
		||||
        this.defaultGenerator = _ -> defaultGenerator.get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Collection<V> aggregate(Iterable<O> iterator) {
 | 
			
		||||
        Map<K, V> map = new HashMap<>();
 | 
			
		||||
        for (O element : iterator) {
 | 
			
		||||
            Iterable<K> keys = this.keySupplier.apply(element);
 | 
			
		||||
            for (K key : keys) {
 | 
			
		||||
                V value = map.getOrDefault(key, this.defaultGenerator.apply(key));
 | 
			
		||||
                this.valueConsumer.accept(value, element);
 | 
			
		||||
                map.putIfAbsent(key, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return map.values();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
package net.tomatentum.marinara.util;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
public class ObjectListAggregator<O, K, V> extends ObjectAggregator<O, K, ArrayList<V>> {
 | 
			
		||||
 | 
			
		||||
    public ObjectListAggregator(Function<O, Iterable<K>> keySupplier, Function<O, V> valueConsumer) {
 | 
			
		||||
        super(keySupplier, 
 | 
			
		||||
        (l, o) -> l.add(valueConsumer.apply(o)),
 | 
			
		||||
        () -> new ArrayList<>());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -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.getClass().getName() + "." + method.getName();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,9 @@ import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOptionChoice;
 | 
			
		||||
@@ -17,6 +20,8 @@ public class CommandConverter<A extends Object, O extends Object, C extends Obje
 | 
			
		||||
        return new CommandConverter<>(spec);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Spec<A, O, C> spec;
 | 
			
		||||
 | 
			
		||||
    CommandConverter(Spec<A, O, C> spec) {
 | 
			
		||||
@@ -24,6 +29,7 @@ public class CommandConverter<A extends Object, O extends Object, C extends Obje
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public A convert(SlashCommandDefinition def) {
 | 
			
		||||
        logger.debug("Converting command {}", def);
 | 
			
		||||
        List<O> options = new ArrayList<>();
 | 
			
		||||
        if (!def.isRootCommand()) {
 | 
			
		||||
            Arrays.stream(def.getSubCommands()).map(this::convertSubCommand).forEach(options::add);
 | 
			
		||||
@@ -35,17 +41,20 @@ public class CommandConverter<A extends Object, O extends Object, C extends Obje
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private O convertSubCommandGroup(SlashCommandDefinition def, InteractionIdentifier identifier) {
 | 
			
		||||
        logger.debug("Converting subCommandGroup {} of {}", identifier, def);
 | 
			
		||||
        SlashCommandIdentifier[] subCommands = def.getSubCommands(identifier.name());
 | 
			
		||||
        List<O> convertedSubCommands = Arrays.stream(subCommands).map(this::convertSubCommand).toList();
 | 
			
		||||
        return spec.convertSubCommandGroup(identifier, convertedSubCommands);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private O convertSubCommand(SlashCommandIdentifier identifier) {
 | 
			
		||||
        logger.debug("Converting subCommand {}", identifier);
 | 
			
		||||
        List<O> options = Arrays.stream(identifier.options()).map(this::convertOption).toList();
 | 
			
		||||
        return spec.convertSubCommand(identifier, options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private O convertOption(SlashCommandOption option) {
 | 
			
		||||
        logger.debug("Converting option {}", option);
 | 
			
		||||
        List<C> choices = Arrays.stream(SlashCommandDefinition.getActualChoices(option)).map(spec::convertChoice).toList();
 | 
			
		||||
        return spec.convertOption(option, choices);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition;
 | 
			
		||||
import net.tomatentum.marinara.util.ObjectAggregator;
 | 
			
		||||
 | 
			
		||||
public class CommandRegisterer<A extends Object> {
 | 
			
		||||
 | 
			
		||||
    public static <A extends Object> CommandRegisterer<A> of(Strategy<A> strategy, CommandConverter<A, ?, ?> converter) {
 | 
			
		||||
        return new CommandRegisterer<A>(strategy, converter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Strategy<A> strategy;
 | 
			
		||||
    private CommandConverter<A, ?, ?> converter;
 | 
			
		||||
 | 
			
		||||
    CommandRegisterer(Strategy<A> strategy, CommandConverter<A, ?, ?> converter) {
 | 
			
		||||
        this.strategy = strategy;
 | 
			
		||||
        this.converter = converter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void register(SlashCommandDefinition[] slashDefs) {
 | 
			
		||||
        Set<ServerCommandList<A>> serverCommands = new ObjectAggregator<SlashCommandDefinition, Long, ServerCommandList<A>>(
 | 
			
		||||
                def -> Arrays.stream(def.serverIds()).boxed().toList(),
 | 
			
		||||
                (l, o) -> {
 | 
			
		||||
                    logger.debug("Added {} for server ({}) registration.", o.rootIdentifier(), l.serverId());
 | 
			
		||||
                    l.add(converter.convert(o));
 | 
			
		||||
                },
 | 
			
		||||
                ServerCommandList::new)
 | 
			
		||||
            .aggregate(Arrays.asList(slashDefs)).stream()
 | 
			
		||||
            .collect(Collectors.toSet());
 | 
			
		||||
 | 
			
		||||
        Set<A> globalCommands = Arrays.stream(slashDefs)
 | 
			
		||||
            .filter(x -> x.serverIds().length <= 0)
 | 
			
		||||
            .peek(c -> logger.debug("Added {} for global registration.", c.rootIdentifier()))
 | 
			
		||||
            .map(converter::convert)
 | 
			
		||||
            .collect(Collectors.toSet());
 | 
			
		||||
 | 
			
		||||
        serverCommands.forEach(strategy::registerServer);
 | 
			
		||||
        strategy.registerGlobal(globalCommands);
 | 
			
		||||
        logger.info("Registered all SlashCommands");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public interface Strategy<A extends Object> {
 | 
			
		||||
        void registerServer(ServerCommandList<A> commands);
 | 
			
		||||
        void registerGlobal(Set<A> defs);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.structure.data.ButtonStructureData;
 | 
			
		||||
 | 
			
		||||
public interface ComponentStructureConverter<B extends Object> {
 | 
			
		||||
 | 
			
		||||
    B convertButton(ButtonStructureData data);
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData;
 | 
			
		||||
 | 
			
		||||
public interface ContextObjectProvider {
 | 
			
		||||
 | 
			
		||||
    public Object convertCommandOption(Object context, String optionName);
 | 
			
		||||
@@ -7,5 +9,5 @@ public interface ContextObjectProvider {
 | 
			
		||||
    public Object getComponentContextObject(Object context, Class<?> type);
 | 
			
		||||
    public Object getInteractionContextObject(Object context, Class<?> type);
 | 
			
		||||
 | 
			
		||||
    public Object getAutocompleteFocusedOption(Object context);
 | 
			
		||||
    public AutocompleteOptionData getAutocompleteFocusedOption(Object context);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import io.leangen.geantyref.GenericTypeReflector;
 | 
			
		||||
import net.tomatentum.cutin.util.ReflectionUtil;
 | 
			
		||||
import net.tomatentum.marinara.interaction.ident.InteractionIdentifier;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
import net.tomatentum.marinara.util.ReflectionUtil;
 | 
			
		||||
 | 
			
		||||
public class IdentifierProvider {
 | 
			
		||||
 | 
			
		||||
@@ -20,8 +20,9 @@ public class IdentifierProvider {
 | 
			
		||||
        return new IdentifierProvider(Arrays.asList(converter));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private Map<Class<?>, Converter<?>> converter;
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    private IdentifierProvider(List<Converter<?>> converter) {
 | 
			
		||||
        this.converter = new HashMap<>();
 | 
			
		||||
@@ -29,7 +30,7 @@ public class IdentifierProvider {
 | 
			
		||||
            if (conv.getClass().getName().contains("$$Lambda"))
 | 
			
		||||
                throw new IllegalArgumentException("Lambdas cannot be used for IdentifierConverter because of Type erasure.");
 | 
			
		||||
            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))
 | 
			
		||||
                throw new IllegalArgumentException("Only full Class types are supported by IdentiferConverters");
 | 
			
		||||
            this.converter.put((Class<?>) parameterType, conv);
 | 
			
		||||
@@ -42,12 +43,14 @@ public class IdentifierProvider {
 | 
			
		||||
            context.getClass());
 | 
			
		||||
 | 
			
		||||
        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")
 | 
			
		||||
        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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,6 @@ package net.tomatentum.marinara.wrapper;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition;
 | 
			
		||||
 | 
			
		||||
public abstract class LibraryWrapper {
 | 
			
		||||
 | 
			
		||||
    private List<Consumer<Object>> interactionSubscriber;
 | 
			
		||||
@@ -15,7 +12,7 @@ public abstract class LibraryWrapper {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void handleInteraction(Object context) {
 | 
			
		||||
        interactionSubscriber.forEach((o) -> o.accept(context));
 | 
			
		||||
        interactionSubscriber.forEach(o -> o.accept(context));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void subscribeInteractions(Consumer<Object> consumer) {
 | 
			
		||||
@@ -25,9 +22,9 @@ public abstract class LibraryWrapper {
 | 
			
		||||
        interactionSubscriber.remove(consumer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract void registerSlashCommands(SlashCommandDefinition[] defs); 
 | 
			
		||||
    
 | 
			
		||||
    public abstract CommandRegisterer<?> getRegisterer();  
 | 
			
		||||
    public abstract IdentifierProvider createIdentifierProvider();
 | 
			
		||||
    public abstract ContextObjectProvider getContextObjectProvider();
 | 
			
		||||
    public abstract void respondAutocomplete(Object context, List<Object> options);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper;
 | 
			
		||||
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
 | 
			
		||||
public class ServerCommandList<A> extends HashSet<A>{
 | 
			
		||||
 | 
			
		||||
    private long serverId;
 | 
			
		||||
    
 | 
			
		||||
    public ServerCommandList(long serverId) {
 | 
			
		||||
        this.serverId = serverId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long serverId() {
 | 
			
		||||
        return serverId;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,20 +10,18 @@ plugins {
 | 
			
		||||
    `java-library`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    // Use Maven Central for resolving dependencies.
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    // Use JUnit Jupiter for testing.
 | 
			
		||||
    testImplementation(libs.junit.jupiter)
 | 
			
		||||
    testImplementation(libs.mockito)
 | 
			
		||||
    testImplementation(libs.discord4j)
 | 
			
		||||
 | 
			
		||||
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 | 
			
		||||
    implementation(libs.log4j)
 | 
			
		||||
    testImplementation(libs.slf4j.simple)
 | 
			
		||||
 | 
			
		||||
    implementation(libs.slf4j)
 | 
			
		||||
    implementation(libs.discord4j) {
 | 
			
		||||
//        exclude(module="discord4j-voice")
 | 
			
		||||
        exclude(module="discord4j-voice")
 | 
			
		||||
    }
 | 
			
		||||
    implementation(libs.geantyref)
 | 
			
		||||
    implementation(project(":lib"))
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,9 @@ import java.util.List;
 | 
			
		||||
import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent;
 | 
			
		||||
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
 | 
			
		||||
import discord4j.core.event.domain.interaction.ComponentInteractionEvent;
 | 
			
		||||
import discord4j.core.event.domain.interaction.InteractionCreateEvent;
 | 
			
		||||
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.wrapper.ContextObjectProvider;
 | 
			
		||||
 | 
			
		||||
@@ -79,24 +81,25 @@ public class Discord4JContextObjectProvider implements ContextObjectProvider {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getInteractionContextObject(Object context, Class<?> type) {
 | 
			
		||||
        ComponentInteractionEvent componentInteractionEvent = (ComponentInteractionEvent) context;
 | 
			
		||||
        InteractionCreateEvent interactionEvent = (InteractionCreateEvent) context;
 | 
			
		||||
        switch (type.getName()) {
 | 
			
		||||
            case "discord4j.core.object.entity.channel.MessageChannel":
 | 
			
		||||
                return componentInteractionEvent.getInteraction().getChannel().block();
 | 
			
		||||
                return interactionEvent.getInteraction().getChannel().block();
 | 
			
		||||
            case "discord4j.core.object.entity.Guild":
 | 
			
		||||
                return componentInteractionEvent.getInteraction().getGuild().block();
 | 
			
		||||
                return interactionEvent.getInteraction().getGuild().block();
 | 
			
		||||
            case "discord4j.core.object.entity.Member":
 | 
			
		||||
                return componentInteractionEvent.getInteraction().getMember().orElse(null);
 | 
			
		||||
                return interactionEvent.getInteraction().getMember().orElse(null);
 | 
			
		||||
            case "discord4j.core.object.entity.User":
 | 
			
		||||
                return componentInteractionEvent.getInteraction().getUser();
 | 
			
		||||
        }
 | 
			
		||||
                return interactionEvent.getInteraction().getUser();
 | 
			
		||||
            default:
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getAutocompleteFocusedOption(Object context) {
 | 
			
		||||
        ChatInputAutoCompleteEvent interaction = (ChatInputAutoCompleteEvent) context;
 | 
			
		||||
        return getOptionValue(interaction.getFocusedOption());
 | 
			
		||||
    public AutocompleteOptionData getAutocompleteFocusedOption(Object context) {
 | 
			
		||||
        ApplicationCommandInteractionOption option = ((ChatInputAutoCompleteEvent) context).getFocusedOption();
 | 
			
		||||
        return new AutocompleteOptionData(option.getName(), getOptionValue(option));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,11 @@ public class Discord4JConverterSpec implements CommandConverter.Spec<Application
 | 
			
		||||
            .name(option.name())
 | 
			
		||||
            .description(option.description())
 | 
			
		||||
            .required(option.required())
 | 
			
		||||
            .autocomplete(option.autocomplete())
 | 
			
		||||
            .autocomplete(option.autocompletes().length > 0)
 | 
			
		||||
			.minLength((int) option.range().min())
 | 
			
		||||
			.minValue(option.range().min())
 | 
			
		||||
			.maxLength((int)option.range().max())
 | 
			
		||||
			.maxValue(option.range().max())
 | 
			
		||||
            .choices(choices)
 | 
			
		||||
            .build();
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper.discord4j;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import discord4j.core.GatewayDiscordClient;
 | 
			
		||||
import discord4j.discordjson.json.ApplicationCommandRequest;
 | 
			
		||||
import discord4j.rest.service.ApplicationService;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.CommandRegisterer;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ServerCommandList;
 | 
			
		||||
 | 
			
		||||
public class Discord4JRegistererStrategy implements CommandRegisterer.Strategy<ApplicationCommandRequest> {
 | 
			
		||||
 | 
			
		||||
    private ApplicationService appService;
 | 
			
		||||
    private long applicationId;
 | 
			
		||||
 | 
			
		||||
    public Discord4JRegistererStrategy(GatewayDiscordClient api) {
 | 
			
		||||
        this.appService = api.getRestClient().getApplicationService();
 | 
			
		||||
        this.applicationId = api.getRestClient().getApplicationId().block();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void registerServer(ServerCommandList<ApplicationCommandRequest> commands) {
 | 
			
		||||
        appService.bulkOverwriteGuildApplicationCommand(applicationId, commands.serverId(), new ArrayList<>(commands));
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void registerGlobal(Set<ApplicationCommandRequest> defs) {
 | 
			
		||||
        appService.bulkOverwriteGlobalApplicationCommand(applicationId, new ArrayList<>(defs));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +1,21 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper.discord4j;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.function.UnaryOperator;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import discord4j.core.GatewayDiscordClient;
 | 
			
		||||
import discord4j.core.event.domain.interaction.ChatInputAutoCompleteEvent;
 | 
			
		||||
import discord4j.core.event.domain.interaction.InteractionCreateEvent;
 | 
			
		||||
import discord4j.core.object.command.ApplicationCommandInteractionOption;
 | 
			
		||||
import discord4j.core.object.command.ApplicationCommandOption.Type;
 | 
			
		||||
import discord4j.discordjson.json.ApplicationCommandOptionChoiceData;
 | 
			
		||||
import discord4j.discordjson.json.ApplicationCommandOptionData;
 | 
			
		||||
import discord4j.discordjson.json.ApplicationCommandRequest;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.CommandConverter;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.CommandRegisterer;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.IdentifierProvider;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.LibraryWrapper;
 | 
			
		||||
@@ -26,58 +24,38 @@ import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.ButtonIdent
 | 
			
		||||
import net.tomatentum.marinara.wrapper.discord4j.identifierconverter.SlashCommandIdentifierConverter;
 | 
			
		||||
 | 
			
		||||
public class Discord4JWrapper extends LibraryWrapper {
 | 
			
		||||
 | 
			
		||||
    public static final Function<List<ApplicationCommandInteractionOption>, List<ApplicationCommandInteractionOption>> SUB_FILTER = (i) ->
 | 
			
		||||
    public static final UnaryOperator<List<ApplicationCommandInteractionOption>> SUB_FILTER = i ->
 | 
			
		||||
        i.stream()
 | 
			
		||||
            .filter(o -> o.getType().equals(Type.SUB_COMMAND) || o.getType().equals(Type.SUB_COMMAND_GROUP))
 | 
			
		||||
            .toList();
 | 
			
		||||
    
 | 
			
		||||
    public static final Function<List<ApplicationCommandInteractionOption>, List<ApplicationCommandInteractionOption>> ARG_FILTER = (i) ->
 | 
			
		||||
    public static final UnaryOperator<List<ApplicationCommandInteractionOption>> ARG_FILTER = i ->
 | 
			
		||||
            i.stream()
 | 
			
		||||
                .filter(o -> !o.getType().equals(Type.SUB_COMMAND) && !o.getType().equals(Type.SUB_COMMAND_GROUP))
 | 
			
		||||
                .toList();
 | 
			
		||||
 | 
			
		||||
    private GatewayDiscordClient api;
 | 
			
		||||
    private Discord4JContextObjectProvider contextObjectProvider;
 | 
			
		||||
    private CommandConverter<ApplicationCommandRequest, ApplicationCommandOptionData, ApplicationCommandOptionChoiceData> commandConverter;
 | 
			
		||||
    private CommandRegisterer<ApplicationCommandRequest> commandRegisterer;
 | 
			
		||||
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    public Discord4JWrapper(GatewayDiscordClient api) {
 | 
			
		||||
        this.api = api;
 | 
			
		||||
        this.contextObjectProvider = new Discord4JContextObjectProvider();
 | 
			
		||||
        this.commandConverter = CommandConverter.of(new Discord4JConverterSpec());
 | 
			
		||||
        var converter = CommandConverter.of(new Discord4JConverterSpec());
 | 
			
		||||
 | 
			
		||||
        if (api != null)
 | 
			
		||||
        if (api != null) {
 | 
			
		||||
            this.commandRegisterer = CommandRegisterer.of(new Discord4JRegistererStrategy(api), converter);
 | 
			
		||||
            api.on(InteractionCreateEvent.class)
 | 
			
		||||
                .subscribe(event -> handleInteraction(event));
 | 
			
		||||
        else
 | 
			
		||||
        }else
 | 
			
		||||
            logger.warn("GatewayDiscordClient was null so no Events were subscribed to.");
 | 
			
		||||
            
 | 
			
		||||
        logger.info("Discord4J wrapper loaded!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void registerSlashCommands(SlashCommandDefinition[] defs) {
 | 
			
		||||
        HashMap<Long, List<ApplicationCommandRequest>> serverCommands = new HashMap<>();
 | 
			
		||||
        List<ApplicationCommandRequest> globalCommands = new ArrayList<>();
 | 
			
		||||
        long applicationId = api.getRestClient().getApplicationId().block();
 | 
			
		||||
 | 
			
		||||
        for (SlashCommandDefinition slashCommandDefinition : defs) {
 | 
			
		||||
            ApplicationCommandRequest request = this.commandConverter.convert(slashCommandDefinition);
 | 
			
		||||
            if (slashCommandDefinition.rootIdentifier().serverIds().length > 0) {
 | 
			
		||||
                for (long serverId : slashCommandDefinition.rootIdentifier().serverIds()) {
 | 
			
		||||
                    serverCommands.putIfAbsent(serverId, new ArrayList<>());
 | 
			
		||||
                    serverCommands.get(serverId).add(request);
 | 
			
		||||
                }
 | 
			
		||||
            }else
 | 
			
		||||
                globalCommands.add(request);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (long serverId : serverCommands.keySet()) {
 | 
			
		||||
            api.getRestClient().getApplicationService().bulkOverwriteGuildApplicationCommand(applicationId, serverId, serverCommands.get(serverId));
 | 
			
		||||
        }
 | 
			
		||||
        api.getRestClient().getApplicationService().bulkOverwriteGlobalApplicationCommand(applicationId, globalCommands);
 | 
			
		||||
    public CommandRegisterer<?> getRegisterer() {
 | 
			
		||||
        return this.commandRegisterer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -94,4 +72,15 @@ public class Discord4JWrapper extends LibraryWrapper {
 | 
			
		||||
        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
 | 
			
		||||
    public InteractionIdentifier convert(ChatInputAutoCompleteEvent context) {
 | 
			
		||||
        InteractionIdentifier last = InteractionIdentifier.builder()
 | 
			
		||||
            .type(InteractionType.AUTOCOMPLETE)
 | 
			
		||||
            .name(context.getCommandName())
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        List<ApplicationCommandInteractionOption> options = Discord4JWrapper.SUB_FILTER.apply(context.getOptions());
 | 
			
		||||
        String commandName = context.getCommandName();
 | 
			
		||||
 | 
			
		||||
        if (!options.isEmpty()) {
 | 
			
		||||
            List<ApplicationCommandInteractionOption> sub_options = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions());
 | 
			
		||||
            if (!sub_options.isEmpty())
 | 
			
		||||
                return InteractionIdentifier.createHierarchy(
 | 
			
		||||
                    InteractionType.AUTOCOMPLETE, 
 | 
			
		||||
                    commandName, 
 | 
			
		||||
                    options.getFirst().getName(),
 | 
			
		||||
                    sub_options.getFirst().getName());
 | 
			
		||||
            else
 | 
			
		||||
                return InteractionIdentifier.createHierarchy(
 | 
			
		||||
                    InteractionType.AUTOCOMPLETE, 
 | 
			
		||||
                    commandName, 
 | 
			
		||||
                    options.getFirst().getName());
 | 
			
		||||
        }else
 | 
			
		||||
            return InteractionIdentifier.createHierarchy(
 | 
			
		||||
                InteractionType.AUTOCOMPLETE, 
 | 
			
		||||
                commandName); 
 | 
			
		||||
            last = InteractionIdentifier.builder()
 | 
			
		||||
                .type(InteractionType.AUTOCOMPLETE)
 | 
			
		||||
                .name(options.getFirst().getName())
 | 
			
		||||
                .parent(last)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
            List<ApplicationCommandInteractionOption> subOptions = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions());
 | 
			
		||||
            if (!subOptions.isEmpty())
 | 
			
		||||
                last = InteractionIdentifier.builder()
 | 
			
		||||
                    .type(InteractionType.AUTOCOMPLETE)
 | 
			
		||||
                    .name(subOptions.getFirst().getName())
 | 
			
		||||
                    .parent(last)
 | 
			
		||||
                    .build();
 | 
			
		||||
        }
 | 
			
		||||
        return last;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,27 +13,28 @@ public class SlashCommandIdentifierConverter implements IdentifierProvider.Conve
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    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());
 | 
			
		||||
        String commandName = context.getCommandName();
 | 
			
		||||
 | 
			
		||||
        if (!options.isEmpty()) {
 | 
			
		||||
            List<ApplicationCommandInteractionOption> sub_options = Discord4JWrapper.SUB_FILTER.apply(options.getFirst().getOptions());
 | 
			
		||||
            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); 
 | 
			
		||||
            last = InteractionIdentifier.builder()
 | 
			
		||||
                .type(InteractionType.COMMAND)
 | 
			
		||||
                .name(options.getFirst().getName())
 | 
			
		||||
                .parent(last)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
            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.discord4j.Discord4JWrapper;
 | 
			
		||||
 | 
			
		||||
public class AutoCompleteTest {
 | 
			
		||||
class AutoCompleteTest {
 | 
			
		||||
    
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testAutocomplete() {
 | 
			
		||||
    void testAutocomplete() {
 | 
			
		||||
        ApplicationCommandInteractionOption optionMock = 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.
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestAutocomplete());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestAutocomplete());
 | 
			
		||||
        wrapper.handleInteraction(autoCompleteEventMock);
 | 
			
		||||
        verify(autoCompleteEventMock).respondWithSuggestions(any());
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,15 +13,15 @@ import net.tomatentum.marinara.wrapper.LibraryWrapper;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper;
 | 
			
		||||
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
public class ButtonTest {
 | 
			
		||||
class ButtonTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testButtonExecution() {
 | 
			
		||||
    void testButtonExecution() {
 | 
			
		||||
        ButtonInteractionEvent buttonEventMock = CommonMocks.getButtonEventMock("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.getRegistry().addInteractions(new TestButton());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestButton());
 | 
			
		||||
        wrapper.handleInteraction(buttonEventMock);
 | 
			
		||||
        assertTrue(TestButton.didRun);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,16 +24,16 @@ import net.tomatentum.marinara.wrapper.discord4j.checks.PermissionCheck;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
public class InteractionCheckTest {
 | 
			
		||||
class InteractionCheckTest {
 | 
			
		||||
    
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testInteractionCheck() {
 | 
			
		||||
    void testInteractionCheck() {
 | 
			
		||||
        ButtonInteractionEvent buttonEventMock = CommonMocks.getButtonEventMock("test");
 | 
			
		||||
 | 
			
		||||
        LibraryWrapper wrapper = new Discord4JWrapper(null);
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getCheckRegistry().addCheck(new TestInteractionCheck());
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestButton());
 | 
			
		||||
        marinara.getCheckContainer().addAllMethods(new TestInteractionCheck());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestButton());
 | 
			
		||||
        wrapper.handleInteraction(buttonEventMock);
 | 
			
		||||
 | 
			
		||||
        assertTrue(TestInteractionCheck.preExecuted);
 | 
			
		||||
@@ -42,7 +42,7 @@ public class InteractionCheckTest {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testPermissionCheck() {
 | 
			
		||||
    void testPermissionCheck() {
 | 
			
		||||
        Member memberMock = mock();
 | 
			
		||||
        Interaction interactionMock = mock();
 | 
			
		||||
 | 
			
		||||
@@ -54,8 +54,8 @@ public class InteractionCheckTest {
 | 
			
		||||
 | 
			
		||||
        LibraryWrapper wrapper = new Discord4JWrapper(null);
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getCheckRegistry().addCheck(new PermissionCheck());
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestButton());
 | 
			
		||||
        marinara.getCheckContainer().addAllMethods(new PermissionCheck());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestButton());
 | 
			
		||||
 | 
			
		||||
        wrapper.handleInteraction(buttonEventMock);
 | 
			
		||||
        assertFalse(TestButton.didPermRun);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,11 @@ import discord4j.core.object.command.ApplicationCommandOption.Type;
 | 
			
		||||
import net.tomatentum.marinara.Marinara;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.LibraryWrapper;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.discord4j.Discord4JWrapper;
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
public class SlashCommandTest {
 | 
			
		||||
 | 
			
		||||
    String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN");
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
class SlashCommandTest {
 | 
			
		||||
 | 
			
		||||
    private static String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN");
 | 
			
		||||
    GatewayDiscordClient client;
 | 
			
		||||
 | 
			
		||||
    @BeforeAll
 | 
			
		||||
@@ -41,8 +42,8 @@ public class SlashCommandTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    void testSlashCommand() {
 | 
			
		||||
        Marinara marinara = Marinara.load(new Discord4JWrapper(client));
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestCommand());
 | 
			
		||||
        marinara.getRegistry().registerCommands();
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestCommand());
 | 
			
		||||
        marinara.registerCommands();
 | 
			
		||||
        System.out.println("Success!");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@@ -64,7 +65,7 @@ public class SlashCommandTest {
 | 
			
		||||
        
 | 
			
		||||
        LibraryWrapper wrapper = new Discord4JWrapper(client);
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestCommand());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestCommand());
 | 
			
		||||
 | 
			
		||||
        wrapper.handleInteraction(eventMock);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,21 +2,36 @@ package net.tomatentum.marinara.test.discord4j;
 | 
			
		||||
 | 
			
		||||
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.ChatInputInteractionEvent;
 | 
			
		||||
import discord4j.discordjson.json.ApplicationCommandOptionChoiceData;
 | 
			
		||||
import net.tomatentum.marinara.interaction.InteractionHandler;
 | 
			
		||||
import net.tomatentum.marinara.interaction.annotation.AutoComplete;
 | 
			
		||||
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 {
 | 
			
		||||
 | 
			
		||||
    @SlashCommand(name = "test")
 | 
			
		||||
    @AutoComplete
 | 
			
		||||
    public void autocomplete(ChatInputAutoCompleteEvent context, String value) {
 | 
			
		||||
    @SlashCommand(
 | 
			
		||||
        name = "test",
 | 
			
		||||
        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!");
 | 
			
		||||
        assertEquals(value, "test");
 | 
			
		||||
        context.respondWithSuggestions(Collections.emptyList());
 | 
			
		||||
        assertEquals("test", value);
 | 
			
		||||
        return new ApplicationCommandOptionChoiceData[]{
 | 
			
		||||
            ApplicationCommandOptionChoiceData.builder().name("TestValue").value("test").build()
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
 | 
			
		||||
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
 | 
			
		||||
import net.tomatentum.marinara.interaction.InteractionHandler;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.CommandChoices;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommandOption;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.option.SlashCommandOptionType;
 | 
			
		||||
@@ -20,12 +21,12 @@ public class TestCommand implements InteractionHandler {
 | 
			
		||||
                name = "foo",
 | 
			
		||||
                description = "foo bar is very fooby",
 | 
			
		||||
                type = SlashCommandOptionType.STRING,
 | 
			
		||||
                choiceEnum = TestChoiceEnum.class
 | 
			
		||||
                choices = @CommandChoices(cenum = TestChoiceEnum.class)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        )
 | 
			
		||||
    public void exec(ChatInputInteractionEvent event, String test) {
 | 
			
		||||
        assertEquals(test, "test");
 | 
			
		||||
        assertEquals("test", test);
 | 
			
		||||
        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,18 +10,15 @@ plugins {
 | 
			
		||||
    `java-library`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    // Use Maven Central for resolving dependencies.
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    // Use JUnit Jupiter for testing.
 | 
			
		||||
    testImplementation(libs.junit.jupiter)
 | 
			
		||||
    testImplementation(libs.mockito)
 | 
			
		||||
 | 
			
		||||
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 | 
			
		||||
    implementation(libs.log4j)
 | 
			
		||||
    testImplementation(libs.slf4j.simple)
 | 
			
		||||
 | 
			
		||||
    implementation(libs.slf4j)
 | 
			
		||||
    implementation(libs.javacord)
 | 
			
		||||
    implementation(libs.geantyref)
 | 
			
		||||
    implementation(project(":lib"))
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,12 @@ package net.tomatentum.marinara.wrapper.javacord;
 | 
			
		||||
 | 
			
		||||
import org.javacord.api.interaction.AutocompleteInteraction;
 | 
			
		||||
import org.javacord.api.interaction.ButtonInteraction;
 | 
			
		||||
import org.javacord.api.interaction.InteractionBase;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandInteraction;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandInteractionOption;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandOptionType;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.option.AutocompleteOptionData;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
 | 
			
		||||
 | 
			
		||||
public class JavacordContextObjectProvider implements ContextObjectProvider {
 | 
			
		||||
@@ -16,14 +18,14 @@ public class JavacordContextObjectProvider implements ContextObjectProvider {
 | 
			
		||||
            return null;
 | 
			
		||||
        SlashCommandInteraction interaction = (SlashCommandInteraction) context;
 | 
			
		||||
        if (!interaction.getArguments().isEmpty())
 | 
			
		||||
            return getOptionValue(interaction.getOptionByName(optionName).get());
 | 
			
		||||
            return getOptionValue(interaction.getOptionByName(optionName).orElse(null));
 | 
			
		||||
 | 
			
		||||
        SlashCommandInteractionOption subCommandOption = interaction.getOptions().getFirst();
 | 
			
		||||
 | 
			
		||||
        if (!subCommandOption.getOptions().isEmpty())
 | 
			
		||||
            subCommandOption = subCommandOption.getOptions().getFirst();
 | 
			
		||||
 | 
			
		||||
        return getOptionValue(subCommandOption.getOptionByName(optionName).get());
 | 
			
		||||
        return getOptionValue(subCommandOption.getOptionByName(optionName).orElse(null));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Object getOptionValue(SlashCommandInteractionOption option) {
 | 
			
		||||
@@ -89,7 +91,7 @@ public class JavacordContextObjectProvider implements ContextObjectProvider {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getInteractionContextObject(Object context, Class<?> type) {
 | 
			
		||||
        ButtonInteraction button = (ButtonInteraction) context;
 | 
			
		||||
        InteractionBase button = (InteractionBase) context;
 | 
			
		||||
        switch (type.getName()) {
 | 
			
		||||
            case "org.javacord.api.entity.channel.TextChannel":
 | 
			
		||||
                return button.getChannel().orElse(null);
 | 
			
		||||
@@ -102,9 +104,9 @@ public class JavacordContextObjectProvider implements ContextObjectProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getAutocompleteFocusedOption(Object context) {
 | 
			
		||||
        AutocompleteInteraction interaction = (AutocompleteInteraction) context;
 | 
			
		||||
        return getOptionValue(interaction.getFocusedOption());
 | 
			
		||||
    public AutocompleteOptionData getAutocompleteFocusedOption(Object context) {
 | 
			
		||||
        SlashCommandInteractionOption option = ((AutocompleteInteraction) context).getFocusedOption();
 | 
			
		||||
        return new AutocompleteOptionData(option.getName(), getOptionValue(option));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,11 @@ public class JavacordConverterSpec implements CommandConverter.Spec<SlashCommand
 | 
			
		||||
            .setName(option.name())
 | 
			
		||||
            .setDescription(option.description())
 | 
			
		||||
            .setRequired(option.required())
 | 
			
		||||
            .setAutocompletable(option.autocomplete())
 | 
			
		||||
            .setAutocompletable(option.autocompletes().length > 0)
 | 
			
		||||
			.setMinLength((long) option.range().min())
 | 
			
		||||
			.setDecimalMinValue(option.range().min())
 | 
			
		||||
			.setMaxLength((long) option.range().max())
 | 
			
		||||
			.setDecimalMaxValue(option.range().max())
 | 
			
		||||
            .setChoices(choices)
 | 
			
		||||
			.build();
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper.javacord;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.javacord.api.DiscordApi;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandBuilder;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.wrapper.CommandRegisterer;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ServerCommandList;
 | 
			
		||||
 | 
			
		||||
public class JavacordRegistererStrategy implements CommandRegisterer.Strategy<SlashCommandBuilder> {
 | 
			
		||||
 | 
			
		||||
    private DiscordApi api;
 | 
			
		||||
 | 
			
		||||
    public JavacordRegistererStrategy(DiscordApi api) {
 | 
			
		||||
        this.api = api;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void registerServer(ServerCommandList<SlashCommandBuilder> commands) {
 | 
			
		||||
        api.bulkOverwriteServerApplicationCommands(commands.serverId(), commands);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void registerGlobal(Set<SlashCommandBuilder> defs) {
 | 
			
		||||
        api.bulkOverwriteGlobalApplicationCommands(defs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +1,18 @@
 | 
			
		||||
package net.tomatentum.marinara.wrapper.javacord;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.apache.logging.log4j.Logger;
 | 
			
		||||
import org.javacord.api.DiscordApi;
 | 
			
		||||
import org.javacord.api.interaction.AutocompleteInteraction;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandBuilder;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandOption;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandOptionChoice;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.SlashCommandDefinition;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.CommandConverter;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.CommandRegisterer;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.ContextObjectProvider;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.IdentifierProvider;
 | 
			
		||||
import net.tomatentum.marinara.util.LoggerUtil;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.LibraryWrapper;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.javacord.identifierconverter.AutocompleteIdentifierConverter;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.javacord.identifierconverter.ButtonIdentifierConverter;
 | 
			
		||||
@@ -22,43 +20,26 @@ import net.tomatentum.marinara.wrapper.javacord.identifierconverter.SlashCommand
 | 
			
		||||
 | 
			
		||||
public class JavacordWrapper extends LibraryWrapper {
 | 
			
		||||
 | 
			
		||||
    private DiscordApi api;
 | 
			
		||||
    private JavacordContextObjectProvider contextObjectProvider;
 | 
			
		||||
    private CommandConverter<SlashCommandBuilder, SlashCommandOption, SlashCommandOptionChoice> commandConverter;
 | 
			
		||||
    private CommandRegisterer<SlashCommandBuilder> commandRegisterer;
 | 
			
		||||
    
 | 
			
		||||
    private Logger logger = LoggerUtil.getLogger(getClass());
 | 
			
		||||
    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
			
		||||
 | 
			
		||||
    public JavacordWrapper(DiscordApi api) {
 | 
			
		||||
        this.api = api;
 | 
			
		||||
        this.contextObjectProvider = new JavacordContextObjectProvider();
 | 
			
		||||
        this.commandConverter = CommandConverter.of(new JavacordConverterSpec());
 | 
			
		||||
        var converter = CommandConverter.of(new JavacordConverterSpec());
 | 
			
		||||
 | 
			
		||||
        if (api != null)
 | 
			
		||||
            api.addInteractionCreateListener((e) -> handleInteraction(e.getInteraction()));
 | 
			
		||||
        else
 | 
			
		||||
        if (api != null) {
 | 
			
		||||
            this.commandRegisterer = CommandRegisterer.of(new JavacordRegistererStrategy(api), converter);
 | 
			
		||||
            api.addInteractionCreateListener(e -> handleInteraction(e.getInteraction()));
 | 
			
		||||
        }else
 | 
			
		||||
            logger.warn("DiscordApi was null so no Events were subscribed to.");
 | 
			
		||||
        logger.info("Javacord wrapper loaded!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void registerSlashCommands(SlashCommandDefinition[] defs) {
 | 
			
		||||
        HashMap<Long, Set<SlashCommandBuilder>> serverCommands = new HashMap<>();
 | 
			
		||||
        Set<SlashCommandBuilder> globalCommands = new HashSet<>();
 | 
			
		||||
        for (SlashCommandDefinition slashCommandDefinition : defs) {
 | 
			
		||||
            SlashCommandBuilder builder = commandConverter.convert(slashCommandDefinition);
 | 
			
		||||
            if (slashCommandDefinition.rootIdentifier().serverIds().length > 0) {
 | 
			
		||||
                for (long serverId : slashCommandDefinition.rootIdentifier().serverIds()) {
 | 
			
		||||
                    serverCommands.putIfAbsent(serverId, new HashSet<>());
 | 
			
		||||
                    serverCommands.get(serverId).add(builder);
 | 
			
		||||
                }
 | 
			
		||||
            }else
 | 
			
		||||
                globalCommands.add(builder);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (long serverId : serverCommands.keySet()) {
 | 
			
		||||
            api.bulkOverwriteServerApplicationCommands(serverId, serverCommands.get(serverId));
 | 
			
		||||
        }
 | 
			
		||||
        api.bulkOverwriteGlobalApplicationCommands(globalCommands);
 | 
			
		||||
    public CommandRegisterer<?> getRegisterer() {
 | 
			
		||||
        return this.commandRegisterer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -75,4 +56,15 @@ public class JavacordWrapper extends LibraryWrapper {
 | 
			
		||||
        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
 | 
			
		||||
    public InteractionIdentifier convert(AutocompleteInteraction context) {
 | 
			
		||||
        InteractionIdentifier last = InteractionIdentifier.builder()
 | 
			
		||||
            .type(InteractionType.AUTOCOMPLETE)
 | 
			
		||||
            .name(context.getCommandName())
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        List<SlashCommandInteractionOption> options = context.getOptions();
 | 
			
		||||
        String commandName = context.getCommandName();
 | 
			
		||||
        if (!options.isEmpty()) {
 | 
			
		||||
            List<SlashCommandInteractionOption> sub_options = context.getOptions().getFirst().getOptions();
 | 
			
		||||
            if (!sub_options.isEmpty())
 | 
			
		||||
                return InteractionIdentifier.createHierarchy(
 | 
			
		||||
                    InteractionType.AUTOCOMPLETE, 
 | 
			
		||||
                    commandName, 
 | 
			
		||||
                    options.getFirst().getName(),
 | 
			
		||||
                    sub_options.getFirst().getName());
 | 
			
		||||
            else
 | 
			
		||||
                return InteractionIdentifier.createHierarchy(
 | 
			
		||||
                    InteractionType.AUTOCOMPLETE, 
 | 
			
		||||
                    commandName, 
 | 
			
		||||
                    options.getFirst().getName());
 | 
			
		||||
        }else
 | 
			
		||||
            return InteractionIdentifier.createHierarchy(
 | 
			
		||||
                InteractionType.AUTOCOMPLETE, 
 | 
			
		||||
                commandName); 
 | 
			
		||||
            last = InteractionIdentifier.builder()
 | 
			
		||||
                .type(InteractionType.AUTOCOMPLETE)
 | 
			
		||||
                .name(options.getFirst().getName())
 | 
			
		||||
                .parent(last)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
            List<SlashCommandInteractionOption> subOptions = context.getOptions().getFirst().getOptions();
 | 
			
		||||
            if (!subOptions.isEmpty())
 | 
			
		||||
                last = InteractionIdentifier.builder()
 | 
			
		||||
                    .type(InteractionType.AUTOCOMPLETE)
 | 
			
		||||
                    .name(subOptions.getFirst().getName())
 | 
			
		||||
                    .parent(last)
 | 
			
		||||
                    .build();
 | 
			
		||||
        }
 | 
			
		||||
        return last;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,26 +13,28 @@ public class SlashCommandIdentifierConverter implements IdentifierProvider.Conve
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public InteractionIdentifier convert(SlashCommandInteraction context) {
 | 
			
		||||
        InteractionIdentifier last = InteractionIdentifier.builder()
 | 
			
		||||
            .type(InteractionType.COMMAND)
 | 
			
		||||
            .name(context.getCommandName())
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        List<SlashCommandInteractionOption> options = context.getOptions();
 | 
			
		||||
        String commandName = context.getCommandName();
 | 
			
		||||
        if (!options.isEmpty()) {
 | 
			
		||||
            List<SlashCommandInteractionOption> sub_options = context.getOptions().getFirst().getOptions();
 | 
			
		||||
            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); 
 | 
			
		||||
            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.javacord.JavacordWrapper;
 | 
			
		||||
 | 
			
		||||
public class AutoCompleteTest {
 | 
			
		||||
class AutoCompleteTest {
 | 
			
		||||
    
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testAutocomplete() {
 | 
			
		||||
    void testAutocomplete() {
 | 
			
		||||
 | 
			
		||||
        SlashCommandInteractionOption optionMock = 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.
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestAutocomplete());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestAutocomplete());
 | 
			
		||||
        wrapper.handleInteraction(autocompleteInteractionMock);
 | 
			
		||||
        verify(autocompleteInteractionMock).respondWithChoices(any());
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,13 +12,13 @@ import net.tomatentum.marinara.wrapper.LibraryWrapper;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper;
 | 
			
		||||
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
public class ButtonTest {
 | 
			
		||||
class ButtonTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testButtonExecution() {
 | 
			
		||||
    void testButtonExecution() {
 | 
			
		||||
        LibraryWrapper wrapper = new JavacordWrapper(null); //null okay as we don't use the discord API in this test.
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestButton());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestButton());
 | 
			
		||||
        wrapper.handleInteraction(CommonMocks.getButtonInteractionMock("test"));
 | 
			
		||||
        assertTrue(TestButton.didRun);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,25 +19,25 @@ import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper;
 | 
			
		||||
import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck;
 | 
			
		||||
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
public class InteractionCheckTest {
 | 
			
		||||
class InteractionCheckTest {
 | 
			
		||||
    
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testInteractionCheck() {
 | 
			
		||||
    void testInteractionCheck() {
 | 
			
		||||
        LibraryWrapper wrapper = new JavacordWrapper(null);
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getCheckRegistry().addCheck(new TestInteractionCheck());
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestButton());
 | 
			
		||||
        marinara.getCheckContainer().addAllMethods(new TestInteractionCheck());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestButton());
 | 
			
		||||
        wrapper.handleInteraction(CommonMocks.getButtonInteractionMock("test"));
 | 
			
		||||
        assertTrue(TestInteractionCheck.preExecuted);
 | 
			
		||||
        assertTrue(TestInteractionCheck.postExecuted);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testPermissionCheck() {
 | 
			
		||||
    void testPermissionCheck() {
 | 
			
		||||
        LibraryWrapper wrapper = new JavacordWrapper(null);
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getCheckRegistry().addCheck(new PermissionCheck());
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestButton());
 | 
			
		||||
        marinara.getCheckContainer().addAllMethods(new PermissionCheck());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestButton());
 | 
			
		||||
 | 
			
		||||
        Server serverMock = mock();
 | 
			
		||||
        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.javacord.JavacordWrapper;
 | 
			
		||||
@TestInstance(Lifecycle.PER_CLASS)
 | 
			
		||||
public class SlashCommandTest {
 | 
			
		||||
class SlashCommandTest {
 | 
			
		||||
 | 
			
		||||
    String DISCORD_TOKEN = System.getenv("DISCORD_TEST_TOKEN");
 | 
			
		||||
    DiscordApi api;
 | 
			
		||||
@@ -41,8 +41,8 @@ public class SlashCommandTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    void testSlashCommand() {
 | 
			
		||||
        Marinara marinara = Marinara.load(new JavacordWrapper(api));
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestCommand());
 | 
			
		||||
        marinara.getRegistry().registerCommands();
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestCommand());
 | 
			
		||||
        marinara.registerCommands();
 | 
			
		||||
        System.out.println("Success!");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@@ -50,7 +50,7 @@ public class SlashCommandTest {
 | 
			
		||||
    void testSlashCommandExecution() {
 | 
			
		||||
        LibraryWrapper wrapper = new JavacordWrapper(api);
 | 
			
		||||
        Marinara marinara = Marinara.load(wrapper);
 | 
			
		||||
        marinara.getRegistry().addInteractions(new TestCommand());
 | 
			
		||||
        marinara.getInteractionContainer().addAllMethods(new TestCommand());
 | 
			
		||||
 | 
			
		||||
        SlashCommandInteractionOption optionMock = mock();
 | 
			
		||||
        SlashCommandInteraction interactionMock = mock();
 | 
			
		||||
 
 | 
			
		||||
@@ -5,18 +5,32 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
import org.javacord.api.interaction.AutocompleteInteraction;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandInteraction;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.InteractionHandler;
 | 
			
		||||
import net.tomatentum.marinara.interaction.annotation.AutoComplete;
 | 
			
		||||
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 {
 | 
			
		||||
    
 | 
			
		||||
    @SlashCommand(name = "test")
 | 
			
		||||
    @AutoComplete
 | 
			
		||||
    @SlashCommand(
 | 
			
		||||
        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) {
 | 
			
		||||
        System.out.println("Success!");
 | 
			
		||||
        assertEquals(value, "test");
 | 
			
		||||
        assertEquals("test", value);
 | 
			
		||||
        context.respondWithChoices(Collections.emptyList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,8 @@ import net.tomatentum.marinara.wrapper.javacord.checks.PermissionCheck.HasPermis
 | 
			
		||||
 | 
			
		||||
public class TestButton implements InteractionHandler {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static boolean didRun = false;
 | 
			
		||||
 | 
			
		||||
    @Button("test")
 | 
			
		||||
    @TestCheck
 | 
			
		||||
    public void exec(ButtonInteraction interaction, TextChannel channel, Message message, User member, Server server) {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import org.javacord.api.interaction.SlashCommandInteraction;
 | 
			
		||||
 | 
			
		||||
import net.tomatentum.marinara.interaction.InteractionHandler;
 | 
			
		||||
import net.tomatentum.marinara.interaction.commands.annotation.CommandChoices;
 | 
			
		||||
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 TestCommand implements InteractionHandler {
 | 
			
		||||
 | 
			
		||||
    @SlashCommand(
 | 
			
		||||
        name = "test", 
 | 
			
		||||
        description = "testingen",
 | 
			
		||||
@@ -21,12 +23,13 @@ public class TestCommand implements InteractionHandler {
 | 
			
		||||
                name = "foo",
 | 
			
		||||
                description = "foo bar is very fooby",
 | 
			
		||||
                type = SlashCommandOptionType.STRING,
 | 
			
		||||
                choiceEnum = TestChoiceEnum.class
 | 
			
		||||
                choices = @CommandChoices(cenum = TestChoiceEnum.class)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        )
 | 
			
		||||
    public void exec(SlashCommandInteraction interaction, String test) {
 | 
			
		||||
        assertEquals(test, "test");
 | 
			
		||||
        assertEquals("test", test);
 | 
			
		||||
        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