[AB-77] moving the templating module into the core module

adding a possibility to overlay specific templates for particular servers
adding commands to configure templates
adding file parameter support
This commit is contained in:
Sheldan
2021-02-19 16:27:27 +01:00
parent 909dc87d94
commit 43eca33113
361 changed files with 2158 additions and 2591 deletions

View File

@@ -68,6 +68,11 @@
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -106,12 +111,6 @@
<type>test-jar</type>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.templating</groupId>
<artifactId>templating-interface</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>

View File

@@ -9,6 +9,7 @@ import dev.sheldan.abstracto.core.command.exception.IncorrectParameterException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.CommandParameterHandler;
import dev.sheldan.abstracto.core.command.handler.CommandParameterIterators;
import dev.sheldan.abstracto.core.command.service.CommandManager;
@@ -110,7 +111,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
try {
String contentStripped = event.getMessage().getContentRaw();
List<String> parameters = Arrays.asList(contentStripped.split(" "));
UnParsedCommandParameter unParsedParameter = new UnParsedCommandParameter(contentStripped);
UnParsedCommandParameter unParsedParameter = new UnParsedCommandParameter(contentStripped, event.getMessage());
String commandName = commandManager.getCommandName(parameters.get(0), event.getGuild().getIdLong());
foundCommand = commandManager.findCommandByParameters(commandName, unParsedParameter);
tryToExecuteFoundCommand(event, foundCommand, unParsedParameter);
@@ -254,7 +255,6 @@ public class CommandReceivedHandler extends ListenerAdapter {
Parameter param = command.getConfiguration().getParameters().get(0);
CommandParameterIterators iterators = new CommandParameterIterators(channelIterator, emoteIterator, memberIterator, roleIterator);
boolean reminderActive = false;
List<CommandParameterHandler> orderedHandlers = parameterHandlers.stream().sorted(comparing(CommandParameterHandler::getPriority)).collect(Collectors.toList());
List<CompletableFuture> futures = new ArrayList<>();
for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) {
if(i < command.getConfiguration().getParameters().size() && !param.isRemainder()) {
@@ -262,9 +262,9 @@ public class CommandReceivedHandler extends ListenerAdapter {
} else {
reminderActive = true;
}
String value = unParsedCommandParameter.getParameters().get(i);
UnparsedCommandParameterPiece value = unParsedCommandParameter.getParameters().get(i);
boolean handlerMatched = false;
for (CommandParameterHandler handler : orderedHandlers) {
for (CommandParameterHandler handler : parameterHandlers) {
try {
if (handler.handles(param.getType())) {
handlerMatched = true;
@@ -286,24 +286,25 @@ public class CommandReceivedHandler extends ListenerAdapter {
}
}
if(!handlerMatched) {
Object valueAsString = value.getValue().toString();
if(!reminderActive) {
parsedParameters.add(value);
parsedParameters.add(valueAsString);
} else {
if(!param.isListParam()) {
if(parsedParameters.isEmpty()) {
parsedParameters.add(value);
parsedParameters.add(valueAsString);
} else {
int lastIndex = parsedParameters.size() - 1;
parsedParameters.set(lastIndex, parsedParameters.get(lastIndex) + " " + value);
parsedParameters.set(lastIndex, parsedParameters.get(lastIndex) + " " + valueAsString);
}
} else {
if(parsedParameters.isEmpty()) {
ArrayList<Object> list = new ArrayList<>();
list.add(value);
list.add(valueAsString);
parsedParameters.add(list);
} else {
int lastIndex = parsedParameters.size() - 1;
((List)parsedParameters.get(lastIndex)).add(value);
((List)parsedParameters.get(lastIndex)).add(valueAsString);
}
}
}
@@ -339,5 +340,6 @@ public class CommandReceivedHandler extends ListenerAdapter {
public void postConstruct() {
metricService.registerCounter(COMMANDS_PROCESSED_COUNTER, "Commands processed");
metricService.registerCounter(COMMANDS_WRONG_PARAMETER_COUNTER, "Commands with incorrect parameter");
this.parameterHandlers = parameterHandlers.stream().sorted(comparing(CommandParameterHandler::getPriority)).collect(Collectors.toList());
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.AChannelParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.TextChannelParameterHandler;
import dev.sheldan.abstracto.core.models.database.AChannel;
@@ -29,10 +30,10 @@ public class AChannelParameterHandlerImpl implements AChannelParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
TextChannel textChannel = (TextChannel) textChannelParameterHandler.handle(input, iterators, clazz, context);
if(textChannel == null) {
Long channelId = Long.parseLong(input);
Long channelId = Long.parseLong((String) input.getValue());
AChannel actualInstance = channelManagementService.loadChannel(channelId);
return AChannel.builder().fake(true).id(actualInstance.getId()).build();
} else {

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.AEmoteParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.EmoteParameterHandler;
import dev.sheldan.abstracto.core.models.database.AEmote;
@@ -25,12 +26,12 @@ public class AEmoteParameterHandlerImpl implements AEmoteParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
Emote emote = (Emote) emoteParameterHandler.handle(input, iterators, Emote.class, context);
if(emote != null) {
return emoteService.getFakeEmoteFromEmote(emote);
} else {
return emoteService.getFakeEmote(input);
return emoteService.getFakeEmote(input.getValue());
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.ARoleParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.RoleParameterHandler;
import dev.sheldan.abstracto.core.models.database.ARole;
@@ -25,7 +26,7 @@ public class ARoleParameterHandlerImpl implements ARoleParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
Role role = (Role) roleParameterHandler.handle(input, iterators, Role.class, context);
return roleService.getFakeRoleFromRole(role);
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.AUserInAServerParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.MemberParameterHandler;
import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException;
@@ -23,7 +24,7 @@ public class AUserInAServerParameterHandlerImpl implements AUserInAServerParamet
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture handleAsync(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public CompletableFuture handleAsync(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
CompletableFuture<AUserInAServer> future = new CompletableFuture<>();
memberParameterHandler.handleAsync(input, iterators, Member.class, context).whenComplete((o, throwable) -> {
try {
@@ -32,7 +33,7 @@ public class AUserInAServerParameterHandlerImpl implements AUserInAServerParamet
Member member = (Member) o;
actualInstance = userInServerManagementService.loadOrCreateUser(member);
} else {
Long userId = Long.parseLong(input);
Long userId = Long.parseLong((String) input.getValue());
actualInstance = userInServerManagementService.loadAUserInAServerOptional(context.getGuild().getIdLong(), userId).orElseThrow(() -> new UserInServerNotFoundException(0L));
}
future.complete(AUserInAServer.builder().userInServerId(actualInstance.getUserInServerId()).build());

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.BooleanParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component;
@@ -13,8 +14,8 @@ public class BooleanParameterHandlerImpl implements BooleanParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
return Boolean.valueOf(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
return Boolean.valueOf((String) input.getValue());
}
@Override

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.exception.ChannelGroupNotFoundException;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.ChannelGroupParameterHandler;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -22,10 +23,11 @@ public class ChannelGroupParameterHandlerImpl implements ChannelGroupParameterHa
private ServerManagementService serverManagementService;
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
AServer server = serverManagementService.loadServer(context.getGuild().getIdLong());
AChannelGroup actualInstance = channelGroupManagementService.findByNameAndServerOptional(input, server)
.orElseThrow(() -> new ChannelGroupNotFoundException(input, channelGroupManagementService.getAllAvailableAsString(server)));
String inputString = (String) input.getValue();
AChannelGroup actualInstance = channelGroupManagementService.findByNameAndServerOptional(inputString, server)
.orElseThrow(() -> new ChannelGroupNotFoundException(inputString, channelGroupManagementService.getAllAvailableAsString(server)));
ChannelGroupType channelGroupType = ChannelGroupType
.builder()
.id(actualInstance.getChannelGroupType().getId())

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.ChannelGroupTypeParameterHandler;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.management.ChannelGroupTypeManagementService;
@@ -24,8 +25,8 @@ public class ChannelGroupTypeParameterHandlerImpl implements ChannelGroupTypePar
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
ChannelGroupType actualGroupType = channelGroupTypeManagementService.findChannelGroupTypeByKey(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
ChannelGroupType actualGroupType = channelGroupTypeManagementService.findChannelGroupTypeByKey((String) input.getValue());
return ChannelGroupType
.builder()
.groupTypeKey(actualGroupType.getGroupTypeKey())

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.CommandKeyParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component;
@@ -15,8 +16,8 @@ public class CommandKeyParameterHandlerImpl implements CommandKeyParameterHandle
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
return CommandParameterKey.getEnumFromKey(clazz, input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
return CommandParameterKey.getEnumFromKey(clazz, (String) input.getValue());
}
@Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.DoubleParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component;
@@ -13,8 +14,8 @@ public class DoubleParameterHandlerImpl implements DoubleParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
return Double.parseDouble(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
return Double.parseDouble((String) input.getValue());
}
@Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.DurationParameterHandler;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import net.dv8tion.jda.api.entities.Message;
@@ -16,8 +17,8 @@ public class DurationParameterHandlerImpl implements DurationParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
return ParseUtils.parseDuration(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
return ParseUtils.parseDuration((String) input.getValue());
}
@Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.EmoteParameterHandler;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Message;
@@ -18,13 +19,14 @@ public class EmoteParameterHandlerImpl implements EmoteParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
Matcher matcher = Message.MentionType.EMOTE.getPattern().matcher(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
String inputString = (String) input.getValue();
Matcher matcher = Message.MentionType.EMOTE.getPattern().matcher(inputString);
if(matcher.matches()) {
return iterators.getEmoteIterator().next();
} else {
if(StringUtils.isNumeric(input)) {
long emoteId = Long.parseLong(input);
if(StringUtils.isNumeric(inputString)) {
long emoteId = Long.parseLong(inputString);
return context.getGuild().getEmoteById(emoteId);
} else {
return null;

View File

@@ -0,0 +1,53 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.exception.NoAttachmentFoundException;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.FileParameterHandler;
import dev.sheldan.abstracto.core.service.HttpService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
@Component
public class FileParameterHandlerImpl implements FileParameterHandler {
@Autowired
private HttpService httpService;
@Override
public boolean async() {
return true;
}
@Override
public CompletableFuture<Object> handleAsync(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
if(context.getAttachments().isEmpty()) {
throw new NoAttachmentFoundException();
}
Message.Attachment attachment = (Message.Attachment) input.getValue();
CompletableFuture<Object> result = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try {
result.complete(httpService.downloadFileToTempFile(attachment.getUrl()));
} catch (IOException e) {
result.completeExceptionally(e);
}
});
return result;
}
@Override
public boolean handles(Class clazz) {
return clazz.equals(File.class);
}
@Override
public Integer getPriority() {
return CommandConstants.CORE_HANDLER_PRIORITY;
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.EmoteParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.FullEmoteParameterHandler;
import dev.sheldan.abstracto.core.models.FullEmote;
@@ -26,13 +27,13 @@ public class FullEmoteParameterHandlerImpl implements FullEmoteParameterHandler
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
Emote emote = (Emote) emoteParameterHandler.handle(input, iterators, Emote.class, context);
AEmote aEmote;
if(emote != null) {
aEmote = emoteService.getFakeEmoteFromEmote(emote);
} else {
aEmote = emoteService.getFakeEmote(input);
aEmote = emoteService.getFakeEmote(input.getValue());
}
return FullEmote.builder().emote(emote).fakeEmote(aEmote).build();
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.FullRoleParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.RoleParameterHandler;
import dev.sheldan.abstracto.core.models.FullRole;
@@ -26,7 +27,7 @@ public class FullRoleParameterHandlerImpl implements FullRoleParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
Role role = (Role) roleParameterHandler.handle(input, iterators, Role.class, context);
ARole aRole = roleService.getFakeRoleFromRole(role);
return FullRole.builder().role(aRole).serverRole(role).build();

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.IntegerParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component;
@@ -14,8 +15,8 @@ public class IntegerParameterHandlerImpl implements IntegerParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
return Integer.parseInt(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
return Integer.parseInt((String) input.getValue());
}
@Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.LongParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component;
@@ -14,8 +15,8 @@ public class LongParameterHandlerImpl implements LongParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
return Long.parseLong(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
return Long.parseLong((String) input.getValue());
}
@Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.MemberParameterHandler;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
@@ -22,13 +23,14 @@ public class MemberParameterHandlerImpl implements MemberParameterHandler {
}
@Override
public CompletableFuture<Object> handleAsync(String input, CommandParameterIterators iterators, Class clazz, Message context) {
Matcher matcher = Message.MentionType.USER.getPattern().matcher(input);
public CompletableFuture<Object> handleAsync(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
String inputString = (String) input.getValue();
Matcher matcher = Message.MentionType.USER.getPattern().matcher(inputString);
if(matcher.matches()) {
return CompletableFuture.completedFuture(iterators.getMemberIterator().next());
} else {
// TODO add handling for names
long userId = Long.parseLong(input);
long userId = Long.parseLong(inputString);
return context.getGuild().retrieveMemberById(userId).submit().thenApply(member -> member);
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.RoleParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
@@ -16,12 +17,13 @@ public class RoleParameterHandlerImpl implements RoleParameterHandler {
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
Matcher matcher = Message.MentionType.ROLE.getPattern().matcher(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
String inputString = (String) input.getValue();
Matcher matcher = Message.MentionType.ROLE.getPattern().matcher(inputString);
if(matcher.matches()) {
return iterators.getRoleIterator().next();
} else {
long roleId = Long.parseLong(input);
long roleId = Long.parseLong(inputString);
return context.getGuild().getRoleById(roleId);
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.TextChannelParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
@@ -16,12 +17,13 @@ public class TextChannelParameterHandlerImpl implements TextChannelParameterHand
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
Matcher matcher = Message.MentionType.CHANNEL.getPattern().matcher(input);
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Class clazz, Message context) {
String inputString = (String) input.getValue();
Matcher matcher = Message.MentionType.CHANNEL.getPattern().matcher(inputString);
if(matcher.matches()) {
return iterators.getChannelIterator().next();
} else {
long channelId = Long.parseLong(input);
long channelId = Long.parseLong(inputString);
return context.getGuild().getTextChannelById(channelId);
}
}

View File

@@ -37,7 +37,7 @@ public class ConditionPostExecution implements PostCommandExecution {
.member(commandContext.getAuthor())
.build())
.build();
channelService.sendEmbedTemplateInChannel(GENERIC_COMMAND_EXCEPTION_MODEL_KEY, conditionModel, commandContext.getChannel());
channelService.sendEmbedTemplateInTextChannelList(GENERIC_COMMAND_EXCEPTION_MODEL_KEY, conditionModel, commandContext.getChannel());
}
}
}

View File

@@ -6,7 +6,6 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ResultState;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -15,9 +14,6 @@ import org.springframework.stereotype.Service;
@Slf4j
public class ExceptionPostExecution implements PostCommandExecution {
@Autowired
private TemplateService templateService;
@Autowired
private ExceptionService exceptionService;

View File

@@ -4,7 +4,6 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.CommandReceivedHandler;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.ModuleInterface;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.exception.CommandNotFoundException;
import dev.sheldan.abstracto.core.command.exception.InsufficientParametersException;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
@@ -47,14 +46,12 @@ public class CommandManager implements CommandRegistry {
}
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()) {
if(commandConfiguration.getNecessaryParameterCount() > unParsedCommandParameter.getParameters().size()) {
String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount() - 1).getName();
metricService.incrementCounter(CommandReceivedHandler.COMMANDS_WRONG_PARAMETER_COUNTER);
throw new InsufficientParametersException(o, nextParameterName);
}
parameterFit = paramCountFits || hasRemainderParameter;
parameterFit = true;
} else {
parameterFit = unParsedCommandParameter.getParameters().isEmpty();
}

View File

@@ -26,6 +26,7 @@ import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -120,6 +121,9 @@ public class CommandServiceBean implements CommandService {
builder.append(" ");
}
commandConfig.getParameters().forEach(parameter -> {
if(parameter.getType().equals(File.class)) {
return;
}
String[] enclosing = parameter.isOptional() ? OPTIONAL_ENCLOSING : MANDATORY_ENCLOSING;
builder.append(enclosing[0]);
builder.append(parameter.getName());
@@ -163,14 +167,14 @@ public class CommandServiceBean implements CommandService {
}
@Override
public UnParsedCommandParameter getUnParsedCommandParameter(String messageContent) {
return new UnParsedCommandParameter(messageContent);
public UnParsedCommandParameter getUnParsedCommandParameter(String messageContent, Message message) {
return new UnParsedCommandParameter(messageContent, message);
}
@Override
public CompletableFuture<Parameters> getParametersForCommand(String commandName, Message messageContainingContent) {
String contentStripped = messageContainingContent.getContentRaw();
UnParsedCommandParameter unParsedParameter = getUnParsedCommandParameter(contentStripped);
UnParsedCommandParameter unParsedParameter = getUnParsedCommandParameter(contentStripped, messageContainingContent);
Command command = commandRegistry.findCommandByParameters(commandName, unParsedParameter);
return commandReceivedHandler.getParsedParameters(unParsedParameter, command, messageContainingContent);
}

View File

@@ -11,8 +11,8 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.templating.Templatable;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.Templatable;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageChannel;
@@ -46,13 +46,13 @@ public class ExceptionServiceBean implements ExceptionService {
GenericExceptionModel exceptionModel = buildCommandModel(throwable, context);
log.info("Reporting generic exception {} of command {} towards channel {} in server {}.",
throwable.getClass().getSimpleName(), command.getConfiguration().getName(), context.getChannel().getId(), context.getGuild().getId());
channelService.sendEmbedTemplateInChannel("generic_command_exception", exceptionModel, context.getChannel());
channelService.sendEmbedTemplateInTextChannelList("generic_command_exception", exceptionModel, context.getChannel());
} catch (Exception e) {
log.error("Failed to notify about exception.", e);
}
} else if(throwable instanceof Templatable){
GenericExceptionModel exceptionModel = buildCommandModel(throwable, context);
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, exceptionModel);
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, exceptionModel, context.getGuild().getIdLong());
channelService.sendTextToChannel(text, context.getChannel());
} else {
channelService.sendTextToChannel(throwable.getLocalizedMessage(), context.getChannel());

View File

@@ -16,8 +16,8 @@ import dev.sheldan.abstracto.core.models.template.commands.ListChannelGroupsMode
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -48,7 +48,7 @@ public class ListChannelGroups extends AbstractConditionableCommand {
List<AChannelGroup> channelGroups = channelGroupManagementService.findAllInServer(server);
ListChannelGroupsModel template = (ListChannelGroupsModel) ContextConverter.fromCommandContext(commandContext, ListChannelGroupsModel.class);
template.setGroups(convertAChannelGroupToChannelGroupChannel(channelGroups));
MessageToSend response = templateService.renderEmbedTemplate("listChannelGroups_response", template);
MessageToSend response = templateService.renderEmbedTemplate("listChannelGroups_response", template, commandContext.getGuild().getIdLong());
channelService.sendMessageToSendToChannel(response, commandContext.getChannel());
return CommandResult.fromIgnored();
}

View File

@@ -19,7 +19,6 @@ import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildChannel;
@@ -45,9 +44,6 @@ public class PostTargetCommand extends AbstractConditionableCommand {
@Autowired
private PostTargetService postTargetService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@@ -76,7 +72,7 @@ public class PostTargetCommand extends AbstractConditionableCommand {
postTargetEntries.add(postTargetEntry);
}
});
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(POST_TARGET_SHOW_TARGETS, posttargetDisplayModel, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(POST_TARGET_SHOW_TARGETS, posttargetDisplayModel, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
String targetName = (String) commandContext.getParameters().getParameters().get(0);

View File

@@ -18,7 +18,7 @@ import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -60,7 +60,7 @@ public class Allow extends AbstractConditionableCommand {
commandService.unRestrictCommand(command, server);
} else {
// TODO refactor to use exception
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object()));
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object(), commandContext.getGuild().getIdLong()));
}
return CommandResult.fromSuccess();
}

View File

@@ -17,7 +17,7 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -57,7 +57,7 @@ public class AllowRole extends AbstractConditionableCommand {
ACommand command = commandManagementService.findCommandByName(name);
commandService.allowCommandForRole(command, actualRole);
} else {
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object()));
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object(), commandContext.getGuild().getIdLong()));
}
return CommandResult.fromSuccess();
}

View File

@@ -17,7 +17,7 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -56,7 +56,8 @@ public class DisAllowRole extends AbstractConditionableCommand {
ACommand command = commandManagementService.findCommandByName(name);
commandService.disAllowCommandForRole(command, actualRole);
} else {
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object()));
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE,
new Object(), commandContext.getGuild().getIdLong()));
}
return CommandResult.fromSuccess();
}

View File

@@ -18,7 +18,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -49,7 +49,7 @@ public class DisableFeature extends AbstractConditionableCommand {
if(commandContext.getParameters().getParameters().isEmpty()) {
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
model.setFeatures(featureConfigService.getAllFeatures());
String response = templateService.renderTemplate("disable_features_response", model);
String response = templateService.renderTemplate("disable_features_response", model, commandContext.getGuild().getIdLong());
return channelService.sendTextToChannel(response, commandContext.getChannel())
.thenApply(aVoid -> CommandResult.fromSuccess());
} else {

View File

@@ -16,7 +16,6 @@ import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.management.FeatureModeManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -38,9 +37,6 @@ public class DisableMode extends AbstractConditionableCommand {
@Autowired
private FeatureModeManagementService featureModeManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private ServerManagementService serverManagementService;

View File

@@ -19,7 +19,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -52,7 +52,7 @@ public class EnableFeature extends AbstractConditionableCommand {
if(commandContext.getParameters().getParameters().isEmpty()) {
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
model.setFeatures(featureConfigService.getAllFeatures());
String response = templateService.renderTemplate("enable_features_response", model);
String response = templateService.renderTemplate("enable_features_response", model, commandContext.getGuild().getIdLong());
return channelService.sendTextToChannel(response, commandContext.getChannel())
.thenApply(message -> CommandResult.fromSuccess());
} else {
@@ -62,7 +62,8 @@ public class EnableFeature extends AbstractConditionableCommand {
FeatureValidationResult featureSetup = featureConfigService.validateFeatureSetup(feature, server);
if(Boolean.FALSE.equals(featureSetup.getValidationResult())) {
log.info("Feature {} has failed the setup validation. Notifying user.", flagKey);
channelService.sendTextToChannelNotAsync(templateService.renderTemplatable(featureSetup), commandContext.getChannel());
channelService.sendTextToChannelNotAsync(templateService.renderTemplatable(featureSetup, commandContext.getGuild().getIdLong()),
commandContext.getChannel());
}
featureFlagService.enableFeature(feature, server);
if(feature.getRequiredFeatures() != null) {

View File

@@ -16,7 +16,6 @@ import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.management.FeatureModeManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -38,9 +37,6 @@ public class EnableMode extends AbstractConditionableCommand {
@Autowired
private FeatureModeManagementService featureModeManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private ServerManagementService serverManagementService;

View File

@@ -58,7 +58,7 @@ public class FeatureModes extends AbstractConditionableCommand {
featureModes = featureModeService.getEffectiveFeatureModes(server, feature);
}
FeatureModesModel model = FeatureModesModel.builder().featureModes(featureModes).build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(FEATURE_MODES_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(FEATURE_MODES_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}

View File

@@ -19,8 +19,8 @@ import dev.sheldan.abstracto.core.service.management.DefaultFeatureFlagManagemen
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -69,7 +69,7 @@ public class Features extends AbstractConditionableCommand {
.collect(Collectors.toList());
defaultFeatureFlagProperties.sort(Comparator.comparing(FeatureFlagProperty::getFeatureName));
featuresModel.setDefaultFeatures(featureFlagConverter.fromFeatureFlagProperties(defaultFeatureFlagProperties));
MessageToSend messageToSend = templateService.renderEmbedTemplate("features_response", featuresModel);
MessageToSend messageToSend = templateService.renderEmbedTemplate("features_response", featuresModel, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}

View File

@@ -17,7 +17,7 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -57,7 +57,8 @@ public class MakeAffected extends AbstractConditionableCommand {
commandService.makeRoleAffectedByCommand(command, actualRole);
} else {
// TODO refactor to use exception
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object()));
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE,
new Object(), commandContext.getGuild().getIdLong()));
}
return CommandResult.fromSuccess();
}

View File

@@ -17,7 +17,7 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -57,7 +57,8 @@ public class MakeImmune extends AbstractConditionableCommand {
commandService.makeRoleImmuneForCommand(command, actualRole);
} else {
// TODO refactor to use exception
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object()));
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE,
new Object(), commandContext.getGuild().getIdLong()));
}
return CommandResult.fromSuccess();
}

View File

@@ -18,7 +18,7 @@ import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -60,7 +60,8 @@ public class Restrict extends AbstractConditionableCommand {
commandService.restrictCommand(command, server);
} else {
// TODO Refactor to use exception
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE, new Object()));
return CommandResult.fromError(templateService.renderTemplate(CommandServiceBean.NO_FEATURE_COMMAND_FOUND_EXCEPTION_TEMPLATE,
new Object(), commandContext.getGuild().getIdLong()));
}
return CommandResult.fromSuccess();
}

View File

@@ -0,0 +1,86 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.exception.CustomTemplateNotFoundException;
import dev.sheldan.abstracto.core.models.template.commands.GetCustomTemplateModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.model.database.CustomTemplate;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.CustomTemplateManagementService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class GetCustomTemplate extends AbstractConditionableCommand {
@Autowired
private CustomTemplateManagementService customTemplateManagementService;
@Autowired
private FileService fileService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
private static final String GET_CUSTOM_TEMPLATE_FILE_NAME_TEMPLATE_KEY = "getCustomTemplate_file_name";
private static final String GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY = "getCustomTemplate_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String templateKey = (String) commandContext.getParameters().getParameters().get(0);
Optional<CustomTemplate> templateOptional = customTemplateManagementService.getCustomTemplate(templateKey, commandContext.getGuild().getIdLong());
if(templateOptional.isPresent()) {
CustomTemplate template = templateOptional.get();
GetCustomTemplateModel model = (GetCustomTemplateModel) ContextConverter.slimFromCommandContext(commandContext, GetCustomTemplateModel.class);
model.setCreated(template.getCreated());
model.setLastModified(template.getLastModified());
model.setTemplateContent(template.getContent());
model.setTemplateKey(templateKey);
return FutureUtils.toSingleFutureGeneric(channelService.sendFileToChannel(template.getContent(),
GET_CUSTOM_TEMPLATE_FILE_NAME_TEMPLATE_KEY, GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
throw new CustomTemplateNotFoundException(templateKey, commandContext.getGuild());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("getCustomTemplate")
.module(ConfigModuleInterface.CONFIG)
.supportsEmbedException(true)
.parameters(parameters)
.async(true)
.help(helpInfo)
.templated(true)
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,88 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.exception.TemplateNotFoundException;
import dev.sheldan.abstracto.core.models.template.commands.GetTemplateModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.model.database.Template;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.TemplateManagementService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class GetTemplate extends AbstractConditionableCommand {
@Autowired
private TemplateManagementService templateManagementService;
@Autowired
private FileService fileService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
private static final String GET_TEMPLATE_FILE_NAME_TEMPLATE_KEY = "getTemplate_file_name";
private static final String GET_TEMPLATE_RESPONSE_TEMPLATE_KEY = "getTemplate_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String templateKey = (String) commandContext.getParameters().getParameters().get(0);
Optional<Template> templateOptional = templateManagementService.getTemplateByKey(templateKey);
if(templateOptional.isPresent()) {
Template template = templateOptional.get();
GetTemplateModel model = (GetTemplateModel) ContextConverter.slimFromCommandContext(commandContext, GetTemplateModel.class);
model.setCreated(template.getCreated());
model.setLastModified(template.getLastModified());
model.setTemplateContent(template.getContent());
model.setTemplateKey(templateKey);
return FutureUtils.toSingleFutureGeneric(channelService.sendFileToChannel(template.getContent(),
GET_TEMPLATE_FILE_NAME_TEMPLATE_KEY, GET_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
throw new TemplateNotFoundException(templateKey);
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter);
return CommandConfiguration.builder()
.name("getTemplate")
.module(ConfigModuleInterface.CONFIG)
.supportsEmbedException(true)
.async(true)
.parameters(parameters)
.help(helpInfo)
.templated(true)
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,64 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.exception.CustomTemplateNotFoundException;
import dev.sheldan.abstracto.core.templating.model.database.CustomTemplate;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.CustomTemplateManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@Component
public class ResetTemplate extends AbstractConditionableCommand {
@Autowired
private CustomTemplateManagementService customTemplateManagementService;
@Autowired
private TemplateService templateService;
@Override
public CommandResult execute(CommandContext commandContext) {
String templateKey = (String) commandContext.getParameters().getParameters().get(0);
Optional<CustomTemplate> templateOptional = customTemplateManagementService.getCustomTemplate(templateKey, commandContext.getGuild().getIdLong());
if (templateOptional.isPresent()) {
customTemplateManagementService.deleteCustomTemplate(templateOptional.get());
templateService.clearCache();
return CommandResult.fromSuccess();
}
throw new CustomTemplateNotFoundException(templateKey, commandContext.getGuild());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter);
return CommandConfiguration.builder()
.name("resetTemplate")
.module(ConfigModuleInterface.CONFIG)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)
.templated(true)
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,83 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.command.exception.AbstractoTemplatedException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.CustomTemplateManagementService;
import dev.sheldan.abstracto.core.utils.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
@Component
@Slf4j
public class SetTemplate extends AbstractConditionableCommand {
@Autowired
private CustomTemplateManagementService customTemplateManagementService;
@Autowired
private FileService fileService;
@Autowired
private TemplateService templateService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameter = commandContext.getParameters().getParameters();
String templateKey = (String) parameter.get(0);
File templateFile = (File) parameter.get(1);
try {
String templateContent = FileUtils.readFileToString(templateFile, StandardCharsets.UTF_8);
customTemplateManagementService.createOrUpdateCustomTemplate(templateKey, templateContent, commandContext.getGuild().getIdLong());
templateService.clearCache();
return CommandResult.fromSuccess();
} catch (IOException e) {
log.error("IO Exception when loading input file.", e);
throw new AbstractoTemplatedException("Failed to set template.", "failed_to_set_template_exception", e);
} finally {
try {
fileService.safeDelete(templateFile);
} catch (IOException e) {
log.error("Failed to delete downloaded template file.", e);
}
}
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
@Override
public CommandConfiguration getConfiguration() {
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
Parameter fileAttachmentParameter = Parameter.builder().name("file").type(File.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter, fileAttachmentParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("setTemplate")
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)
.causesReaction(true)
.build();
}
}

View File

@@ -23,8 +23,8 @@ import dev.sheldan.abstracto.core.models.template.commands.help.HelpModuleOvervi
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -124,7 +124,7 @@ public class Help implements Command {
HelpModuleDetailsModel model = (HelpModuleDetailsModel) ContextConverter.fromCommandContext(commandContext, HelpModuleDetailsModel.class);
model.setModule(module);
model.setSubModules(subModules);
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_module_details_response", model);
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_module_details_response", model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
} else if(commandRegistry.commandExists(parameter)) {
@@ -141,7 +141,7 @@ public class Help implements Command {
}
model.setUsage(commandService.generateUsage(command));
model.setCommand(command.getConfiguration());
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_command_details_response", model);
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_command_details_response", model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
} else {

View File

@@ -11,7 +11,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.template.commands.EchoModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -36,7 +36,7 @@ public class Echo extends AbstractConditionableCommand {
sb.append(o.toString())
);
EchoModel model = EchoModel.builder().text(sb.toString()).build();
String textToSend = templateService.renderTemplate(TEMPLATE_NAME, model);
String textToSend = templateService.renderTemplate(TEMPLATE_NAME, model, commandContext.getGuild().getIdLong());
channelService.sendTextToChannel(textToSend, commandContext.getChannel());
return CommandResult.fromIgnored();
}

View File

@@ -11,7 +11,6 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.template.commands.PingModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -22,9 +21,6 @@ public class Ping implements Command {
public static final String PING_TEMPLATE = "ping_response";
@Autowired
private TemplateService templateService;
@Autowired
private MessageService messageService;
@@ -35,7 +31,7 @@ public class Ping implements Command {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
long ping = commandContext.getJda().getGatewayPing();
PingModel model = PingModel.builder().latency(ping).build();
return channelService.sendTextTemplateInChannel(PING_TEMPLATE, model, commandContext.getChannel())
return channelService.sendTextTemplateInTextChannel(PING_TEMPLATE, model, commandContext.getChannel())
.thenApply(message -> CommandResult.fromIgnored());
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.core.config;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ServerContext {
private Long serverId;
public void clear() {
this.serverId = null;
}
}

View File

@@ -8,8 +8,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.TextChannel;
@@ -26,9 +25,6 @@ import java.util.function.Consumer;
@Slf4j
public class InteractiveServiceBean implements InteractiveService {
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.templating.Templatable;
import dev.sheldan.abstracto.core.templating.Templatable;
public class NoChannelProvidedException extends AbstractoRunTimeException implements Templatable {
public NoChannelProvidedException() {

View File

@@ -10,7 +10,7 @@ import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
@@ -67,7 +67,7 @@ public class PostTargetSetupStep extends AbstractConfigSetupStep {
.postTargetKey(postTargetStepParameter.getPostTargetKey())
.currentTextChannel(currentTextChannel)
.build();
String messageText = templateService.renderTemplate(FEATURE_SETUP_POST_TARGET_MESSAGE_TEMPLATE_KEY, model);
String messageText = templateService.renderTemplate(FEATURE_SETUP_POST_TARGET_MESSAGE_TEMPLATE_KEY, model, user.getGuildId());
AChannel channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(user.getGuildId(), user.getUserId());

View File

@@ -7,7 +7,7 @@ import dev.sheldan.abstracto.core.models.template.commands.SetupSummaryModel;
import dev.sheldan.abstracto.core.service.DelayedActionService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -46,7 +46,7 @@ public class SetupSummaryStep extends AbstractConfigSetupStep {
.builder()
.actionConfigs(parameter.getDelayedActionList())
.build();
String messageToSend = templateService.renderTemplate(FEATURE_SETUP_CONFIRMATION_TEMPLATE_KEY, model);
String messageToSend = templateService.renderTemplate(FEATURE_SETUP_CONFIRMATION_TEMPLATE_KEY, model, user.getGuildId());
AChannel channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(user.getGuildId(), user.getUserId());

View File

@@ -10,7 +10,7 @@ import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
@@ -58,7 +58,7 @@ public class SystemConfigSetupStep extends AbstractConfigSetupStep {
.configKey(systemConfigStepParameter.getConfigKey())
.defaultConfig(defaultConfig)
.build();
String messageText = templateService.renderTemplate(SETUP_SYSTEM_CONFIG_MESSAGE_TEMPLATE_KEY, model);
String messageText = templateService.renderTemplate(SETUP_SYSTEM_CONFIG_MESSAGE_TEMPLATE_KEY, model, user.getGuildId());
AChannel channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(user.getGuildId(), user.getUserId());

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -1,15 +1,14 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.CategoryNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.exception.*;
import dev.sheldan.abstracto.core.metrics.service.CounterMetric;
import dev.sheldan.abstracto.core.metrics.service.MetricService;
import dev.sheldan.abstracto.core.metrics.service.MetricTag;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FileService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.*;
@@ -21,6 +20,8 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -47,6 +48,9 @@ public class ChannelServiceBean implements ChannelService {
@Autowired
private AllowedMentionService allowedMentionService;
@Autowired
private FileService fileService;
@Autowired
private MetricService metricService;
@@ -330,13 +334,27 @@ public class ChannelServiceBean implements ChannelService {
@Override
@Transactional
public List<CompletableFuture<Message>> sendEmbedTemplateInChannel(String templateKey, Object model, MessageChannel channel) {
public List<CompletableFuture<Message>> sendEmbedTemplateInTextChannelList(String templateKey, Object model, TextChannel channel) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(templateKey, model, channel.getGuild().getIdLong());
return sendMessageToSendToChannel(messageToSend, channel);
}
@Override
public List<CompletableFuture<Message>> sendEmbedTemplateInMessageChannelList(String templateKey, Object model, MessageChannel channel) {
// message channel on its own, does not have a guild, so we cant say for which server we want to render the template
MessageToSend messageToSend = templateService.renderEmbedTemplate(templateKey, model);
return sendMessageToSendToChannel(messageToSend, channel);
}
@Override
public CompletableFuture<Message> sendTextTemplateInChannel(String templateKey, Object model, MessageChannel channel) {
public CompletableFuture<Message> sendTextTemplateInTextChannel(String templateKey, Object model, TextChannel channel) {
String text = templateService.renderTemplate(templateKey, model, channel.getGuild().getIdLong());
return sendTextToChannel(text, channel);
}
@Override
public CompletableFuture<Message> sendTextTemplateInMessageChannel(String templateKey, Object model, MessageChannel channel) {
// message channel on its own, does not have a guild, so we cant say for which server we want to render the template
String text = templateService.renderTemplate(templateKey, model);
return sendTextToChannel(text, channel);
}
@@ -386,7 +404,7 @@ public class ChannelServiceBean implements ChannelService {
@Override
public CompletableFuture<Message> sendSimpleTemplateToChannel(Long serverId, Long channelId, String template) {
TextChannel textChannel = getTextChannelFromServer(serverId, channelId);
return sendTextTemplateInChannel(template, new Object(), textChannel);
return sendTextTemplateInTextChannel(template, new Object(), textChannel);
}
@Override
@@ -430,6 +448,32 @@ public class ChannelServiceBean implements ChannelService {
return textChannel.getManager().setSlowmode(seconds).submit();
}
@Override
public List<CompletableFuture<Message>> sendFileToChannel(String fileContent, String fileNameTemplate, String messageTemplate, Object model, TextChannel channel) {
String fileName = templateService.renderTemplate(fileNameTemplate, model);
File tempFile = fileService.createTempFile(fileName);
try {
fileService.writeContentToFile(tempFile, fileContent);
long maxFileSize = channel.getGuild().getMaxFileSize();
// in this case, we cannot upload the file, so we need to fail
if(tempFile.length() > maxFileSize) {
throw new UploadFileTooLargeException(tempFile.length(), maxFileSize);
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(messageTemplate, model);
messageToSend.setFileToSend(tempFile);
return sendMessageToSendToChannel(messageToSend, channel);
} catch (IOException e) {
log.error("Failed to write local temporary file for template download.", e);
throw new AbstractoRunTimeException(e);
} finally {
try {
fileService.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to safely delete local temporary file for template download.", e);
}
}
}
@PostConstruct
public void postConstruct() {

View File

@@ -7,7 +7,7 @@ import dev.sheldan.abstracto.core.interactive.*;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.template.commands.SetupCompletedNotificationModel;
import dev.sheldan.abstracto.core.models.template.commands.SetupInitialMessageModel;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -104,7 +104,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
.featureConfig(featureConfig)
.build();
TextChannel textChannel = textChannelInGuild.get();
String text = templateService.renderTemplate(FEATURE_SETUP_INITIAL_MESSAGE_TEMPLATE_KEY, setupInitialMessageModel);
String text = templateService.renderTemplate(FEATURE_SETUP_INITIAL_MESSAGE_TEMPLATE_KEY, setupInitialMessageModel, user.getGuildId());
channelService.sendTextToChannel(text, textChannel);
return executeFeatureSetup(featureConfig, steps, user, new ArrayList<>());
}
@@ -181,7 +181,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
.builder()
.featureConfig(featureConfig)
.build();
String text = templateService.renderTemplate(templateName, model);
String text = templateService.renderTemplate(templateName, model, aServerChannelUserId.getGuildId());
Optional<TextChannel> textChannel = channelService.getTextChannelFromServerOptional(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
textChannel.ifPresent(channel -> channelService.sendTextToChannel(text, channel));
}

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.utils.FileUtils;
import dev.sheldan.abstracto.core.utils.FileService;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@@ -18,14 +18,14 @@ public class HttpServiceBean implements HttpService {
private OkHttpClient client;
@Autowired
private FileUtils fileUtils;
private FileService fileService;
@Override
public File downloadFileToTempFile(String url) throws IOException {
Request request = new Request.Builder().url(url).get().build();
File tempFile = fileUtils.createTempFile(Math.random() + "");
File tempFile = fileService.createTempFile(Math.random() + "");
Response execute = client.newCall(request).execute();
fileUtils.writeBytesToFile(tempFile, execute.body().bytes());
fileService.writeBytesToFile(tempFile, execute.body().bytes());
return tempFile;
}
}

View File

@@ -7,8 +7,8 @@ import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
@@ -119,7 +119,7 @@ public class MessageServiceBean implements MessageService {
@Override
public CompletableFuture<Void> sendEmbedToUser(User user, String template, Object model) {
return openPrivateChannelForUser(user).thenCompose(privateChannel ->
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(template, model, privateChannel)));
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(template, model, privateChannel)));
}
@NotNull
@@ -131,7 +131,7 @@ public class MessageServiceBean implements MessageService {
public CompletableFuture<Message> sendEmbedToUserWithMessage(User user, String template, Object model) {
log.trace("Sending direct message with template {} to user {}.", template, user.getIdLong());
return openPrivateChannelForUser(user).thenCompose(privateChannel ->
channelService.sendEmbedTemplateInChannel(template, model, privateChannel).get(0));
channelService.sendEmbedTemplateInMessageChannelList(template, model, privateChannel).get(0));
}
@Override

View File

@@ -4,7 +4,7 @@ import com.google.gson.Gson;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.menu.Paginator;
import dev.sheldan.abstracto.core.model.PaginatorConfiguration;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,7 +29,7 @@ public class PaginatorServiceBean implements PaginatorService {
private MessageService messageService;
@Override
public Paginator createPaginatorFromTemplate(String templateKey, Object model, EventWaiter waiter) {
public Paginator createPaginatorFromTemplate(String templateKey, Object model, EventWaiter waiter, Long server) {
String embedConfig = templateService.renderTemplate(templateKey + "_paginator", model);
PaginatorConfiguration configuration = gson.fromJson(embedConfig, PaginatorConfiguration.class);
List<String> items = configuration.getItems();

View File

@@ -10,7 +10,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.PostTarget;
import dev.sheldan.abstracto.core.service.management.DefaultPostTargetManagementService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.core.templating.config;
import freemarker.cache.TemplateLookupContext;
import freemarker.cache.TemplateLookupResult;
import freemarker.cache.TemplateLookupStrategy;
import java.io.IOException;
public class AbstractoTemplateLookupStrategy extends TemplateLookupStrategy {
@Override
public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException {
if(ctx.getCustomLookupCondition() != null) {
return ctx.lookupWithLocalizedThenAcquisitionStrategy(ctx.getCustomLookupCondition() + "/" + ctx.getTemplateName(), ctx.getTemplateLocale());
} else {
return ctx.lookupWithLocalizedThenAcquisitionStrategy(ctx.getTemplateName(), ctx.getTemplateLocale());
}
}
}

View File

@@ -0,0 +1,64 @@
package dev.sheldan.abstracto.core.templating.config;
import dev.sheldan.abstracto.core.templating.loading.DatabaseTemplateLoader;
import dev.sheldan.abstracto.core.templating.method.DateMethod;
import dev.sheldan.abstracto.core.templating.method.DurationMethod;
import dev.sheldan.abstracto.core.templating.method.SafeFieldIterations;
import dev.sheldan.abstracto.core.templating.model.database.AutoLoadMacro;
import dev.sheldan.abstracto.core.templating.service.management.AutoLoadMacroManagementService;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
/**
* Configuration bean used to provide the {@link Configuration} bean to the spring context.
*/
@org.springframework.context.annotation.Configuration
public class FreemarkerConfiguration {
@Autowired
private DatabaseTemplateLoader templateLoader;
@Autowired
private DurationMethod durationMethod;
@Autowired
private DateMethod instantMethod;
@Autowired
private SafeFieldIterations safeFieldIterations;
@Autowired
private AutoLoadMacroManagementService macroManagementService;
/**
* Creates a {@link Configuration} bean with the appropriate configuration which includes:
* The correct compatibility version and the provided formatter methods to be used in the templates.
* The encoding of the templates is set to UTF-8.
* @return A configured {@link Configuration} bean according to the configuration
*/
@Bean
public Configuration freeMarkerConfiguration() throws IOException, TemplateException {
FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory();
factory.setPreTemplateLoaders(templateLoader);
Configuration configuration = factory.createConfiguration();
configuration.setSharedVariable("fmtDuration", durationMethod);
configuration.setSharedVariable("formatDate", instantMethod);
configuration.setSharedVariable("safeFieldLength", safeFieldIterations);
List<String> macrosToLoad = macroManagementService.loadAllMacros().stream()
.map(AutoLoadMacro::getKey).collect(Collectors.toList());
configuration.setAutoIncludes(macrosToLoad);
configuration.setTemplateLookupStrategy(new AbstractoTemplateLookupStrategy());
configuration.setEncoding(Locale.getDefault(), "utf-8");
// needed to support default methods in interfaces
configuration.setIncompatibleImprovements(Configuration.VERSION_2_3_29);
return configuration;
}
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.abstracto.core.templating.config;
import dev.sheldan.abstracto.core.config.ServerContext;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.ThreadLocalTargetSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
@Configuration
public class ServerContextConfiguration {
@Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("serverContext");
return result;
}
@Primary
@Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
@Bean(name = "serverContext")
@Scope(scopeName = "prototype")
public ServerContext serverContext() {
return new ServerContext();
}
}

View File

@@ -0,0 +1,76 @@
package dev.sheldan.abstracto.core.templating.loading;
import dev.sheldan.abstracto.core.config.ServerContext;
import dev.sheldan.abstracto.core.templating.model.EffectiveTemplate;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.EffectiveTemplateManagementService;
import freemarker.cache.TemplateLoader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Optional;
/**
* Loads the the template from the database to be used by Freemarker. This bean is also used when the templates within
* templates are used.
*/
@Component
public class DatabaseTemplateLoader implements TemplateLoader {
@Autowired
private TemplateService templateService;
@Autowired
private EffectiveTemplateManagementService effectiveTemplateManagementService;
@Autowired
private ServerContext serverContext;
/**
* Loads the content of the template object
* @param s The key of the template to load
* @return The template loaded from the database
*/
@Override
public Object findTemplateSource(String s) throws IOException {
Optional<EffectiveTemplate> templateByKey;
if(s.contains("/")) {
String[] parts = s.split("/");
templateByKey = effectiveTemplateManagementService.getTemplateByKeyAndServer(parts[1], Long.parseLong(parts[0]));
} else {
templateByKey = effectiveTemplateManagementService.getTemplateByKey(s);
}
return templateByKey.orElseThrow(() -> new IOException(String.format("Failed to load template. %s", s)));
}
@Override
public long getLastModified(Object o) {
EffectiveTemplate casted = (EffectiveTemplate) o;
Optional<EffectiveTemplate> templateByKey;
if(serverContext.getServerId() != null) {
templateByKey = effectiveTemplateManagementService.getTemplateByKeyAndServer(casted.getKey(), serverContext.getServerId());
} else {
templateByKey = effectiveTemplateManagementService.getTemplateByKey(casted.getKey());
}
return templateByKey.map(template -> template.getLastModified().getEpochSecond()).orElse(Long.MAX_VALUE);
}
/**
* Retrieves the content of the template from the retrieved {@link EffectiveTemplate} object
* @param o The retrieved {@link EffectiveTemplate} object from the database
* @param s The encoding of the object
* @return The content of the template as a String reader
*/
@Override
public Reader getReader(Object o, String s) throws IOException {
return new StringReader(((EffectiveTemplate) o).getContent());
}
@Override
public void closeTemplateSource(Object o) throws IOException {
// do nothing for now
}
}

View File

@@ -0,0 +1,55 @@
package dev.sheldan.abstracto.core.templating.method;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import freemarker.ext.beans.StringModel;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* Formats the passed {@link Instant} or {@link OffsetDateTime} object with the given Formatter. The format will be directly passed to {@link DateTimeFormatter}.
*/
@Component
public class DateMethod implements TemplateMethodModelEx {
@Autowired
private TemplateService service;
/**
* Renders the given {@link Instant} object with the given String. Internally {@link DateTimeFormatter} will be used.
* @param arguments The list of arguments, first element must be an {@link Instant} or {@link OffsetDateTime} and the second one must be a {@link String}.
* @return The formatted {@link Instant} as a string.
* @throws TemplateModelException If there are less or more arguments in the list and if the first element is not a {@link Instant} of {@link OffsetDateTime}
*/
@Override
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 2) {
throw new TemplateModelException("Incorrect parameters passed.");
}
Object wrappedObject = ((StringModel) arguments.get(0)).getWrappedObject();
boolean isOffsetDateTime = wrappedObject instanceof OffsetDateTime;
boolean isInstant = wrappedObject instanceof Instant;
if(!isInstant && !isOffsetDateTime) {
throw new TemplateModelException("Passed argument was not a instant object");
}
String formatString = ((SimpleScalar) arguments.get(1)).getAsString();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString)
.withZone(ZoneId.systemDefault());
if(isInstant) {
Instant timeStamp = (Instant) wrappedObject;
return formatter.format(timeStamp);
} else {
OffsetDateTime offsetDateTime = (OffsetDateTime) wrappedObject;
return formatter.format(offsetDateTime);
}
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.core.templating.method;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import freemarker.ext.beans.StringModel;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
/**
* Method used to format the {@link Duration} object, as its not natively supported by Freemarker.
* This method only accepts a single {@link Duration} object as the first parameter.
*/
@Component
public class DurationMethod implements TemplateMethodModelEx {
@Autowired
private TemplateService service;
/**
* This method expects a single Duration object in the list of arguments. It will throw a {@link TemplateModelException}
* otherwise
* @param arguments The parameters passed to this method, should be only a single duration.
* @return The string representation of the {@link Duration} object
* @throws TemplateModelException In case the amount of parameters is not correct, or the first object was not a {@link Duration}
*/
@Override
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 1) {
throw new TemplateModelException("Incorrect parameters passed.");
}
Object o = arguments.get(0);
if(!(o instanceof StringModel)) {
throw new TemplateModelException("Passed object was not a StringModel.");
}
Object wrappedObject = ((StringModel) o).getWrappedObject();
if(!(wrappedObject instanceof Duration)) {
throw new TemplateModelException("Passed argument was not a duration object");
}
Duration duration = (Duration) wrappedObject;
// upgrading to java 9 makes this nicer
HashMap<String, Object> parameters = new HashMap<>();
long days = duration.toDays();
parameters.put("days", days);
long hours = duration.toHours() % 24;
parameters.put("hours", hours);
long minutes = duration.toMinutes() % 60;
parameters.put("minutes", minutes);
long seconds = duration.get(ChronoUnit.SECONDS) % 60;
parameters.put("seconds", seconds);
return service.renderTemplateWithMap("duration_formatting", parameters);
}
}

View File

@@ -0,0 +1,95 @@
package dev.sheldan.abstracto.core.templating.method;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import freemarker.template.DefaultListAdapter;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component
public class SafeFieldIterations implements TemplateMethodModelEx {
@Autowired
private TemplateService service;
@Override
public Object exec(List arguments) throws TemplateModelException {
if(arguments.size() != 4) {
throw new TemplateModelException("Incorrect amount of parameters.");
}
Object adapterObject = arguments.get(0);
if(!(adapterObject instanceof DefaultListAdapter)) {
throw new TemplateModelException("Passed object was not a DefaultListAdapter.");
}
Object wrappedList = ((DefaultListAdapter) adapterObject).getWrappedObject();
if(!(wrappedList instanceof List)) {
throw new TemplateModelException("Passed object was not a List.");
}
List wrappedObject = (List) wrappedList;
Object templateParameter = arguments.get(1);
if(!(templateParameter instanceof SimpleScalar)) {
throw new TemplateModelException("Passed object for template was not a SimpleScalar.");
}
String appliedTemplate = ((SimpleScalar) templateParameter).getAsString();
Object fieldNameTemplateParameter = arguments.get(2);
if(!(fieldNameTemplateParameter instanceof SimpleScalar)) {
throw new TemplateModelException("Passed object for field name template was not a SimpleScalar.");
}
String fieldNameTemplate = ((SimpleScalar) fieldNameTemplateParameter).getAsString();
Object inlineParameter = arguments.get(3);
if(!(inlineParameter instanceof SimpleScalar)) {
throw new TemplateModelException("Passed object for inline was not a SimpleScalar.");
}
String inline = ((SimpleScalar) inlineParameter).getAsString();
List<StringBuilder> result = new ArrayList<>();
StringBuilder currentBuilder = new StringBuilder();
String firstEmbedTitle = service.renderTemplateWithMap(fieldNameTemplate, getEmbedCountParameters(1, wrappedObject));
currentBuilder.append(newFieldHeader(firstEmbedTitle, inline));
String finalClosingString = "\"}";
String closingString = finalClosingString + ",";
int splitFieldCounts = 1;
for (Object ob: wrappedObject) {
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("object", ob);
String s = service.renderTemplateWithMap(appliedTemplate, parameters);
if((currentBuilder.toString().length() + s.length() > 1024)) {
currentBuilder.append(closingString);
result.add(currentBuilder);
currentBuilder = new StringBuilder();
splitFieldCounts += 1;
String renderedName = service.renderTemplateWithMap(fieldNameTemplate, getEmbedCountParameters(splitFieldCounts, wrappedObject));
currentBuilder.append(newFieldHeader(renderedName, inline));
}
currentBuilder.append(s);
}
currentBuilder.append(finalClosingString);
result.add(currentBuilder);
StringBuilder bigBuilder = new StringBuilder();
for (StringBuilder innerBuilder: result) {
bigBuilder.append(innerBuilder.toString());
}
return bigBuilder.toString();
}
private String newFieldHeader(String name, String inline) {
return String.format("{ \"name\": \"%s\", \"inline\": \"%s\", \"value\": \"", name, inline);
}
private HashMap<String, Object> getEmbedCountParameters(Integer count, List<? extends Object> objects) {
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("count", count);
parameters.put("list", objects);
return parameters;
}
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* The container class used to deserialize the embed configuration for the author in {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
@Getter
@Setter
@Builder
public class EmbedAuthor {
/**
* The name used in the {@link net.dv8tion.jda.api.entities.MessageEmbed} author
*/
private String name;
/**
* The URL used in the {@link net.dv8tion.jda.api.entities.MessageEmbed} author
*/
private String url;
/**
* The picture used as the avatar of the author in {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private String avatar;
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* The container class used to deserialize the embed configuration for the color in {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
@Setter
@Getter
@Builder
public class EmbedColor {
/**
* The red part of RGB
*/
private Integer r;
/**
* The green part of RGB
*/
private Integer g;
/**
* The blue part of RGB
*/
private Integer b;
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.OffsetDateTime;
import java.util.List;
/**
* The whole container object used to deserialize the whole embed configuration
* https://raw.githubusercontent.com/DV8FromTheWorld/JDA/assets/assets/docs/embeds/07-addField.png
*/
@Getter
@Setter
@Builder
public class EmbedConfiguration {
/**
* The {@link EmbedAuthor} object holding the configuration for the author of the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private EmbedAuthor author;
/**
* The {@link EmbedTitle} object holding the configuration for the title in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private EmbedTitle title;
/**
* The {@link EmbedColor} object holding the configuration for the color in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private EmbedColor color;
/**
* The description which is going to be used in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private String description;
/**
* The link to the image used as a thumbnail in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private String thumbnail;
/**
* The link to the image used as the image in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private String imageUrl;
/**
* The collection containing all the objects containing the configuration for the fields in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private List<EmbedField> fields;
/**
* The {@link EmbedFooter} object holding the configuration for the footer in {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private EmbedFooter footer;
/**
* The {@link OffsetDateTime} object used as the time stamp in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
private OffsetDateTime timeStamp;
/**
* The message which is posted along the {@link net.dv8tion.jda.api.entities.MessageEmbed} as a normal message.
*/
private String additionalMessage;
private boolean preventEmptyEmbed = false;
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* The container class used to deserialize the embed configuration for a field in {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
@Getter
@Setter
@Builder
public class EmbedField {
/**
* The name of the field to be set
*/
private String name;
/**
* The value of the field to be set
*/
private String value;
/**
* Whether or not the field should be rendered inline within the {@link net.dv8tion.jda.api.entities.MessageEmbed}.
* This means, if multiple fields can be put on the same height in the {@link net.dv8tion.jda.api.entities.MessageEmbed} this will be done by discord.
*/
private Boolean inline;
private Boolean forceNewMessage;
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* The container object used to deserialize the embed configuration for the footer in the {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
@Getter
@Setter
@Builder
public class EmbedFooter {
/**
* The text which is going to be used as the footer text
*/
private String text;
/**
* The link to the image which is going to be used as the icon of the footer
*/
private String icon;
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* The container class used to deserialize the embed configuration used in the title in {@link net.dv8tion.jda.api.entities.MessageEmbed}
*/
@Getter
@Setter
@Builder
public class EmbedTitle {
/**
* The text which is going to be used as the title of the embed
*/
private String title;
/**
* The link which is used when clicking on the title of the embed
*/
private String url;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.templating.repository;
import dev.sheldan.abstracto.core.templating.model.database.AutoLoadMacro;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AutoLoadMacroRepository extends JpaRepository<AutoLoadMacro, String> {
@NotNull
@Override
List<AutoLoadMacro> findAll();
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.core.templating.repository;
import dev.sheldan.abstracto.core.templating.model.database.CustomTemplate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* Repository used to load the templates from the database.
*/
@Repository
public interface CustomTemplateRepository extends JpaRepository<CustomTemplate, String> {
Optional<CustomTemplate> findByKeyAndServerId(String key, Long serverId);
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.abstracto.core.templating.repository;
import dev.sheldan.abstracto.core.templating.model.EffectiveTemplate;
import dev.sheldan.abstracto.core.templating.model.database.Template;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* Repository used to load the templates from the database.
*/
@Repository
public interface EffectiveTemplateRepository extends JpaRepository<Template, String> {
@Query(value = "SELECT \n" +
"t.key AS key, \n" +
"COALESCE(c_t.content, t.content) AS content,\n" +
"COALESCE(c_t.last_modified, t.last_modified) AS lastModified\n" +
"FROM template t\n" +
"LEFT OUTER JOIN custom_template c_t\n" +
"ON t.key = c_t.key and c_t.server_id = :serverId\n" +
"WHERE t.key = :key", nativeQuery = true)
Optional<EffectiveTemplate> findByKeyAndServerId(@Param("key") String key, @Param("serverId") Long serverId);
@Query(value = "SELECT \n" +
"t.key AS key, \n" +
"t.content AS content,\n" +
"t.last_modified AS lastModified\n" +
"FROM template t\n" +
"WHERE t.key = :key", nativeQuery = true)
Optional<EffectiveTemplate> findByKey(@Param("key") String key);
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.templating.repository;
import dev.sheldan.abstracto.core.templating.model.database.Template;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* Repository used to load the templates from the database.
*/
@Repository
public interface TemplateRepository extends JpaRepository<Template, String> {
}

View File

@@ -0,0 +1,320 @@
package dev.sheldan.abstracto.core.templating.service;
import com.google.gson.Gson;
import dev.sheldan.abstracto.core.config.ServerContext;
import dev.sheldan.abstracto.core.templating.Templatable;
import dev.sheldan.abstracto.core.templating.exception.TemplatingException;
import dev.sheldan.abstracto.core.templating.model.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.awt.*;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
/**
* Bean used to render a template, identified by a key, with the passed model.
*/
@Slf4j
@Component
public class TemplateServiceBean implements TemplateService {
public static final double MAX_FIELD_COUNT = 25D;
@Autowired
private Configuration configuration;
@Autowired
private Gson gson;
@Autowired
private ServerContext serverContext;
/**
* Formats the passed passed count with the embed used for formatting pages.
*
* @param count The index of the page you want formatted.
* @return The rendered template as a string object
*/
private String getPageString(Integer count) {
HashMap<String, Object> params = new HashMap<>();
params.put("count", count);
return renderTemplateWithMap("embed_page_count", params);
}
/**
* Retrieves the key which gets suffixed with '_embed' and this retrieves the embed configuration. This configuration is then rendered
* and de-serialized with GSON into a {@link EmbedConfiguration} object. This object is then rendered into a {@link MessageToSend} and returned.
* If the individual element do not fit in an embed, for example, if the field count is to high, another embed will be created in the {@link MessageToSend} object.
* If multiple embeds are necessary to provide what the {@link EmbedConfiguration} wanted, this method will automatically set the footer of the additional {@link MessageEmbed}
* with a formatted page count.
* This method will to try its best to provided a message which can be handled by discord without rejecting it. Besides that, the content from the rendered template, will be passed
* into the {@link EmbedBuilder} directly.
*
* @param key The key of the embed template to be used for rendering.
* @param model The model providing the properties to be used for rendering
* @return The {@link MessageToSend} object which is properly split up in order to be send to discord.
*/
@Override
public MessageToSend renderEmbedTemplate(String key, Object model) {
String embedConfig = this.renderTemplate(key + "_embed", model);
EmbedConfiguration embedConfiguration = gson.fromJson(embedConfig, EmbedConfiguration.class);
List<EmbedBuilder> embedBuilders = new ArrayList<>();
embedBuilders.add(new EmbedBuilder());
String description = embedConfiguration.getDescription();
if (description != null) {
double neededIndices = Math.ceil(description.length() / (double) MessageEmbed.TEXT_MAX_LENGTH) - 1;
extendIfNecessary(embedBuilders, neededIndices);
for (int i = 0; i < neededIndices + 1; i++) {
String descriptionText = description.substring(MessageEmbed.TEXT_MAX_LENGTH * i, Math.min(MessageEmbed.TEXT_MAX_LENGTH * (i + 1), description.length()));
embedBuilders.get(i).setDescription(descriptionText);
}
}
EmbedAuthor author = embedConfiguration.getAuthor();
EmbedBuilder firstBuilder = embedBuilders.get(0);
if (author != null) {
firstBuilder.setAuthor(author.getName(), author.getUrl(), author.getAvatar());
}
String thumbnail = embedConfiguration.getThumbnail();
if (thumbnail != null) {
firstBuilder.setThumbnail(thumbnail);
}
EmbedTitle title = embedConfiguration.getTitle();
if (title != null) {
firstBuilder.setTitle(title.getTitle(), title.getUrl());
}
EmbedFooter footer = embedConfiguration.getFooter();
if (footer != null) {
firstBuilder.setFooter(footer.getText(), footer.getIcon());
}
if (embedConfiguration.getFields() != null) {
createFieldsForEmbed(embedBuilders, embedConfiguration);
}
firstBuilder.setTimestamp(embedConfiguration.getTimeStamp());
firstBuilder.setImage(embedConfiguration.getImageUrl());
EmbedColor color = embedConfiguration.getColor();
if (color != null) {
int colorToSet = new Color(color.getR(), color.getG(), color.getB()).getRGB();
embedBuilders.forEach(embedBuilder -> embedBuilder.setColor(colorToSet));
}
List<MessageEmbed> embeds = new ArrayList<>();
if ((embedBuilders.size() > 1 || !embedBuilders.get(0).isEmpty()) && !isEmptyEmbed(embedConfiguration)) {
embeds = embedBuilders.stream().map(EmbedBuilder::build).collect(Collectors.toList());
}
return MessageToSend.builder()
.embeds(embeds)
.message(embedConfiguration.getAdditionalMessage())
.build();
}
@Override
public MessageToSend renderEmbedTemplate(String key, Object model, Long serverId) {
try {
serverContext.setServerId(serverId);
return renderEmbedTemplate(key, model);
} finally {
serverContext.clear();
}
}
private boolean isEmptyEmbed(EmbedConfiguration configuration) {
if (configuration.isPreventEmptyEmbed()) {
return configuration.getFields() == null && configuration.getDescription() == null && configuration.getImageUrl() == null;
}
return false;
}
@Override
public MessageToSend renderTemplateToMessageToSend(String key, Object model) {
return MessageToSend.builder().message(renderTemplate(key, model)).build();
}
@Override
public MessageToSend renderTemplateToMessageToSend(String key, Object model, Long serverId) {
try {
serverContext.setServerId(serverId);
return renderTemplateToMessageToSend(key, model);
} finally {
serverContext.clear();
}
}
private void createFieldsForEmbed(List<EmbedBuilder> embedBuilders, EmbedConfiguration configuration) {
for (int i = 0; i < configuration.getFields().size(); i++) {
EmbedField field = configuration.getFields().get(i);
if (field != null && field.getValue() != null && field.getValue().length() > MessageEmbed.VALUE_MAX_LENGTH) {
String substring = field.getValue().substring(MessageEmbed.VALUE_MAX_LENGTH);
field.setValue(field.getValue().substring(0, MessageEmbed.VALUE_MAX_LENGTH));
EmbedField secondPart = EmbedField.builder().inline(field.getInline()).name(field.getName() + " 2").value(substring).build();
configuration.getFields().add(i + 1, secondPart);
}
}
int actualCurrentIndex = 0;
int neededMessages = 0;
for (int i = 0; i < configuration.getFields().size(); i++) {
EmbedField field = configuration.getFields().get(i);
boolean lastMessageInEmbed = ((actualCurrentIndex + 1) % MAX_FIELD_COUNT) == 0;
boolean isStartOfNewMessage = (actualCurrentIndex % MAX_FIELD_COUNT) == 0;
boolean newMessageForcedWithinEmbeds = Boolean.TRUE.equals(field.getForceNewMessage()) && !lastMessageInEmbed;
boolean startOfNewMessage = actualCurrentIndex != 0 && isStartOfNewMessage;
if (newMessageForcedWithinEmbeds || startOfNewMessage) {
actualCurrentIndex = 0;
neededMessages++;
} else {
actualCurrentIndex++;
}
extendIfNecessary(embedBuilders, neededMessages);
EmbedField embedField = configuration.getFields().get(i);
boolean inline = embedField.getInline() != null ? embedField.getInline() : Boolean.FALSE;
embedBuilders.get(neededMessages).addField(embedField.getName(), embedField.getValue(), inline);
}
}
/**
* Enlarges the passed list of builders, if the passed index is not yet available within the list.
* When a new builder is needed, this will automatically set the footer with a page indicator.
*
* @param builders The current list of {@link EmbedBuilder} builders used.
* @param neededIndex The desired index in the list which should be available for using.
*/
private void extendIfNecessary(List<EmbedBuilder> builders, double neededIndex) {
if (neededIndex > builders.size() - 1) {
for (int i = builders.size(); i < neededIndex + 1; i++) {
EmbedBuilder e = new EmbedBuilder();
e.setFooter(getPageString(i + 1));
builders.add(e);
}
}
}
/**
* Renders the template identified by the key with the passed {@link HashMap}
*
* @param key The key of the template to be rendered.
* @param parameters The {@link HashMap} to be used as the parameters for the template
* @return The rendered template as a string
*/
@Override
public String renderTemplateWithMap(String key, HashMap<String, Object> parameters) {
try {
return renderTemplateToString(key, parameters);
} catch (IOException | TemplateException e) {
log.warn("Failed to render template. ", e);
throw new TemplatingException(e);
}
}
@Override
public String renderTemplateWithMap(String key, HashMap<String, Object> parameters, Long serverId) {
try {
serverContext.setServerId(serverId);
return renderTemplateWithMap(key, parameters);
} finally {
serverContext.clear();
}
}
/**
* Renders the template identified by the key with the passed object as model
*
* @param key The key of the template to be rendered
* @param model The object containing the model to be used in the template
* @return The rendered template as a string
*/
@Override
public String renderTemplate(String key, Object model) {
try {
return renderTemplateToString(key, model);
} catch (IOException | TemplateException e) {
log.warn("Failed to render template. ", e);
throw new TemplatingException(e);
}
}
@Override
public String renderTemplate(String key, Object model, Long serverId) {
try {
serverContext.setServerId(serverId);
return renderTemplate(key, model);
} finally {
serverContext.clear();
}
}
/**
* Loads the given key as a template, and renders it, returns the result as a String
*
* @param key The key of the template to render
* @param model The parameters which are given to the template
* @return The rendered template in a String
* @throws freemarker.template.TemplateNotFoundException In case the template could not be found
* @throws TemplateException In case the rendering failed
*/
private String renderTemplateToString(String key, Object model) throws IOException, TemplateException {
StringWriter result = new StringWriter();
Template template = configuration.getTemplate(key, null, serverContext.getServerId(), null, true, false);
template.process(model, result);
return result.toString();
}
/**
* Renders a simple template identified by key without any model. This will cause exceptions in case there are references to a model in the provided template.
*
* @param key The key of the template to be rendered
* @return The rendered template as a string
*/
@Override
public String renderSimpleTemplate(String key) {
return renderTemplate(key, new Object());
}
@Override
public String renderSimpleTemplate(String key, Long serverId) {
try {
serverContext.setServerId(serverId);
return renderSimpleTemplate(key, serverId);
} finally {
serverContext.clear();
}
}
/**
* Renders the {@link Templatable} object using the template key and the model and returns it as a string.
*
* @param templatable The {@link Templatable} object to be rendered
* @return The rendered {@link Templatable} as a string
*/
@Override
public String renderTemplatable(Templatable templatable) {
return renderTemplate(templatable.getTemplateName(), templatable.getTemplateModel());
}
@Override
public String renderTemplatable(Templatable templatable, Long serverId) {
try {
serverContext.setServerId(serverId);
return renderTemplatable(templatable);
} finally {
serverContext.clear();
}
}
@Override
public void clearCache() {
configuration.getCacheStorage().clear();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.templating.service.management;
import dev.sheldan.abstracto.core.templating.model.database.AutoLoadMacro;
import dev.sheldan.abstracto.core.templating.repository.AutoLoadMacroRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class AutoLoadMacroManagementServiceBean implements AutoLoadMacroManagementService {
@Autowired
private AutoLoadMacroRepository repository;
@Override
public List<AutoLoadMacro> loadAllMacros() {
return repository.findAll();
}
}

View File

@@ -0,0 +1,69 @@
package dev.sheldan.abstracto.core.templating.service.management;
import dev.sheldan.abstracto.core.exception.TemplateNotFoundException;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.database.CustomTemplate;
import dev.sheldan.abstracto.core.templating.repository.CustomTemplateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Component
public class CustomTemplateManagementServiceBean implements CustomTemplateManagementService {
@Autowired
private CustomTemplateRepository customTemplateRepository;
@Autowired
private TemplateManagementService templateManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public CustomTemplate createOrUpdateCustomTemplate(String templateKey, String templateContent, Long serverId) {
if(!templateManagementService.templateExists(templateKey)) {
throw new TemplateNotFoundException(templateKey);
}
Optional<CustomTemplate> customTemplateOptional = getCustomTemplate(templateKey, serverId);
customTemplateOptional.ifPresent(customTemplate -> customTemplate.setContent(templateContent));
return customTemplateOptional.orElseGet(() -> createCustomTemplate(templateKey, templateContent, serverId));
}
@Override
public CustomTemplate createCustomTemplate(String templateKey, String templateContent, Long serverId) {
CustomTemplate template = CustomTemplate
.builder()
.server(serverManagementService.loadServer(serverId))
.content(templateContent)
.key(templateKey)
.build();
customTemplateRepository.save(template);
return template;
}
@Override
public Optional<CustomTemplate> getCustomTemplate(String templateKey, Long serverId) {
return customTemplateRepository.findByKeyAndServerId(templateKey, serverId);
}
@Override
public boolean doesCustomTemplateExist(String templateKey, Long serverId) {
return getCustomTemplate(templateKey, serverId).isPresent();
}
@Override
public void deleteCustomTemplateByKey(String templateKey, Long serverId) {
Optional<CustomTemplate> customTemplateOptional = getCustomTemplate(templateKey, serverId);
customTemplateOptional.ifPresent(this::deleteCustomTemplate);
}
@Override
public void deleteCustomTemplate(CustomTemplate customTemplate) {
customTemplateRepository.delete(customTemplate);
}
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.core.templating.service.management;
import dev.sheldan.abstracto.core.templating.model.EffectiveTemplate;
import dev.sheldan.abstracto.core.templating.repository.EffectiveTemplateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class EffectiveTemplateManagementServiceBean implements EffectiveTemplateManagementService {
@Autowired
private EffectiveTemplateRepository effectiveTemplateRepository;
@Override
public Optional<EffectiveTemplate> getTemplateByKeyAndServer(String key, Long server) {
return effectiveTemplateRepository.findByKeyAndServerId(key, server);
}
@Override
public Optional<EffectiveTemplate> getTemplateByKey(String key) {
return effectiveTemplateRepository.findByKey(key);
}
}

View File

@@ -0,0 +1,56 @@
package dev.sheldan.abstracto.core.templating.service.management;
import dev.sheldan.abstracto.core.templating.model.database.Template;
import dev.sheldan.abstracto.core.templating.repository.TemplateRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Optional;
/**
* ManagementService bean used to retrieve the templates by key from the database.
* This class uses the {@link TemplateRepository} bean to load the {@link Template} objects.
*/
@Component
@Slf4j
public class TemplateManagementServiceBean implements TemplateManagementService {
@Autowired
private TemplateRepository repository;
/**
* Returns the template from the database by key
* @param key They template key to search for
* @return An {@link Optional} containing the {@link Template} if any was found.
*/
@Override
public Optional<Template> getTemplateByKey(String key) {
return repository.findById(key);
}
/**
* Returns whether or not the template identified by the key exists in the database
* @param key They key of the template to search for
* @return Whether or not the template exists in the database
*/
@Override
public boolean templateExists(String key) {
return repository.existsById(key);
}
/**
* Creates a {@link Template} object and stores it in the database. Returns the newly created object.
* @param key They key of the template to create.
* @param content The content the template should have
* @return The {@link Template} which was created
*/
@Override
public Template createTemplate(String key, String content) {
Template build = Template.builder().key(key).content(content).lastModified(Instant.now()).build();
repository.save(build);
return build;
}
}

View File

@@ -103,6 +103,30 @@
<column name="feature_id" valueComputed="${coreFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="setTemplate"/>
<column name="module_id" valueComputed="${configModule}"/>
<column name="feature_id" valueComputed="${coreFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="getTemplate"/>
<column name="module_id" valueComputed="${configModule}"/>
<column name="feature_id" valueComputed="${coreFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="resetTemplate"/>
<column name="module_id" valueComputed="${configModule}"/>
<column name="feature_id" valueComputed="${coreFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="getCustomTemplate"/>
<column name="module_id" valueComputed="${configModule}"/>
<column name="feature_id" valueComputed="${coreFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="setupFeature"/>
<column name="module_id" valueComputed="${configModule}"/>

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
<include file="templating-tables/tables.xml" relativeToChangelogFile="true"/>
<include file="templating-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,18 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="templates-auto_load_macro">
<insert tableName="auto_load_macro">
<column name="key" value="safe_macro"/>
</insert>
<insert tableName="auto_load_macro">
<column name="key" value="member_user_name"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="auto_load_macro.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,22 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="auto_load_macro-table">
<createTable tableName="auto_load_macro">
<column name="key" type="VARCHAR(255)">
<constraints nullable="false" primaryKey="true" primaryKeyName="auto_load_macro_pkey"/>
</column>
</createTable>
</changeSet>
<!-- TODO doesnt work for initial config, because auto load macros are defined, but templates not present yet
<changeSet author="Sheldan" id="auto_load_macro-fk_assignable_role_emote">
<addForeignKeyConstraint baseColumnNames="key" baseTableName="auto_load_macro" constraintName="fk_auto_load_macro_template" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="key" referencedTableName="template" validate="true"/>
</changeSet>
-->
</databaseChangeLog>

View File

@@ -0,0 +1,45 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="custom_template-table">
<createTable tableName="custom_template">
<column name="key" type="VARCHAR(255)">
<constraints nullable="false" />
</column>
<column name="section" type="VARCHAR(255)"/>
<column name="last_modified" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="content" type="VARCHAR(4000)"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="server_id, key" tableName="custom_template" constraintName="pk_custom_template" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS custom_template_update_trigger ON custom_template;
CREATE TRIGGER custom_template_update_trigger BEFORE UPDATE ON custom_template FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS custom_template_insert_trigger ON custom_template;
CREATE TRIGGER custom_template_insert_trigger BEFORE INSERT ON custom_template FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS custom_template_last_modified_trigger ON custom_template;
CREATE TRIGGER custom_template_last_modified_trigger BEFORE UPDATE ON custom_template FOR EACH ROW EXECUTE PROCEDURE last_modified_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS custom_template_last_modified_insert_trigger ON custom_template;
CREATE TRIGGER custom_template_last_modified_insert_trigger BEFORE INSERT ON custom_template FOR EACH ROW EXECUTE PROCEDURE last_modified_trigger_procedure();
</sql>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="custom_template" constraintName="fk_custom_template_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="key" baseTableName="custom_template" constraintName="fk_custom_template_template" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="key" referencedTableName="template" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE FUNCTION last_modified_trigger_procedure() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.last_modified := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;

View File

@@ -0,0 +1,13 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="trigger_functions.xml" relativeToChangelogFile="true"/>
<include file="template.xml" relativeToChangelogFile="true"/>
<include file="custom_template.xml" relativeToChangelogFile="true"/>
<include file="auto_load_macro.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,39 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="template-table">
<createTable tableName="template">
<column name="key" type="VARCHAR(255)">
<constraints nullable="false" primaryKey="true" primaryKeyName="template_pkey"/>
</column>
<column name="section" type="VARCHAR(255)"/>
<column name="last_modified" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="content" type="VARCHAR(4000)"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
DROP TRIGGER IF EXISTS template_update_trigger ON template;
CREATE TRIGGER template_update_trigger BEFORE UPDATE ON template FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS template_insert_trigger ON template;
CREATE TRIGGER template_insert_trigger BEFORE INSERT ON template FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS template_last_modified_trigger ON template;
CREATE TRIGGER template_last_modified_trigger BEFORE UPDATE ON template FOR EACH ROW EXECUTE PROCEDURE last_modified_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS template_last_modified_insert_trigger ON template;
CREATE TRIGGER template_last_modified_insert_trigger BEFORE INSERT ON template FOR EACH ROW EXECUTE PROCEDURE last_modified_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="last_modified_trigger" dbms="postgresql">
<sqlFile encoding="utf8" path="sql/last_modified_trigger.sql"
relativeToChangelogFile="true"
splitStatements="false"/>
</changeSet>
</databaseChangeLog>

View File

@@ -7,4 +7,5 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog-3.8.xsd" >
<include file="1.0-core/collection.xml" relativeToChangelogFile="true"/>
<include file="1.0-templating/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.ChannelService;
import net.dv8tion.jda.api.entities.Message;
@@ -14,7 +15,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class AChannelParameterHandlerImplTest {
public class AChannelParameterHandlerImplTest extends AbstractParameterHandlerTest {
@InjectMocks
private AChannelParameterHandlerImpl testUnit;
@@ -49,10 +50,10 @@ public class AChannelParameterHandlerImplTest {
@Test
public void testProperChannelMention() {
String input = "test";
when(textChannelParameterHandler.handle(input, iterators, TextChannel.class, message)).thenReturn(channel);
UnparsedCommandParameterPiece piece = getPieceWithValue("input");
when(textChannelParameterHandler.handle(piece, iterators, TextChannel.class, message)).thenReturn(channel);
when(channelService.getFakeChannelFromTextChannel(channel)).thenReturn(aChannel);
AChannel parsed = (AChannel) testUnit.handle(input, iterators, TextChannel.class, message);
AChannel parsed = (AChannel) testUnit.handle(piece, iterators, TextChannel.class, message);
Assert.assertEquals(aChannel, parsed);
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.service.EmoteService;
import net.dv8tion.jda.api.entities.Emote;
@@ -14,8 +15,9 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class AEmoteParameterHandlerImplImplTest {
public class AEmoteParameterHandlerImplImplTest extends AbstractParameterHandlerTest {
public static final String INPUT = "input";
@InjectMocks
private AEmoteParameterHandlerImpl testUnit;
@@ -49,19 +51,19 @@ public class AEmoteParameterHandlerImplImplTest {
@Test
public void testProperEmoteMention() {
String input = "test";
when(emoteParameterHandler.handle(input, iterators, Emote.class, message)).thenReturn(emote);
UnparsedCommandParameterPiece piece = getPieceWithValue(INPUT);
when(emoteParameterHandler.handle(piece, iterators, Emote.class, message)).thenReturn(emote);
when(emoteService.getFakeEmoteFromEmote(emote)).thenReturn(aEmote);
AEmote parsed = (AEmote) testUnit.handle(input, iterators, AEmote.class, message);
AEmote parsed = (AEmote) testUnit.handle(piece, iterators, AEmote.class, message);
Assert.assertEquals(aEmote, parsed);
}
@Test
public void testDefaultEmoteHandling() {
String input = "test";
when(emoteParameterHandler.handle(input, iterators, Emote.class, message)).thenReturn(null);
when(emoteService.getFakeEmote(input)).thenReturn(aEmote);
AEmote parsed = (AEmote) testUnit.handle(input, iterators, AEmote.class, message);
UnparsedCommandParameterPiece piece = getPieceWithValue(INPUT);
when(emoteParameterHandler.handle(piece, iterators, Emote.class, message)).thenReturn(null);
when(emoteService.getFakeEmote(INPUT)).thenReturn(aEmote);
AEmote parsed = (AEmote) testUnit.handle(piece, iterators, AEmote.class, message);
Assert.assertEquals(aEmote, parsed);
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.RoleService;
@@ -15,7 +16,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ARoleParameterHandlerImplImplTest {
public class ARoleParameterHandlerImplImplTest extends AbstractParameterHandlerTest {
@InjectMocks
private ARoleParameterHandlerImpl testUnit;
@@ -50,10 +51,10 @@ public class ARoleParameterHandlerImplImplTest {
@Test
public void testProperRoleMention() {
String input = "test";
when(roleParameterHandler.handle(input, iterators, Role.class, message)).thenReturn(role);
UnparsedCommandParameterPiece piece = getPieceWithValue("test");
when(roleParameterHandler.handle(piece, iterators, Role.class, message)).thenReturn(role);
when(roleService.getFakeRoleFromRole(role)).thenReturn(aRole);
ARole parsed = (ARole) testUnit.handle(input, iterators, AEmote.class, message);
ARole parsed = (ARole) testUnit.handle(piece, iterators, AEmote.class, message);
Assert.assertEquals(aRole, parsed);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
public abstract class AbstractParameterHandlerTest {
protected UnparsedCommandParameterPiece getPieceWithValue(String value) {
return UnparsedCommandParameterPiece.builder().value(value).build();
}
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -7,7 +8,7 @@ import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class BooleanParameterHandlerImplTest {
public class BooleanParameterHandlerImplTest extends AbstractParameterHandlerTest {
@InjectMocks
private BooleanParameterHandlerImpl testUnit;
@@ -24,22 +25,26 @@ public class BooleanParameterHandlerImplTest {
@Test
public void testTrueParsing() {
Assert.assertTrue((Boolean)testUnit.handle("true", null, null, null));
UnparsedCommandParameterPiece piece = getPieceWithValue("true");
Assert.assertTrue((Boolean)testUnit.handle(piece, null, null, null));
}
@Test
public void testAnyOtherText() {
Assert.assertFalse((Boolean)testUnit.handle("test", null, null, null));
UnparsedCommandParameterPiece piece = getPieceWithValue("test");
Assert.assertFalse((Boolean)testUnit.handle(piece, null, null, null));
}
@Test
public void testNullInput() {
Assert.assertFalse((Boolean)testUnit.handle(null, null, null, null));
UnparsedCommandParameterPiece piece = getPieceWithValue(null);
Assert.assertFalse((Boolean)testUnit.handle(piece, null, null, null));
}
@Test
public void testEmptyStringAsInput() {
Assert.assertFalse((Boolean)testUnit.handle("", null, null, null));
UnparsedCommandParameterPiece piece = getPieceWithValue("");
Assert.assertFalse((Boolean)testUnit.handle(piece, null, null, null));
}
}

View File

@@ -7,7 +7,7 @@ import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class DoubleParameterHandlerImplTest {
public class DoubleParameterHandlerImplTest extends AbstractParameterHandlerTest {
@InjectMocks
private DoubleParameterHandlerImpl testUnit;
@@ -24,32 +24,32 @@ public class DoubleParameterHandlerImplTest {
@Test
public void testSuccessfulParse() {
Assert.assertEquals(5D, testUnit.handle("5", null, null, null));
Assert.assertEquals(5D, testUnit.handle(getPieceWithValue("5"), null, null, null));
}
@Test
public void testNegativeNumber() {
Assert.assertEquals(-5D, testUnit.handle("-5", null, null, null));
Assert.assertEquals(-5D, testUnit.handle(getPieceWithValue("-5"), null, null, null));
}
public void testDecimal() {
Assert.assertEquals(3.14D, testUnit.handle("3.14", null, null, null));
Assert.assertEquals(3.14D, testUnit.handle(getPieceWithValue("3.14"), null, null, null));
}
@Test(expected = NumberFormatException.class)
public void testTextAsInput() {
testUnit.handle("someText", null, null, null);
testUnit.handle(getPieceWithValue("someText"), null, null, null);
}
@Test(expected = NullPointerException.class)
public void testNullInput() {
testUnit.handle(null, null, null, null);
testUnit.handle(getPieceWithValue(null), null, null, null);
}
@Test(expected = NumberFormatException.class)
public void testEmptyStringAsInput() {
testUnit.handle("", null, null, null);
testUnit.handle(getPieceWithValue(""), null, null, null);
}
}

View File

@@ -11,7 +11,7 @@ import java.time.Duration;
import java.time.temporal.ChronoUnit;
@RunWith(MockitoJUnitRunner.class)
public class DurationParameterHandlerImplTest {
public class DurationParameterHandlerImplTest extends AbstractParameterHandlerTest {
@InjectMocks
private DurationParameterHandlerImpl testUnit;
@@ -28,23 +28,23 @@ public class DurationParameterHandlerImplTest {
@Test
public void testSimpleParsing() {
Assert.assertEquals(Duration.ofMinutes(1), testUnit.handle("1m", null, null, null));
Assert.assertEquals(Duration.ofMinutes(1), testUnit.handle(getPieceWithValue("1m"), null, null, null));
}
@Test
public void testMoreComplicatedParsing() {
Duration targetDuration = Duration.ofDays(4).plus(5, ChronoUnit.HOURS).plus(5, ChronoUnit.MINUTES);
Assert.assertEquals(targetDuration, testUnit.handle("5m5h4d", null, null, null));
Assert.assertEquals(targetDuration, testUnit.handle(getPieceWithValue("5h5m4d"), null, null, null));
}
@Test(expected = DurationFormatException.class)
public void testNullInput() {
testUnit.handle(null, null, null, null);
testUnit.handle(getPieceWithValue(null), null, null, null);
}
@Test(expected = DurationFormatException.class)
public void testEmptyStringAsInput() {
testUnit.handle("", null, null, null);
testUnit.handle(getPieceWithValue(""), null, null, null);
}
}

Some files were not shown because too many files have changed in this diff Show More