moved commands into the core, because its a core functionality

This commit is contained in:
Sheldan
2020-04-04 18:08:55 +02:00
parent a957c66905
commit 9b46f8d187
90 changed files with 352 additions and 407 deletions

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto.core.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

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.core.command;
import dev.sheldan.abstracto.core.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.listener.FeatureAware;
public interface Command extends FeatureAware {
CommandResult execute(CommandContext commandContext);
CommandConfiguration getConfiguration();
}

View File

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

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.core.command;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Getter @Builder
public class CommandHierarchy {
private List<PackedModule> rootModules;
public PackedModule getModuleWithName(String name){
for (PackedModule module: rootModules) {
PackedModule found = getModuleWithName(name, module);
if(found != null){
return found;
}
}
return null;
}
private PackedModule getModuleWithName(String name, PackedModule module){
if(module.getModuleInterface().getInfo().getName().equals(name)){
return module;
} else {
for (PackedModule subModule: module.getSubModules()) {
PackedModule possibleModule = getModuleWithName(name, subModule);
if(possibleModule != null){
return possibleModule;
}
}
return null;
}
}
public Command getCommandWithName(String name) {
for (PackedModule module: rootModules) {
Command command = getCommandFromModule(name, module);
if(command != null){
return command;
}
}
return null;
}
private Command getCommandFromModule(String name, PackedModule module){
Command foundCommand = module.getCommands().stream().filter(command -> command.getConfiguration().getName().equals(name)).findAny().orElse(null);
if(foundCommand == null){
for (PackedModule subModule: module.getSubModules()) {
Command command = getCommandFromModule(name, subModule);
if(command != null){
return command;
}
}
return null;
} else {
return foundCommand;
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.core.command;
import dev.sheldan.abstracto.core.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

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.command;
import lombok.Builder;
import lombok.Getter;
@Getter @Builder
public class HelpInfo {
private String usage;
private String longHelp;
private boolean templated;
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.core.command;
import dev.sheldan.abstracto.core.command.module.ModuleInfo;
public interface ModuleInterface {
ModuleInfo getInfo();
String getParentModule();
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.command;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Builder
@Getter
public class PackedModule {
private ModuleInterface moduleInterface;
private PackedModule parentModule;
private List<PackedModule> subModules;
private List<Command> commands;
}

View File

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

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.core.command.execution;
import dev.sheldan.abstracto.core.command.HelpInfo;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Getter @Builder
public class CommandConfiguration {
private String name;
private String module;
private String description;
private List<Parameter> parameters;
private List<String> aliases;
private boolean causesReaction;
private boolean templated;
private HelpInfo help;
public int getNecessaryParameterCount(){
return (int) parameters.stream().filter(parameter -> !parameter.isOptional()).count();
}
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.core.command.execution;
import dev.sheldan.abstracto.core.models.UserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
@Builder
@Getter
public class CommandContext {
private TextChannel channel;
private Guild guild;
private Member author;
private Message message;
private UserInitiatedServerContext userInitiatedContext;
private Parameters parameters;
private JDA jda;
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.core.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

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.core.command.execution;
import dev.sheldan.abstracto.core.models.UserInitiatedServerContext;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Slf4j
public class ContextConverter {
public static <T extends UserInitiatedServerContext> UserInitiatedServerContext fromCommandContext(CommandContext commandContext, Class<T> clazz) {
Method m = null;
try {
m = clazz.getMethod("builder");
UserInitiatedServerContext.UserInitiatedServerContextBuilder<?, ?> builder = (UserInitiatedServerContext.UserInitiatedServerContextBuilder) m.invoke(null, null);
return builder
.member(commandContext.getAuthor())
.guild(commandContext.getGuild())
.messageChannel(commandContext.getChannel())
.channel(commandContext.getUserInitiatedContext().getChannel())
.server(commandContext.getUserInitiatedContext().getServer())
.aUserInAServer(commandContext.getUserInitiatedContext().getAUserInAServer())
.user(commandContext.getUserInitiatedContext().getUser())
.build();
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("Failed to execute builder method", e);
}
throw new RuntimeException("Failed to create model from context");
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.command.execution;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter @Builder
public class Parameter {
private String name;
private Class type;
private String description;
private boolean optional;
private boolean remainder;
private Integer maxLength;
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.core.command.execution;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Builder
public class Parameters {
@Getter
private List<Object> parameters;
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.core.command.execution;
public enum ResultState {
ERROR, SUCCESSFUL, IGNORED
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.command.meta;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.ModuleInterface;
import dev.sheldan.abstracto.core.command.module.ModuleInfo;
import net.dv8tion.jda.api.entities.Message;
import java.util.List;
public interface CommandRegistry {
Command findCommandByParameters(String name, UnParsedCommandParameter context);
Command findCommand(String message);
List<Command> getAllCommands();
List<Command> getAllCommandsFromModule(ModuleInterface module);
boolean isCommand(Message message);
}

View File

@@ -0,0 +1,38 @@
package dev.sheldan.abstracto.core.command.meta;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
public class UnParsedCommandParameter {
private static Pattern SPLIT_REGEX = Pattern.compile("\"([^\"]*)\"|(\\S+)");
public UnParsedCommandParameter(String parameters) {
this.parameters = new ArrayList<>();
Matcher m = SPLIT_REGEX.matcher(parameters);
boolean skippedCommand = false;
while (m.find()) {
if(!skippedCommand) {
skippedCommand = true;
continue;
}
if (m.group(1) != null) {
String group = m.group(1);
if(!group.equals("")) {
this.parameters.add(group);
}
} else {
String group = m.group(2);
if(!group.equals("")) {
this.parameters.add(group);
}
}
}
}
private List<String> parameters;
}

View File

@@ -0,0 +1,32 @@
package dev.sheldan.abstracto.core.command.models;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import lombok.*;
import javax.persistence.*;
@Entity
@Table(name = "channel_group_command")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class AChannelGroupCommand {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "command_id", nullable = false)
@Getter
@Setter
@Column
private ACommand command;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id", nullable = false)
@Getter
@Setter
@Column
private AChannelGroup group;
private Boolean enabled;
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.core.command.models;
import lombok.*;
import javax.persistence.*;
@Entity
@Table(name = "command")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class ACommand {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@Getter
@Setter
@JoinColumn(name = "module_id")
private AModule module;
}

View File

@@ -0,0 +1,34 @@
package dev.sheldan.abstracto.core.command.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "module")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class AModule {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true)
@Builder.Default
@JoinColumn(name = "module_id")
private List<ACommand> commands = new ArrayList<>();
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.command.module;
import dev.sheldan.abstracto.core.command.ModuleInterface;
import org.springframework.stereotype.Service;
@Service
public class AbstracatoModuleInterface implements ModuleInterface {
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name("default").description("Default module provided by abstracto").build();
}
@Override
public String getParentModule() {
return null;
}
}

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.command.module;
import lombok.Builder;
import lombok.Getter;
@Getter @Builder
public class ModuleInfo {
private String name;
private String description;
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.command.service;
import dev.sheldan.abstracto.core.command.models.ACommand;
public interface CommandService {
ACommand createCommand(String name, String moduleName);
Boolean doesCommandExist(String name);
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.core.command.service;
import dev.sheldan.abstracto.core.command.CommandHierarchy;
import dev.sheldan.abstracto.core.command.ModuleInterface;
import java.util.List;
public interface ModuleRegistry {
CommandHierarchy getDetailedModules();
List<ModuleInterface> getModuleInterfaces();
}

View File

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

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.command.service.management;
import dev.sheldan.abstracto.core.command.models.ACommand;
import dev.sheldan.abstracto.core.command.models.AModule;
public interface CommandManagementService {
ACommand createCommand(String name, String moduleName);
ACommand createCommand(String name, AModule moduleName);
ACommand findCommandByName(String name);
Boolean doesCommandExist(String name);
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.core.command.service.management;
import dev.sheldan.abstracto.core.command.models.AModule;
public interface ModuleManagementService {
AModule createModule(String name);
AModule getOrCreate(String name);
AModule findModuleByName(String name);
Boolean doesModuleExist(String name);
}

View File

@@ -0,0 +1,32 @@
package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.command.models.ACommand;
import lombok.*;
import javax.persistence.*;
@Entity
@Table(name = "channel_group_command")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class AChannelGroupCommand {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "command_id", nullable = false)
@Getter
@Setter
@Column
private ACommand command;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id", nullable = false)
@Getter
@Setter
@Column
private AChannelGroup group;
private Boolean enabled;
}