added warn and ban command

changed command context to be a member instead of user, commands are only supported within guilds
added custom exception templating for the error message
added error message in case the parameters for a command were too little
added support for templates on command help descriptions
added post execution to be executed to print exception messages
refactored command handling to be able to support exceptions
added user and userInServer management
This commit is contained in:
Sheldan
2020-03-18 18:21:06 +01:00
parent 167bbb0f8b
commit bd848d31d3
82 changed files with 913 additions and 151 deletions

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto.command</groupId>
<artifactId>command</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>command-support</artifactId>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.command</groupId>
<artifactId>command-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-interface</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

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

View File

@@ -0,0 +1,83 @@
package dev.sheldan.abstracto.commands.management;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.Module;
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 net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
public class CommandManager implements CommandRegistry {
@Autowired
private List<Command> commands;
@Override
public Command findCommandByParameters(String name, UnParsedCommandParameter unParsedCommandParameter) {
Optional<Command> commandOptional = commands.stream().filter((Command o )-> {
CommandConfiguration commandConfiguration = o.getConfiguration();
if(!commandConfiguration.getName().equals(name)) {
return false;
}
boolean parameterFit;
if(commandConfiguration.getParameters() != null){
boolean paramCountFits = unParsedCommandParameter.getParameters().size() >= commandConfiguration.getNecessaryParameterCount();
boolean hasRemainderParameter = commandConfiguration.getParameters().stream().anyMatch(Parameter::isRemainder);
if(unParsedCommandParameter.getParameters().size() < commandConfiguration.getNecessaryParameterCount()) {
String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount()).getName();
throw new InsufficientParametersException("Insufficient parameters", o, nextParameterName);
}
parameterFit = paramCountFits || hasRemainderParameter;
} else {
parameterFit = unParsedCommandParameter.getParameters().size() == 0;
}
return parameterFit;
}).findFirst();
if(commandOptional.isPresent()){
return commandOptional.get();
}
throw new CommandNotFoundException("Command not found.");
}
public Command findCommand(String name) {
Optional<Command> commandOptional = commands.stream().filter((Command o )-> {
CommandConfiguration commandConfiguration = o.getConfiguration();
return commandConfiguration.getName().equals(name);
}).findFirst();
if(commandOptional.isPresent()){
return commandOptional.get();
}
throw new CommandNotFoundException("Command not found.");
}
@Override
public List<Command> getAllCommands() {
return commands;
}
@Override
public List<Command> getAllCommandsFromModule(Module module) {
List<Command> commands = new ArrayList<>();
this.getAllCommands().forEach(command -> {
if(command.getConfiguration().getModule().equals(module.getInfo().getName())){
commands.add(command);
}
});
return commands;
}
@Override
public boolean isCommand(Message message) {
return message.getContentRaw().startsWith("!");
}
}

View File

@@ -0,0 +1,108 @@
package dev.sheldan.abstracto.commands.management;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.PostCommandExecution;
import dev.sheldan.abstracto.command.execution.*;
import dev.sheldan.abstracto.command.meta.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.AServer;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class CommandReceivedHandler extends ListenerAdapter {
@Autowired
private CommandManager commandManager;
@Autowired
private List<PostCommandExecution> executions;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Override
@Transactional
public void onMessageReceived(@Nonnull MessageReceivedEvent event) {
if(!commandManager.isCommand(event.getMessage())) {
return;
}
if(!event.isFromGuild()) {
return;
}
CommandContext.CommandContextBuilder commandContextBuilder = CommandContext.builder()
.author(event.getMember())
.guild(event.getGuild())
.channel(event.getTextChannel())
.message(event.getMessage())
.jda(event.getJDA())
.commandTemplateContext(buildTemplateParameter(event));
Command foundCommand = null;
try {
List<String> parameters = Arrays.asList(event.getMessage().getContentStripped().split(" "));
UnParsedCommandParameter unparsedParameter = new UnParsedCommandParameter();
unparsedParameter.setParameters(parameters.subList(1, parameters.size()));
String withoutPrefix = parameters.get(0).substring(1);
foundCommand = commandManager.findCommandByParameters(withoutPrefix, unparsedParameter);
Parameters parsedParameters = getParsedParameters(unparsedParameter, foundCommand, event.getMessage());
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
Result result = foundCommand.execute(commandContext);
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, result, foundCommand);
}
} catch (Exception e) {
Result result = Result.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, result, foundCommand);
}
}
}
private CommandTemplateContext buildTemplateParameter(MessageReceivedEvent event) {
AChannel channel = channelManagementService.loadChannel(event.getChannel().getIdLong());
AServer server = serverManagementService.loadServer(event.getGuild().getIdLong());
return CommandTemplateContext.builder().channel(channel).server(server).build();
}
public Parameters getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message){
List<Object> parsedParameters = new ArrayList<>();
int mentionedChannelsCount = 0;
int mentionedUserCount = 0;
for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) {
Parameter param = command.getConfiguration().getParameters().get(i);
String value = unParsedCommandParameter.getParameters().get(i);
if(param.getType().equals(Integer.class)){
parsedParameters.add(Integer.parseInt(value));
} else if(param.getType().equals(Double.class)){
parsedParameters.add(Double.parseDouble(value));
} else if(param.getType().equals(GuildChannel.class)){
parsedParameters.add(message.getMentionedChannels().get(mentionedChannelsCount));
mentionedChannelsCount++;
} else if(param.getType().equals(Member.class)) {
parsedParameters.add(message.getMentionedMembers().get(mentionedUserCount));
mentionedUserCount++;
} else {
parsedParameters.add(value);
}
}
return Parameters.builder().parameters(parsedParameters).build();
}
}

View File

@@ -0,0 +1,88 @@
package dev.sheldan.abstracto.commands.management;
import dev.sheldan.abstracto.command.*;
import dev.sheldan.abstracto.command.Module;
import dev.sheldan.abstracto.command.meta.CommandRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ModuleManager implements ModuleRegistry {
@Autowired
private List<Module> modules;
@Autowired
private CommandRegistry commandRegistry;
@Override
public CommandHierarchy getDetailedModules() {
List<PackedModule> modulesWithCommands = new ArrayList<>();
List<Module> currentModules = getModules();
currentModules.forEach(module -> {
List<Command> commands = commandRegistry.getAllCommandsFromModule(module);
PackedModule packed = PackedModule.builder().commands(commands).module(module).subModules(new ArrayList<>()).build();
modulesWithCommands.add(packed);
});
return getHierarchicalPacks(modulesWithCommands, currentModules);
}
private CommandHierarchy getHierarchicalPacks(List<PackedModule> modules, List<Module> currentModules){
List<PackedModule> hierarchical = modules.stream().filter(packedModule -> packedModule.getModule().getParentModule() == null).collect(Collectors.toList());
List<PackedModule> subModules = modules.stream().filter(packedModule -> packedModule.getModule().getParentModule() != null).collect(Collectors.toList());
subModules.forEach(module -> {
List<Module> path = getModulePath(module, currentModules);
Collections.reverse(path);
Module rootModule = path.get(0);
Optional<PackedModule> any = hierarchical.stream().filter(moduleInList -> moduleInList.getModule().getInfo().getName().equals(rootModule.getInfo().getName())).findAny();
if(any.isPresent()){
PackedModule currentNodeInHierarchy = any.get();
for (int i = 1; i < path.size(); i++) {
Optional<PackedModule> nextInHierarchy = currentNodeInHierarchy.getSubModules().stream().filter(module1 -> module1.getModule().equals(module.getModule())).findAny();
if(nextInHierarchy.isPresent()){
currentNodeInHierarchy = nextInHierarchy.get();
} else {
currentNodeInHierarchy.getSubModules().add(module);
currentNodeInHierarchy = module;
}
}
if(path.size() == 1){
currentNodeInHierarchy.getSubModules().add(module);
}
}
});
return CommandHierarchy.builder().rootModules(hierarchical).build();
}
private List<Module> getModulePath(PackedModule moduleToPathFor, List<Module> currentModules){
List<Module> modulesBetweenRootAndThis = new ArrayList<>();
Module current = moduleToPathFor.getModule();
modulesBetweenRootAndThis.add(current);
while(current.getParentModule() != null){
String parentModule = current.getParentModule();
Optional<Module> possibleModule = currentModules.stream().filter(module1 -> module1.getInfo().getName().equals(parentModule)).findFirst();
if(possibleModule.isPresent()){
Module foundModule = possibleModule.get();
modulesBetweenRootAndThis.add(foundModule);
current = foundModule;
} else {
break;
}
}
return modulesBetweenRootAndThis;
}
@Override
public List<Module> getModules() {
return modules;
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.commands.management.exception;
import dev.sheldan.abstracto.command.TemplatedException;
public class CommandNotFoundException extends RuntimeException implements TemplatedException {
public CommandNotFoundException(String s) {
super(s);
}
@Override
public String getTemplateName() {
return "command_not_found";
}
@Override
public Object getTemplateModel() {
return null;
}
}

View File

@@ -0,0 +1,32 @@
package dev.sheldan.abstracto.commands.management.exception;
import dev.sheldan.abstracto.command.TemplatedException;
import dev.sheldan.abstracto.command.Command;
import lombok.Getter;
import java.util.HashMap;
@Getter
public class InsufficientParametersException extends RuntimeException implements TemplatedException {
private Command command;
private String parameterName;
public InsufficientParametersException(String s, Command command, String parameterName) {
super(s);
this.command = command;
this.parameterName = parameterName;
}
@Override
public String getTemplateName() {
return "insufficient_parameters";
}
@Override
public Object getTemplateModel() {
HashMap<String, Object> model = new HashMap<>();
model.put("parameterName", parameterName);
return model;
}
}

View File

@@ -0,0 +1,33 @@
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.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.ResultState;
import dev.sheldan.abstracto.templating.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ExceptionPostExecution implements PostCommandExecution {
@Autowired
private TemplateService templateService;
@Override
public void execute(CommandContext commandContext, Result result, Command command) {
if(result.getResult().equals(ResultState.ERROR)) {
if(result.getThrowable() != null) {
if(result.getThrowable() instanceof TemplatedException) {
TemplatedException exception = (TemplatedException) result.getThrowable();
String text = templateService.renderTemplate(exception.getTemplateName(), exception.getTemplateModel());
commandContext.getChannel().sendMessage(text).queue();
} else {
commandContext.getChannel().sendMessage(result.getMessage()).queue();
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
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.ResultState;
import org.springframework.stereotype.Service;
@Service
public class ReactionPostExecution implements PostCommandExecution {
@Override
public void execute(CommandContext commandContext, Result result, Command command) {
if(result.getResult().equals(ResultState.ERROR)) {
commandContext.getMessage().addReaction("⚠️").queue();
} else {
if(command.getConfiguration().isCausesReaction()){
commandContext.getMessage().addReaction("").queue();
}
}
}
}

View File

@@ -0,0 +1 @@
Insufficient parameters: ${parameterName} was not found.