mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-28 08:38:52 +00:00
[AB-57] [AB-61] reworked commands and services to work with completable futures and moved the database operations to the very last operation so we have transaction safety in more areas
added some cache annotations to the default repository functions reworked how the undo cations are processed within commands, they are executed in a post command listener when the state is error added a counter id to generate ids to be unique within servers, changed a few tables to be unique within a server added future utils class for wrapping a list of futures into one moved abstracto tables to separate schema in the installer refactored experience gain to work with more futures and delayed database access
This commit is contained in:
@@ -90,6 +90,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
||||
CommandContext.CommandContextBuilder commandContextBuilder = CommandContext.builder()
|
||||
.author(event.getMember())
|
||||
.guild(event.getGuild())
|
||||
.undoActions(new ArrayList<>())
|
||||
.channel(event.getTextChannel())
|
||||
.message(event.getMessage())
|
||||
.jda(event.getJDA())
|
||||
@@ -130,6 +131,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
||||
.channel(event.getTextChannel())
|
||||
.message(event.getMessage())
|
||||
.jda(event.getJDA())
|
||||
.undoActions(commandContext.getUndoActions()) // TODO really do this? it would need to guarantee that its available and usable
|
||||
.userInitiatedContext(rebuildUserContext)
|
||||
.parameters(parsedParameters).build();
|
||||
CommandResult failedResult = CommandResult.fromError(throwable.getMessage(), throwable);
|
||||
@@ -140,7 +142,10 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
||||
commandResult = self.executeCommand(foundCommand, commandContext);
|
||||
}
|
||||
} else {
|
||||
commandResult = CommandResult.fromCondition(conditionResult);
|
||||
// TODO can it be done nicer?
|
||||
if(conditionResult.getException() != null) {
|
||||
throw conditionResult.getException();
|
||||
}
|
||||
}
|
||||
if(commandResult != null) {
|
||||
self.executePostCommandListener(foundCommand, commandContext, commandResult);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.sheldan.abstracto.core.command.post;
|
||||
|
||||
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.execution.ResultState;
|
||||
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
|
||||
import dev.sheldan.abstracto.core.service.UndoActionService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UndoActionPostExecution implements PostCommandExecution {
|
||||
|
||||
@Autowired
|
||||
private UndoActionService undoActionService;
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
|
||||
ResultState result = commandResult.getResult();
|
||||
if(result.equals(ResultState.ERROR) && commandContext.getUndoActions() != null && !commandContext.getUndoActions().isEmpty()) {
|
||||
undoActionService.performActionsFuture(commandContext.getUndoActions()).whenComplete((aVoid, undoThrowable) -> {
|
||||
if(undoThrowable != null) {
|
||||
log.warn("Undo actions failed.", undoThrowable);
|
||||
} else {
|
||||
log.info("Successfully executed undo actions.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,6 @@ public interface CommandRepository extends JpaRepository<ACommand, Long> {
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
Optional<ACommand> findByNameIgnoreCase(String name);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsByNameIgnoreCase(String name);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import dev.sheldan.abstracto.core.models.template.commands.PostTargetModelEntry;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.templating.service.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
@@ -27,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@@ -47,7 +49,7 @@ public class PostTargetCommand extends AbstractConditionableCommand {
|
||||
private ChannelService channelService;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
if(commandContext.getParameters().getParameters().isEmpty()) {
|
||||
PostTargetDisplayModel posttargetDisplayModel = (PostTargetDisplayModel) ContextConverter.fromCommandContext(commandContext, PostTargetDisplayModel.class);
|
||||
AServer server = commandContext.getUserInitiatedContext().getServer();
|
||||
@@ -67,8 +69,8 @@ public class PostTargetCommand extends AbstractConditionableCommand {
|
||||
postTargetEntries.add(postTargetEntry);
|
||||
}
|
||||
});
|
||||
channelService.sendEmbedTemplateInChannel(POST_TARGET_SHOW_TARGETS, posttargetDisplayModel, commandContext.getChannel());
|
||||
return CommandResult.fromSuccess();
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(POST_TARGET_SHOW_TARGETS, posttargetDisplayModel, commandContext.getChannel()))
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
String targetName = (String) commandContext.getParameters().getParameters().get(0);
|
||||
if(!postTargetService.validPostTarget(targetName)) {
|
||||
@@ -78,7 +80,7 @@ public class PostTargetCommand extends AbstractConditionableCommand {
|
||||
Guild guild = channel.getGuild();
|
||||
postTargetManagement.createOrUpdate(targetName, guild.getIdLong(), channel.getIdLong());
|
||||
log.info("Setting posttarget {} in {} to {}", targetName, guild.getIdLong(), channel.getId());
|
||||
return CommandResult.fromSuccess();
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,6 +93,7 @@ public class PostTargetCommand extends AbstractConditionableCommand {
|
||||
.name("posttarget")
|
||||
.module(ChannelsModuleInterface.CHANNELS)
|
||||
.parameters(parameters)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.help(helpInfo)
|
||||
.templated(true)
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Disable extends AbstractConditionableCommand {
|
||||
@@ -40,12 +41,13 @@ public class Disable extends AbstractConditionableCommand {
|
||||
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
if(commandContext.getParameters().getParameters().isEmpty()) {
|
||||
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
|
||||
model.setFeatures(featureConfigService.getAllFeatures());
|
||||
String response = templateService.renderTemplate("disable_features_response", model);
|
||||
channelService.sendTextToChannelNoFuture(response, commandContext.getChannel());
|
||||
return channelService.sendTextToChannel(response, commandContext.getChannel())
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
} else {
|
||||
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
|
||||
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(flagKey);
|
||||
@@ -55,8 +57,8 @@ public class Disable extends AbstractConditionableCommand {
|
||||
featureFlagService.disableFeature(featureDisplay, commandContext.getUserInitiatedContext().getServer())
|
||||
);
|
||||
}
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
}
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,6 +70,7 @@ public class Disable extends AbstractConditionableCommand {
|
||||
.name("disable")
|
||||
.module(ConfigModuleInterface.CONFIG)
|
||||
.parameters(parameters)
|
||||
.async(true)
|
||||
.help(helpInfo)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Enable extends AbstractConditionableCommand {
|
||||
@@ -40,18 +41,19 @@ public class Enable extends AbstractConditionableCommand {
|
||||
private TemplateService templateService;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
if(commandContext.getParameters().getParameters().isEmpty()) {
|
||||
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
|
||||
model.setFeatures(featureConfigService.getAllFeatures());
|
||||
String response = templateService.renderTemplate("enable_features_response", model);
|
||||
channelService.sendTextToChannelNoFuture(response, commandContext.getChannel());
|
||||
return channelService.sendTextToChannel(response, commandContext.getChannel())
|
||||
.thenApply(message -> CommandResult.fromSuccess());
|
||||
} else {
|
||||
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
|
||||
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(flagKey);
|
||||
FeatureValidationResult featureSetup = featureConfigService.validateFeatureSetup(feature, commandContext.getUserInitiatedContext().getServer());
|
||||
if(Boolean.FALSE.equals(featureSetup.getValidationResult())) {
|
||||
channelService.sendTextToChannelNoFuture(templateService.renderTemplatable(featureSetup), commandContext.getChannel());
|
||||
channelService.sendTextToChannelNotAsync(templateService.renderTemplatable(featureSetup), commandContext.getChannel());
|
||||
}
|
||||
featureFlagService.enableFeature(feature, commandContext.getUserInitiatedContext().getServer());
|
||||
if(feature.getRequiredFeatures() != null) {
|
||||
@@ -59,8 +61,8 @@ public class Enable extends AbstractConditionableCommand {
|
||||
featureFlagService.enableFeature(featureDisplay, commandContext.getUserInitiatedContext().getServer());
|
||||
});
|
||||
}
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
}
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,6 +74,7 @@ public class Enable extends AbstractConditionableCommand {
|
||||
.name("enable")
|
||||
.module(ConfigModuleInterface.CONFIG)
|
||||
.parameters(parameters)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.templated(true)
|
||||
.help(helpInfo)
|
||||
|
||||
@@ -9,10 +9,14 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureEnum;
|
||||
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
|
||||
import dev.sheldan.abstracto.core.models.template.commands.PingModel;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.MessageService;
|
||||
import dev.sheldan.abstracto.templating.service.TemplateService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Service
|
||||
public class Ping implements Command {
|
||||
|
||||
@@ -21,13 +25,18 @@ public class Ping implements Command {
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
long ping = commandContext.getJda().getGatewayPing();
|
||||
PingModel model = PingModel.builder().latency(ping).build();
|
||||
String text = templateService.renderTemplate(PING_TEMPLATE, model);
|
||||
commandContext.getChannel().sendMessage(text).queue();
|
||||
return CommandResult.fromSuccess();
|
||||
return channelService.sendTextTemplateInChannel(PING_TEMPLATE, model, commandContext.getChannel())
|
||||
.thenApply(message -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,6 +46,7 @@ public class Ping implements Command {
|
||||
.name("ping")
|
||||
.module(UtilityModuleInterface.UTILITY)
|
||||
.templated(true)
|
||||
.async(true)
|
||||
.help(helpInfo)
|
||||
.causesReaction(false)
|
||||
.build();
|
||||
|
||||
@@ -16,5 +16,6 @@ public interface ChannelGroupRepository extends JpaRepository<AChannelGroup, Lon
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<AChannelGroup> findByServer(AServer server);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsByGroupNameAndServer(String name, AServer server);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface ChannelRepository extends JpaRepository<AChannel, Long> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<AChannel> findAll();
|
||||
Optional<AChannel> findById(@NonNull Long aLong);
|
||||
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsById(@NonNull Long aLong);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ public interface ConfigRepository extends JpaRepository<AConfig, Long> {
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
AConfig findAConfigByServerIdAndName(Long serverId, String name);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsAConfigByServerIdAndName(Long serverId, String name);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsAConfigByServerAndName(AServer server, String name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.CounterId;
|
||||
import dev.sheldan.abstracto.core.models.database.Counter;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface CounterRepository extends JpaRepository<Counter, CounterId> {
|
||||
@Query(value = "SELECT next_counter(:counterKey, :serverId)", nativeQuery = true)
|
||||
Long getNewCounterForKey(@Param("serverId") Long serverId, @Param("counterKey") String counterKey);
|
||||
}
|
||||
@@ -2,10 +2,16 @@ package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.ADefaultConfig;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
|
||||
@Repository
|
||||
public interface DefaultConfigRepository extends JpaRepository<ADefaultConfig, Long> {
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
ADefaultConfig findByName(String name);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsByName(String name);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.DefaultEmote;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface DefaultEmoteRepository extends JpaRepository<DefaultEmote, Long> {
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
DefaultEmote getByEmoteKey(String emoteKey);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<DefaultEmote> findAll();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.DefaultFeatureFlag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface DefaultFeatureFlagRepository extends JpaRepository<DefaultFeatureFlag, Long> {
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<DefaultFeatureFlag> findAll();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.DefaultPostTarget;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface DefaultPostTargetRepository extends JpaRepository<DefaultPostTarget, Long> {
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<DefaultPostTarget> findAll();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AEmote;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
@@ -26,4 +28,9 @@ public interface EmoteRepository extends JpaRepository<AEmote, Integer> {
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsByEmoteIdAndServerRef(String emoteId, AServer server);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
Optional<AEmote> findById(@NonNull Integer aLong);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,16 @@ import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
|
||||
import dev.sheldan.abstracto.core.models.database.AFeatureMode;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
|
||||
@Repository
|
||||
public interface FeatureModeRepository extends JpaRepository<AFeatureMode, Long> {
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
AFeatureMode findByFeatureFlag(AFeatureFlag featureFlag);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsByFeatureFlag_ServerAndFeatureFlag_Feature(AServer server, AFeature feature);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ public interface PostTargetRepository extends JpaRepository<PostTarget, Long> {
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
PostTarget findPostTargetByNameAndServerReference(String name, AServer server);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsByNameAndServerReference(String name, AServer server);
|
||||
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<PostTarget> findByServerReference(AServer server);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.ARole;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface RoleRepository extends JpaRepository<ARole, Long> {
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
Optional<ARole> findById(@NonNull Long aLong);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface ServerRepository extends JpaRepository<AServer, Long> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
Optional<AServer> findById(@NonNull Long aLong);
|
||||
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsById(@NonNull Long aLong);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
List<AServer> findAll();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
package dev.sheldan.abstracto.core.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.persistence.QueryHint;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<AUser, Long> {
|
||||
@NotNull
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
Optional<AUser> findById(@NonNull Long aLong);
|
||||
|
||||
@Override
|
||||
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
|
||||
boolean existsById(@NonNull Long aLong);
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ public class ChannelServiceBean implements ChannelService {
|
||||
private TemplateService templateService;
|
||||
|
||||
@Override
|
||||
public void sendTextToAChannelNoFuture(String text, AChannel channel) {
|
||||
public void sendTextToAChannelNotAsync(String text, AChannel channel) {
|
||||
sendTextToAChannel(text, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTextToChannelNoFuture(String text, MessageChannel channel) {
|
||||
public void sendTextToChannelNotAsync(String text, MessageChannel channel) {
|
||||
sendTextToChannel(text, channel);
|
||||
}
|
||||
|
||||
@@ -232,6 +232,12 @@ public class ChannelServiceBean implements ChannelService {
|
||||
return sendMessageToSendToChannel(messageToSend, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> sendTextTemplateInChannel(String templateKey, Object model, MessageChannel channel) {
|
||||
String text = templateService.renderTemplate(templateKey, model);
|
||||
return sendTextToChannel(text, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<TextChannel> createTextChannel(String name, AServer server, Long categoryId) {
|
||||
Optional<Guild> guildById = botService.getGuildById(server.getId());
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package dev.sheldan.abstracto.core.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.repository.CounterRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CounterServiceBean implements CounterService {
|
||||
|
||||
@Autowired
|
||||
private CounterRepository counterRepository;
|
||||
|
||||
@Override
|
||||
public Long getNextCounterValue(AServer server, String key) {
|
||||
return counterRepository.getNewCounterForKey(server.getId(), key);
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,20 @@ package dev.sheldan.abstracto.core.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.ConfiguredEmoteNotUsableException;
|
||||
import dev.sheldan.abstracto.core.exception.EmoteNotInServerException;
|
||||
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;
|
||||
import dev.sheldan.abstracto.core.models.database.AEmote;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.templating.service.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@@ -38,6 +37,9 @@ public class MessageServiceBean implements MessageService {
|
||||
@Autowired
|
||||
private MessageServiceBean self;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Override
|
||||
public void addReactionToMessage(String emoteKey, Long serverId, Message message) {
|
||||
addReactionToMessageWithFuture(emoteKey, serverId, message);
|
||||
@@ -45,25 +47,14 @@ 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);
|
||||
if(guildByIdOptional.isPresent()) {
|
||||
Guild guild = guildByIdOptional.get();
|
||||
if(Boolean.TRUE.equals(emote.getCustom())) {
|
||||
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
|
||||
if(emoteById != null) {
|
||||
return message.addReaction(emoteById).submit();
|
||||
} else {
|
||||
log.error("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guild.getId());
|
||||
throw new ConfiguredEmoteNotUsableException(emote);
|
||||
}
|
||||
} else {
|
||||
return message.addReaction(emote.getEmoteKey()).submit();
|
||||
}
|
||||
} else {
|
||||
log.error("Cannot add reaction, guild not found {}", serverId);
|
||||
throw new GuildNotFoundException(serverId);
|
||||
}
|
||||
Guild guild = botService.getGuildByIdNullable(serverId);
|
||||
return addReactionToMessageWithFuture(emoteKey, guild, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Guild guild, Message message) {
|
||||
AEmote emote = emoteService.getEmoteOrDefaultEmote(emoteKey, guild.getIdLong());
|
||||
return addReactionToMessageWithFuture(emote, guild, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,6 +66,21 @@ public class MessageServiceBean implements MessageService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> addReactionToMessageWithFuture(AEmote emote, Guild guild, Message message) {
|
||||
if(Boolean.TRUE.equals(emote.getCustom())) {
|
||||
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
|
||||
if(emoteById != null) {
|
||||
return message.addReaction(emoteById).submit();
|
||||
} else {
|
||||
log.error("Emote with key {} and id {} for guild {} was not found.", emote.getName() , emote.getEmoteId(), guild.getId());
|
||||
throw new ConfiguredEmoteNotUsableException(emote);
|
||||
}
|
||||
} else {
|
||||
return message.addReaction(emote.getEmoteKey()).submit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> addReactionToMessageWithFuture(Long emoteId, Long serverId, Message message) {
|
||||
Emote emoteById = botService.getInstance().getEmoteById(emoteId);
|
||||
@@ -224,30 +230,31 @@ public class MessageServiceBean implements MessageService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageToUser(AUserInAServer userInAServer, String text, MessageChannel feedbackChannel) {
|
||||
public CompletableFuture<Message> sendMessageToUser(AUserInAServer userInAServer, String text) {
|
||||
Member memberInServer = botService.getMemberInServer(userInAServer);
|
||||
sendMessageToUser(memberInServer.getUser(), text, feedbackChannel);
|
||||
return sendMessageToUser(memberInServer.getUser(), text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageToUser(User user, String text, MessageChannel feedbackChannel) {
|
||||
CompletableFuture<Message> messageFuture = new CompletableFuture<>();
|
||||
|
||||
user.openPrivateChannel().queue(privateChannel ->
|
||||
privateChannel.sendMessage(text).queue(messageFuture::complete, messageFuture::completeExceptionally)
|
||||
);
|
||||
|
||||
messageFuture.exceptionally(e -> {
|
||||
log.warn("Failed to send message. ", e);
|
||||
if(feedbackChannel != null){
|
||||
self.sendFeedbackAboutException(e, feedbackChannel);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
public CompletableFuture<Message> sendTemplateToUser(User user, String template, Object model) {
|
||||
String message = templateService.renderTemplate(template, model);
|
||||
return sendMessageToUser(user, message);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void sendFeedbackAboutException(Throwable e, MessageChannel feedbackChannel) {
|
||||
channelService.sendTextToChannelNoFuture(String.format("Failed to send message: %s", e.getMessage()), feedbackChannel);
|
||||
@Override
|
||||
public CompletableFuture<Void> sendEmbedToUser(User user, String template, Object model) {
|
||||
return user.openPrivateChannel().submit().thenCompose(privateChannel ->
|
||||
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(template, model, privateChannel)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> sendEmbedToUserWithMessage(User user, String template, Object model) {
|
||||
return user.openPrivateChannel().submit().thenCompose(privateChannel ->
|
||||
channelService.sendEmbedTemplateInChannel(template, model, privateChannel).get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> sendMessageToUser(User user, String text) {
|
||||
return user.openPrivateChannel().flatMap(privateChannel -> privateChannel.sendMessage(text)).submit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@ package dev.sheldan.abstracto.core.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.UndoAction;
|
||||
import dev.sheldan.abstracto.core.models.UndoActionInstance;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@@ -20,6 +23,12 @@ public class UndoActionServiceBean implements UndoActionService {
|
||||
|
||||
@Override
|
||||
public void performActions(List<UndoActionInstance> actionsToPerform) {
|
||||
performActionsFuture(actionsToPerform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> performActionsFuture(List<UndoActionInstance> actionsToPerform) {
|
||||
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
actionsToPerform.forEach(undoActionInstance -> {
|
||||
UndoAction action = undoActionInstance.getAction();
|
||||
List<Long> ids = undoActionInstance.getIds();
|
||||
@@ -28,19 +37,20 @@ public class UndoActionServiceBean implements UndoActionService {
|
||||
log.error("Not the correct amount of ids provided for the channel deletion undo action.");
|
||||
return;
|
||||
}
|
||||
deleteChannel(ids.get(0), ids.get(1));
|
||||
futures.add(deleteChannel(ids.get(0), ids.get(1)));
|
||||
} else if(action.equals(UndoAction.DELETE_MESSAGE)) {
|
||||
if(ids.size() != 2) {
|
||||
log.error("Not the correct amount of ids provided for the message deletion undo action.");
|
||||
return;
|
||||
}
|
||||
botService.deleteMessage(ids.get(0), ids.get(1));
|
||||
futures.add(botService.deleteMessage(ids.get(0), ids.get(1)));
|
||||
}
|
||||
});
|
||||
return FutureUtils.toSingleFutureGeneric(futures);
|
||||
}
|
||||
|
||||
private void deleteChannel(Long serverId, Long channelId) {
|
||||
channelService.deleteTextChannel(serverId, channelId).exceptionally(throwable -> {
|
||||
private CompletableFuture<Void> deleteChannel(Long serverId, Long channelId) {
|
||||
return channelService.deleteTextChannel(serverId, channelId).exceptionally(throwable -> {
|
||||
log.error("Failed to execute undo action channel delete for channel {} in server {}", channelId, serverId, throwable);
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.sheldan.abstracto.core.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
@@ -48,10 +49,15 @@ public class UserInServerManagementServiceBean implements UserInServerManagement
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AUserInAServer> loadUser(Long userInServerId) {
|
||||
public Optional<AUserInAServer> loadUserConditional(Long userInServerId) {
|
||||
return userInServerRepository.findById(userInServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AUserInAServer loadUser(Long userInServerId) {
|
||||
return loadUserConditional(userInServerId).orElseThrow(() -> new UserInServerNotFoundException(userInServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AUserInAServer createUserInServer(Member member) {
|
||||
return this.createUserInServer(member.getGuild().getIdLong(), member.getIdLong());
|
||||
|
||||
@@ -8,4 +8,5 @@
|
||||
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
|
||||
<include file="core-tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="core-seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="core-functions/functions.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
|
||||
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
|
||||
<changeSet author="me" id="counter_function" dbms="postgres">
|
||||
<sqlFile encoding="utf8" path="sql/counter_function.sql"
|
||||
relativeToChangelogFile="true"
|
||||
stripComments="false"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
|
||||
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
|
||||
<include file="counter_function.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,30 @@
|
||||
CREATE OR REPLACE FUNCTION next_counter(p_counter_key VARCHAR(255), p_server_id BIGINT)
|
||||
RETURNS bigint as $new_count$
|
||||
DECLARE v_next_count bigint;
|
||||
v_exists int;
|
||||
BEGIN
|
||||
SELECT count(1)
|
||||
FROM COUNTER
|
||||
INTO v_exists
|
||||
WHERE server_reference = p_server_id
|
||||
AND counter_key = p_counter_key;
|
||||
|
||||
IF v_exists >= 1 THEN
|
||||
SELECT MAX(counter) + 1
|
||||
INTO v_next_count
|
||||
FROM counter
|
||||
WHERE server_reference = p_server_id
|
||||
AND counter_key = p_counter_key;
|
||||
|
||||
UPDATE counter
|
||||
SET counter = v_next_count
|
||||
WHERE server_reference = p_server_id
|
||||
AND counter_key = p_counter_key;
|
||||
ELSE
|
||||
v_next_count := 1;
|
||||
INSERT INTO counter (counter_key, server_reference, counter)
|
||||
VALUES (p_counter_key, p_server_id, v_next_count);
|
||||
END IF;
|
||||
RETURN v_next_count;
|
||||
END;
|
||||
$new_count$ LANGUAGE plpgsql;
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
|
||||
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
|
||||
<changeSet author="Sheldan" id="counters-table">
|
||||
<createTable tableName="counter">
|
||||
<column autoIncrement="true" name="counter" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="counter_key" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addPrimaryKey
|
||||
columnNames="counter_key, server_id"
|
||||
constraintName="pk_counter"
|
||||
tableName="counter"/>
|
||||
</changeSet>
|
||||
<changeSet author="Sheldan" id="counter-counter_server">
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="counter" constraintName="fk_counter_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -23,5 +23,6 @@
|
||||
<include file="feature_mode.xml" relativeToChangelogFile="true"/>
|
||||
<include file="lock.xml" relativeToChangelogFile="true"/>
|
||||
<include file="system_config.xml" relativeToChangelogFile="true"/>
|
||||
<include file="users.xml" relativeToChangelogFile="true"/>
|
||||
<include file="auser.xml" relativeToChangelogFile="true"/>
|
||||
<include file="counter.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -23,8 +23,8 @@ public class CommandDisabledCondition implements CommandCondition {
|
||||
ACommand acommand = commandManagementService.findCommandByName(command.getConfiguration().getName());
|
||||
Boolean booleanResult = channelGroupCommandService.isCommandEnabled(acommand, context.getUserInitiatedContext().getChannel());
|
||||
if(!booleanResult) {
|
||||
throw new CommandDisabledException();
|
||||
return ConditionResult.builder().result(true).exception(new CommandDisabledException()).build();
|
||||
}
|
||||
return ConditionResult.builder().result(booleanResult).reason("Command is disabled.").build();
|
||||
return ConditionResult.builder().result(true).reason("Command is disabled.").build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public class CommandDisallowedCondition implements CommandCondition {
|
||||
}
|
||||
}
|
||||
List<Role> allowedRoles = roleService.getRolesFromGuild(commandForServer.getAllowedRoles());
|
||||
throw new InsufficientPermissionException(allowedRoles);
|
||||
InsufficientPermissionException exception = new InsufficientPermissionException(allowedRoles);
|
||||
return ConditionResult.builder().result(false).exception(exception).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.sheldan.abstracto.core.command.condition;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -10,4 +11,6 @@ import lombok.Setter;
|
||||
public class ConditionResult {
|
||||
private boolean result;
|
||||
private String reason;
|
||||
private Object additionalInfo;
|
||||
private AbstractoRunTimeException exception;
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ public class FeatureEnabledCondition implements CommandCondition {
|
||||
public ConditionResult shouldExecute(CommandContext context, Command command) {
|
||||
FeatureEnum feature = command.getFeature();
|
||||
boolean featureFlagValue = true;
|
||||
String reason = "";
|
||||
if(feature != null) {
|
||||
featureFlagValue = featureFlagService.getFeatureFlagValue(feature, context.getGuild().getIdLong());
|
||||
if(!featureFlagValue) {
|
||||
throw new FeatureDisabledException(featureConfigService.getFeatureDisplayForFeature(feature));
|
||||
FeatureDisabledException exception = new FeatureDisabledException(featureConfigService.getFeatureDisplayForFeature(command.getFeature()));
|
||||
return ConditionResult.builder().result(false).exception(exception).build();
|
||||
}
|
||||
}
|
||||
return ConditionResult.builder().reason(reason).result(featureFlagValue).build();
|
||||
return ConditionResult.builder().result(true).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ public class ImmuneUserCondition implements CommandCondition {
|
||||
Member member = any.get();
|
||||
for (ARole role : commandForServer.getImmuneRoles()) {
|
||||
if (roleService.memberHasRole(member, role)) {
|
||||
throw new ImmuneUserException(roleService.getRoleFromGuild(role));
|
||||
ImmuneUserException exception = new ImmuneUserException(roleService.getRoleFromGuild(role));
|
||||
return ConditionResult.builder().result(false).exception(exception).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ public class AbstractoTemplatedException extends AbstractoRunTimeException imple
|
||||
this.templateKey = templateKey;
|
||||
}
|
||||
|
||||
public AbstractoTemplatedException(String message, String templateKey, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.templateKey = templateKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return templateKey;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.sheldan.abstracto.core.command.execution;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.config.Parameters;
|
||||
import dev.sheldan.abstracto.core.models.UndoActionInstance;
|
||||
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
@@ -11,6 +12,8 @@ import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -22,4 +25,5 @@ public class CommandContext {
|
||||
private UserInitiatedServerContext userInitiatedContext;
|
||||
private Parameters parameters;
|
||||
private JDA jda;
|
||||
private List<UndoActionInstance> undoActions;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.sheldan.abstracto.core.command.execution;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
|
||||
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -34,4 +35,21 @@ public class ContextConverter {
|
||||
}
|
||||
throw new AbstractoRunTimeException("Failed to create model from context");
|
||||
}
|
||||
|
||||
public static <T extends SlimUserInitiatedServerContext> SlimUserInitiatedServerContext slimFromCommandContext(CommandContext commandContext, Class<T> clazz) {
|
||||
Method m = null;
|
||||
try {
|
||||
m = clazz.getMethod("builder");
|
||||
SlimUserInitiatedServerContext.SlimUserInitiatedServerContextBuilder<?, ?> builder = (SlimUserInitiatedServerContext.SlimUserInitiatedServerContextBuilder) m.invoke(null, null);
|
||||
return builder
|
||||
.member(commandContext.getAuthor())
|
||||
.guild(commandContext.getGuild())
|
||||
.message(commandContext.getMessage())
|
||||
.channel(commandContext.getChannel())
|
||||
.build();
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
log.error("Failed to execute builder method", e);
|
||||
}
|
||||
throw new AbstractoRunTimeException("Failed to create model from context");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ public class InteractiveUtils {
|
||||
public void sendTimeoutMessage(Long serverId, Long channelId) {
|
||||
String s = templateService.renderSimpleTemplate("setup_configuration_timeout");
|
||||
Optional<TextChannel> channelOptional = channelService.getTextChannelInGuild(serverId, channelId);
|
||||
channelOptional.ifPresent(channel -> channelService.sendTextToChannelNoFuture(s, channel));
|
||||
channelOptional.ifPresent(channel -> channelService.sendTextToChannelNotAsync(s, channel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package dev.sheldan.abstracto.core.models;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class CounterId implements Serializable {
|
||||
@Column(name = "server_id")
|
||||
private Long serverId;
|
||||
@Column(name = "counter_key")
|
||||
private String counterKey;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CounterId counterId = (CounterId) o;
|
||||
return Objects.equals(serverId, counterId.serverId) &&
|
||||
Objects.equals(counterKey, counterId.counterKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(serverId, counterKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package dev.sheldan.abstracto.core.models;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
public class ServerSpecificId implements Serializable {
|
||||
@Column(name = "server_id")
|
||||
private Long serverId;
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ServerSpecificId that = (ServerSpecificId) o;
|
||||
return Objects.equals(serverId, that.serverId) &&
|
||||
Objects.equals(id, that.id);
|
||||
}
|
||||
|
||||
public ServerSpecificId() {
|
||||
}
|
||||
|
||||
public ServerSpecificId(Long serverId, Long id) {
|
||||
this.serverId = serverId;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(serverId, id);
|
||||
}
|
||||
|
||||
public Long getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(Long serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.sheldan.abstracto.core.models.context;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Setter
|
||||
public class SlimServerContext {
|
||||
private Guild guild;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package dev.sheldan.abstracto.core.models.context;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
public class SlimUserInitiatedServerContext extends SlimServerContext {
|
||||
private MessageChannel channel;
|
||||
private Member member;
|
||||
private Message message;
|
||||
}
|
||||
@@ -11,7 +11,8 @@ import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
||||
|
||||
@Getter @NoArgsConstructor
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
public class UserInitiatedServerContext extends ServerContext {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.sheldan.abstracto.core.models.database;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.CounterId;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "counter")
|
||||
@Getter
|
||||
@Builder
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Cacheable
|
||||
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
public class Counter {
|
||||
|
||||
@EmbeddedId
|
||||
private CounterId counterId;
|
||||
|
||||
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||
@MapsId("serverId")
|
||||
@JoinColumn(name = "serverReference", referencedColumnName = "id", nullable = false)
|
||||
private AServer server;
|
||||
|
||||
@Column(name = "counter")
|
||||
private Long counter;
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface ChannelService {
|
||||
void sendTextToAChannelNoFuture(String text, AChannel channel);
|
||||
void sendTextToChannelNoFuture(String text, MessageChannel channel);
|
||||
void sendTextToAChannelNotAsync(String text, AChannel channel);
|
||||
void sendTextToChannelNotAsync(String text, MessageChannel channel);
|
||||
CompletableFuture<Message> sendTextToAChannel(String text, AChannel channel);
|
||||
CompletableFuture<Message> sendMessageToAChannel(Message message, AChannel channel);
|
||||
CompletableFuture<Message> sendMessageToChannel(Message message, MessageChannel channel);
|
||||
@@ -36,6 +36,7 @@ public interface ChannelService {
|
||||
CompletableFuture<Void> deleteTextChannel(AChannel channel);
|
||||
CompletableFuture<Void> deleteTextChannel(Long serverId, Long channelId);
|
||||
List<CompletableFuture<Message>> sendEmbedTemplateInChannel(String templateKey, Object model, MessageChannel channel);
|
||||
CompletableFuture<Message> sendTextTemplateInChannel(String templateKey, Object model, MessageChannel channel);
|
||||
|
||||
CompletableFuture<TextChannel> createTextChannel(String name, AServer server, Long categoryId);
|
||||
Optional<TextChannel> getChannelFromAChannel(AChannel channel);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package dev.sheldan.abstracto.core.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
|
||||
public interface CounterService {
|
||||
Long getNextCounterValue(AServer server, String key);
|
||||
}
|
||||
@@ -4,10 +4,7 @@ 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;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -15,7 +12,9 @@ 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(String emoteKey, Guild guild, Message message);
|
||||
CompletableFuture<Void> addReactionToMessageWithFuture(AEmote emote, Long serverId, Message message);
|
||||
CompletableFuture<Void> addReactionToMessageWithFuture(AEmote emote, Guild guild, 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);
|
||||
@@ -36,6 +35,9 @@ public interface MessageService {
|
||||
CompletableFuture<Long> createStatusMessageId(MessageToSend messageToSend, MessageChannel channel);
|
||||
void updateStatusMessage(AChannel channel, Long messageId, MessageToSend messageToSend);
|
||||
void updateStatusMessage(MessageChannel channel, Long messageId, MessageToSend messageToSend);
|
||||
void sendMessageToUser(AUserInAServer userInAServer, String text, MessageChannel feedbackChannel);
|
||||
void sendMessageToUser(User user, String text, MessageChannel feedbackChannel);
|
||||
CompletableFuture<Message> sendMessageToUser(AUserInAServer userInAServer, String text);
|
||||
CompletableFuture<Message> sendTemplateToUser(User user, String template, Object model);
|
||||
CompletableFuture<Void> sendEmbedToUser(User user, String template, Object model);
|
||||
CompletableFuture<Message> sendEmbedToUserWithMessage(User user, String template, Object model);
|
||||
CompletableFuture<Message> sendMessageToUser(User user, String text);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package dev.sheldan.abstracto.core.service;
|
||||
import dev.sheldan.abstracto.core.models.UndoActionInstance;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface UndoActionService {
|
||||
void performActions(List<UndoActionInstance> actionsToPerform);
|
||||
CompletableFuture<Void> performActionsFuture(List<UndoActionInstance> actionsToPerform);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ public interface UserInServerManagementService {
|
||||
AUserInAServer loadUser(Long serverId, Long userId);
|
||||
AUserInAServer loadUser(AServer server, AUser user);
|
||||
AUserInAServer loadUser(Member member);
|
||||
Optional<AUserInAServer> loadUser(Long userInServerId);
|
||||
Optional<AUserInAServer> loadUserConditional(Long userInServerId);
|
||||
AUserInAServer loadUser(Long userInServerId);
|
||||
AUserInAServer createUserInServer(Member member);
|
||||
AUserInAServer createUserInServer(Long guildId, Long userId);
|
||||
List<AUserInAServer> getUserInAllServers(Long userId);
|
||||
|
||||
@@ -1,16 +1,34 @@
|
||||
package dev.sheldan.abstracto.core.utils;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@Slf4j
|
||||
public class CompletableFutureList<T> {
|
||||
private CompletableFuture<Void> mainFuture;
|
||||
private List<CompletableFuture<T>> futures;
|
||||
|
||||
public CompletableFutureList(List<CompletableFuture<T>> futures) {
|
||||
this.mainFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
this.futures = futures;
|
||||
}
|
||||
|
||||
public List<T> getObjects() {
|
||||
List<T> result = new ArrayList<>();
|
||||
futures.forEach(future -> {
|
||||
if(!future.isCompletedExceptionally()) {
|
||||
result.add(future.join());
|
||||
} else {
|
||||
log.warn("Future completed with exception {}.", future.join());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.core.utils;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@NoArgsConstructor
|
||||
public class FutureUtils {
|
||||
|
||||
public static <T> CompletableFuture<Void> toSingleFutureGeneric(List<CompletableFuture<T>> futures) {
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user