added assignable role place module including: setting up, configuring, deleting commands and documentation

upgraded JDA version to 4.2.0
refactored multiple interfaces to be more convenient/contain more information (reaction added/removed now gets the actual event)
added generic way to check for conditions. these conditions are provided by modules and are loosely connected via condition context and a condition name
added changeable flag to emotes to indicate that they can be updated via setEmote
refactored emote parsing in command parameters, the command parameters will now contain a fake emote
added feature to embed templates for fields to force a new message regardless of the discord limit
added some more functionality to message and channel service regarding field edit/embed sending
introduced the full emote parameter, to have both the emote (if custom) and a fake aemote at hand
refactored some methods to already throw exceptions within the retrieval methods, instead of optionals which need to be dealt outside
changed getEmotes to getEmotesBag to have duplicates of emotes
fixed setEmote to behave correctly with new parameter types
fixed creation of emotes, which previously created additional instances
fixed templating multiple fields handling
refactored command handling to allow async commands, they are the same interface, but configuration dicates whether or not it is async
added generic exception reporting for async commands
refactored a bunch of service methods to be named optional, and the non optional methods throw exceptions in case nothing is found
added a few more customized exceptions
added clearing freemarker internal template cache to clear cache
added feature to skip, not use, embeds if they look to be empty (no fields, no description, no attachment)
added virtual env to gitignore
fixed initial sync of roles un-marking roles as deleted
added some convenience methods to remove reactions from users directly
fixed post command handling in case it is not a templatable instance
fixed exceptions without cause in generic exception model
This commit is contained in:
Sheldan
2020-07-23 02:22:58 +02:00
parent 5317199bf4
commit fd4d784081
201 changed files with 6547 additions and 527 deletions

View File

@@ -7,22 +7,20 @@ import dev.sheldan.abstracto.core.command.exception.IncorrectParameter;
import dev.sheldan.abstracto.core.command.exception.ParameterTooLong;
import dev.sheldan.abstracto.core.command.service.CommandManager;
import dev.sheldan.abstracto.core.command.service.CommandService;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.core.command.execution.*;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.Constants;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.MemberNotFoundException;
import dev.sheldan.abstracto.core.exception.RoleNotFoundInDBException;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.FullRole;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.*;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
@@ -32,7 +30,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
@@ -68,6 +65,18 @@ public class CommandReceivedHandler extends ListenerAdapter {
@Autowired
private CommandService commandService;
@Autowired
private EmoteService emoteService;
@Autowired
private ExceptionService exceptionService;
@Autowired
private EmoteManagementService emoteManagementService;
@Autowired
private RoleService roleService;
@Override
@Transactional
public void onMessageReceived(@Nonnull MessageReceivedEvent event) {
@@ -85,47 +94,84 @@ public class CommandReceivedHandler extends ListenerAdapter {
.message(event.getMessage())
.jda(event.getJDA())
.userInitiatedContext(userInitiatedContext);
Command foundCommand = null;
final Command foundCommand;
try {
String contentStripped = event.getMessage().getContentStripped();
List<String> parameters = Arrays.asList(contentStripped.split(" "));
UnParsedCommandParameter unParsedParameter = new UnParsedCommandParameter(contentStripped);
String commandName = commandManager.getCommandName(parameters.get(0), event.getGuild().getIdLong());
foundCommand = commandManager.findCommandByParameters(commandName, unParsedParameter);
Parameters parsedParameters = getParsedParameters(unParsedParameter, foundCommand, event.getMessage(), userInitiatedContext);
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
ConditionResult conditionResult = commandService.isCommandExecutable(foundCommand, commandContext);
CommandResult commandResult;
if(conditionResult.isResult()) {
commandResult = self.executeCommand(foundCommand, commandContext);
} else {
commandResult = CommandResult.fromCondition(conditionResult);
}
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, commandResult, foundCommand);
}
tryToExecuteFoundCommand(event, userInitiatedContext, commandContextBuilder, foundCommand, unParsedParameter);
} catch (Exception e) {
log.error("Exception when preparing command.", e);
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, commandResult, foundCommand);
}
self.executePostCommandListener(null, commandContext, commandResult);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void tryToExecuteFoundCommand(@Nonnull MessageReceivedEvent event, UserInitiatedServerContext userInitiatedContext, CommandContext.CommandContextBuilder commandContextBuilder, Command foundCommand, UnParsedCommandParameter unParsedParameter) {
try {
Parameters parsedParameters = getParsedParameters(unParsedParameter, foundCommand, event.getMessage(), userInitiatedContext);
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
ConditionResult conditionResult = commandService.isCommandExecutable(foundCommand, commandContext);
CommandResult commandResult = null;
if(conditionResult.isResult()) {
if(foundCommand.getConfiguration().isAsync()) {
foundCommand.executeAsync(commandContext).thenAccept(result ->
executePostCommandListener(foundCommand, commandContext, result)
).exceptionally(throwable -> {
log.error("Asynchronous command {} failed.", foundCommand.getConfiguration().getName(), throwable);
UserInitiatedServerContext rebuildUserContext = buildTemplateParameter(event);
CommandContext rebuildContext = CommandContext.builder()
.author(event.getMember())
.guild(event.getGuild())
.channel(event.getTextChannel())
.message(event.getMessage())
.jda(event.getJDA())
.userInitiatedContext(rebuildUserContext)
.parameters(parsedParameters).build();
CommandResult failedResult = CommandResult.fromError(throwable.getMessage(), throwable);
self.executePostCommandListener(foundCommand, rebuildContext, failedResult);
return null;
});
} else {
commandResult = self.executeCommand(foundCommand, commandContext);
}
} else {
commandResult = CommandResult.fromCondition(conditionResult);
}
if(commandResult != null) {
self.executePostCommandListener(foundCommand, commandContext, commandResult);
}
} catch (Exception e) {
log.error("Exception when executing command.", e);
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
self.executePostCommandListener(foundCommand, commandContext, commandResult);
}
}
@Transactional
public void executePostCommandListener(Command foundCommand, CommandContext commandContext, CommandResult result) {
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, result, foundCommand);
}
}
@Transactional
public CommandResult executeCommand(Command foundCommand, CommandContext commandContext) {
return foundCommand.execute(commandContext);
}
private UserInitiatedServerContext buildTemplateParameter(MessageReceivedEvent event) {
Optional<AChannel> channel = channelManagementService.loadChannel(event.getChannel().getIdLong());
AChannel channel = channelManagementService.loadChannel(event.getChannel().getIdLong());
AServer server = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
AUserInAServer user = userInServerManagementService.loadUser(event.getMember());
AChannel channel1 = channel.orElseThrow(() -> new ChannelNotFoundException(event.getChannel().getIdLong(), event.getGuild().getIdLong()));
return UserInitiatedServerContext
.builder()
.channel(channel1)
.channel(channel)
.server(server)
.member(event.getMember())
.aUserInAServer(user)
@@ -141,9 +187,9 @@ public class CommandReceivedHandler extends ListenerAdapter {
return Parameters.builder().parameters(parsedParameters).build();
}
Iterator<TextChannel> channelIterator = message.getMentionedChannels().iterator();
Iterator<Emote> emoteIterator = message.getEmotes().iterator();
Iterator<Emote> emoteIterator = message.getEmotesBag().iterator();
Iterator<Member> memberIterator = message.getMentionedMembers().iterator();
Iterator<Role> roleIterator = message.getMentionedRoles().iterator();
Iterator<Role> roleIterator = message.getMentionedRolesBag().iterator();
Parameter param = command.getConfiguration().getParameters().get(0);
boolean reminderActive = false;
for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) {
@@ -175,21 +221,69 @@ public class CommandReceivedHandler extends ListenerAdapter {
} else {
parsedParameters.add(memberIterator.next());
}
} else if(param.getType().equals(Emote.class)) {
} else if(param.getType().equals(FullEmote.class)) {
// TODO maybe rework, this fails if two emotes are needed, and the second one is an emote, the first one a default one
// the second one shadows the first one, and there are too little parameters to go of
if (emoteIterator.hasNext()) {
parsedParameters.add(emoteIterator.next());
try {
Long emoteId = Long.parseLong(value);
if(emoteManagementService.emoteExists(emoteId)) {
AEmote aEmote = AEmote.builder().emoteId(emoteId).custom(true).build();
FullEmote emote = FullEmote.builder().fakeEmote(aEmote).build();
parsedParameters.add(emote);
}
} catch (Exception ex) {
Emote actualEmote = emoteIterator.next();
AEmote fakeEmote = emoteService.getFakeEmote(actualEmote);
FullEmote emote = FullEmote.builder().fakeEmote(fakeEmote).emote(actualEmote).build();
parsedParameters.add(emote);
}
} else {
parsedParameters.add(value);
try {
Long emoteId = Long.parseLong(value);
if(emoteManagementService.emoteExists(emoteId)) {
// we do not need to load the actual emote, as there is no guarantee that it exists anyway
// there might be multiple emotes with the same emoteId, so we dont have any gain to fetch any of them
AEmote aEmote = AEmote.builder().emoteId(emoteId).custom(true).build();
FullEmote emote = FullEmote.builder().fakeEmote(aEmote).build();
parsedParameters.add(emote);
}
} catch (Exception ex) {
AEmote fakeEmote = emoteService.getFakeEmote(value);
FullEmote emote = FullEmote.builder().fakeEmote(fakeEmote).build();
parsedParameters.add(emote);
}
}
} else if(param.getType().equals(AEmote.class)) {
// TODO maybe rework, this fails if two emotes are needed, and the second one is an emote, the first one a default one
// the second one shadows the first one, and there are too little parameters to go of
if (emoteIterator.hasNext()) {
parsedParameters.add(emoteService.getFakeEmote(emoteIterator.next()));
} else {
parsedParameters.add(emoteService.getFakeEmote(value));
}
} else if(CommandParameterKey.class.isAssignableFrom(param.getType())) {
CommandParameterKey cast = (CommandParameterKey) CommandParameterKey.getEnumFromKey(param.getType(), value);
parsedParameters.add(cast);
} else if(param.getType().equals(FullRole.class)) {
ARole aRole;
if(StringUtils.isNumeric(value)) {
long roleId = Long.parseLong(value);
aRole = roleManagementService.findRoleOptional(roleId).orElseThrow(() -> new RoleNotFoundInDBException(roleId));
} else {
long roleId = roleIterator.next().getIdLong();
aRole = roleManagementService.findRoleOptional(roleId).orElseThrow(() -> new RoleNotFoundInDBException(roleId));
}
Role role = roleService.getRoleFromGuild(aRole);
FullRole fullRole = FullRole.builder().role(aRole).serverRole(role).build();
parsedParameters.add(fullRole);
} else if(param.getType().equals(ARole.class)) {
if(StringUtils.isNumeric(value)) {
long roleId = Long.parseLong(value);
parsedParameters.add(roleManagementService.findRole(roleId, userInitiatedServerContext.getServer()).orElseThrow(() -> new RoleNotFoundInDBException(roleId, message.getGuild().getIdLong())));
parsedParameters.add(roleManagementService.findRoleOptional(roleId).orElseThrow(() -> new RoleNotFoundInDBException(roleId)));
} else {
long roleId = roleIterator.next().getIdLong();
parsedParameters.add(roleManagementService.findRole(roleId, userInitiatedServerContext.getServer()).orElseThrow(() -> new RoleNotFoundInDBException(roleId, message.getGuild().getIdLong())));
parsedParameters.add(roleManagementService.findRoleOptional(roleId).orElseThrow(() -> new RoleNotFoundInDBException(roleId)));
}
} else if(param.getType().equals(Boolean.class)) {
parsedParameters.add(Boolean.valueOf(value));
@@ -209,6 +303,8 @@ public class CommandReceivedHandler extends ListenerAdapter {
}
} catch (NoSuchElementException e) {
throw new IncorrectParameter(command, param.getType(), param.getName());
} catch (IllegalArgumentException e) {
}
}

View File

@@ -1,8 +1,8 @@
package dev.sheldan.abstracto.core.command.post;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.templating.Templatable;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ResultState;
@@ -18,24 +18,17 @@ public class ExceptionPostExecution implements PostCommandExecution {
@Autowired
private TemplateService templateService;
@Autowired
private ExceptionService exceptionService;
@Override
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.ERROR)) {
ResultState result = commandResult.getResult();
if(result.equals(ResultState.ERROR)) {
Throwable throwable = commandResult.getThrowable();
if(throwable != null) {
log.error("Exception: ", throwable);
if(throwable instanceof Templatable) {
Templatable exception = (Templatable) throwable;
String text = templateService.renderTemplate(exception.getTemplateName(), exception.getTemplateModel());
commandContext.getChannel().sendMessage(text).queue();
} else {
if(throwable.getCause() == null) {
commandContext.getChannel().sendMessage(throwable.getClass().getSimpleName() + ": " + commandResult.getMessage()).queue();
} else {
Throwable cause = throwable.getCause();
commandContext.getChannel().sendMessage(throwable.getClass().getSimpleName() + ": " + commandResult.getMessage() + ": " + cause.getClass().getSimpleName() + ":" + cause.getMessage()).queue();
}
}
log.info("Exception handling for exception {}.", throwable.getClass().getSimpleName());
exceptionService.reportExceptionToContext(throwable, commandContext, command);
}
}
}

View File

@@ -20,7 +20,7 @@ public class ReactionPostExecution implements PostCommandExecution {
@Override
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
ResultState result = commandResult.getResult();
if(result.equals(ResultState.ERROR)) {
if(result.equals(ResultState.ERROR) || result.equals(ResultState.REPORTED_ERROR)) {
messageService.addReactionToMessage(WARN_REACTION_EMOTE, commandContext.getGuild().getIdLong(), commandContext.getMessage());
if(commandResult.getMessage() != null && commandResult.getThrowable() == null){
commandContext.getChannel().sendMessage(commandResult.getMessage()).queue();

View File

@@ -0,0 +1,52 @@
package dev.sheldan.abstracto.core.command.service;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.models.exception.GenericExceptionModel;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.templating.Templatable;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ExceptionServiceBean implements ExceptionService {
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Override
public CommandResult reportExceptionToContext(Throwable throwable, CommandContext context, Command command) {
if(command != null && command.getConfiguration().isReportsException()) {
try {
FullUser fullUser = FullUser
.builder()
.aUserInAServer(context.getUserInitiatedContext().getAUserInAServer())
.member(context.getAuthor())
.build();
GenericExceptionModel modMailExceptionModel = GenericExceptionModel
.builder()
.user(fullUser)
.throwable(throwable)
.build();
channelService.sendEmbedTemplateInChannel("generic_command_exception", modMailExceptionModel, context.getChannel());
} catch (Exception e) {
log.error("Failed to notify about assignable role exception.", e);
}
} else if(throwable instanceof Templatable){
Templatable exception = (Templatable) throwable;
String text = templateService.renderTemplate(exception.getTemplateName(), exception.getTemplateModel());
channelService.sendTextToChannel(text, context.getChannel());
} else {
channelService.sendTextToChannel(throwable.getLocalizedMessage(), context.getChannel());
}
return CommandResult.fromReportedError();
}
}

View File

@@ -9,8 +9,9 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -23,25 +24,22 @@ public class SetEmote extends AbstractConditionableCommand {
@Autowired
private EmoteManagementService emoteManagementService;
@Autowired
private EmoteService emoteService;
@Override
public CommandResult execute(CommandContext commandContext) {
String emoteKey = (String) commandContext.getParameters().getParameters().get(0);
Object o = commandContext.getParameters().getParameters().get(1);
if(o instanceof String) {
String emote = (String) o;
emoteManagementService.setEmoteToDefaultEmote(emoteKey, emote, commandContext.getGuild().getIdLong());
} else {
Emote emote = (Emote) o;
// todo check if usable
emoteManagementService.setEmoteToCustomEmote(emoteKey, emote, commandContext.getGuild().getIdLong());
}
List<Object> parameters = commandContext.getParameters().getParameters();
String emoteKey = (String) parameters.get(0);
AEmote emote = (AEmote) parameters.get(1);
emoteManagementService.setEmoteToAEmote(emoteKey, emote, commandContext.getGuild().getIdLong());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter emoteKey = Parameter.builder().name("emoteKey").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(net.dv8tion.jda.api.entities.Emote.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(AEmote.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(emoteKey, emote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()

View File

@@ -48,9 +48,10 @@ public class InteractiveServiceBean implements InteractiveService {
@Override
public void createMessageWithResponse(String messageText, AUserInAServer responder, AChannel channel, Long messageId, Consumer<MessageReceivedEvent> action, Runnable finalAction) {
channelService.sendTextToAChannel(messageText, channel);
Long channelId = channel.getId();
eventWaiter.waitForEvent(MessageReceivedEvent.class, event -> {
if(event != null) {
return event.getAuthor().getIdLong() == responder.getUserReference().getId() && event.getMessage().getIdLong() != messageId;
return event.getAuthor().getIdLong() == responder.getUserReference().getId() && event.getMessage().getIdLong() != messageId && event.getMessage().getChannel().getIdLong() == channelId;
}
return false;
}, action, 1, TimeUnit.MINUTES, finalAction);
@@ -94,7 +95,7 @@ public class InteractiveServiceBean implements InteractiveService {
}
private void addEmoteToBuilder(String key, Consumer<Void> consumer, Long serverId, ButtonMenu.Builder builder, HashMap<String, Consumer<Void>> actions) {
AEmote emoteOrFakeEmote = emoteService.getEmoteOrFakeEmote(key, serverId);
AEmote emoteOrFakeEmote = emoteService.getEmoteOrDefaultEmote(key, serverId);
if(Boolean.TRUE.equals(emoteOrFakeEmote.getCustom())){
Optional<Emote> emote = botService.getEmote(serverId, emoteOrFakeEmote);
emote.ifPresent(emote1 -> {

View File

@@ -1,6 +1,5 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -21,7 +20,6 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@@ -59,7 +57,7 @@ public class PostTargetSetupStep extends AbstractConfigSetupStep {
TextChannel currentTextChannel;
if(postTargetManagement.postTargetExists(postTargetStepParameter.getPostTargetKey(), user.getGuildId())) {
PostTarget postTarget = postTargetManagement.getPostTarget(postTargetStepParameter.getPostTargetKey(), user.getGuildId());
currentTextChannel = botService.getTextChannelFromServer(user.getGuildId(), postTarget.getChannelReference().getId()).orElse(null);
currentTextChannel = botService.getTextChannelFromServerOptional(user.getGuildId(), postTarget.getChannelReference().getId()).orElse(null);
} else {
currentTextChannel = null;
}
@@ -70,48 +68,44 @@ public class PostTargetSetupStep extends AbstractConfigSetupStep {
.build();
String messageTemplateKey = "setup_post_target_message";
String messageText = templateService.renderTemplate(messageTemplateKey, model);
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
AChannel channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
if(channel.isPresent()) {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
try {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
try {
SetupStepResult result;
Message message = event.getMessage();
if(checkForExit(message)) {
result = SetupStepResult.fromCancelled();
} else {
if(message.getMentionedChannels().size() == 0) {
throw new NoChannelProvidedException("No channel was provided.");
}
TextChannel textChannel = message.getMentionedChannels().get(0);
PostTargetDelayedActionConfig build = PostTargetDelayedActionConfig
.builder()
.postTargetKey(postTargetStepParameter.getPostTargetKey())
.serverId(user.getGuildId())
.textChannel(textChannel)
.channelId(textChannel.getIdLong())
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
result = SetupStepResult
.builder()
.result(SetupStepResultType.SUCCESS)
.delayedActionConfigList(delayedSteps)
.build();
SetupStepResult result;
Message message = event.getMessage();
if(checkForExit(message)) {
result = SetupStepResult.fromCancelled();
} else {
if(message.getMentionedChannels().size() == 0) {
throw new NoChannelProvidedException("No channel was provided.");
}
future.complete(result);
} catch (Exception e) {
log.error("Failed to handle post target step.", e);
future.completeExceptionally(new SetupStepException(e));
TextChannel textChannel = message.getMentionedChannels().get(0);
PostTargetDelayedActionConfig build = PostTargetDelayedActionConfig
.builder()
.postTargetKey(postTargetStepParameter.getPostTargetKey())
.serverId(user.getGuildId())
.textChannel(textChannel)
.channelId(textChannel.getIdLong())
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
result = SetupStepResult
.builder()
.result(SetupStepResultType.SUCCESS)
.delayedActionConfigList(delayedSteps)
.build();
}
};
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), configAction, finalAction);
} else {
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
}
future.complete(result);
} catch (Exception e) {
log.error("Failed to handle post target step.", e);
future.completeExceptionally(new SetupStepException(e));
}
};
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel, parameter.getPreviousMessageId(), configAction, finalAction);
return future;
}

View File

@@ -1,6 +1,5 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -14,7 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@@ -48,35 +46,31 @@ public class SetupSummaryStep extends AbstractConfigSetupStep {
.actionConfigs(parameter.getDelayedActionList())
.build();
String messageToSend = templateService.renderTemplate("setup_confirmation", model);
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
AChannel channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
if(channel.isPresent()) {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<Void> confirmation = (Void none) -> {
try {
self.executeDelayedSteps(parameter);
SetupStepResult result = SetupStepResult
.builder()
.result(SetupStepResultType.SUCCESS)
.build();
future.complete(result);
} catch (Exception e) {
future.completeExceptionally(e);
}
};
Consumer<Void> denial = (Void none) -> {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<Void> confirmation = (Void none) -> {
try {
self.executeDelayedSteps(parameter);
SetupStepResult result = SetupStepResult
.builder()
.result(SetupStepResultType.CANCELLED)
.result(SetupStepResultType.SUCCESS)
.build();
future.complete(result);
};
interactiveService.createMessageWithConfirmation(messageToSend, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), confirmation, denial, finalAction);
} else {
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
}
} catch (Exception e) {
future.completeExceptionally(e);
}
};
Consumer<Void> denial = (Void none) -> {
SetupStepResult result = SetupStepResult
.builder()
.result(SetupStepResultType.CANCELLED)
.build();
future.complete(result);
};
interactiveService.createMessageWithConfirmation(messageToSend, aUserInAServer, channel, parameter.getPreviousMessageId(), confirmation, denial, finalAction);
return future;
}

View File

@@ -1,6 +1,5 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AConfig;
@@ -21,7 +20,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@@ -61,47 +59,43 @@ public class SystemConfigSetupStep extends AbstractConfigSetupStep {
.build();
String messageTemplateKey = "setup_system_config_message";
String messageText = templateService.renderTemplate(messageTemplateKey, model);
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
AChannel channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
if(channel.isPresent()) {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
try {
SetupStepResult result;
Message message = event.getMessage();
if(checkForExit(message)) {
result = SetupStepResult.fromCancelled();
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
try {
SetupStepResult result;
Message message = event.getMessage();
if(checkForExit(message)) {
result = SetupStepResult.fromCancelled();
} else {
AConfig config;
if(checkForKeep(message)) {
config = self.loadDefaultConfig(systemConfigStepParameter);
} else {
AConfig config;
if(checkForKeep(message)) {
config = self.loadDefaultConfig(systemConfigStepParameter);
} else {
config = self.checkValidity(user, systemConfigStepParameter, event);
}
SystemConfigDelayedActionConfig build = SystemConfigDelayedActionConfig
.builder()
.configKey(systemConfigStepParameter.getConfigKey())
.serverId(user.getGuildId())
.value(config)
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
result = SetupStepResult
.builder()
.result(SetupStepResultType.SUCCESS)
.delayedActionConfigList(delayedSteps)
.build();
config = self.checkValidity(user, systemConfigStepParameter, event);
}
future.complete(result);
} catch (Exception e) {
log.warn("Failed to handle system config. Retrying..", e);
future.completeExceptionally(new SetupStepException(e));
SystemConfigDelayedActionConfig build = SystemConfigDelayedActionConfig
.builder()
.configKey(systemConfigStepParameter.getConfigKey())
.serverId(user.getGuildId())
.value(config)
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
result = SetupStepResult
.builder()
.result(SetupStepResultType.SUCCESS)
.delayedActionConfigList(delayedSteps)
.build();
}
};
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), configAction, finalAction);
} else {
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
}
future.complete(result);
} catch (Exception e) {
log.warn("Failed to handle system config. Retrying..", e);
future.completeExceptionally(new SetupStepException(e));
}
};
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel, parameter.getPreviousMessageId(), configAction, finalAction);
return future;
}

View File

@@ -72,14 +72,14 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
AServerAChannelAUser authorUser = AServerAChannelAUser
.builder()
.guild(serverManagementService.loadOrCreate(cachedMessage.getServerId()))
.channel(channelManagementService.loadChannel(cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getServerId(), cachedMessage.getChannelId())))
.channel(channelManagementService.loadChannel(cachedMessage.getChannelId()))
.aUserInAServer(userInServerManagementService.loadUser(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build();
GuildChannelMember authorMember = GuildChannelMember
.builder()
.guild(botService.getGuildByIdNullable(cachedMessage.getServerId()))
.textChannel(botService.getTextChannelFromServer(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getServerId(), cachedMessage.getChannelId())))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getChannelId())))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build();
listener.forEach(messageDeletedListener -> {

View File

@@ -67,7 +67,7 @@ public class ReactionUpdatedListener extends ListenerAdapter {
self.callAddedListeners(event, cachedMessage, reaction);
messageCache.putMessageInCache(cachedMessage);
}).exceptionally(throwable -> {
log.error("Failed to add reaction to message {} ", event.getMessageIdLong(), throwable);
log.error("Failed to handle add reaction to message {} ", event.getMessageIdLong(), throwable);
return null;
})
).exceptionally(throwable -> {
@@ -112,7 +112,7 @@ public class ReactionUpdatedListener extends ListenerAdapter {
return;
}
try {
reactedAddedListener.executeReactionAdded(cachedMessage, event.getReaction(), userInAServer);
reactedAddedListener.executeReactionAdded(cachedMessage, event, userInAServer);
} catch (Exception e) {
log.warn(String.format("Failed to execute reaction added listener %s.", reactedAddedListener.getClass().getName()), e);
}
@@ -151,7 +151,7 @@ public class ReactionUpdatedListener extends ListenerAdapter {
return;
}
try {
reactionRemovedListener.executeReactionRemoved(cachedMessage, event.getReaction(), userInAServer);
reactionRemovedListener.executeReactionRemoved(cachedMessage, event, userInAServer);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction removed listener %s.", reactionRemovedListener.getClass().getName()), e);
}

View File

@@ -7,13 +7,23 @@ import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.stereotype.Repository;
import javax.persistence.QueryHint;
import java.util.Optional;
@Repository
public interface EmoteRepository extends JpaRepository<AEmote, Long> {
public interface EmoteRepository extends JpaRepository<AEmote, Integer> {
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
AEmote findAEmoteByNameAndServerRef(String name, AServer server);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByNameAndServerRef(String name, AServer server);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
Optional<AEmote> findByEmoteId(Long emoteId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByEmoteId(Long emoteId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByEmoteIdAndServerRef(String emoteId, AServer server);
}

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -52,17 +52,17 @@ public class BotServiceBean implements BotService {
Optional<Guild> guildOptional = getGuildById(serverId);
if(guildOptional.isPresent()) {
Guild guild = guildOptional.get();
Optional<TextChannel> textChannelOptional = this.getTextChannelFromServer(guild, channelId);
Optional<TextChannel> textChannelOptional = this.getTextChannelFromServerOptional(guild, channelId);
if(textChannelOptional.isPresent()) {
TextChannel textChannel = textChannelOptional.get();
Member member = guild.getMemberById(userId);
return GuildChannelMember.builder().guild(guild).textChannel(textChannel).member(member).build();
} else {
throw new ChannelNotFoundException(channelId, serverId);
throw new ChannelNotFoundException(channelId);
}
}
else {
throw new GuildException(serverId);
throw new GuildNotFoundException(serverId);
}
}
@@ -72,7 +72,7 @@ public class BotServiceBean implements BotService {
if(guildById != null) {
return guildById.getMemberById(memberId);
} else {
throw new GuildException(serverId);
throw new GuildNotFoundException(serverId);
}
}
@@ -82,7 +82,7 @@ public class BotServiceBean implements BotService {
if(guildById != null) {
return isUserInGuild(guildById, aUserInAServer);
} else {
throw new GuildException(aUserInAServer.getServerReference().getId());
throw new GuildNotFoundException(aUserInAServer.getServerReference().getId());
}
}
@@ -103,10 +103,13 @@ public class BotServiceBean implements BotService {
@Override
public CompletableFuture<Void> deleteMessage(Long serverId, Long channelId, Long messageId) {
Optional<TextChannel> textChannelOptional = getTextChannelFromServer(serverId, channelId);
Optional<TextChannel> textChannelOptional = getTextChannelFromServerOptional(serverId, channelId);
if(textChannelOptional.isPresent()) {
TextChannel textChannel = textChannelOptional.get();
return textChannel.deleteMessageById(messageId).submit();
return textChannel.deleteMessageById(messageId).submit().exceptionally(throwable -> {
log.warn("Deleting the message {} in channel {} in guild {} failed.", messageId, channelId, serverId, throwable);
return null;
});
} else {
log.warn("Could not find channel {} in guild {} to delete message {} in.", channelId, serverId, messageId);
}
@@ -140,22 +143,40 @@ public class BotServiceBean implements BotService {
Emote emoteById = guild.getEmoteById(emote.getEmoteId());
return Optional.ofNullable(emoteById);
}
throw new GuildException(serverId);
throw new GuildNotFoundException(serverId);
}
@Override
public Optional<TextChannel> getTextChannelFromServer(Guild guild, Long textChannelId) {
public Optional<Emote> getEmote(AEmote emote) {
if(Boolean.FALSE.equals(emote.getCustom())) {
return Optional.empty();
}
return Optional.ofNullable(instance.getEmoteById(emote.getEmoteId()));
}
@Override
public Optional<TextChannel> getTextChannelFromServerOptional(Guild guild, Long textChannelId) {
return Optional.ofNullable(guild.getTextChannelById(textChannelId));
}
@Override
public Optional<TextChannel> getTextChannelFromServer(Long serverId, Long textChannelId) {
public TextChannel getTextChannelFromServer(Guild guild, Long textChannelId) {
return getTextChannelFromServerOptional(guild, textChannelId).orElseThrow(() -> new ChannelNotFoundException(textChannelId));
}
@Override
public Optional<TextChannel> getTextChannelFromServerOptional(Long serverId, Long textChannelId) {
Optional<Guild> guildOptional = getGuildById(serverId);
if(guildOptional.isPresent()) {
Guild guild = guildOptional.get();
return Optional.ofNullable(guild.getTextChannelById(textChannelId));
}
throw new GuildException(serverId);
throw new GuildNotFoundException(serverId);
}
@Override
public TextChannel getTextChannelFromServer(Long serverId, Long textChannelId) {
return getTextChannelFromServerOptional(serverId, textChannelId).orElseThrow(() -> new ChannelNotFoundException(textChannelId));
}
@Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +16,9 @@ public class CacheServiceBean {
private SessionFactory sessionFactory;
@Autowired
private TemplateService templateService;
@Autowired
public CacheServiceBean(EntityManagerFactory factory) {
SessionFactory unWrapped = factory.unwrap(SessionFactory.class);
@@ -26,5 +30,6 @@ public class CacheServiceBean {
public void clearCaches() {
sessionFactory.getCache().evictAllRegions();
templateService.clearCache();
}
}

View File

@@ -5,7 +5,6 @@ import dev.sheldan.abstracto.core.command.exception.CommandNotFoundException;
import dev.sheldan.abstracto.core.command.models.database.ACommand;
import dev.sheldan.abstracto.core.command.service.management.ChannelGroupCommandManagementService;
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -16,8 +15,6 @@ import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class ChannelGroupServiceBean implements ChannelGroupService {
@@ -55,8 +52,7 @@ public class ChannelGroupServiceBean implements ChannelGroupService {
@Override
public void addChannelToChannelGroup(String channelGroupName, Long channelId, Long serverId) {
Optional<AChannel> aChannel = channelManagementService.loadChannel(channelId);
AChannel channel = aChannel.orElseThrow(() -> new ChannelNotFoundException(channelId, serverId));
AChannel channel = channelManagementService.loadChannel(channelId);
addChannelToChannelGroup(channelGroupName, channel);
}
@@ -77,8 +73,7 @@ public class ChannelGroupServiceBean implements ChannelGroupService {
@Override
public void removeChannelFromChannelGroup(String channelGroupName, Long channelId, Long serverId) {
Optional<AChannel> aChannel = channelManagementService.loadChannel(channelId);
AChannel channel = aChannel.orElseThrow(() -> new ChannelNotFoundException(channelId, serverId));
AChannel channel = channelManagementService.loadChannel(channelId);
removeChannelFromChannelGroup(channelGroupName, channel);
}

View File

@@ -3,12 +3,13 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.CategoryNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.restaction.MessageAction;
import org.apache.commons.lang3.StringUtils;
@@ -50,22 +51,22 @@ public class ChannelServiceBean implements ChannelService {
return sendTextToChannel(text, textChannel);
} else {
log.error("Channel {} to post towards was not found in server {}", channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId());
}
} else {
log.error("Guild {} was not found when trying to post a message", channel.getServer().getId());
throw new GuildException(channel.getServer().getId());
throw new GuildNotFoundException(channel.getServer().getId());
}
}
@Override
public CompletableFuture<Message> sendMessageToAChannel(Message message, AChannel channel) {
Optional<TextChannel> textChannelOpt = botService.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
Optional<TextChannel> textChannelOpt = botService.getTextChannelFromServerOptional(channel.getServer().getId(), channel.getId());
if(textChannelOpt.isPresent()) {
TextChannel textChannel = textChannelOpt.get();
return sendMessageToChannel(message, textChannel);
}
throw new ChannelNotFoundException(channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId());
}
@Override
@@ -87,11 +88,11 @@ public class ChannelServiceBean implements ChannelService {
return sendEmbedToChannel(embed, textChannel);
} else {
log.error("Channel {} to post towards was not found in server {}", channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId(), guild.getIdLong());
throw new ChannelNotFoundException(channel.getId());
}
} else {
log.error("Guild {} was not found when trying to post a message", channel.getServer().getId());
throw new GuildException(channel.getServer().getId());
throw new GuildNotFoundException(channel.getServer().getId());
}
}
@@ -102,11 +103,16 @@ public class ChannelServiceBean implements ChannelService {
@Override
public List<CompletableFuture<Message>> sendMessageToSendToAChannel(MessageToSend messageToSend, AChannel channel) {
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(channel.getServer().getId(), channel.getId());
if(textChannelFromServer.isPresent()) {
return sendMessageToSendToChannel(messageToSend, textChannelFromServer.get());
}
throw new ChannelNotFoundException(channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId());
}
@Override
public CompletableFuture<Message> sendMessageToSendToAChannel(MessageToSend messageToSend, AChannel channel, Integer embedIndex) {
return sendEmbedToAChannel(messageToSend.getEmbeds().get(embedIndex), channel);
}
@Override
@@ -134,22 +140,27 @@ public class ChannelServiceBean implements ChannelService {
@Override
public Optional<TextChannel> getTextChannelInGuild(Long serverId, Long channelId) {
return botService.getTextChannelFromServer(serverId, channelId);
return botService.getTextChannelFromServerOptional(serverId, channelId);
}
@Override
public void editMessageInAChannel(MessageToSend messageToSend, AChannel channel, Long messageId) {
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(channel.getServer().getId(), channel.getId());
if(textChannelFromServer.isPresent()) {
TextChannel textChannel = textChannelFromServer.get();
editMessageInAChannel(messageToSend, textChannel, messageId);
} else {
throw new ChannelNotFoundException(channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId());
}
}
@Override
public void editMessageInAChannel(MessageToSend messageToSend, MessageChannel channel, Long messageId) {
editMessageInAChannelFuture(messageToSend, channel, messageId);
}
@Override
public CompletableFuture<Message> editMessageInAChannelFuture(MessageToSend messageToSend, MessageChannel channel, Long messageId) {
MessageAction messageAction;
if(!StringUtils.isBlank(messageToSend.getMessage())) {
messageAction = channel.editMessageById(messageId, messageToSend.getMessage());
@@ -163,7 +174,42 @@ public class ChannelServiceBean implements ChannelService {
throw new AbstractoRunTimeException("Message to send did not contain anything to send.");
}
}
messageAction.queue();
return messageAction.submit();
}
@Override
public CompletableFuture<Message> editEmbedMessageInAChannel(MessageEmbed embedToSend, MessageChannel channel, Long messageId) {
return channel.editMessageById(messageId, embedToSend).submit();
}
@Override
public CompletableFuture<Message> editTextMessageInAChannel(String text, MessageChannel channel, Long messageId) {
return channel.editMessageById(messageId, text).submit();
}
@Override
public List<CompletableFuture<Message>> editMessagesInAChannelFuture(MessageToSend messageToSend, MessageChannel channel, List<Long> messageIds) {
List<CompletableFuture<Message>> futures = new ArrayList<>();
futures.add(editMessageInAChannelFuture(messageToSend, channel ,messageIds.get(0)));
for (int i = 1; i < messageIds.size(); i++) {
Long messageIdToUpdate = messageIds.get(i);
futures.add(channel.editMessageById(messageIdToUpdate, messageToSend.getEmbeds().get(i)).submit());
}
return futures;
}
@Override
public CompletableFuture<Message> removeFieldFromMessage(MessageChannel channel, Long messageId, Integer index) {
return removeFieldFromMessage(channel, messageId, index, 0);
}
@Override
public CompletableFuture<Message> removeFieldFromMessage(MessageChannel channel, Long messageId, Integer index, Integer embedIndex) {
return channel.retrieveMessageById(messageId).submit().thenCompose(message -> {
EmbedBuilder embedBuilder = new EmbedBuilder(message.getEmbeds().get(embedIndex));
embedBuilder.getFields().remove(index.intValue());
return channel.editMessageById(messageId, embedBuilder.build()).submit();
});
}
@Override
@@ -177,7 +223,7 @@ public class ChannelServiceBean implements ChannelService {
if(textChannelById != null) {
return textChannelById.delete().submit();
}
throw new ChannelNotFoundException(channelId, serverId);
throw new ChannelNotFoundException(channelId);
}
@Override
@@ -198,11 +244,11 @@ public class ChannelServiceBean implements ChannelService {
}
throw new CategoryNotFoundException(categoryId, server.getId());
}
throw new GuildException(server.getId());
throw new GuildNotFoundException(server.getId());
}
@Override
public Optional<TextChannel> getChannelFromAChannel(AChannel channel) {
return botService.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
return botService.getTextChannelFromServerOptional(channel.getServer().getId(), channel.getId());
}
}

View File

@@ -0,0 +1,45 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.InvalidConditionParametersException;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.ConditionContextVariable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@Component
public class ConditionServiceBean implements ConditionService {
@Autowired
private List<SystemCondition> conditionList;
@Override
public boolean checkConditions(ConditionContextInstance context) {
Optional<SystemCondition> matchingCondition = conditionList
.stream()
.filter(systemCondition -> systemCondition.getConditionName().equalsIgnoreCase(context.getConditionName()))
.findAny();
return matchingCondition.map(systemCondition -> {
verifyConditionContext(context, systemCondition);
return systemCondition.checkCondition(context);
}).orElse(true);
}
private void verifyConditionContext(ConditionContextInstance contextInstance, SystemCondition condition) {
for (ConditionContextVariable conditionContextVariable : condition.getExpectedContext().getExpectedVariables()) {
HashMap<String, Object> providedParameters = contextInstance.getParameters();
if(!providedParameters.containsKey(conditionContextVariable.getName())) {
throw new InvalidConditionParametersException(String.format("Variable %s was not present", conditionContextVariable.getName()));
}
Class expectedType = conditionContextVariable.getType();
Object providedParameter = providedParameters.get(conditionContextVariable.getName());
if(!expectedType.isInstance(providedParameter)) {
throw new InvalidConditionParametersException(String.format("Variable %s was of type %s instead of %s.",
conditionContextVariable.getName(), providedParameter.getClass(), expectedType));
}
}
}
}

View File

@@ -86,7 +86,7 @@ public class EmoteServiceBean implements EmoteService {
}
@Override
public AEmote getEmoteOrFakeEmote(String emoteKey, Long serverId) {
public AEmote getEmoteOrDefaultEmote(String emoteKey, Long serverId) {
Optional<AEmote> emoteOptional = emoteManagementService.loadEmoteByName(emoteKey, serverId);
return emoteOptional.orElseGet(() -> AEmote.builder().emoteKey(getDefaultEmote(emoteKey)).custom(false).name(emoteKey).build());
}
@@ -97,13 +97,9 @@ public class EmoteServiceBean implements EmoteService {
}
@Override
public boolean isReactionEmoteAEmote(MessageReaction.ReactionEmote reaction, AEmote storedEmote, Emote actualEmoteInGuild) {
public boolean isReactionEmoteAEmote(MessageReaction.ReactionEmote reaction, AEmote storedEmote) {
if(reaction.isEmote() && storedEmote.getCustom()) {
if(actualEmoteInGuild != null) {
return actualEmoteInGuild.equals(reaction.getEmote());
} else {
return false;
}
return reaction.getEmote().getIdLong() == storedEmote.getEmoteId();
} else if(reaction.isEmoji()){
return reaction.getEmoji().equals(storedEmote.getEmoteKey());
}
@@ -128,4 +124,16 @@ public class EmoteServiceBean implements EmoteService {
}
}
@Override
public AEmote getFakeEmote(Object object) {
if(object instanceof Emote) {
Emote emote = (Emote) object;
return AEmote.builder().fake(true).emoteKey(emote.getName()).custom(true).animated(emote.isAnimated()).emoteId(emote.getIdLong()).build();
} else if(object instanceof String) {
String emoteText = (String) object;
return AEmote.builder().fake(true).custom(false).emoteKey(emoteText).build();
}
throw new IllegalArgumentException("Not possible to convert given object to AEmote.");
}
}

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.cache.*;
@@ -84,7 +84,7 @@ public class MessageCacheBean implements MessageCache {
CompletableFuture<CachedMessage> future = new CompletableFuture<>();
Optional<Guild> guildOptional = botService.getGuildById(guildId);
if(guildOptional.isPresent()) {
Optional<TextChannel> textChannelByIdOptional = botService.getTextChannelFromServer(guildOptional.get(), textChannelId);
Optional<TextChannel> textChannelByIdOptional = botService.getTextChannelFromServerOptional(guildOptional.get(), textChannelId);
if(textChannelByIdOptional.isPresent()) {
TextChannel textChannel = textChannelByIdOptional.get();
textChannel.retrieveMessageById(messageId).queue(message ->
@@ -100,11 +100,11 @@ public class MessageCacheBean implements MessageCache {
);
} else {
log.error("Not able to load message {} in channel {} in guild {}. Text channel not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new ChannelNotFoundException(textChannelId, guildId));
future.completeExceptionally(new ChannelNotFoundException(textChannelId));
}
} else {
log.error("Not able to load message {} in channel {} in guild {}. Guild not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new GuildException(guildId));
future.completeExceptionally(new GuildNotFoundException(guildId));
}
return future;

View File

@@ -1,7 +1,8 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.EmoteNotDefinedException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.ExceptionNotInServerException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
@@ -44,8 +45,8 @@ public class MessageServiceBean implements MessageService {
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message) {
AEmote emote = emoteService.getEmoteOrDefaultEmote(emoteKey, serverId);
Optional<Guild> guildByIdOptional = botService.getGuildById(serverId);
AEmote emote = emoteService.getEmoteOrFakeEmote(emoteKey, serverId);
if(guildByIdOptional.isPresent()) {
Guild guild = guildByIdOptional.get();
if(Boolean.TRUE.equals(emote.getCustom())) {
@@ -61,10 +62,129 @@ public class MessageServiceBean implements MessageService {
}
} else {
log.error("Cannot add reaction, guild not found {}", serverId);
throw new GuildException(serverId);
throw new GuildNotFoundException(serverId);
}
}
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(AEmote emote, Long serverId, Message message) {
if(Boolean.TRUE.equals(emote.getCustom())) {
return addReactionToMessageWithFuture(emote.getEmoteId(), serverId, message);
} else {
return message.addReaction(emote.getEmoteKey()).submit();
}
}
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(Long emoteId, Long serverId, Message message) {
Emote emoteById = botService.getInstance().getEmoteById(emoteId);
if(emoteById == null) {
throw new ExceptionNotInServerException(emoteId);
}
return message.addReaction(emoteById).submit();
}
@Override
public CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Message message) {
if(Boolean.TRUE.equals(emote.getCustom())) {
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById == null) {
throw new ExceptionNotInServerException(emote.getEmoteId());
}
return message.removeReaction(emoteById).submit();
} else {
return message.removeReaction(emote.getEmoteKey()).submit();
}
}
@Override
public CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Message message) {
if(Boolean.TRUE.equals(emote.getCustom())) {
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById == null) {
throw new ExceptionNotInServerException(emote.getEmoteId());
}
return message.clearReactions(emoteById).submit();
} else {
return message.clearReactions(emote.getEmoteKey()).submit();
}
}
@Override
public CompletableFuture<Void> removeReactionFromMessageWithFuture(Integer emoteId, Long serverId, Message message) {
AEmote emote = emoteManagementService.loadEmote(emoteId);
return removeReactionFromMessageWithFuture(emote, serverId, message);
}
@Override
public CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Long serverId, Message message) {
AEmote emote = emoteManagementService.loadEmote(emoteId);
return clearReactionFromMessageWithFuture(emote, message);
}
@Override
public CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId) {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
Integer emoteId = emote.getId();
return channel.retrieveMessageById(messageId).submit()
.thenCompose(message1 -> removeReactionFromMessageWithFuture(emoteId, serverId, message1));
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId) {
Guild guild = botService.getGuildByIdNullable(serverId);
Member memberById = guild.getMemberById(userId);
return removeReactionOfUserFromMessageWithFuture(emote, serverId, channelId, messageId, memberById);
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Member member) {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
Integer emoteId = emote.getId();
return channel.retrieveMessageById(messageId).submit()
.thenCompose(message1 -> removeReactionOfUserFromMessageWithFuture(emoteId, message1, member));
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Member member) {
if(Boolean.TRUE.equals(emote.getCustom())) {
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById == null) {
throw new ExceptionNotInServerException(emote.getEmoteId());
}
return message.removeReaction(emoteById, member.getUser()).submit();
} else {
return message.removeReaction(emote.getEmoteKey(), member.getUser()).submit();
}
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member) {
AEmote emote = emoteManagementService.loadEmote(emoteId);
return removeReactionOfUserFromMessageWithFuture(emote, message, member);
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Long userId) {
Member memberById = message.getGuild().getMemberById(userId);
return removeReactionOfUserFromMessageWithFuture(emote, message, memberById);
}
@Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId) {
Member memberById = message.getGuild().getMemberById(userId);
AEmote emote = emoteManagementService.loadEmote(emoteId);
return removeReactionOfUserFromMessageWithFuture(emote, message, memberById);
}
@Override
public CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId) {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
Integer emoteId = emote.getId();
return channel.retrieveMessageById(messageId).submit()
.thenCompose(message1 -> clearReactionFromMessageWithFuture(emoteId, serverId, message1));
}
@Override
public List<CompletableFuture<Void>> addReactionsToMessageWithFuture(List<String> emoteKeys, Long serverId, Message message) {
List<CompletableFuture<Void>> futures = new ArrayList<>();

View File

@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.exception.PostTargetNotFoundException;
import dev.sheldan.abstracto.core.exception.PostTargetNotValidException;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -68,10 +68,10 @@ public class PostTargetServiceBean implements PostTargetService {
} else {
log.error("Incorrect post target configuration: {} points to {} on server {}", target.getName(),
target.getChannelReference().getId(), target.getServerReference().getId());
throw new ChannelNotFoundException(target.getChannelReference().getId(), target.getServerReference().getId());
throw new ChannelNotFoundException(target.getChannelReference().getId());
}
} else {
throw new GuildException(target.getServerReference().getId());
throw new GuildNotFoundException(target.getServerReference().getId());
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.exception.RoleDeletedException;
import dev.sheldan.abstracto.core.exception.RoleNotFoundInDBException;
import dev.sheldan.abstracto.core.exception.RoleNotFoundInGuildException;
import dev.sheldan.abstracto.core.models.database.ARole;
@@ -16,6 +17,7 @@ import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@@ -32,31 +34,99 @@ public class RoleServiceBean implements RoleService {
public void addRoleToUser(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) {
Guild guild = guildById.get();
Role roleById = guild.getRoleById(role.getId());
if(roleById != null) {
guild.addRoleToMember(aUserInAServer.getUserReference().getId(), roleById).queue();
} else {
throw new RoleNotFoundInGuildException(role.getId(), aUserInAServer.getServerReference().getId());
}
addRoleToUser(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
throw new GuildException(aUserInAServer.getServerReference().getId());
throw new GuildNotFoundException(aUserInAServer.getServerReference().getId());
}
}
@Override
public CompletableFuture<Void> addRoleToUserFuture(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) {
return addRoleToUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
throw new GuildNotFoundException(aUserInAServer.getServerReference().getId());
}
}
@Override
public void addRoleToMember(Member member, ARole role) {
Guild guild = member.getGuild();
addRoleToUser(guild, role, member.getIdLong());
}
@Override
public CompletableFuture<Void> addRoleToMemberFuture(Member member, ARole role) {
Guild guild = member.getGuild();
return addRoleToUserFuture(guild, role, member.getIdLong());
}
@Override
public void removeRoleFromMember(Member member, ARole role) {
Guild guild = member.getGuild();
removeRoleFromUser(guild, role, member.getIdLong());
}
@Override
public CompletableFuture<Void> removeRoleFromMemberFuture(Member member, ARole role) {
Guild guild = member.getGuild();
return removeRoleFromUserFuture(guild, role, member.getIdLong());
}
private CompletableFuture<Void> addRoleToUserFuture(Guild guild, ARole role, Long userId) {
if(role.getDeleted()) {
log.warn("Not possible to add role to user. Role {} was marked as deleted.", role.getId());
throw new RoleDeletedException(role);
}
Role roleById = guild.getRoleById(role.getId());
if(roleById != null) {
return guild.addRoleToMember(userId, roleById).submit();
} else {
throw new RoleNotFoundInGuildException(role.getId(), guild.getIdLong());
}
}
private void addRoleToUser(Guild guild, ARole role, Long userId) {
addRoleToUserFuture(guild, role, userId);
}
private CompletableFuture<Void> removeRoleFromUserFuture(Guild guild, ARole role, Long userId) {
if(role.getDeleted()) {
log.warn("Not possible to remove role from user. Role {} was marked as deleted.", role.getId());
throw new RoleDeletedException(role);
}
Role roleById = guild.getRoleById(role.getId());
if(roleById != null) {
return guild.removeRoleFromMember(userId, roleById).submit();
} else {
throw new RoleNotFoundInGuildException(role.getId(), guild.getIdLong());
}
}
private void removeRoleFromUser(Guild guild, ARole role, Long userId) {
removeRoleFromUserFuture(guild, role, userId);
}
@Override
public void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) {
Guild guild = guildById.get();
Role roleById = guild.getRoleById(role.getId());
if(roleById != null) {
guild.removeRoleFromMember(aUserInAServer.getUserReference().getId(), roleById).queue();
} else {
throw new RoleNotFoundInGuildException(role.getId(), aUserInAServer.getServerReference().getId());
}
removeRoleFromUser(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
throw new GuildException(aUserInAServer.getServerReference().getId());
throw new GuildNotFoundException(aUserInAServer.getServerReference().getId());
}
}
@Override
public CompletableFuture<Void> removeRoleFromUserFuture(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) {
return removeRoleFromUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
throw new GuildNotFoundException(aUserInAServer.getServerReference().getId());
}
}
@@ -67,18 +137,22 @@ public class RoleServiceBean implements RoleService {
@Override
public void markDeleted(Long id, AServer server) {
Optional<ARole> role = roleManagementService.findRole(id, server);
ARole role1 = role.orElseThrow(() -> new RoleNotFoundInDBException(id, server.getId()));
Optional<ARole> role = roleManagementService.findRoleOptional(id);
ARole role1 = role.orElseThrow(() -> new RoleNotFoundInDBException(id));
roleManagementService.markDeleted(role1);
}
@Override
public Role getRoleFromGuild(ARole role) {
if(role.getDeleted()) {
log.warn("Trying to load role {} which is marked as deleted.", role.getId());
throw new RoleDeletedException(role);
}
Optional<Guild> guildById = botService.getGuildById(role.getServer().getId());
if(guildById.isPresent()) {
return guildById.get().getRoleById(role.getId());
} else {
throw new GuildException(role.getServer().getId());
throw new GuildNotFoundException(role.getServer().getId());
}
}
@@ -106,4 +180,11 @@ public class RoleServiceBean implements RoleService {
public boolean isRoleInServer(ARole role) {
return getRoleFromGuild(role) != null;
}
@Override
public boolean canBotInteractWithRole(ARole role) {
Role jdaRole = getRoleFromGuild(role);
Member selfMember = jdaRole.getGuild().getSelfMember();
return selfMember.canInteract(jdaRole);
}
}

View File

@@ -125,7 +125,7 @@ public class SetupServiceBean implements SetupService {
if(throwable instanceof Templatable) {
Templatable exception = (Templatable) throwable;
String text = templateService.renderTemplate(exception.getTemplateName(), exception.getTemplateModel());
Optional<TextChannel> channelOptional = botService.getTextChannelFromServer(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
Optional<TextChannel> channelOptional = botService.getTextChannelFromServerOptional(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
channelOptional.ifPresent(channel -> channelService.sendTextToChannel(text, channel));
}
}

View File

@@ -81,12 +81,13 @@ public class StartupServiceBean implements Startup {
}
// TODO mark deleted roles ad deleted, use intersect for that
private void synchronizeRolesOf(Guild guild, AServer existingAServer){
List<Role> existingRoles = guild.getRoles();
List<ARole> knownARoles = existingAServer.getRoles();
Set<Long> knownRolesId = SnowflakeUtils.getOwnItemsIds(knownARoles);
Set<Long> availableRoles = SnowflakeUtils.getSnowflakeIds(existingRoles);
Set<Long> newRoles = SetUtils.disjunction(availableRoles, knownRolesId);
Set<Long> newRoles = SetUtils.difference(availableRoles, knownRolesId);
newRoles.forEach(aLong -> {
ARole newRole = roleManagementService.createRole(aLong, existingAServer);
log.trace("Adding new role: {}", aLong);

View File

@@ -24,10 +24,15 @@ public class ChannelManagementServiceBean implements ChannelManagementService {
private LockService lockService;
@Override
public Optional<AChannel> loadChannel(Long id) {
public Optional<AChannel> loadChannelOptional(Long id) {
return repository.findById(id);
}
@Override
public AChannel loadChannel(Long id) {
return loadChannelOptional(id).orElseThrow(() -> new ChannelNotFoundException(id));
}
@Override
public AChannel createChannel(Long id, AChannelType type, AServer server) {
lockService.lockTable(TableLocks.CHANNELS);
@@ -42,15 +47,14 @@ public class ChannelManagementServiceBean implements ChannelManagementService {
.build();
return repository.save(build);
} else {
Optional<AChannel> channelOptional = loadChannel(id);
Optional<AChannel> channelOptional = loadChannelOptional(id);
return channelOptional.orElse(null);
}
}
@Override
public AChannel markAsDeleted(Long id) {
Optional<AChannel> channelOptional = loadChannel(id);
AChannel channel = channelOptional.orElseThrow(() -> new ChannelNotFoundException(id, 0L));
AChannel channel = loadChannel(id);
channel.setDeleted(true);
return channel;
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.exception.EmoteNotFoundException;
import dev.sheldan.abstracto.core.exception.EmoteNotFoundInDbException;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.repository.EmoteRepository;
@@ -24,22 +25,41 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
private DefaultEmoteManagementService defaultEmoteManagementService;
@Override
public Optional<AEmote> loadEmote(Long id) {
public Optional<AEmote> loadEmoteOptional(Integer id) {
return repository.findById(id);
}
@Override
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return this.createCustomEmote(name, emoteKey, emoteId, animated, server);
public AEmote loadEmote(Integer id) {
return loadEmoteOptional(id).orElseThrow(() -> new EmoteNotFoundInDbException(id));
}
@Override
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server) {
validateEmoteName(name);
public Optional<AEmote> loadEmote(Long id) {
return repository.findByEmoteId(id);
}
@Override
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId, boolean validateName) {
AServer server = serverManagementService.loadOrCreate(serverId);
return this.createCustomEmote(name, emoteKey, emoteId, animated, server, validateName);
}
@Override
public AEmote createCustomEmote(String name, AEmote fakeEmote, Long serverId, boolean validateName) {
AServer server = serverManagementService.loadOrCreate(serverId);
return this.createCustomEmote(name, fakeEmote.getEmoteKey(), fakeEmote.getEmoteId(), fakeEmote.getAnimated(), server, validateName);
}
@Override
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server, boolean validateName) {
if(validateName) {
validateEmoteName(name);
}
AEmote emoteToCreate = AEmote
.builder()
.custom(true)
.changeable(true)
.name(name)
.animated(animated)
.emoteId(emoteId)
@@ -51,17 +71,20 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
}
@Override
public AEmote createDefaultEmote(String name, String emoteKey, Long serverId) {
public AEmote createDefaultEmote(String name, String emoteKey, Long serverId, boolean validateName) {
AServer server = serverManagementService.loadOrCreate(serverId);
return createDefaultEmote(name, emoteKey, server);
return createDefaultEmote(name, emoteKey, server, validateName);
}
@Override
public AEmote createDefaultEmote(String name, String emoteKey, AServer server) {
validateEmoteName(name);
public AEmote createDefaultEmote(String name, String emoteKey, AServer server, boolean validateName) {
if(validateName) {
validateEmoteName(name);
}
AEmote emoteToCreate = AEmote
.builder()
.custom(false)
.changeable(true)
.name(name)
.emoteKey(emoteKey)
.serverRef(server)
@@ -87,7 +110,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
AEmote emote;
Optional<AEmote> emoteOptional = loadEmoteByName(name, server);
if(!emoteOptional.isPresent()) {
emote = this.createCustomEmote(name, emoteKey, emoteId, animated, server);
emote = this.createCustomEmote(name, emoteKey, emoteId, animated, server, true);
} else {
emote = emoteOptional.get();
emote.setEmoteKey(emoteKey);
@@ -105,7 +128,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
AEmote emoteBeingSet;
Optional<AEmote> emoteOptional = loadEmoteByName(name, serverId);
if(!emoteOptional.isPresent()) {
emoteBeingSet = this.createCustomEmote(name, emote.getName(), emote.getIdLong(), emote.isAnimated(), server);
emoteBeingSet = this.createCustomEmote(name, emote.getName(), emote.getIdLong(), emote.isAnimated(), server, true);
} else {
emoteBeingSet = emoteOptional.get();
emoteBeingSet.setCustom(true);
@@ -123,7 +146,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
AEmote emoteBeingSet;
Optional<AEmote> emoteOptional = loadEmoteByName(name, serverId);
if(!emoteOptional.isPresent()) {
emoteBeingSet = this.createDefaultEmote(name, emoteKey, server);
emoteBeingSet = this.createDefaultEmote(name, emoteKey, server, true);
} else {
emoteBeingSet = emoteOptional.get();
emoteBeingSet.setEmoteKey(emoteKey);
@@ -133,12 +156,50 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
return emoteBeingSet;
}
@Override
public AEmote setEmoteToAEmote(String name, AEmote emote, Long serverId) {
Optional<AEmote> emoteOptional = loadEmoteByName(name, serverId);
if(!emoteOptional.isPresent()) {
return createEmote(name, emote, serverId, true);
} else {
AEmote emoteBeingSet = emoteOptional.get();
if(emote.getCustom()) {
emoteBeingSet.setCustom(emote.getCustom());
emoteBeingSet.setEmoteId(emote.getEmoteId());
emoteBeingSet.setEmoteKey(emote.getEmoteKey());
} else {
emoteBeingSet.setCustom(false);
emoteBeingSet.setEmoteKey(emote.getEmoteKey());
}
return emoteBeingSet;
}
}
@Override
public AEmote createEmote(String name, AEmote emote, Long serverId, boolean validateName) {
if(emote.getCustom()) {
return this.createCustomEmote(name, emote, serverId, validateName);
} else {
return this.createDefaultEmote(name, emote.getEmoteKey(), serverId, validateName);
}
}
@Override
public boolean emoteExists(String name, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return emoteExists(name, server);
}
@Override
public boolean emoteExists(Long emoteId) {
return repository.existsByEmoteId(emoteId);
}
@Override
public void deleteEmote(AEmote aEmote) {
repository.delete(aEmote);
}
@Override
public boolean emoteExists(String name, AServer server) {
return repository.existsByNameAndServerRef(name, server);

View File

@@ -1,6 +1,5 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.PostTargetNotValidException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -12,7 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
@Slf4j
@@ -56,15 +54,13 @@ public class PostTargetManagementBean implements PostTargetManagement {
@Override
public PostTarget createOrUpdate(String name, AServer server, Long channelId) {
Optional<AChannel> dbChannelOpt = channelManagementService.loadChannel(channelId);
AChannel dbChannel = dbChannelOpt.orElseThrow(() -> new ChannelNotFoundException(channelId, server.getId()));
AChannel dbChannel = channelManagementService.loadChannel(channelId);
return createOrUpdate(name, server, dbChannel);
}
@Override
public PostTarget createOrUpdate(String name, Long serverId, Long channelId) {
Optional<AChannel> dbChannelOpt = channelManagementService.loadChannel(channelId);
AChannel dbChannel = dbChannelOpt.orElseThrow(() -> new ChannelNotFoundException(channelId, serverId));
AChannel dbChannel = channelManagementService.loadChannel(channelId);
AServer dbServer = serverManagementService.loadOrCreate(serverId);
return createOrUpdate(name, dbServer, dbChannel);
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.exception.RoleNotFoundInDBException;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.repository.RoleRepository;
@@ -26,10 +27,15 @@ public class RoleManagementServiceBean implements RoleManagementService {
}
@Override
public Optional<ARole> findRole(Long id, AServer server) {
public Optional<ARole> findRoleOptional(Long id) {
return repository.findById(id);
}
@Override
public ARole findRole(Long id) {
return findRoleOptional(id).orElseThrow(() -> new RoleNotFoundInDBException(id));
}
@Override
public void markDeleted(ARole role) {
role.setDeleted(true);

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.repository.ServerRepository;
import lombok.extern.slf4j.Slf4j;
@@ -37,6 +37,16 @@ public class ServerManagementServiceBean implements ServerManagementService {
}
}
@Override
public AServer loadServer(Long id) {
return loadServerOptional(id).orElseThrow(() -> new GuildNotFoundException(id));
}
@Override
public Optional<AServer> loadServerOptional(Long id) {
return repository.findById(id);
}
@Override
public void addChannelToServer(AServer server, AChannel channel) {
server.getChannels().add(channel);
@@ -51,9 +61,8 @@ public class ServerManagementServiceBean implements ServerManagementService {
@Override
public AUserInAServer addUserToServer(Long serverId, Long userId) {
log.info("Adding user {} to server {}", userId, serverId);
Optional<AServer> server = repository.findById(serverId);
AUser user = userManagementService.loadUser(userId);
AServer serverReference = server.orElseThrow(() -> new GuildException(serverId));
AServer serverReference = loadServer(serverId);
AUserInAServer aUserInAServer = AUserInAServer.builder().serverReference(serverReference).userReference(user).build();
serverReference.getUsers().add(aUserInAServer);
return aUserInAServer;

View File

@@ -1,12 +1,7 @@
spring.jpa.show-sql = false
spring.jpa.hibernate.ddl-auto = none
spring.jpa.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.format_sql = false
log4j.logger.org.hibernate.SQL=info
log4j.logger.org.hibernate.type.descriptor.sql=trace
log4j.logger.org.hibernate.type=trace
spring.jpa.properties.hibernate.generate_statistics = false
spring.jpa.properties.hibernate.cache.use_second_level_cache=true

View File

@@ -14,6 +14,7 @@
<column name="animated" type="BOOLEAN"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="custom" type="BOOLEAN"/>
<column name="changeable" type="BOOLEAN"/>
<column name="emote_id" type="BIGINT"/>
<column name="emote_key" type="VARCHAR(255)"/>
<column name="name" type="VARCHAR(255)"/>

View File

@@ -5,8 +5,11 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.listener.FeatureAware;
import java.util.concurrent.CompletableFuture;
public interface Command extends FeatureAware {
CommandResult execute(CommandContext commandContext);
default CommandResult execute(CommandContext commandContext) {return CommandResult.fromSuccess();};
default CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {return CompletableFuture.completedFuture(CommandResult.fromSuccess());};
CommandConfiguration getConfiguration();
}

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.command.condition;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.models.InsufficientPermissionMessage;
import dev.sheldan.abstracto.core.command.models.exception.InsufficientPermissionMessage;
import dev.sheldan.abstracto.core.command.models.database.ACommand;
import dev.sheldan.abstracto.core.command.models.database.ACommandInAServer;
import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService;

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.command.condition;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.models.FeatureDisabledMessage;
import dev.sheldan.abstracto.core.command.models.exception.FeatureDisabledMessage;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.command.condition;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.models.IncorrectFeatureModeMessage;
import dev.sheldan.abstracto.core.command.models.exception.IncorrectFeatureModeMessage;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeatureMode;
import dev.sheldan.abstracto.core.service.FeatureConfigService;

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.command.condition;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.models.UserImmuneMessage;
import dev.sheldan.abstracto.core.command.models.exception.UserImmuneMessage;
import dev.sheldan.abstracto.core.command.models.database.ACommand;
import dev.sheldan.abstracto.core.command.models.database.ACommandInAServer;
import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService;

View File

@@ -13,6 +13,12 @@ public class CommandConfiguration {
private String module;
private String description;
@Builder.Default
private boolean async = false;
@Builder.Default
private boolean reportsException = false;
@Builder.Default
private List<Parameter> parameters = new ArrayList<>();

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.core.command.exception;
import dev.sheldan.abstracto.core.command.models.exception.CommandParameterKeyValueWrongTypeModel;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.templating.Templatable;
import java.util.List;
public class CommandParameterKeyValueWrongTypeException extends AbstractoRunTimeException implements Templatable {
private CommandParameterKeyValueWrongTypeModel model;
public CommandParameterKeyValueWrongTypeException(List<String> expectedValues) {
super("Command parameter value did not have expected values present");
this.model = CommandParameterKeyValueWrongTypeModel.builder().expectedValues(expectedValues).build();
}
@Override
public String getTemplateName() {
return "command_parameter_value_wrong_type_exception";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.core.command.execution;
import dev.sheldan.abstracto.core.command.exception.CommandParameterKeyValueWrongTypeException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public interface CommandParameterKey {
static <T extends Enum<T>> T getEnumFromKey(Class<T> clazz, String key) {
try {
if(clazz != null && key != null ) {
return Enum.valueOf(clazz, key.trim().toUpperCase());
}
} catch (IllegalArgumentException e) {
List<T> ts = Arrays.asList(clazz.getEnumConstants());
List<String> keys = ts.stream().map(Enum::toString).map(String::toLowerCase).collect(Collectors.toList());
throw new CommandParameterKeyValueWrongTypeException(keys);
}
throw new IllegalArgumentException("Clazz and key must not be null");
}
}

View File

@@ -23,6 +23,10 @@ public class CommandResult {
return CommandResult.builder().result(ResultState.SELF_DESTRUCT).build();
}
public static CommandResult fromReportedError() {
return CommandResult.builder().result(ResultState.REPORTED_ERROR).build();
}
public static CommandResult fromError(String message){
return CommandResult.builder().result(ResultState.ERROR).message(message).build();
}

View File

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

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.core.command.models.exception;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class CommandParameterKeyValueWrongTypeModel {
private List<String> expectedValues;
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.command.models;
package dev.sheldan.abstracto.core.command.models.exception;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import lombok.Builder;

View File

@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.core.command.models.exception;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.templating.Templatable;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class GenericExceptionModel {
private FullUser user;
private Throwable throwable;
public Templatable getTemplate() {
Throwable current = throwable;
while(!(current instanceof Templatable) && (current.getCause() != null && !current.getCause().equals(current))) {
current = current.getCause();
}
if(current instanceof Templatable) {
return (Templatable) current;
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.command.models;
package dev.sheldan.abstracto.core.command.models.exception;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.config.FeatureConfig;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.command.models;
package dev.sheldan.abstracto.core.command.models.exception;
import lombok.Builder;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.command.models;
package dev.sheldan.abstracto.core.command.models.exception;
import lombok.Builder;
import lombok.Getter;

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.core.command.service;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
public interface ExceptionService {
CommandResult reportExceptionToContext(Throwable exception, CommandContext context, Command command);
}

View File

@@ -4,6 +4,12 @@ public class AbstractoRunTimeException extends RuntimeException {
public AbstractoRunTimeException(String message) {
super(message);
}
public AbstractoRunTimeException() {
super();
}
public AbstractoRunTimeException(Throwable throwable) {
super(throwable);
}
public AbstractoRunTimeException(String message, Throwable cause) {
super(message, cause);

View File

@@ -7,12 +7,10 @@ import java.util.HashMap;
public class ChannelNotFoundException extends AbstractoRunTimeException implements Templatable {
private final Long channelId;
private final Long guildId;
public ChannelNotFoundException(Long channelId, Long guildId) {
super("");
public ChannelNotFoundException(Long channelId) {
super("Channel not found in database");
this.channelId = channelId;
this.guildId = guildId;
}
@Override
@@ -24,7 +22,6 @@ public class ChannelNotFoundException extends AbstractoRunTimeException implemen
public Object getTemplateModel() {
HashMap<String, Long> param = new HashMap<>();
param.put("channelId", this.channelId);
param.put("guildID", this.guildId);
return param;
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.core.exception;
import dev.sheldan.abstracto.templating.Templatable;
import java.util.HashMap;
import java.util.List;
public class EmoteNotFoundInDbException extends AbstractoRunTimeException implements Templatable {
private final Integer emoteId;
public EmoteNotFoundInDbException(Integer emoteId) {
super("");
this.emoteId = emoteId;
}
@Override
public String getTemplateName() {
return "emote_not_found_in_db_exception";
}
@Override
public Object getTemplateModel() {
HashMap<String, Object> param = new HashMap<>();
param.put("emoteId", this.emoteId);
return param;
}
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.core.exception;
import dev.sheldan.abstracto.core.models.exception.EmoteNotUsable;
import dev.sheldan.abstracto.templating.Templatable;
import net.dv8tion.jda.api.entities.Emote;
public class EmoteNotUsableException extends AbstractoRunTimeException implements Templatable {
private EmoteNotUsable model;
public EmoteNotUsableException(Emote emote) {
super("");
this.model = EmoteNotUsable.builder().emote(emote).build();
}
@Override
public String getTemplateName() {
return "emote_not_usable";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.core.exception;
import dev.sheldan.abstracto.core.models.exception.EmoteNotInServerModel;
import dev.sheldan.abstracto.templating.Templatable;
public class ExceptionNotInServerException extends AbstractoRunTimeException implements Templatable {
private EmoteNotInServerModel model;
public ExceptionNotInServerException(Long emoteId) {
super("Emote not available in server");
this.model = EmoteNotInServerModel.builder().emoteId(emoteId).build();
}
@Override
public String getTemplateName() {
return "emote_not_available_in_server_exception";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -4,15 +4,15 @@ import dev.sheldan.abstracto.templating.Templatable;
import java.util.HashMap;
public class GuildException extends AbstractoRunTimeException implements Templatable {
public class GuildNotFoundException extends AbstractoRunTimeException implements Templatable {
private final Long guildId;
public GuildException(String message, Long guildId) {
public GuildNotFoundException(String message, Long guildId) {
super(message);
this.guildId = guildId;
}
public GuildException(Long guildId) {
public GuildNotFoundException(Long guildId) {
super("");
this.guildId = guildId;
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.core.exception;
public class InvalidConditionParametersException extends AbstractoRunTimeException {
public InvalidConditionParametersException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.core.exception;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.exception.RoleDeletedModel;
import dev.sheldan.abstracto.templating.Templatable;
public class RoleDeletedException extends AbstractoRunTimeException implements Templatable {
private RoleDeletedModel model;
public RoleDeletedException(ARole role) {
super("Role has been marked as deleted and cannot be used.");
this.model = RoleDeletedModel.builder().role(role).build();
}
@Override
public String getTemplateName() {
return "";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -7,12 +7,10 @@ import java.util.HashMap;
public class RoleNotFoundInDBException extends AbstractoRunTimeException implements Templatable {
private final Long roleId;
private final Long serverId;
public RoleNotFoundInDBException(Long roleId, Long serverId) {
public RoleNotFoundInDBException(Long roleId) {
super("");
this.roleId = roleId;
this.serverId = serverId;
}
@Override
@@ -24,7 +22,6 @@ public class RoleNotFoundInDBException extends AbstractoRunTimeException impleme
public Object getTemplateModel() {
HashMap<String, Long> param = new HashMap<>();
param.put("roleId", this.roleId);
param.put("serverId", this.serverId);
return param;
}
}

View File

@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
public interface ReactedAddedListener extends FeatureAware {
void executeReactionAdded(CachedMessage message, MessageReaction reaction, AUserInAServer userAdding);
void executeReactionAdded(CachedMessage message, GuildMessageReactionAddEvent event, AUserInAServer userAdding);
}

View File

@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
public interface ReactedRemovedListener extends FeatureAware {
void executeReactionRemoved(CachedMessage message, MessageReaction reaction, AUserInAServer userRemoving);
void executeReactionRemoved(CachedMessage message, GuildMessageReactionRemoveEvent reaction, AUserInAServer userRemoving);
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class ConditionContext {
private List<ConditionContextVariable> expectedVariables;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
@Getter
@Setter
@Builder
public class ConditionContextInstance {
private HashMap<String, Object> parameters;
private String conditionName;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class ConditionContextVariable {
private String name;
private Class type;
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.core.models;
import dev.sheldan.abstracto.core.models.database.AEmote;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Emote;
import java.io.Serializable;
@Getter
@Setter
@Builder
public class FullEmote implements Serializable {
private AEmote fakeEmote;
private Emote emote;
public String getEmoteRepr() {
if(!fakeEmote.getCustom()) {
return fakeEmote.getEmoteKey();
} else if(emote != null) {
return emote.getAsMention();
} else {
return fakeEmote.getEmoteId().toString();
}
}
}

View File

@@ -12,4 +12,12 @@ import net.dv8tion.jda.api.entities.Role;
public class FullRole {
private ARole role;
private Role serverRole;
public String getRoleRepr() {
if(serverRole != null) {
return serverRole.getAsMention();
} else {
return role.getId().toString();
}
}
}

View File

@@ -25,6 +25,7 @@ public class AEmote implements Serializable {
@Column
private String name;
// the way discord calls them and the unicode char for default Tweemoji emotes
@Column
@Setter
private String emoteKey;
@@ -61,6 +62,15 @@ public class AEmote implements Serializable {
this.updated = Instant.now();
}
@Column(name = "changeable")
@Getter
@Setter
@Builder.Default
private boolean changeable = true;
@Transient
private boolean fake;
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.core.models.exception;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class EmoteNotInServerModel {
private Long emoteId;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.models.exception;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Emote;
@Getter
@Setter
@Builder
public class EmoteNotUsable {
private Emote emote;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.models.exception;
import dev.sheldan.abstracto.core.models.database.ARole;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class RoleDeletedModel {
private ARole role;
}

View File

@@ -27,8 +27,11 @@ public interface BotService {
CompletableFuture<Void> deleteMessage(Long channelId, Long messageId);
CompletableFuture<Member> forceReloadMember(Member member);
Optional<Emote> getEmote(Long serverId, AEmote emote);
Optional<TextChannel> getTextChannelFromServer(Guild serverId, Long textChannelId);
Optional<TextChannel> getTextChannelFromServer(Long serverId, Long textChannelId);
Optional<Emote> getEmote(AEmote emote);
Optional<TextChannel> getTextChannelFromServerOptional(Guild serverId, Long textChannelId);
TextChannel getTextChannelFromServer(Guild serverId, Long textChannelId);
Optional<TextChannel> getTextChannelFromServerOptional(Long serverId, Long textChannelId);
TextChannel getTextChannelFromServer(Long serverId, Long textChannelId);
Optional<Guild> getGuildById(Long serverId);
Guild getGuildByIdNullable(Long serverId);
Member getBotInGuild(AServer server);

View File

@@ -22,10 +22,17 @@ public interface ChannelService {
CompletableFuture<Message> sendEmbedToAChannel(MessageEmbed embed, AChannel channel);
CompletableFuture<Message> sendEmbedToChannel(MessageEmbed embed, MessageChannel channel);
List<CompletableFuture<Message>> sendMessageToSendToAChannel(MessageToSend messageToSend, AChannel channel);
CompletableFuture<Message> sendMessageToSendToAChannel(MessageToSend messageToSend, AChannel channel, Integer embedIndex);
List<CompletableFuture<Message>> sendMessageToSendToChannel(MessageToSend messageToSend, MessageChannel textChannel);
Optional<TextChannel> getTextChannelInGuild(Long serverId, Long channelId);
void editMessageInAChannel(MessageToSend messageToSend, AChannel channel, Long messageId);
void editMessageInAChannel(MessageToSend messageToSend, MessageChannel channel, Long messageId);
CompletableFuture<Message> editMessageInAChannelFuture(MessageToSend messageToSend, MessageChannel channel, Long messageId);
CompletableFuture<Message> editEmbedMessageInAChannel(MessageEmbed embedToSend, MessageChannel channel, Long messageId);
CompletableFuture<Message> editTextMessageInAChannel(String text, MessageChannel channel, Long messageId);
List<CompletableFuture<Message>> editMessagesInAChannelFuture(MessageToSend messageToSend, MessageChannel channel, List<Long> messageIds);
CompletableFuture<Message> removeFieldFromMessage(MessageChannel channel, Long messageId, Integer index);
CompletableFuture<Message> removeFieldFromMessage(MessageChannel channel, Long messageId, Integer index, Integer embedIndex);
CompletableFuture<Void> deleteTextChannel(AChannel channel);
CompletableFuture<Void> deleteTextChannel(Long serverId, Long channelId);
List<CompletableFuture<Message>> sendEmbedTemplateInChannel(String templateKey, Object model, MessageChannel channel);

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
public interface ConditionService {
boolean checkConditions(ConditionContextInstance context);
}

View File

@@ -15,9 +15,10 @@ public interface EmoteService {
String getEmoteAsMention(AEmote emote, Long serverId);
String getUsableEmoteOrDefault(Long serverId, String name);
void throwIfEmoteDoesNotExist(String emoteKey, Long serverId);
AEmote getEmoteOrFakeEmote(String emoteKey, Long serverId);
AEmote getEmoteOrDefaultEmote(String emoteKey, Long serverId);
String getDefaultEmote(String emoteKey);
boolean isReactionEmoteAEmote(MessageReaction.ReactionEmote reaction, AEmote storedEmote, Emote actualEmoteInGuild);
boolean isReactionEmoteAEmote(MessageReaction.ReactionEmote reaction, AEmote storedEmote);
Optional<CachedReaction> getReactionFromMessageByEmote(CachedMessage message, AEmote emote);
boolean compareAEmote(AEmote a, AEmote b);
AEmote getFakeEmote(Object object);
}

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User;
@@ -13,6 +15,20 @@ import java.util.concurrent.CompletableFuture;
public interface MessageService {
void addReactionToMessage(String emoteKey, Long serverId, Message message);
CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message);
CompletableFuture<Void> addReactionToMessageWithFuture(AEmote emote, Long serverId, Message message);
CompletableFuture<Void> addReactionToMessageWithFuture(Long emoteId, Long serverId, Message message);
CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Message message);
CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Message message);
CompletableFuture<Void> removeReactionFromMessageWithFuture(Integer emoteId, Long serverId, Message message);
CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Long serverId, Message message);
CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Member member);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Member member);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Long userId);
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId);
CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
List<CompletableFuture<Void>> addReactionsToMessageWithFuture(List<String> emoteKeys, Long serverId, Message message);
CompletableFuture<Void> deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId);
CompletableFuture<Message> createStatusMessage(MessageToSend messageToSend, AChannel channel);

View File

@@ -7,10 +7,17 @@ import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface RoleService {
void addRoleToUser(AUserInAServer aUserInAServer, ARole role);
CompletableFuture<Void> addRoleToUserFuture(AUserInAServer aUserInAServer, ARole role);
void addRoleToMember(Member member, ARole role);
CompletableFuture<Void> addRoleToMemberFuture(Member member, ARole role);
void removeRoleFromMember(Member member, ARole role);
CompletableFuture<Void> removeRoleFromMemberFuture(Member member, ARole role);
void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role);
CompletableFuture<Void> removeRoleFromUserFuture(AUserInAServer aUserInAServer, ARole role);
void markDeleted(Role role, AServer server);
void markDeleted(Long id, AServer server);
Role getRoleFromGuild(ARole role);
@@ -19,4 +26,5 @@ public interface RoleService {
boolean memberHasRole(Member member, Role role);
boolean memberHasRole(Member member, ARole role);
boolean isRoleInServer(ARole role);
boolean canBotInteractWithRole(ARole role);
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.ConditionContext;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
public interface SystemCondition {
boolean checkCondition(ConditionContextInstance conditionContext);
String getConditionName();
ConditionContext getExpectedContext();
}

View File

@@ -7,7 +7,8 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import java.util.Optional;
public interface ChannelManagementService {
Optional<AChannel> loadChannel(Long id);
Optional<AChannel> loadChannelOptional(Long id);
AChannel loadChannel(Long id);
AChannel createChannel(Long id, AChannelType type, AServer server);
AChannel markAsDeleted(Long id);
boolean channelExists(Long id);

View File

@@ -7,16 +7,23 @@ import net.dv8tion.jda.api.entities.Emote;
import java.util.Optional;
public interface EmoteManagementService {
Optional<AEmote> loadEmote(Long id);
AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) ;
AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server) ;
AEmote createDefaultEmote(String name, String emoteKey, Long serverId) ;
AEmote createDefaultEmote(String name, String emoteKey, AServer server) ;
Optional<AEmote> loadEmoteOptional(Integer id);
AEmote loadEmote(Integer id);
AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId, boolean validateName);
AEmote createCustomEmote(String name, AEmote fakeEmote, Long serverId, boolean validateName);
AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server, boolean validateName);
AEmote createDefaultEmote(String name, String emoteKey, Long serverId, boolean validateName);
AEmote createDefaultEmote(String name, String emoteKey, AServer server, boolean validateName);
Optional<AEmote> loadEmoteByName(String name, Long serverId);
Optional<AEmote> loadEmoteByName(String name, AServer server);
AEmote setEmoteToCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) ;
AEmote setEmoteToCustomEmote(String name, Emote emote, Long serverId) ;
AEmote setEmoteToDefaultEmote(String name, String emoteKey, Long serverId) ;
AEmote setEmoteToCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId);
AEmote setEmoteToCustomEmote(String name, Emote emote, Long serverId);
AEmote setEmoteToDefaultEmote(String name, String emoteKey, Long serverId);
AEmote setEmoteToAEmote(String name, AEmote emote, Long serverId);
AEmote createEmote(String name, AEmote emote, Long serverId, boolean validateName);
boolean emoteExists(String name, Long serverId);
boolean emoteExists(Long emoteId);
void deleteEmote(AEmote aEmote);
Optional<AEmote> loadEmote(Long id);
boolean emoteExists(String name, AServer server);
}

View File

@@ -7,6 +7,7 @@ import java.util.Optional;
public interface RoleManagementService {
ARole createRole(Long id, AServer server);
Optional<ARole> findRole(Long id, AServer server);
Optional<ARole> findRoleOptional(Long id);
ARole findRole(Long id);
void markDeleted(ARole role);
}

View File

@@ -3,10 +3,13 @@ package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.models.database.*;
import java.util.List;
import java.util.Optional;
public interface ServerManagementService {
AServer createServer(Long id);
AServer loadOrCreate(Long id);
AServer loadServer(Long id);
Optional<AServer> loadServerOptional(Long id);
void addChannelToServer(AServer server, AChannel channel);
AUserInAServer addUserToServer(AServer server, AUser user);
AUserInAServer addUserToServer(Long serverId, Long userId);

View File

@@ -1,7 +1,6 @@
package dev.sheldan.abstracto.core.utils;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -16,7 +15,6 @@ import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;
@Component
@Slf4j
@@ -38,8 +36,7 @@ public class ContextUtils {
m = clazz.getMethod("builder");
UserInitiatedServerContext.UserInitiatedServerContextBuilder<?, ?> builder = (UserInitiatedServerContext.UserInitiatedServerContextBuilder) m.invoke(null, null);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(message.getServerId(), message.getAuthorId());
Optional<AChannel> channelOptional = channelManagementService.loadChannel(message.getChannelId());
AChannel channel = channelOptional.orElseThrow(() -> new ChannelNotFoundException(message.getChannelId(), message.getServerId()));
AChannel channel = channelManagementService.loadChannel(message.getChannelId());
return builder
.member(guildChannelMember.getMember())
.guild(guildChannelMember.getGuild())

View File

@@ -49,8 +49,7 @@ public class ContextUtilsTest {
AUserInAServer aUserInAServer = AUserInAServer.builder().userReference(AUser.builder().id(AUTHOR_ID).build()).serverReference(server).build();
when(userInServerManagementService.loadUser(eq(SERVER_ID), eq(AUTHOR_ID))).thenReturn(aUserInAServer);
AChannel channel = AChannel.builder().id(CHANNEL_ID).build();
Optional<AChannel> op = Optional.of(channel);
when(channelManagementService.loadChannel(eq(CHANNEL_ID))).thenReturn(op);
when(channelManagementService.loadChannel(eq(CHANNEL_ID))).thenReturn(channel);
}
@Test