diff --git a/pom.xml b/pom.xml
index 161303b7..1a071ae7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,5 +106,16 @@
annotations
24.0.1
+
+
+ ch.qos.logback
+ logback-classic
+ 1.4.6
+
+
+ ch.qos.logback
+ logback-core
+ 1.4.6
+
diff --git a/src/examples/embeds.json b/src/examples/embeds.json
index 3916ecfd..25d07091 100644
--- a/src/examples/embeds.json
+++ b/src/examples/embeds.json
@@ -1,7 +1,7 @@
{
"insufficientPermissions": {
"title": "Insufficient Permissions",
- "description": "`{prefix}{label}` requires specific permissions to be executed",
+ "description": "`{name}` requires specific permissions to be executed",
"color": "#ff0000",
"fields": [
{
@@ -10,21 +10,6 @@
}
]
},
- "guildMuted": {
- "title": "Insufficient Permissions",
- "description": "This guild is muted!",
- "color": "#ff0000"
- },
- "channelMuted": {
- "title": "Insufficient Permissions",
- "description": "This channel is muted!",
- "color": "#ff0000"
- },
- "userMuted": {
- "title": "Insufficient Permissions",
- "description": "This channel is muted!",
- "color": "#ff0000"
- },
"typeAdaptingFailed": {
"title": "Syntax Error",
"description": "`{usage}`",
diff --git a/src/main/java/com/github/kaktushose/jda/commands/JDACommands.java b/src/main/java/com/github/kaktushose/jda/commands/JDACommands.java
index f9cc21ee..f8aabf22 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/JDACommands.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/JDACommands.java
@@ -4,7 +4,7 @@
import com.github.kaktushose.jda.commands.dispatching.DispatcherSupervisor;
import com.github.kaktushose.jda.commands.dispatching.RuntimeSupervisor;
import com.github.kaktushose.jda.commands.dispatching.adapter.TypeAdapterRegistry;
-import com.github.kaktushose.jda.commands.dispatching.filter.FilterRegistry;
+import com.github.kaktushose.jda.commands.dispatching.middleware.MiddlewareRegistry;
import com.github.kaktushose.jda.commands.dispatching.validation.ValidatorRegistry;
import com.github.kaktushose.jda.commands.reflect.ImplementationRegistry;
import com.github.kaktushose.jda.commands.reflect.InteractionRegistry;
@@ -30,7 +30,7 @@ public class JDACommands {
private final JDAContext jdaContext;
private final ImplementationRegistry implementationRegistry;
private final DispatcherSupervisor dispatcherSupervisor;
- private final FilterRegistry filterRegistry;
+ private final MiddlewareRegistry middlewareRegistry;
private final TypeAdapterRegistry adapterRegistry;
private final ValidatorRegistry validatorRegistry;
private final DependencyInjector dependencyInjector;
@@ -43,7 +43,7 @@ protected JDACommands() {
jdaContext = null;
implementationRegistry = null;
runtimeSupervisor = null;
- filterRegistry = null;
+ middlewareRegistry = null;
adapterRegistry = null;
validatorRegistry = null;
dependencyInjector = null;
@@ -63,12 +63,12 @@ private JDACommands(Object jda, Class> clazz, LocalizationFunction function, S
dependencyInjector = new DependencyInjector();
dependencyInjector.index(clazz, packages);
- filterRegistry = new FilterRegistry();
+ middlewareRegistry = new MiddlewareRegistry();
adapterRegistry = new TypeAdapterRegistry();
validatorRegistry = new ValidatorRegistry();
implementationRegistry = new ImplementationRegistry(
dependencyInjector,
- filterRegistry,
+ middlewareRegistry,
adapterRegistry,
validatorRegistry
);
@@ -81,7 +81,7 @@ private JDACommands(Object jda, Class> clazz, LocalizationFunction function, S
interactionRegistry.index(clazz, packages);
- updater = new SlashCommandUpdater(this);
+ updater = new SlashCommandUpdater(this, function);
updater.updateAllCommands();
jdaContext.performTask(it -> it.addEventListener(dispatcherSupervisor));
@@ -225,12 +225,12 @@ public JDAContext getJdaContext() {
}
/**
- * Gets the {@link FilterRegistry}.
+ * Gets the {@link MiddlewareRegistry}.
*
- * @return the {@link FilterRegistry}
+ * @return the {@link MiddlewareRegistry}
*/
- public FilterRegistry getFilterRegistry() {
- return filterRegistry;
+ public MiddlewareRegistry getMiddlewareRegistry() {
+ return middlewareRegistry;
}
/**
diff --git a/src/main/java/com/github/kaktushose/jda/commands/annotations/Component.java b/src/main/java/com/github/kaktushose/jda/commands/annotations/Implementation.java
similarity index 70%
rename from src/main/java/com/github/kaktushose/jda/commands/annotations/Component.java
rename to src/main/java/com/github/kaktushose/jda/commands/annotations/Implementation.java
index 5b9dec9f..6336c3f9 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/annotations/Component.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/annotations/Implementation.java
@@ -1,7 +1,7 @@
package com.github.kaktushose.jda.commands.annotations;
import com.github.kaktushose.jda.commands.annotations.constraints.Constraint;
-import com.github.kaktushose.jda.commands.dispatching.filter.FilterRegistry.FilterPosition;
+import com.github.kaktushose.jda.commands.dispatching.middleware.Priority;
import com.github.kaktushose.jda.commands.reflect.ImplementationRegistry;
import java.lang.annotation.*;
@@ -16,16 +16,18 @@
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
-public @interface Component {
+public @interface Implementation {
/**
- * Gets the {@link FilterPosition FilterPosition} to register the
- * {@link com.github.kaktushose.jda.commands.dispatching.filter.Filter Filter} at. If the component is not a subtype
- * of {@link com.github.kaktushose.jda.commands.dispatching.filter.Filter Filter}, this field can be ignored.
+ * Gets the {@link Priority} to register the
+ * {@link com.github.kaktushose.jda.commands.dispatching.middleware.Middleware Middleware} with. If this
+ * implementation is not a subtype
+ * of {@link com.github.kaktushose.jda.commands.dispatching.middleware.Middleware Middleware}, this field can be
+ * ignored.
*
- * @return the {@link FilterPosition FilterPosition}
+ * @return the {@link Priority}
*/
- FilterPosition position() default FilterPosition.UNKNOWN;
+ Priority priority() default Priority.NORMAL;
/**
* Gets the annotation the {@link com.github.kaktushose.jda.commands.dispatching.validation.Validator Validator}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/annotations/interactions/Cooldown.java b/src/main/java/com/github/kaktushose/jda/commands/annotations/interactions/Cooldown.java
index 2ec0776f..db60c670 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/annotations/interactions/Cooldown.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/annotations/interactions/Cooldown.java
@@ -1,5 +1,7 @@
package com.github.kaktushose.jda.commands.annotations.interactions;
+import com.github.kaktushose.jda.commands.dispatching.middleware.impl.CooldownMiddleware;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -11,7 +13,7 @@
*
* @author Kaktushose
* @version 2.0.0
- * @see com.github.kaktushose.jda.commands.dispatching.filter.impl.CooldownFilter CooldownFilter
+ * @see CooldownMiddleware CooldownFilter
* @since 1.0.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
diff --git a/src/main/java/com/github/kaktushose/jda/commands/data/CommandTree.java b/src/main/java/com/github/kaktushose/jda/commands/data/CommandTree.java
index aaff70ab..4a72c929 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/data/CommandTree.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/data/CommandTree.java
@@ -2,6 +2,7 @@
import com.github.kaktushose.jda.commands.reflect.interactions.commands.SlashCommandDefinition;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
+import net.dv8tion.jda.api.interactions.commands.localization.LocalizationFunction;
import java.util.Collection;
import java.util.List;
@@ -80,12 +81,12 @@ private String[] resolveLabel(String label) {
*
* @return a {@link List} of {@link SlashCommandData}
*/
- public List getCommands() {
- return root.getCommandData();
+ public List getCommands(LocalizationFunction localizationFunction) {
+ return root.getCommandData(localizationFunction);
}
/**
- * Gets the sanitized labels of all {@link SlashCommandData} returned by {@link #getCommands()}.
+ * Gets the sanitized labels of all {@link SlashCommandData} returned by {@link #getCommands(LocalizationFunction)} ()}.
* The labels will match the regex {@code ^[\w-]+$}. Furthermore, if the label consists of more than three spaces
* any additional space will be replaced with {@code _} due to Discords limitations on SubcommandGroups.
*
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/adapter/TypeAdapterRegistry.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/adapter/TypeAdapterRegistry.java
index 67eb68a2..0e46a920 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/adapter/TypeAdapterRegistry.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/adapter/TypeAdapterRegistry.java
@@ -140,7 +140,7 @@ public void adapt(@NotNull SlashCommandContext context) {
ErrorMessageFactory messageFactory = context.getImplementationRegistry().getErrorMessageFactory();
log.debug("Type adapting arguments...");
- arguments.add(new CommandEvent(context));
+ arguments.add(new CommandEvent(context));
for (int i = 0; i < command.getActualParameters().size(); i++) {
ParameterDefinition parameter = command.getActualParameters().get(i);
@@ -158,7 +158,7 @@ public void adapt(@NotNull SlashCommandContext context) {
IllegalStateException exception = new IllegalStateException(
"Command input doesn't match parameter length! Please report this error the the devs of jda-commands."
);
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, exception));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, exception));
throw exception;
}
@@ -184,7 +184,7 @@ public void adapt(@NotNull SlashCommandContext context) {
Optional> parsed = adapter.get().parse(raw, context);
if (parsed.isEmpty()) {
log.debug("Type adapting failed!");
- context.setCancelled(true).setErrorMessage(messageFactory.getTypeAdaptingFailedMessage(context));
+ context.setCancelled(messageFactory.getTypeAdaptingFailedMessage(context));
break;
}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/Filter.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/Filter.java
deleted file mode 100644
index 31bee223..00000000
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/Filter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.github.kaktushose.jda.commands.dispatching.filter;
-
-import com.github.kaktushose.jda.commands.dispatching.interactions.Context;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Generic top level interface for the filter chain. A filter performs filtering tasks on a {@link Context}
- * before execution. A filter might modify the {@link Context} or even cancel the whole event.
- *
- * @author Kaktushose
- * @version 2.0.0
- * @since 2.0.0
- */
-public interface Filter {
-
- /**
- * Performs the filtering on a {@link Context} object. Use {@link Context#setCancelled(boolean)} to
- * indicate that the {@link Context} didn't pass the filter.
- *
- * @param context the {@link Context} to filter
- */
- void apply(@NotNull Context context);
-
-}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/FilterRegistry.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/FilterRegistry.java
deleted file mode 100644
index 9ecbb35d..00000000
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/FilterRegistry.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package com.github.kaktushose.jda.commands.dispatching.filter;
-
-import com.github.kaktushose.jda.commands.dispatching.adapter.TypeAdapterRegistry;
-import com.github.kaktushose.jda.commands.dispatching.filter.impl.*;
-import com.github.kaktushose.jda.commands.dispatching.interactions.commands.SlashCommandContext;
-import org.jetbrains.annotations.NotNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-/**
- * Central registry for all {@link Filter Filters}.
- *
- * @author Kaktushose
- * @version 2.0.0
- * @see Filter
- * @since 2.0.0
- */
-public class FilterRegistry {
-
- private static final Logger log = LoggerFactory.getLogger(FilterRegistry.class);
- private final Map> filters;
-
- /**
- * Constructs a new FilterRegistry. This will register the following {@link Filter Filters} by default:
- *
- * - {@link ConstraintFilter}
- * - {@link CooldownFilter}
- * - {@link DirectMessageFilter}
- * - {@link PermissionsFilter}
- * - {@link UserMuteFilter}
- *
- */
- public FilterRegistry() {
- this.filters = new EnumMap<>(FilterPosition.class);
-
- register(new UserMuteFilter(), FilterPosition.BEFORE_ROUTING);
- register(new PermissionsFilter(), FilterPosition.BEFORE_ADAPTING);
- register(new DirectMessageFilter(), FilterPosition.BEFORE_ADAPTING);
- register(new CooldownFilter(), FilterPosition.BEFORE_ADAPTING);
- register(new ConstraintFilter(), FilterPosition.BEFORE_EXECUTION);
- }
-
- /**
- * Register a {@link Filter} for a specific {@link FilterPosition FilterPosition}. One {@link Filter} can be
- * registered multiple times, even for the same {@link FilterPosition FilterPosition}.
- *
- * @param filter the {@link Filter} to register
- * @param position the {@link FilterPosition FilterPosition} at which the {@link Filter} gets registered
- */
- public void register(@NotNull Filter filter, @NotNull FilterPosition position) {
- filters.putIfAbsent(position, new HashSet<>());
- filters.get(position).add(filter);
- log.debug("Registered filter {} for position {}", filter.getClass().getName(), position);
- }
-
- /**
- * Unregisters all occurrences of the given {@link Filter} regardless of the {@link FilterPosition FilterPosition}.
- *
- * @param filter the {@link Filter} to unregister
- */
- public void unregister(@NotNull Class extends Filter> filter) {
- filters.keySet().forEach(position -> unregister(filter, position));
- log.debug("Unregistered filter(s) {}", filter.getName());
- }
-
- /**
- * Unregisters all occurrences of the given {@link Filter} with the given {@link FilterPosition}.
- *
- * @param filter the {@link Filter} to unregister
- * @param position the {@link FilterPosition} to use
- */
- public void unregister(Class extends Filter> filter, FilterPosition position) {
- Set filterSet = filters.get(position);
- if (filterSet != null) {
- filterSet.removeIf(current -> current.getClass().isAssignableFrom(filter));
- }
- }
-
- /**
- * Retrieves all available {@link Filter Filters} regardless of their {@link FilterPosition FilterPosition}.
- *
- * @return all registered {@link Filter Filters}
- */
- public Collection getAll() {
- return filters.values()
- .stream()
- .flatMap(Set::stream)
- .collect(Collectors.toUnmodifiableSet());
- }
-
- /**
- * Retrieves all {@link Filter Filters} that are registered for the given {@link FilterPosition FilterPosition}.
- *
- * @param position the {@link FilterPosition} to retrieve the {@link Filter Filters} for
- * @return all registered {@link Filter Filters}
- */
- public Collection getAll(@NotNull FilterPosition position) {
- return Collections.unmodifiableCollection(filters.get(position));
- }
-
- /**
- * Enum describing different filter positions.
- *
- * @author Kaktushose
- * @version 2.2.0
- * @see TypeAdapterRegistry
- * @since 2.0.0
- */
- public enum FilterPosition {
-
- /**
- * Filter will be executed before command routing. The command will not be present in the
- * {@link SlashCommandContext}.
- */
- BEFORE_ROUTING,
-
- /**
- * Filter will be executed before type adapting. The command will be present in the
- * {@link SlashCommandContext} but not the type adapted input.
- */
- BEFORE_ADAPTING,
-
- /**
- * Filter will be executed just before the command execution.
- */
- BEFORE_EXECUTION,
-
- /**
- * Filter will not be executed.
- */
- UNKNOWN
- }
-}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/impl/package-info.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/impl/package-info.java
deleted file mode 100644
index 2bc023df..00000000
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/impl/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Implementations of frequently needed filters.
- */
-package com.github.kaktushose.jda.commands.dispatching.filter.impl;
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/package-info.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/package-info.java
deleted file mode 100644
index 4e76eb2b..00000000
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Filtering system for commands.
- */
-package com.github.kaktushose.jda.commands.dispatching.filter;
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/Context.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/Context.java
index c725afb7..e4dcf295 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/Context.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/Context.java
@@ -57,18 +57,6 @@ public MessageCreateData getErrorMessage() {
return errorMessage;
}
- /**
- * Set the {@link Message} to send if an error occurred.
- *
- * @param message the {@link Message} to send
- * @return the current CommandContext instance
- */
- @NotNull
- public Context setErrorMessage(@NotNull MessageCreateData message) {
- this.errorMessage = message;
- return this;
- }
-
/**
* Gets the corresponding {@link ImplementationRegistry} instance.
*
@@ -105,7 +93,7 @@ public JDACommands getJdaCommands() {
* Set the {@link JDACommands} instance.
*
* @param jdaCommands the {@link JDACommands} instance
- * @return the current CommandContext instance
+ * @return the current Context instance
*/
@NotNull
public Context setJdaCommands(@NotNull JDACommands jdaCommands) {
@@ -123,21 +111,43 @@ public boolean isCancelled() {
}
/**
- * Set whether the context should be cancelled.
+ * Sets this context as cancelled and uses the provided {@link MessageCreateData} as an error message.
*
- * @param cancelled whether the context should be cancelled
- * @return the current CommandContext instance
+ * @param errorMessage the error message as {@link MessageCreateData}
+ * @return the current Context instance
*/
- @NotNull
- public Context setCancelled(final boolean cancelled) {
- this.cancelled = cancelled;
+ public Context setCancelled(MessageCreateData errorMessage) {
+ this.cancelled = true;
+ this.errorMessage = errorMessage;
+ return this;
+ }
+
+ /**
+ * Undoes {@link #setCancelled(MessageCreateData)} and.
+ *
+ * @return the current Context instance
+ */
+ public Context setUncancelled() {
+ this.cancelled = false;
return this;
}
+ /**
+ * Whether this context should send ephemeral replies.
+ *
+ * @return {@code true} if this context should send ephemeral replies
+ */
public boolean isEphemeral() {
return ephemeral;
}
+
+ /**
+ * Sets whether this context should send ephemeral replies.
+ *
+ * @param ephemeral {@code true} if this context should send ephemeral replies
+ * @return the current Context instance
+ */
public Context setEphemeral(boolean ephemeral) {
this.ephemeral = ephemeral;
return this;
@@ -162,10 +172,21 @@ public Context setRuntime(InteractionRuntime runtime) {
return this;
}
+ /**
+ * Gets the {@link GenericInteractionDefinition} this context got created from.
+ *
+ * @return the {@link GenericInteractionDefinition}
+ */
public GenericInteractionDefinition getInteractionDefinition() {
return interactionDefinition;
}
+ /**
+ * Sets the {@link GenericInteractionDefinition}
+ *
+ * @param interactionDefinition the {@link GenericInteractionDefinition} of this context
+ * @return the current Context instance
+ */
public Context setInteractionDefinition(GenericInteractionDefinition interactionDefinition) {
this.interactionDefinition = interactionDefinition;
return this;
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/GenericDispatcher.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/GenericDispatcher.java
index 7cc87650..1df9cb36 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/GenericDispatcher.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/GenericDispatcher.java
@@ -3,9 +3,13 @@
import com.github.kaktushose.jda.commands.JDACommands;
import com.github.kaktushose.jda.commands.dispatching.RuntimeSupervisor;
import com.github.kaktushose.jda.commands.dispatching.adapter.TypeAdapterRegistry;
-import com.github.kaktushose.jda.commands.dispatching.filter.FilterRegistry;
+import com.github.kaktushose.jda.commands.dispatching.middleware.Middleware;
+import com.github.kaktushose.jda.commands.dispatching.middleware.MiddlewareRegistry;
+import com.github.kaktushose.jda.commands.dispatching.middleware.Priority;
import com.github.kaktushose.jda.commands.reflect.ImplementationRegistry;
import com.github.kaktushose.jda.commands.reflect.InteractionRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Abstract base class for event dispatchers.
@@ -16,8 +20,10 @@
*/
public abstract class GenericDispatcher {
+ private final static Logger log = LoggerFactory.getLogger(GenericDispatcher.class);
+
protected final JDACommands jdaCommands;
- protected final FilterRegistry filterRegistry;
+ protected final MiddlewareRegistry middlewareRegistry;
protected final ImplementationRegistry implementationRegistry;
protected final InteractionRegistry interactionRegistry;
protected final TypeAdapterRegistry adapterRegistry;
@@ -30,13 +36,46 @@ public abstract class GenericDispatcher {
*/
public GenericDispatcher(JDACommands jdaCommands) {
this.jdaCommands = jdaCommands;
- filterRegistry = jdaCommands.getFilterRegistry();
+ middlewareRegistry = jdaCommands.getMiddlewareRegistry();
implementationRegistry = jdaCommands.getImplementationRegistry();
interactionRegistry = jdaCommands.getInteractionRegistry();
adapterRegistry = jdaCommands.getAdapterRegistry();
runtimeSupervisor = jdaCommands.getRuntimeSupervisor();
}
+ protected void executeMiddlewares(Context context) {
+ log.debug("Executing middlewares...");
+
+ for (Middleware middleware : middlewareRegistry.getMiddlewares(Priority.PERMISSIONS)) {
+ log.debug("Executing middleware {}", middleware.getClass().getSimpleName());
+ middleware.execute(context);
+ if (context.isCancelled()) {
+ return;
+ }
+ }
+ for (Middleware middleware : middlewareRegistry.getMiddlewares(Priority.HIGH)) {
+ log.debug("Executing middleware {}", middleware.getClass().getSimpleName());
+ middleware.execute(context);
+ if (context.isCancelled()) {
+ return;
+ }
+ }
+ for (Middleware middleware : middlewareRegistry.getMiddlewares(Priority.NORMAL)) {
+ log.debug("Executing middleware {}", middleware.getClass().getSimpleName());
+ middleware.execute(context);
+ if (context.isCancelled()) {
+ return;
+ }
+ }
+ for (Middleware middleware : middlewareRegistry.getMiddlewares(Priority.LOW)) {
+ log.debug("Executing middleware {}", middleware.getClass().getSimpleName());
+ middleware.execute(context);
+ if (context.isCancelled()) {
+ return;
+ }
+ }
+ }
+
/**
* Dispatches a {@link Context}.
*
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/autocomplete/AutoCompleteDispatcher.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/autocomplete/AutoCompleteDispatcher.java
index ff0cd44f..44b7df00 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/autocomplete/AutoCompleteDispatcher.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/autocomplete/AutoCompleteDispatcher.java
@@ -3,6 +3,7 @@
import com.github.kaktushose.jda.commands.JDACommands;
import com.github.kaktushose.jda.commands.dispatching.interactions.Context;
import com.github.kaktushose.jda.commands.dispatching.interactions.GenericDispatcher;
+import com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext;
import com.github.kaktushose.jda.commands.reflect.interactions.AutoCompleteDefinition;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import org.slf4j.Logger;
@@ -43,8 +44,15 @@ public void onEvent(Context context) {
}
AutoCompleteDefinition autoComplete = optionalAutoComplete.get();
- log.debug("Input matches auto complete: {}", autoComplete.getId());
+ context.setInteractionDefinition(autoComplete);
+ executeMiddlewares(context);
+ if (checkCancelled(context)) {
+ log.debug("Interaction execution cancelled by middleware");
+ return;
+ }
+
+ log.debug("Input matches auto complete: {}", autoComplete.getId());
log.info("Executing auto complete {} for user {}", autoComplete.getMethod().getName(), event.getMember());
try {
autoComplete.getMethod().invoke(runtimeSupervisor.getOrCreateInstance(event, autoComplete), new AutoCompleteEvent(context));
@@ -52,4 +60,16 @@ public void onEvent(Context context) {
throw new IllegalStateException("Auto complete execution failed!", exception);
}
}
+
+ @SuppressWarnings("ConstantConditions")
+ private boolean checkCancelled(Context context) {
+ if (context.isCancelled()) {
+ ReplyContext replyContext = new ReplyContext(context);
+ replyContext.getBuilder().applyData(context.getErrorMessage());
+ replyContext.queue();
+ return true;
+ }
+ return false;
+ }
+
}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/commands/CommandDispatcher.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/commands/CommandDispatcher.java
index 821d0575..117fcdee 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/commands/CommandDispatcher.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/commands/CommandDispatcher.java
@@ -6,7 +6,6 @@
import com.github.kaktushose.jda.commands.dispatching.interactions.GenericDispatcher;
import com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext;
import com.github.kaktushose.jda.commands.embeds.ErrorMessageFactory;
-import com.github.kaktushose.jda.commands.reflect.interactions.commands.ContextCommandDefinition;
import com.github.kaktushose.jda.commands.reflect.interactions.commands.GenericCommandDefinition;
import com.github.kaktushose.jda.commands.reflect.interactions.commands.SlashCommandDefinition;
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent;
@@ -53,7 +52,7 @@ public void onEvent(Context context) {
if (optional.isEmpty()) {
IllegalStateException exception = new IllegalStateException("No command found! Please report this error the the devs of jda-commands.");
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, exception));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, exception));
checkCancelled(context);
throw exception;
}
@@ -86,11 +85,17 @@ public void onEvent(Context context) {
arguments = slashContext.getArguments();
} else {
arguments = new ArrayList<>() {{
- add(new CommandEvent(context));
+ add(new CommandEvent(context));
add(((GenericContextInteractionEvent>) event).getTarget());
}};
}
+ executeMiddlewares(context);
+ if (checkCancelled(context)) {
+ log.debug("Interaction execution cancelled by middleware");
+ return;
+ }
+
log.info("Executing command {} for user {}", command.getMethod().getName(), context.getEvent().getMember());
try {
RuntimeSupervisor.InteractionRuntime runtime = runtimeSupervisor.newRuntime(event, command);
@@ -101,7 +106,7 @@ public void onEvent(Context context) {
log.error("Command execution failed!", exception);
// this unwraps the underlying error in case of an exception inside the command class
Throwable throwable = exception instanceof InvocationTargetException ? exception.getCause() : exception;
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, throwable));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, throwable));
checkCancelled(context);
}
}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/components/ComponentDispatcher.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/components/ComponentDispatcher.java
index afc3f9e2..d226fd3a 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/components/ComponentDispatcher.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/components/ComponentDispatcher.java
@@ -67,33 +67,31 @@ public void onEvent(Context context) {
IllegalStateException exception = new IllegalStateException(
"No select menu found! Please report this error the the devs of jda-commands."
);
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, exception));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, exception));
checkCancelled(context);
throw exception;
}
EphemeralInteractionDefinition component = optionalComponent.get();
context.setInteractionDefinition(component).setEphemeral(component.isEphemeral());
- log.debug("Input matches component: {}", component.getId());
- log.info("Executing select component {} for user {}", component.getMethod().getName(), event.getMember());
+ executeMiddlewares(context);
+ if (checkCancelled(context)) {
+ log.debug("Interaction execution cancelled by middleware");
+ return;
+ }
+
+ log.debug("Input matches component: {}", component.getId());
+ log.info("Executing component {} for user {}", component.getMethod().getName(), event.getMember());
context.setRuntime(runtime);
try {
Class> clazz = component.getClass();
if (EntitySelectMenuDefinition.class.isAssignableFrom(clazz)) {
- component.getMethod().invoke(
- runtime.getInstance(),
- new ComponentEvent(context),
- ((EntitySelectInteractionEvent) event).getMentions()
- );
+ component.getMethod().invoke(runtime.getInstance(), new ComponentEvent(context), ((EntitySelectInteractionEvent) event).getMentions());
} else if (StringSelectMenuDefinition.class.isAssignableFrom(clazz)) {
- component.getMethod().invoke(
- runtime.getInstance(),
- new ComponentEvent(context),
- ((StringSelectInteractionEvent) event).getValues()
- );
+ component.getMethod().invoke(runtime.getInstance(), new ComponentEvent(context), ((StringSelectInteractionEvent) event).getValues());
} else if (ButtonDefinition.class.isAssignableFrom(clazz)) {
- component.getMethod().invoke(runtime.getInstance(), new ComponentEvent(context));
+ component.getMethod().invoke(runtime.getInstance(), new ComponentEvent(context));
} else {
throw new IllegalStateException("Unknown component type! Please report this error the the devs of jda-commands.");
}
@@ -102,7 +100,7 @@ public void onEvent(Context context) {
// this unwraps the underlying error in case of an exception inside the command class
Throwable throwable = exception instanceof InvocationTargetException ? exception.getCause() : exception;
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, throwable));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, throwable));
checkCancelled(context);
}
}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/modals/ModalDispatcher.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/modals/ModalDispatcher.java
index 035752e4..f040cc07 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/modals/ModalDispatcher.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/interactions/modals/ModalDispatcher.java
@@ -54,13 +54,20 @@ public void onEvent(Context context) {
IllegalStateException exception = new IllegalStateException(
"No Modal found! Please report this error the the devs of jda-commands."
);
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, exception));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, exception));
checkCancelled(context);
throw exception;
}
ModalDefinition modal = optionalModal.get();
context.setInteractionDefinition(modal).setEphemeral(modal.isEphemeral());
+
+ executeMiddlewares(context);
+ if (checkCancelled(context)) {
+ log.debug("Interaction execution cancelled by middleware");
+ return;
+ }
+
log.debug("Input matches Modal: {}", modal.getId());
log.info("Executing Modal {} for user {}", modal.getMethod().getName(), event.getMember());
try {
@@ -73,7 +80,7 @@ public void onEvent(Context context) {
log.error("Modal execution failed!", exception);
// this unwraps the underlying error in case of an exception inside the command class
Throwable throwable = exception instanceof InvocationTargetException ? exception.getCause() : exception;
- context.setCancelled(true).setErrorMessage(messageFactory.getCommandExecutionFailedMessage(context, throwable));
+ context.setCancelled(messageFactory.getCommandExecutionFailedMessage(context, throwable));
checkCancelled(context);
}
}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/Middleware.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/Middleware.java
new file mode 100644
index 00000000..fefc75ad
--- /dev/null
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/Middleware.java
@@ -0,0 +1,9 @@
+package com.github.kaktushose.jda.commands.dispatching.middleware;
+
+import com.github.kaktushose.jda.commands.dispatching.interactions.Context;
+
+public interface Middleware {
+
+ void execute(Context context);
+
+}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/MiddlewareRegistry.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/MiddlewareRegistry.java
new file mode 100644
index 00000000..50a9ae66
--- /dev/null
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/MiddlewareRegistry.java
@@ -0,0 +1,57 @@
+package com.github.kaktushose.jda.commands.dispatching.middleware;
+
+import com.github.kaktushose.jda.commands.dispatching.middleware.impl.ConstraintMiddleware;
+import com.github.kaktushose.jda.commands.dispatching.middleware.impl.CooldownMiddleware;
+import com.github.kaktushose.jda.commands.dispatching.middleware.impl.PermissionsMiddleware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class MiddlewareRegistry {
+
+ private static final Logger log = LoggerFactory.getLogger(MiddlewareRegistry.class);
+ private final Map> middlewares;
+
+ public MiddlewareRegistry() {
+ middlewares = new HashMap<>();
+ middlewares.put(Priority.LOW, new HashSet<>());
+ middlewares.put(Priority.NORMAL, new HashSet<>());
+ middlewares.put(Priority.HIGH, new HashSet<>());
+ middlewares.put(Priority.PERMISSIONS, new HashSet<>());
+ register(Priority.PERMISSIONS, new PermissionsMiddleware());
+ register(Priority.NORMAL, new ConstraintMiddleware(), new CooldownMiddleware());
+ }
+
+ public MiddlewareRegistry register(Priority priority, Middleware... middlewares) {
+ register(priority, List.of(middlewares));
+ return this;
+ }
+
+ public MiddlewareRegistry register(Priority priority, Collection middlewares) {
+ this.middlewares.get(priority).addAll(middlewares);
+ log.debug("Registered middleware(s) {} with priority {}", middlewares, priority);
+ return this;
+ }
+
+ public MiddlewareRegistry unregister(Priority priority, Middleware... middlewares) {
+ unregister(priority, List.of(middlewares));
+ return this;
+ }
+
+ public MiddlewareRegistry unregister(Priority priority, Collection middlewares) {
+ this.middlewares.get(priority).removeAll(middlewares);
+ log.debug("Unregistered middleware(s) {}", middlewares);
+ return this;
+ }
+
+ public Set getMiddlewares() {
+ return middlewares.values().stream().flatMap(Collection::stream).collect(Collectors.toUnmodifiableSet());
+ }
+
+ public Set getMiddlewares(Priority priority) {
+ return Collections.unmodifiableSet(middlewares.get(priority));
+ }
+
+}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/Priority.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/Priority.java
new file mode 100644
index 00000000..26ac0083
--- /dev/null
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/Priority.java
@@ -0,0 +1,27 @@
+package com.github.kaktushose.jda.commands.dispatching.middleware;
+
+/**
+ * Enum to define with witch priority a {@link Middleware} should be executed.
+ *
+ * @author Kaktushose
+ * @version 4.0.0
+ * @since 4.0.0
+ */
+public enum Priority {
+ /**
+ * Lowest priority, will be executed at the end
+ */
+ LOW,
+ /**
+ * Default priority.
+ */
+ NORMAL,
+ /**
+ * Highest priority for custom implementation, will be executed right after internal middlewares.
+ */
+ HIGH,
+ /**
+ * Middlewares with priority PERMISSIONS will always be executed first
+ */
+ PERMISSIONS
+}
diff --git a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/impl/ConstraintFilter.java b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/impl/ConstraintMiddleware.java
similarity index 69%
rename from src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/impl/ConstraintFilter.java
rename to src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/impl/ConstraintMiddleware.java
index 22859584..0e91a0d0 100644
--- a/src/main/java/com/github/kaktushose/jda/commands/dispatching/filter/impl/ConstraintFilter.java
+++ b/src/main/java/com/github/kaktushose/jda/commands/dispatching/middleware/impl/ConstraintMiddleware.java
@@ -1,11 +1,12 @@
-package com.github.kaktushose.jda.commands.dispatching.filter.impl;
+package com.github.kaktushose.jda.commands.dispatching.middleware.impl;
-import com.github.kaktushose.jda.commands.dispatching.filter.Filter;
import com.github.kaktushose.jda.commands.dispatching.interactions.Context;
import com.github.kaktushose.jda.commands.dispatching.interactions.commands.SlashCommandContext;
+import com.github.kaktushose.jda.commands.dispatching.middleware.Middleware;
import com.github.kaktushose.jda.commands.reflect.ConstraintDefinition;
import com.github.kaktushose.jda.commands.reflect.ParameterDefinition;
import com.github.kaktushose.jda.commands.reflect.interactions.commands.SlashCommandDefinition;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,17 +15,17 @@
import java.util.Objects;
/**
- * A {@link Filter} implementation that will check the parameter constraints a
+ * A {@link Middleware} implementation that will check the parameter constraints a
* {@link SlashCommandDefinition} might have.
*
* @author Kaktushose
- * @version 2.0.0
+ * @version 4.0.0
* @see com.github.kaktushose.jda.commands.dispatching.validation.ValidatorRegistry ValidatorRegistry
* @since 2.0.0
*/
-public class ConstraintFilter implements Filter {
+public class ConstraintMiddleware implements Middleware {
- private static final Logger log = LoggerFactory.getLogger(ConstraintFilter.class);
+ private static final Logger log = LoggerFactory.getLogger(ConstraintMiddleware.class);
/**
* Checks if all parameters fulfill their constraints. Will cancel the {@link Context} if a parameter
@@ -33,7 +34,10 @@ public class ConstraintFilter implements Filter {
* @param ctx the {@link Context} to filter
*/
@Override
- public void apply(@NotNull Context ctx) {
+ public void execute(@NotNull Context ctx) {
+ if (!SlashCommandInteractionEvent.class.isAssignableFrom(ctx.getEvent().getClass())) {
+ return;
+ }
SlashCommandContext context = (SlashCommandContext) ctx;
List