reworked exception handling, different exceptions whether or why it failed, all of which are unchecked

added feature flags for commands/listeners
added enable/disable command
added feature flag handling to command received handler
added boolean parameter support
fixed 1 parameter commands
fixed posttargets not working on multiple servers, removed unique constraint on posttarget names
added setup validation to suggestions
added quartz property loader
moved join/leave logger to moderation
reworked the way the message listener are handled (separate listener around)
This commit is contained in:
Sheldan
2020-04-04 12:55:01 +02:00
parent c9557fccc2
commit bf94af66d5
132 changed files with 1378 additions and 530 deletions

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto.command;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public abstract class AbstractFeatureFlaggedCommand implements ConditionalCommand {
@Autowired
private FeatureEnabledCondition featureEnabledCondition;
@Override
public List<CommandCondition> getConditions() {
return Arrays.asList(featureEnabledCondition);
}
}

View File

@@ -2,10 +2,11 @@ package dev.sheldan.abstracto.command;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.core.listener.FeatureAware;
public interface Command<T> {
public interface Command extends FeatureAware {
Result execute(CommandContext commandContext);
CommandResult execute(CommandContext commandContext);
CommandConfiguration getConfiguration();
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.command;
import dev.sheldan.abstracto.command.execution.CommandContext;
public interface CommandCondition {
boolean shouldExecute(CommandContext commandContext, Command command);
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.command;
import java.util.List;
public interface ConditionalCommand extends Command {
List<CommandCondition> getConditions();
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.command;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FeatureEnabledCondition implements CommandCondition {
@Autowired
private FeatureFlagManagementService featureFlagManagementService;
@Override
public boolean shouldExecute(CommandContext context, Command command) {
String featureName = command.getFeature();
if(featureName != null) {
return featureFlagManagementService.getFeatureFlagValue(featureName, context.getGuild().getIdLong());
}
return false;
}
}

View File

@@ -1,8 +1,8 @@
package dev.sheldan.abstracto.command;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.CommandResult;
public interface PostCommandExecution {
void execute(CommandContext commandContext, Result result, Command command);
void execute(CommandContext commandContext, CommandResult commandResult, Command command);
}

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.command;
public interface TemplatedException {
public interface Templatable {
String getTemplateName();
Object getTemplateModel();
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.command.execution;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter @Builder
public class CommandResult {
private ResultState result;
private String message;
private Throwable throwable;
public static CommandResult fromSuccess() {
return CommandResult.builder().result(ResultState.SUCCESSFUL).build();
}
public static CommandResult fromError(String message){
return CommandResult.builder().result(ResultState.ERROR).message(message).build();
}
public static CommandResult fromError(String message, Throwable throwable) {
return CommandResult.builder().result(ResultState.ERROR).message(message).throwable(throwable).build();
}
}

View File

@@ -17,7 +17,7 @@ public class ContextConverter {
return builder
.member(commandContext.getAuthor())
.guild(commandContext.getGuild())
.textChannel(commandContext.getChannel())
.messageChannel(commandContext.getChannel())
.channel(commandContext.getUserInitiatedContext().getChannel())
.server(commandContext.getUserInitiatedContext().getServer())
.aUserInAServer(commandContext.getUserInitiatedContext().getAUserInAServer())

View File

@@ -1,25 +0,0 @@
package dev.sheldan.abstracto.command.execution;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter @Builder
public class Result {
private ResultState result;
private String message;
private Throwable throwable;
public static Result fromSuccess() {
return Result.builder().result(ResultState.SUCCESSFUL).build();
}
public static Result fromError(String message){
return Result.builder().result(ResultState.ERROR).message(message).build();
}
public static Result fromError(String message, Throwable throwable) {
return Result.builder().result(ResultState.ERROR).message(message).throwable(throwable).build();
}
}

View File

@@ -1,6 +0,0 @@
package dev.sheldan.abstracto.commands.management;
import dev.sheldan.abstracto.command.Command;
public abstract class AbstractCommand implements Command {
}

View File

@@ -6,8 +6,8 @@ import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.command.meta.CommandRegistry;
import dev.sheldan.abstracto.command.meta.UnParsedCommandParameter;
import dev.sheldan.abstracto.commands.management.exception.CommandNotFoundException;
import dev.sheldan.abstracto.commands.management.exception.InsufficientParametersException;
import dev.sheldan.abstracto.commands.management.exception.CommandNotFound;
import dev.sheldan.abstracto.commands.management.exception.InsufficientParameters;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -35,7 +35,7 @@ public class CommandManager implements CommandRegistry {
boolean hasRemainderParameter = commandConfiguration.getParameters().stream().anyMatch(Parameter::isRemainder);
if(unParsedCommandParameter.getParameters().size() < commandConfiguration.getNecessaryParameterCount()) {
String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount() - 1).getName();
throw new InsufficientParametersException("Insufficient parameters", o, nextParameterName);
throw new InsufficientParameters("Insufficient parameters", o, nextParameterName);
}
parameterFit = paramCountFits || hasRemainderParameter;
} else {
@@ -46,7 +46,7 @@ public class CommandManager implements CommandRegistry {
if(commandOptional.isPresent()){
return commandOptional.get();
}
throw new CommandNotFoundException("Command not found.");
throw new CommandNotFound("Command not found.");
}
public Command findCommand(String name) {
@@ -57,7 +57,7 @@ public class CommandManager implements CommandRegistry {
if(commandOptional.isPresent()){
return commandOptional.get();
}
throw new CommandNotFoundException("Command not found.");
throw new CommandNotFound("Command not found.");
}
@Override

View File

@@ -1,20 +1,24 @@
package dev.sheldan.abstracto.commands.management;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.CommandCondition;
import dev.sheldan.abstracto.command.ConditionalCommand;
import dev.sheldan.abstracto.command.PostCommandExecution;
import dev.sheldan.abstracto.command.execution.*;
import dev.sheldan.abstracto.command.meta.UnParsedCommandParameter;
import dev.sheldan.abstracto.commands.management.exception.IncorrectParameterException;
import dev.sheldan.abstracto.commands.management.exception.ParameterTooLongException;
import dev.sheldan.abstracto.commands.management.exception.IncorrectParameter;
import dev.sheldan.abstracto.commands.management.exception.ParameterTooLong;
import dev.sheldan.abstracto.core.Constants;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.management.UserManagementService;
import dev.sheldan.abstracto.core.exception.*;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.UserInitiatedServerContext;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
@@ -29,8 +33,10 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@Service
@Slf4j
public class CommandReceivedHandler extends ListenerAdapter {
@Autowired
@@ -74,18 +80,34 @@ public class CommandReceivedHandler extends ListenerAdapter {
foundCommand = commandManager.findCommandByParameters(commandName, unparsedParameter);
Parameters parsedParameters = getParsedParameters(unparsedParameter, foundCommand, event.getMessage());
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
Result result = foundCommand.execute(commandContext);
if(foundCommand instanceof ConditionalCommand) {
ConditionalCommand castedCommand = (ConditionalCommand) foundCommand;
if (!shouldExecute(commandContext, foundCommand, castedCommand.getConditions())) {
throw new FeatureDisabledException(String.format("Feature `%s` has been disabled. Command is not usable", foundCommand.getFeature()));
}
}
CommandResult commandResult = foundCommand.execute(commandContext);
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, result, foundCommand);
postCommandExecution.execute(commandContext, commandResult, foundCommand);
}
} catch (Exception e) {
Result result = Result.fromError(e.getMessage(), e);
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, result, foundCommand);
postCommandExecution.execute(commandContext, commandResult, foundCommand);
}
}
}
public boolean shouldExecute(CommandContext commandContext, Command command, List<CommandCondition> conditions) {
AtomicBoolean shouldExecute = new AtomicBoolean(true);
if(conditions != null) {
conditions.forEach(condition -> {
shouldExecute.set(shouldExecute.get() && condition.shouldExecute(commandContext, command));
});
}
return shouldExecute.get();
}
private UserInitiatedServerContext buildTemplateParameter(MessageReceivedEvent event) {
@@ -99,7 +121,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
.member(event.getMember())
.aUserInAServer(user)
.user(user.getUserReference())
.textChannel(event.getTextChannel())
.messageChannel(event.getTextChannel())
.guild(event.getGuild())
.build();
}
@@ -122,7 +144,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
}
String value = unParsedCommandParameter.getParameters().get(i);
if(param.getMaxLength() != null && (value.length() + Constants.PARAMETER_LIMIT) > param.getMaxLength()) {
throw new ParameterTooLongException("The passed parameter was too long.", command, param.getName(), value.length(), param.getMaxLength());
throw new ParameterTooLong("The passed parameter was too long.", command, param.getName(), value.length(), param.getMaxLength());
}
try {
if(param.getType().equals(Integer.class)){
@@ -143,18 +165,24 @@ public class CommandReceivedHandler extends ListenerAdapter {
} else {
parsedParameters.add(value);
}
} else if(param.getType().equals(Boolean.class)) {
parsedParameters.add(Boolean.valueOf(value));
} else if (param.getType().equals(Duration.class)) {
parsedParameters.add(ParseUtils.parseDuration(value));
} else {
if(!reminderActive) {
parsedParameters.add(value);
} else {
int lastIndex = parsedParameters.size() - 1;
parsedParameters.set(lastIndex, parsedParameters.get(lastIndex) + " " + value);
if(parsedParameters.size() == 0) {
parsedParameters.add(value);
} else {
int lastIndex = parsedParameters.size() - 1;
parsedParameters.set(lastIndex, parsedParameters.get(lastIndex) + " " + value);
}
}
}
} catch (NoSuchElementException e) {
throw new IncorrectParameterException("The passed parameters did not have the correct type.", command, param.getType(), param.getName());
throw new IncorrectParameter("The passed parameters did not have the correct type.", command, param.getType(), param.getName());
}
}

View File

@@ -1,10 +1,11 @@
package dev.sheldan.abstracto.commands.management.exception;
import dev.sheldan.abstracto.command.TemplatedException;
import dev.sheldan.abstracto.command.Templatable;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
public class CommandNotFoundException extends RuntimeException implements TemplatedException {
public class CommandNotFound extends AbstractoRunTimeException implements Templatable {
public CommandNotFoundException(String s) {
public CommandNotFound(String s) {
super(s);
}

View File

@@ -1,17 +1,18 @@
package dev.sheldan.abstracto.commands.management.exception;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.TemplatedException;
import dev.sheldan.abstracto.command.Templatable;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import java.util.HashMap;
public class IncorrectParameterException extends RuntimeException implements TemplatedException {
public class IncorrectParameter extends AbstractoRunTimeException implements Templatable {
private Command command;
private String parameterName;
private Class clazz;
public IncorrectParameterException(String s, Command command, Class expected, String parameterName) {
public IncorrectParameter(String s, Command command, Class expected, String parameterName) {
super(s);
this.command = command;
this.parameterName = parameterName;

View File

@@ -1,18 +1,19 @@
package dev.sheldan.abstracto.commands.management.exception;
import dev.sheldan.abstracto.command.TemplatedException;
import dev.sheldan.abstracto.command.Templatable;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import lombok.Getter;
import java.util.HashMap;
@Getter
public class InsufficientParametersException extends RuntimeException implements TemplatedException {
public class InsufficientParameters extends AbstractoRunTimeException implements Templatable {
private Command command;
private String parameterName;
public InsufficientParametersException(String s, Command command, String parameterName) {
public InsufficientParameters(String s, Command command, String parameterName) {
super(s);
this.command = command;
this.parameterName = parameterName;

View File

@@ -1,11 +1,12 @@
package dev.sheldan.abstracto.commands.management.exception;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.TemplatedException;
import dev.sheldan.abstracto.command.Templatable;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import java.util.HashMap;
public class ParameterTooLongException extends RuntimeException implements TemplatedException {
public class ParameterTooLong extends AbstractoRunTimeException implements Templatable {
private Command command;
@@ -13,7 +14,7 @@ public class ParameterTooLongException extends RuntimeException implements Templ
private Integer actualLength;
private Integer maximumLength;
public ParameterTooLongException(String s, Command command, String parameterName, Integer actualLength, Integer maximumLength) {
public ParameterTooLong(String s, Command command, String parameterName, Integer actualLength, Integer maximumLength) {
super(s);
this.command = command;
this.parameterName = parameterName;

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.commands.management.post;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.PostCommandExecution;
import dev.sheldan.abstracto.command.TemplatedException;
import dev.sheldan.abstracto.command.Templatable;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.command.execution.ResultState;
import dev.sheldan.abstracto.templating.TemplateService;
import lombok.extern.slf4j.Slf4j;
@@ -19,16 +19,21 @@ public class ExceptionPostExecution implements PostCommandExecution {
private TemplateService templateService;
@Override
public void execute(CommandContext commandContext, Result result, Command command) {
if(result.getResult().equals(ResultState.ERROR)) {
if(result.getThrowable() != null) {
log.warn("Exception", result.getThrowable());
if(result.getThrowable() instanceof TemplatedException) {
TemplatedException exception = (TemplatedException) result.getThrowable();
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.ERROR)) {
Throwable throwable = commandResult.getThrowable();
if(throwable != null) {
if(throwable instanceof Templatable) {
Templatable exception = (Templatable) throwable;
String text = templateService.renderTemplate(exception.getTemplateName(), exception.getTemplateModel());
commandContext.getChannel().sendMessage(text).queue();
} else {
commandContext.getChannel().sendMessage(result.getThrowable().getClass().getSimpleName() + ": " + result.getMessage()).queue();
if(throwable.getCause() == null) {
commandContext.getChannel().sendMessage(throwable.getClass().getSimpleName() + ": " + commandResult.getMessage()).queue();
} else {
Throwable cause = throwable.getCause();
commandContext.getChannel().sendMessage(throwable.getClass().getSimpleName() + ": " + commandResult.getMessage() + ": " + cause.getClass().getSimpleName() + ":" + cause.getMessage()).queue();
}
}
}
}

View File

@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.commands.management.post;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.PostCommandExecution;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.command.execution.ResultState;
import dev.sheldan.abstracto.core.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,11 +18,11 @@ public class ReactionPostExecution implements PostCommandExecution {
private MessageService messageService;
@Override
public void execute(CommandContext commandContext, Result result, Command command) {
if(result.getResult().equals(ResultState.ERROR)) {
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.ERROR)) {
messageService.addReactionToMessage(WARN_REACTION_EMOTE, commandContext.getGuild().getIdLong(), commandContext.getMessage());
if(result.getMessage() != null && result.getThrowable() == null){
commandContext.getChannel().sendMessage(result.getMessage()).queue();
if(commandResult.getMessage() != null && commandResult.getThrowable() == null){
commandContext.getChannel().sendMessage(commandResult.getMessage()).queue();
}
} else {
if(command.getConfiguration().isCausesReaction()){