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

@@ -82,7 +82,7 @@
<dependency>
<groupId>dev.sheldan.abstracto.command</groupId>
<artifactId>command-management</artifactId>
<artifactId>command-support</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.command.Module;
import dev.sheldan.abstracto.command.module.ModuleInfo;
import org.springframework.stereotype.Component;
@Component
public class ChannelsModule implements Module {
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name("channels").description("Includes utilities to configure the channel configuration stored in the database").build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -0,0 +1,54 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
@Service
@Slf4j
public class SetPostTargetCommand implements Command {
@Autowired
private PostTargetManagement postTargetManagement;
@Autowired
private ChannelManagementService channelManagementService;
@Override
@Transactional
public Result execute(CommandContext commandContext) {
GuildChannel channel = (GuildChannel) commandContext.getParameters().getParameters().get(1);
String targetName = (String) commandContext.getParameters().getParameters().get(0);
Guild guild = channel.getGuild();
postTargetManagement.createOrUpdate(targetName, channel.getIdLong(), guild.getIdLong());
log.info("Setting posttarget {} in {} to {}", targetName, guild.getIdLong(), channel.getId());
return Result.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channel = Parameter.builder().name("channel").type(GuildChannel.class).description("The channel to post towards").build();
Parameter postTargetName = Parameter.builder().name("name").type(String.class).description("The name of the post target to redirect").build();
List<Parameter> parameters = Arrays.asList(postTargetName, channel);
return CommandConfiguration.builder()
.name("posttarget")
.module("channels")
.parameters(parameters)
.description("Sets the target of a post done by the bot")
.causesReaction(true)
.build();
}
}

View File

@@ -0,0 +1,120 @@
package dev.sheldan.abstracto.core.commands.help;
import dev.sheldan.abstracto.command.*;
import dev.sheldan.abstracto.command.execution.*;
import dev.sheldan.abstracto.command.module.ModuleInfo;
import dev.sheldan.abstracto.templating.TemplateService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
@Service
public class Help implements Command {
@Autowired
private ModuleRegistry registry;
@Autowired
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
CommandHierarchy commandStructure = registry.getDetailedModules();
StringBuilder sb = new StringBuilder();
if(commandContext.getParameters().getParameters().isEmpty()){
sb.append("Help | Module overview \n");
sb.append("```");
commandStructure.getRootModules().forEach(packedModule -> {
sb.append(getModule(packedModule, 0, true));
sb.append("\n");
});
sb.append("```");
} else {
String parameterValue = commandContext.getParameters().getParameters().get(0).toString();
PackedModule module = commandStructure.getModuleWithName(parameterValue);
if(module != null){
sb.append("Help | Module overview \n");
sb.append(getModule(module, 0, false));
module.getCommands().forEach(command -> {
sb.append(getCommand(command));
});
} else {
Command command = commandStructure.getCommandWithName(parameterValue);
if(command != null) {
sb.append("Help | Command overview");
sb.append("\n");
sb.append(getCommand(command));
}
}
}
commandContext.getChannel().sendMessage(sb.toString()).queue();
return Result.fromSuccess();
}
private String getCommand(Command command){
StringBuilder sb = new StringBuilder();
CommandConfiguration commandConfiguration = command.getConfiguration();
sb.append(String.format("Command: **%s**", commandConfiguration.getName()));
sb.append("\n");
sb.append(String.format("Description: %s", getTemplateOrDefault(commandConfiguration.getDescriptionTemplate(), commandConfiguration.getDescription())));
sb.append("\n");
HelpInfo helpObj = commandConfiguration.getHelp();
if(helpObj != null){
sb.append(String.format("Usage: %s", getTemplateOrDefault(helpObj.getUsageTemplate(), helpObj.getUsage())));
sb.append("\n");
sb.append(String.format("Detailed help: %s", getTemplateOrDefault(helpObj.getLongHelpTemplate(), helpObj.getLongHelp())));
sb.append("\n");
}
return sb.toString();
}
private String getTemplateOrDefault(String templateKey, String defaultText) {
if(templateKey == null) {
return defaultText;
} else {
return templateService.renderTemplate(templateKey, null);
}
}
private String getModule(PackedModule module, int depth, boolean recursive){
StringBuilder sb = new StringBuilder();
String intentation = "";
if(depth > 0){
intentation = StringUtils.repeat("-", depth) + ">";
}
ModuleInfo info = module.getModule().getInfo();
sb.append(String.format(intentation +"**%s** \n", info.getName()));
sb.append(String.format(intentation + "%s \n", info.getDescription()));
if(recursive) {
module.getSubModules().forEach(subModule -> {
sb.append(getModule(subModule, depth + 1, true));
});
}
sb.append("\n");
return sb.toString();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter moduleOrCommandName = Parameter.builder()
.name("name")
.optional(true)
.description("Name of module or command")
.type(String.class)
.build();
return CommandConfiguration.builder()
.name("help")
.module("support")
.parameters(Collections.singletonList(moduleOrCommandName))
.description("Prints the help")
.causesReaction(false)
.build();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.commands.help;
import dev.sheldan.abstracto.command.Module;
import dev.sheldan.abstracto.command.module.ModuleInfo;
import org.springframework.stereotype.Component;
@Component
public class SupportModule implements Module {
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name("support").description("Utilities for support").build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -0,0 +1,50 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.HelpInfo;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.core.commands.utility.model.EchoModel;
import dev.sheldan.abstracto.templating.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class Echo implements Command {
private static final String TEMPLATE_NAME = "echo_response";
@Autowired
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
StringBuilder sb = new StringBuilder();
commandContext.getParameters().getParameters().forEach(o -> {
sb.append(o.toString());
});
EchoModel model = EchoModel.parentBuilder().parent(commandContext.getCommandTemplateContext()).text(sb.toString()).build();
commandContext.getChannel().sendMessage(templateService.renderTemplate(TEMPLATE_NAME, model)).queue();
return Result.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("input").type(String.class).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().usageTemplate("echo_usage").longHelpTemplate("echo_long_help").build();
return CommandConfiguration.builder()
.name("echo")
.module("utility")
.descriptionTemplate("echo_description")
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
}

View File

@@ -0,0 +1,39 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.command.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.core.commands.utility.model.PingModel;
import dev.sheldan.abstracto.templating.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class Ping implements Command {
public static final String PING_TEMPLATE = "ping";
@Autowired
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
long ping = commandContext.getJda().getGatewayPing();
PingModel model = PingModel.parentBuilder().parent(commandContext.getCommandTemplateContext()).latency(ping).build();
String text = templateService.renderTemplate(PING_TEMPLATE, model);
commandContext.getChannel().sendMessage(text).queue();
return Result.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
return CommandConfiguration.builder()
.name("ping")
.module("utility")
.descriptionTemplate("ping_description")
.causesReaction(false)
.build();
}
}

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.command.Module;
import dev.sheldan.abstracto.command.module.ModuleInfo;
import org.springframework.stereotype.Component;
@Component
public class UtilityModule implements Module {
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name("utility").description("General utilities").build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.commands.utility.model;
import dev.sheldan.abstracto.command.execution.CommandTemplateContext;
import lombok.Builder;
import lombok.Getter;
@Getter
public class EchoModel extends CommandTemplateContext {
private String text;
@Builder(builderMethodName = "parentBuilder")
private EchoModel(CommandTemplateContext parent, String text) {
super(parent);
this.text = text;
}
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.core.commands.utility.model;
import dev.sheldan.abstracto.command.execution.CommandTemplateContext;
import lombok.Builder;
import lombok.Getter;
@Getter
public class PingModel extends CommandTemplateContext {
private Long latency;
@Builder(builderMethodName = "parentBuilder")
private PingModel(CommandTemplateContext parent, Long latency) {
super(parent);
this.latency = latency;
}
}

View File

@@ -1,11 +1,6 @@
package dev.sheldan.abstracto.core.service.management;
package dev.sheldan.abstracto.core.management;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.AServer;
import dev.sheldan.abstracto.core.models.PostTarget;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.models.*;
import dev.sheldan.abstracto.repository.ServerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -37,6 +32,13 @@ public class ServerManagementServiceBean implements ServerManagementService {
server.getChannels().add(channel);
}
@Override
public AUserInAServer addUserToServer(AServer server, AUser user) {
AUserInAServer aUserInAServer = AUserInAServer.builder().serverReference(server).userReference(user).build();
server.getUsers().add(aUserInAServer);
return aUserInAServer;
}
@Override
public AChannel getPostTarget(Long serverId, String name) {
AServer server = this.loadServer(serverId);

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.core.management;
import dev.sheldan.abstracto.core.models.AServer;
import dev.sheldan.abstracto.core.models.AUser;
import dev.sheldan.abstracto.core.models.AUserInAServer;
import dev.sheldan.abstracto.repository.UserInServerRepository;
import dev.sheldan.abstracto.repository.UserRepository;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserManagementServiceBean implements UserManagementService {
@Autowired
private UserInServerRepository userInServerRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private ServerManagementService serverManagementService;
@Override
public AUserInAServer loadUser(Long userId, Long serverId) {
AUser user = userRepository.getOne(userId);
AServer server = serverManagementService.loadServer(serverId);
return loadUser(user, server);
}
@Override
public AUserInAServer loadUser(AUser user, AServer server) {
return userInServerRepository.findByServerReferenceAndUserReference(server, user);
}
@Override
public AUserInAServer loadUser(Member member) {
AUserInAServer aUserInAServer = this.loadUser(member.getGuild().getIdLong(), member.getIdLong());
if(aUserInAServer == null) {
return this.createUserInServer(member);
}
return null;
}
@Override
public AUserInAServer createUserInServer(Member member) {
AServer server = serverManagementService.loadServer(member.getGuild().getIdLong());
if(!userRepository.existsById(member.getIdLong())) {
this.createUser(member);
}
AUser aUser = userRepository.getOne(member.getIdLong());
return serverManagementService.addUserToServer(server, aUser);
}
@Override
public AUser createUser(Member member) {
AUser aUser = AUser.builder().id(member.getIdLong()).build();
userRepository.save(aUser);
return aUser;
}
}

View File

@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
public class ChannelServiceBean implements ChannelService {
@Autowired
private BotService botService;
private Bot botService;
@Override
public void sendTextInAChannel(String text, AChannel channel) {

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.models.PostTarget;
import lombok.extern.slf4j.Slf4j;
@@ -17,7 +18,10 @@ public class PostTargetServiceBean implements PostTargetService {
private ServerManagementService serverManagementService;
@Autowired
private BotService botService;
private PostTargetManagement postTargetManagement;
@Autowired
private Bot botService;
@Override
public void sendTextInPostTarget(String text, PostTarget target) {
@@ -34,4 +38,14 @@ public class PostTargetServiceBean implements PostTargetService {
log.warn("Incorrect post target configuration: Guild id {} was not found.", target.getServerReference().getId());
}
}
@Override
public void sendTextInPostTarget(String text, String postTargetName, Long serverId) {
PostTarget postTarget = postTargetManagement.getPostTarget(postTargetName, serverId);
if(postTarget != null) {
this.sendTextInPostTarget(text, postTarget);
} else {
log.warn("PostTarget {} in server {} was not found!", postTargetName, serverId);
}
}
}

View File

@@ -28,7 +28,7 @@ import java.util.Set;
public class StartupManager implements Startup {
@Autowired
private BotService service;
private Bot service;
@Autowired
private List<? extends ListenerAdapter> listeners;

View File

@@ -40,16 +40,14 @@ public class JoinLeaveListener extends ListenerAdapter {
@Transactional
public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
String text = getRenderedEvent(event.getUser(), USER_JOIN_TEMPLATE);
PostTarget postTarget = postTargetManagement.getPostTarget(PostTarget.JOIN_LOG, event.getGuild().getIdLong());
postTargetService.sendTextInPostTarget(text, postTarget);
postTargetService.sendTextInPostTarget(text, PostTarget.JOIN_LOG, event.getGuild().getIdLong());
}
@Override
@Transactional
public void onGuildMemberLeave(@Nonnull GuildMemberLeaveEvent event) {
String text = getRenderedEvent(event.getUser(), USER_LEAVE_TEMPLATE);
PostTarget postTarget = postTargetManagement.getPostTarget(PostTarget.LEAVE_LOG, event.getGuild().getIdLong());
postTargetService.sendTextInPostTarget(text, postTarget);
postTargetService.sendTextInPostTarget(text, PostTarget.LEAVE_LOG, event.getGuild().getIdLong());
}
@NotNull

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.AServer;
import dev.sheldan.abstracto.core.models.AUser;
import dev.sheldan.abstracto.core.models.AUserInAServer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserInServerRepository extends JpaRepository<AUserInAServer, Long> {
AUserInAServer findByServerReferenceAndUserReference(AServer serverId, AUser userId);
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.AUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<AUser, Long> {
}

View File

@@ -0,0 +1 @@
Echos the input back to the same channel