diff --git a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-impl/src/main/java/dev/sheldan/abstracto/assignableroles/listener/AssignableRoleButtonClickedListener.java b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-impl/src/main/java/dev/sheldan/abstracto/assignableroles/listener/AssignableRoleButtonClickedListener.java index d955af754..42e9cb780 100644 --- a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-impl/src/main/java/dev/sheldan/abstracto/assignableroles/listener/AssignableRoleButtonClickedListener.java +++ b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-impl/src/main/java/dev/sheldan/abstracto/assignableroles/listener/AssignableRoleButtonClickedListener.java @@ -219,7 +219,7 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene @Override public Boolean handlesEvent(ButtonClickedListenerModel model) { - return model.getOrigin().equals(AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN); + return AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN.equals(model.getOrigin()); } @Override diff --git a/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/listener/MessageEmbedDeleteButtonClickedListener.java b/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/listener/MessageEmbedDeleteButtonClickedListener.java index 7b851639d..dc3d159ec 100644 --- a/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/listener/MessageEmbedDeleteButtonClickedListener.java +++ b/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/listener/MessageEmbedDeleteButtonClickedListener.java @@ -77,7 +77,7 @@ public class MessageEmbedDeleteButtonClickedListener implements ButtonClickedLis @Override public Boolean handlesEvent(ButtonClickedListenerModel model) { - return model.getOrigin().equals(MessageEmbedServiceBean.MESSAGE_EMBED_DELETE_ORIGIN); + return MessageEmbedServiceBean.MESSAGE_EMBED_DELETE_ORIGIN.equals(model.getOrigin()); } @Override diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Mutes.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Mutes.java new file mode 100644 index 000000000..597dd4691 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Mutes.java @@ -0,0 +1,116 @@ +package dev.sheldan.abstracto.moderation.command; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.execution.CommandContext; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.command.execution.ContextConverter; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.service.ChannelService; +import dev.sheldan.abstracto.core.service.PaginatorService; +import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import dev.sheldan.abstracto.core.templating.service.TemplateService; +import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition; +import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; +import dev.sheldan.abstracto.moderation.converter.MuteEntryConverter; +import dev.sheldan.abstracto.moderation.model.template.command.MuteEntry; +import dev.sheldan.abstracto.moderation.model.template.command.MutesModel; +import dev.sheldan.abstracto.moderation.service.management.MuteManagementService; +import net.dv8tion.jda.api.entities.Member; +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.concurrent.CompletableFuture; + +@Component +public class Mutes extends AbstractConditionableCommand { + + private static final String NO_MUTES_TEMPLATE_KEY = "mutes_no_mutes_found"; + private static final String MUTES_DISPLAY_TEMPLATE_KEY = "mutes_display_response"; + @Autowired + private MuteManagementService muteManagementService; + + @Autowired + private ServerManagementService serverManagementService; + + @Autowired + private UserInServerManagementService userInServerManagementService; + + @Autowired + private TemplateService templateService; + + @Autowired + private ChannelService channelService; + + @Autowired + private MuteEntryConverter muteEntryConverter; + + @Autowired + private Mutes self; + + @Autowired + private PaginatorService paginatorService; + + @Override + public CompletableFuture executeAsync(CommandContext commandContext) { + List mutesToDisplay; + if(commandContext.getParameters().getParameters().isEmpty()) { + AServer server = serverManagementService.loadServer(commandContext.getGuild().getIdLong()); + mutesToDisplay = muteManagementService.getAllMutes(server); + } else { + Member member = (Member) commandContext.getParameters().getParameters().get(0); + if(!member.getGuild().equals(commandContext.getGuild())) { + throw new EntityGuildMismatchException(); + } + mutesToDisplay = muteManagementService.getAllMutesOf(userInServerManagementService.loadOrCreateUser(member)); + } + if(mutesToDisplay.isEmpty()) { + MessageToSend messageToSend = templateService.renderEmbedTemplate(NO_MUTES_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong()); + return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel())) + .thenApply(unused -> CommandResult.fromSuccess()); + } else { + return muteEntryConverter.fromMutes(mutesToDisplay) + .thenCompose(muteEntries -> self.renderMutes(commandContext, muteEntries) + .thenApply(unused -> CommandResult.fromIgnored())); + } + } + + @Transactional + public CompletableFuture renderMutes(CommandContext commandContext, List mutes) { + MutesModel model = (MutesModel) ContextConverter.slimFromCommandContext(commandContext, MutesModel.class); + model.setMutes(mutes); + return paginatorService.createPaginatorFromTemplate(MUTES_DISPLAY_TEMPLATE_KEY, model, commandContext.getChannel(), commandContext.getAuthor().getIdLong()); + } + + @Override + public CommandConfiguration getConfiguration() { + List parameters = new ArrayList<>(); + parameters.add(Parameter.builder().name("member").templated(true).type(Member.class).optional(true).build()); + HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); + return CommandConfiguration.builder() + .name("mutes") + .module(ModerationModuleDefinition.MODERATION) + .templated(true) + .supportsEmbedException(true) + .async(true) + .causesReaction(true) + .parameters(parameters) + .help(helpInfo) + .build(); + } + + @Override + public FeatureDefinition getFeature() { + return ModerationFeatureDefinition.MUTING; + } +} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Warnings.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Warnings.java index a5fd0c614..290250bab 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Warnings.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/Warnings.java @@ -1,7 +1,5 @@ package dev.sheldan.abstracto.moderation.command; -import com.jagrosh.jdautilities.commons.waiter.EventWaiter; -import com.jagrosh.jdautilities.menu.Paginator; import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; import dev.sheldan.abstracto.core.command.config.CommandConfiguration; import dev.sheldan.abstracto.core.command.config.HelpInfo; @@ -38,7 +36,7 @@ import java.util.concurrent.CompletableFuture; @Component public class Warnings extends AbstractConditionableCommand { - public static final String WARNINGS_RESPONSE_TEMPLATE = "warnings_response"; + public static final String WARNINGS_RESPONSE_TEMPLATE = "warnings_display_response"; public static final String NO_WARNINGS_TEMPLATE_KEY = "warnings_no_warnings_found"; @Autowired private WarnManagementService warnManagementService; @@ -52,9 +50,6 @@ public class Warnings extends AbstractConditionableCommand { @Autowired private PaginatorService paginatorService; - @Autowired - private EventWaiter eventWaiter; - @Autowired private ServerManagementService serverManagementService; @@ -86,22 +81,20 @@ public class Warnings extends AbstractConditionableCommand { .thenApply(unused -> CommandResult.fromSuccess()); } else { - return warnEntryConverter.fromWarnings(warnsToDisplay).thenApply(warnEntries -> { - self.renderWarnings(commandContext, warnEntries); - return CommandResult.fromSuccess(); - }); + return warnEntryConverter.fromWarnings(warnsToDisplay) + .thenCompose(warnEntries -> self.renderWarnings(commandContext, warnEntries)) + .thenApply(unused -> CommandResult.fromIgnored()); } } @Transactional - public void renderWarnings(CommandContext commandContext, List warnEntries) { + public CompletableFuture renderWarnings(CommandContext commandContext, List warnEntries) { WarningsModel model = (WarningsModel) ContextConverter.slimFromCommandContext(commandContext, WarningsModel.class); model.setWarnings(warnEntries); - Paginator paginator = paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, eventWaiter, commandContext.getGuild().getIdLong()); - paginator.display(commandContext.getChannel()); + return paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, commandContext.getChannel(), commandContext.getAuthor().getIdLong()); } @Override diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/MuteEntryConverter.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/MuteEntryConverter.java new file mode 100644 index 000000000..c07cffa7a --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/MuteEntryConverter.java @@ -0,0 +1,93 @@ +package dev.sheldan.abstracto.moderation.converter; + +import dev.sheldan.abstracto.core.models.FutureMemberPair; +import dev.sheldan.abstracto.core.models.MemberDisplayModel; +import dev.sheldan.abstracto.core.models.ServerSpecificId; +import dev.sheldan.abstracto.core.service.MemberService; +import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; +import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.moderation.model.database.Mute; +import dev.sheldan.abstracto.moderation.model.template.command.MuteEntry; +import dev.sheldan.abstracto.moderation.service.management.MuteManagementService; +import net.dv8tion.jda.api.entities.Member; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +@Component +public class MuteEntryConverter { + + @Autowired + private MemberService memberService; + + @Autowired + private UserInServerManagementService userInServerManagementService; + + @Autowired + private MuteManagementService muteManagementService; + + @Autowired + private MuteEntryConverter self; + + public CompletableFuture> fromMutes(List mutes) { + Map loadedMutes = new HashMap<>(); + List> allFutures = new ArrayList<>(); + mutes.forEach(mute -> { + CompletableFuture mutingMemberFuture = memberService.getMemberInServerAsync(mute.getMutingUser()); + CompletableFuture mutedMemberFuture = memberService.getMemberInServerAsync(mute.getMutedUser()); + FutureMemberPair futurePair = FutureMemberPair + .builder() + .firstMember(mutingMemberFuture) + .secondMember(mutedMemberFuture) + .build(); + loadedMutes.put(mute.getMuteId(), futurePair); + allFutures.add(mutingMemberFuture); + allFutures.add(mutedMemberFuture); + }); + CompletableFuture> future = new CompletableFuture<>(); + FutureUtils.toSingleFutureGeneric(allFutures) + .whenComplete((unused, throwable) -> future.complete(self.loadFullMuteEntries(loadedMutes))) + .exceptionally(throwable -> { + future.completeExceptionally(throwable); + return null; + }); + return future; + } + + @Transactional + public List loadFullMuteEntries(Map loadedMuteInfo) { + List entries = new ArrayList<>(); + List muteIds = new ArrayList<>(loadedMuteInfo.keySet()); + muteIds.sort(Comparator.comparing(ServerSpecificId::getId)); + muteIds.forEach(muteInfo -> { + FutureMemberPair memberPair = loadedMuteInfo.get(muteInfo); + Mute mute = muteManagementService.findMute(muteInfo.getId(), muteInfo.getServerId()); + Member mutedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null; + MemberDisplayModel mutedUser = MemberDisplayModel + .builder() + .member(mutedMember) + .userId(mute.getMutedUser().getUserReference().getId()) + .build(); + + Member mutingMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null; + MemberDisplayModel mutingUser = MemberDisplayModel + .builder() + .member(mutingMember) + .userId(mute.getMutingUser().getUserReference().getId()) + .build(); + MuteEntry entry = MuteEntry + .builder() + .mutedUser(mutedUser) + .mutingUser(mutingUser) + .mute(mute) + .muteDuration(Duration.between(mute.getMuteDate(), mute.getMuteTargetDate())) + .build(); + entries.add(entry); + }); + return entries; + } +} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/WarnEntryConverter.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/WarnEntryConverter.java index 55596268d..cbc64bbaa 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/WarnEntryConverter.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/converter/WarnEntryConverter.java @@ -14,10 +14,7 @@ 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.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; @Component @@ -38,7 +35,6 @@ public class WarnEntryConverter { public CompletableFuture> fromWarnings(List warnings) { Map loadedWarnings = new HashMap<>(); List> allFutures = new ArrayList<>(); - // TODO maybe optimize to not need to look into the cache twice warnings.forEach(warning -> { CompletableFuture warningMemberFuture = memberService.getMemberInServerAsync(warning.getWarningUser()); CompletableFuture warnedMemberFuture = memberService.getMemberInServerAsync(warning.getWarnedUser()); @@ -59,8 +55,10 @@ public class WarnEntryConverter { @Transactional public List loadFullWarnEntries(Map loadedWarnInfo) { + List warnIds = new ArrayList<>(loadedWarnInfo.keySet()); + warnIds.sort(Comparator.comparing(ServerSpecificId::getId)); List entries = new ArrayList<>(); - loadedWarnInfo.keySet().forEach(warning -> { + warnIds.forEach(warning -> { Warning warn = warnManagementService.findById(warning.getId(), warning.getServerId()); FutureMemberPair memberPair = loadedWarnInfo.get(warning); Member warnedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null; diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/repository/MuteRepository.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/repository/MuteRepository.java index abff39d73..7b57f5add 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/repository/MuteRepository.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/repository/MuteRepository.java @@ -1,6 +1,7 @@ package dev.sheldan.abstracto.moderation.repository; import dev.sheldan.abstracto.core.models.ServerSpecificId; +import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.moderation.model.database.Mute; import org.jetbrains.annotations.NotNull; @@ -18,6 +19,10 @@ public interface MuteRepository extends JpaRepository { List findAllByMutedUserAndMuteEndedFalseOrderByMuteId_IdDesc(AUserInAServer aUserInAServer); + List findAllByMutedUserOrderByMuteId_IdAsc(AUserInAServer aUserInAServer); + + List findAllByServerOrderByMuteId_IdAsc(AServer server); + @NotNull Optional findByMuteId_IdAndMuteId_ServerId(Long muteId, Long serverId); } diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java index 7ce736da4..b06563c96 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java @@ -365,7 +365,7 @@ public class MuteServiceBean implements MuteService { @Override public void completelyUnMuteUser(AUserInAServer aUserInAServer) { log.info("Completely unmuting user {} in server {}.", aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId()); - List allMutesOfUser = muteManagementService.getAllMutesOf(aUserInAServer); + List allMutesOfUser = muteManagementService.getAllActiveMutesOf(aUserInAServer); allMutesOfUser.forEach(mute -> { mute.setMuteEnded(true); cancelUnMuteJob(mute); diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBean.java index 243535a2d..df92f2d83 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBean.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBean.java @@ -3,6 +3,7 @@ package dev.sheldan.abstracto.moderation.service.management; import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException; import dev.sheldan.abstracto.core.models.AServerAChannelMessage; import dev.sheldan.abstracto.core.models.ServerSpecificId; +import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.moderation.model.database.Mute; @@ -84,9 +85,19 @@ public class MuteManagementServiceBean implements MuteManagementService { } @Override - public List getAllMutesOf(AUserInAServer aUserInAServer) { + public List getAllActiveMutesOf(AUserInAServer aUserInAServer) { return muteRepository.findAllByMutedUserAndMuteEndedFalseOrderByMuteId_IdDesc(aUserInAServer); } + @Override + public List getAllMutesOf(AUserInAServer aUserInAServer) { + return muteRepository.findAllByMutedUserOrderByMuteId_IdAsc(aUserInAServer); + } + + @Override + public List getAllMutes(AServer server) { + return muteRepository.findAllByServerOrderByMuteId_IdAsc(server); + } + } diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/collection.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/collection.xml new file mode 100644 index 000000000..121b5aadf --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/collection.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/seedData/command.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/seedData/command.xml new file mode 100644 index 000000000..644a4dc23 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/seedData/command.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/seedData/data.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/seedData/data.xml new file mode 100644 index 000000000..c048f6f53 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.10/seedData/data.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/moderation-changeLog.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/moderation-changeLog.xml index d2aab50fa..b06bb6c80 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/moderation-changeLog.xml +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/moderation-changeLog.xml @@ -12,4 +12,5 @@ + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/command/WarningsTest.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/command/WarningsTest.java deleted file mode 100644 index fa5afaa0d..000000000 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/command/WarningsTest.java +++ /dev/null @@ -1,125 +0,0 @@ -package dev.sheldan.abstracto.moderation.command; - -import com.jagrosh.jdautilities.commons.waiter.EventWaiter; -import com.jagrosh.jdautilities.menu.Paginator; -import dev.sheldan.abstracto.core.command.execution.CommandContext; -import dev.sheldan.abstracto.core.command.execution.CommandResult; -import dev.sheldan.abstracto.core.models.database.AServer; -import dev.sheldan.abstracto.core.models.database.AUserInAServer; -import dev.sheldan.abstracto.core.service.PaginatorService; -import dev.sheldan.abstracto.core.service.management.ServerManagementService; -import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; -import dev.sheldan.abstracto.core.test.command.CommandConfigValidator; -import dev.sheldan.abstracto.core.test.command.CommandTestUtilities; -import dev.sheldan.abstracto.moderation.converter.WarnEntryConverter; -import dev.sheldan.abstracto.moderation.model.database.Warning; -import dev.sheldan.abstracto.moderation.model.template.command.WarnEntry; -import dev.sheldan.abstracto.moderation.model.template.command.WarningsModel; -import dev.sheldan.abstracto.moderation.service.management.WarnManagementService; -import net.dv8tion.jda.api.entities.Member; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.*; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class WarningsTest { - - @InjectMocks - private Warnings testUnit; - - @Mock - private WarnManagementService warnManagementService; - - @Mock - private UserInServerManagementService userInServerManagementService; - - @Mock - private WarnEntryConverter warnEntryConverter; - - @Mock - private PaginatorService paginatorService; - - @Mock - private ServerManagementService serverManagementService; - - @Mock - private EventWaiter eventWaiter; - - @Captor - private ArgumentCaptor captor; - - @Mock - private Warnings self; - - private static final Long SERVER_ID = 1L; - - @Test - public void testNoParametersForWarningsCommand(){ - CommandContext noParams = CommandTestUtilities.getNoParameters(); - Warning firstWarning = Mockito.mock(Warning.class); - WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class); - Warning secondWarning = Mockito.mock(Warning.class); - WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class); - List warningsToDisplay = Arrays.asList(firstWarning, secondWarning); - List modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning); - AServer server = Mockito.mock(AServer.class); - when(serverManagementService.loadServer(noParams.getGuild())).thenReturn(server); - when(warnManagementService.getAllWarningsOfServer(server)).thenReturn(warningsToDisplay); - when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings)); - - CompletableFuture result = testUnit.executeAsync(noParams); - CommandTestUtilities.checkSuccessfulCompletionAsync(result); - verify(self, times(1)).renderWarnings(noParams, modelWarnings); - - } - - @Test - public void testWarningsRendering() { - CommandContext noParams = CommandTestUtilities.getNoParameters(); - WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class); - WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class); - Paginator paginator = Mockito.mock(Paginator.class); - when(noParams.getGuild().getIdLong()).thenReturn(SERVER_ID); - when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter), eq(SERVER_ID))).thenReturn(paginator); - List modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning); - testUnit.renderWarnings(noParams, modelWarnings); - WarningsModel warningsModel = captor.getValue(); - Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0)); - Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1)); - } - - @Test - public void testExecuteWarningsForMember(){ - Member member = Mockito.mock(Member.class); - CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member)); - when(member.getGuild()).thenReturn(parameters.getGuild()); - AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class); - Warning firstWarning = Mockito.mock(Warning.class); - WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class); - Warning secondWarning = Mockito.mock(Warning.class); - WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class); - List warningsToDisplay = Arrays.asList(firstWarning, secondWarning); - List modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning); - when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(warnedUser); - when(warnManagementService.getAllWarnsForUser(warnedUser)).thenReturn(warningsToDisplay); - when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings)); - - CompletableFuture result = testUnit.executeAsync(parameters); - CommandTestUtilities.checkSuccessfulCompletionAsync(result); - verify(self, times(1)).renderWarnings(parameters, modelWarnings); - - } - - @Test - public void validateCommand() { - CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration()); - } -} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/MuteServiceBeanTest.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/MuteServiceBeanTest.java index 2ab7a9079..16f161b7d 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/MuteServiceBeanTest.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/MuteServiceBeanTest.java @@ -361,7 +361,7 @@ public class MuteServiceBeanTest { public void testCompletelyUnMuteNotMutedUser() { when(userBeingMuted.getUserReference()).thenReturn(user); when(userBeingMuted.getServerReference()).thenReturn(server); - when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList()); + when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList()); testUnit.completelyUnMuteUser(userBeingMuted); verify(muteManagementService, times(0)).saveMute(any(Mute.class)); } @@ -370,7 +370,7 @@ public class MuteServiceBeanTest { public void testCompletelyUnMuteNotScheduledMuteUser() { when(userBeingMuted.getUserReference()).thenReturn(user); when(userBeingMuted.getServerReference()).thenReturn(server); - when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute)); + when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute)); testUnit.completelyUnMuteUser(userBeingMuted); verify(muteManagementService, times(1)).saveMute(any(Mute.class)); verify(schedulerService, times(0)).stopTrigger(anyString()); @@ -381,7 +381,7 @@ public class MuteServiceBeanTest { when(mute.getTriggerKey()).thenReturn(TRIGGER); when(userBeingMuted.getUserReference()).thenReturn(user); when(userBeingMuted.getServerReference()).thenReturn(server); - when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute)); + when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute)); testUnit.completelyUnMuteUser(userBeingMuted); verify(muteManagementService, times(1)).saveMute(any(Mute.class)); verify(schedulerService, times(1)).stopTrigger(TRIGGER); @@ -392,7 +392,7 @@ public class MuteServiceBeanTest { when(userBeingMuted.getUserReference()).thenReturn(user); when(userBeingMuted.getServerReference()).thenReturn(server); when(mute.getTriggerKey()).thenReturn(TRIGGER); - when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute)); + when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute)); when(userInServerManagementService.loadOrCreateUser(memberBeingMuted)).thenReturn(userBeingMuted); testUnit.completelyUnMuteMember(memberBeingMuted); verify(muteManagementService, times(1)).saveMute(any(Mute.class)); diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBeanTest.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBeanTest.java index 8a6584249..050892b1a 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBeanTest.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementServiceBeanTest.java @@ -126,7 +126,7 @@ public class MuteManagementServiceBeanTest { Mute mute = Mockito.mock(Mute.class); Mute mute2 = Mockito.mock(Mute.class); when(muteRepository.findAllByMutedUserAndMuteEndedFalseOrderByMuteId_IdDesc(userInAServer)).thenReturn(Arrays.asList(mute, mute2)); - List allMutesOf = testUnit.getAllMutesOf(userInAServer); + List allMutesOf = testUnit.getAllActiveMutesOf(userInAServer); Assert.assertEquals(2, allMutesOf.size()); Assert.assertEquals(mute, allMutesOf.get(0)); Assert.assertEquals(mute2, allMutesOf.get(1)); diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/MuteEntry.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/MuteEntry.java new file mode 100644 index 000000000..c5350b604 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/MuteEntry.java @@ -0,0 +1,28 @@ +package dev.sheldan.abstracto.moderation.model.template.command; + +import dev.sheldan.abstracto.core.models.MemberDisplayModel; +import dev.sheldan.abstracto.moderation.model.database.Mute; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.time.Duration; + +@Getter +@Setter +@Builder +public class MuteEntry { + /** + * The {@link Mute} of this entry + */ + private Mute mute; + /** + * The {@link MemberDisplayModel} containing information about the user being muted. The member property is null if the user left the server + */ + private MemberDisplayModel mutedUser; + /** + * The {@link MemberDisplayModel} containing information about the user muting. The member property is null if the user left the server + */ + private MemberDisplayModel mutingUser; + private Duration muteDuration; +} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/MutesModel.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/MutesModel.java new file mode 100644 index 000000000..9d2e46aa1 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/MutesModel.java @@ -0,0 +1,15 @@ +package dev.sheldan.abstracto.moderation.model.template.command; + +import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@Getter +@Setter +@SuperBuilder +public class MutesModel extends SlimUserInitiatedServerContext { + private List mutes; +} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementService.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementService.java index e829cff21..dc32c0818 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementService.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/management/MuteManagementService.java @@ -1,6 +1,7 @@ package dev.sheldan.abstracto.moderation.service.management; import dev.sheldan.abstracto.core.models.AServerAChannelMessage; +import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.moderation.model.database.Mute; import net.dv8tion.jda.api.entities.Member; @@ -75,5 +76,18 @@ public interface MuteManagementService { * @param aUserInAServer The {@link AUserInAServer} to search the active mutes for * @return A collection of {@link Mute} objects of the user which are active */ + List getAllActiveMutesOf(AUserInAServer aUserInAServer); + + /** + * Retrieves all mutes of the given {@link AUserInAServer} in a collection + * @param aUserInAServer The {@link AUserInAServer} to search the mutes for + * @return A collection of {@link Mute} objects of the user + */ List getAllMutesOf(AUserInAServer aUserInAServer); + + /** + * Retrieves all {@link Mute} from the given {@link AServer} + * @return All found mutes of this server + */ + List getAllMutes(AServer server); } diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/listener/SuggestionButtonVoteClickedListener.java b/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/listener/SuggestionButtonVoteClickedListener.java index 600d136e9..aaa255bb7 100644 --- a/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/listener/SuggestionButtonVoteClickedListener.java +++ b/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/listener/SuggestionButtonVoteClickedListener.java @@ -58,7 +58,7 @@ public class SuggestionButtonVoteClickedListener implements ButtonClickedListene @Override public Boolean handlesEvent(ButtonClickedListenerModel model) { - return model.getOrigin().equals(SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN); + return SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN.equals(model.getOrigin()); } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/listener/ConfirmationButtonClickedListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/listener/ConfirmationButtonClickedListener.java index 791cbea0d..1b8f59298 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/listener/ConfirmationButtonClickedListener.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/listener/ConfirmationButtonClickedListener.java @@ -103,7 +103,7 @@ public class ConfirmationButtonClickedListener implements ButtonClickedListener @Override public Boolean handlesEvent(ButtonClickedListenerModel model) { - return model.getOrigin().equals(CommandReceivedHandler.COMMAND_CONFIRMATION_ORIGIN); + return CommandReceivedHandler.COMMAND_CONFIRMATION_ORIGIN.equals(model.getOrigin()); } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/job/PaginatorCleanupJob.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/job/PaginatorCleanupJob.java new file mode 100644 index 000000000..9a80fb73f --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/job/PaginatorCleanupJob.java @@ -0,0 +1,46 @@ +package dev.sheldan.abstracto.core.job; + +import dev.sheldan.abstracto.core.service.MessageService; +import dev.sheldan.abstracto.core.service.PaginatorServiceBean; +import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.PersistJobDataAfterExecution; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.stereotype.Component; + +@Slf4j +@DisallowConcurrentExecution +@Component +@PersistJobDataAfterExecution +@Setter +public class PaginatorCleanupJob extends QuartzJobBean { + + private String paginatorId; + private String accessorId; + + @Autowired + private PaginatorServiceBean paginatorServiceBean; + + @Autowired + private MessageService messageService; + + @Autowired + private ComponentPayloadManagementService componentPayloadManagementService; + + @Override + protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { + PaginatorServiceBean.PaginatorInfo info = paginatorServiceBean.getPaginatorInfo(paginatorId); + log.info("Executing paginator cleanup for paginator {}", paginatorId); + if(info != null && info.getLastAccessor().equals(accessorId)) { + log.info("Last accessor was {} - which was the start of this job - deleting", info.getLastAccessor()); + paginatorServiceBean.cleanupPaginator(info); + } else { + log.info("The last accessor did either not start this job, or there was no configuration found."); + } + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/PaginatorButtonListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/PaginatorButtonListener.java new file mode 100644 index 000000000..be6dff177 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/PaginatorButtonListener.java @@ -0,0 +1,97 @@ +package dev.sheldan.abstracto.core.listener; + +import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.ListenerPriority; +import dev.sheldan.abstracto.core.listener.async.jda.ButtonClickedListener; +import dev.sheldan.abstracto.core.model.PaginatorButtonPayload; +import dev.sheldan.abstracto.core.models.listener.ButtonClickedListenerModel; +import dev.sheldan.abstracto.core.service.MessageService; +import dev.sheldan.abstracto.core.service.PaginatorServiceBean; +import dev.sheldan.abstracto.core.templating.model.EmbedConfiguration; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import dev.sheldan.abstracto.core.templating.service.TemplateService; +import dev.sheldan.abstracto.core.templating.service.TemplateServiceBean; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; + + +@Component +@Slf4j +public class PaginatorButtonListener implements ButtonClickedListener { + + @Autowired + private PaginatorServiceBean paginatorServiceBean; + + @Autowired + private MessageService messageService; + + @Autowired + private TemplateServiceBean templateServiceBean; + + @Autowired + private TemplateService templateService; + + @Override + public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) { + PaginatorButtonPayload payload = (PaginatorButtonPayload) model.getDeserializedPayload(); + + Message originalMessage = model.getEvent().getMessage(); + if(originalMessage == null) { + return ButtonClickedListenerResult.IGNORED; + } + if(payload.getAllowedUser() != null && model.getEvent().getUser().getIdLong() != payload.getAllowedUser()) { + return ButtonClickedListenerResult.IGNORED; + } + String buttonId = model.getEvent().getComponentId(); + if(buttonId.equals(payload.getExitButtonId())) { + log.info("Deleting paginator {} because of exit button {}.", payload.getPaginatorId(), buttonId); + originalMessage.delete().queue(); + paginatorServiceBean.cleanupPaginatorPayloads(payload); + return ButtonClickedListenerResult.ACKNOWLEDGED; + } + if(payload.getSinglePage()) { + return ButtonClickedListenerResult.IGNORED; + } + int targetPage; + if(buttonId.equals(payload.getStartButtonId())) { + targetPage = 0; + } else if(buttonId.equals(payload.getPreviousButtonId())) { + targetPage = Math.max(paginatorServiceBean.getCurrentPage(payload.getPaginatorId()) - 1, 0); + } else if(buttonId.equals(payload.getNextButtonId())) { + targetPage = Math.min(paginatorServiceBean.getCurrentPage(payload.getPaginatorId()) + 1, payload.getEmbedConfigs().size() - 1); + } else if(buttonId.equals(payload.getLastButtonId())) { + targetPage = payload.getEmbedConfigs().size() - 1; + } else { + return ButtonClickedListenerResult.IGNORED; + } + log.debug("Moving to page {} in paginator {}.", targetPage, payload.getPaginatorId()); + EmbedConfiguration embedConfiguration = payload.getEmbedConfigs().get(targetPage); + MessageToSend messageToSend = templateServiceBean.convertEmbedConfigurationToMessageToSend(embedConfiguration); + messageService.editMessageInChannel(model.getEvent().getMessageChannel(), messageToSend, originalMessage.getIdLong()) + .thenAccept(unused -> log.info("Updated paginator {} to switch to page {}.", payload.getPaginatorId(), targetPage)); + String accessorId = UUID.randomUUID().toString(); + paginatorServiceBean.updateCurrentPage(payload.getPaginatorId(), targetPage, accessorId); + paginatorServiceBean.schedulePaginationDeletion(payload.getPaginatorId(), accessorId); + return ButtonClickedListenerResult.ACKNOWLEDGED; + } + + @Override + public Integer getPriority() { + return ListenerPriority.HIGH; + } + + @Override + public Boolean handlesEvent(ButtonClickedListenerModel model) { + return PaginatorServiceBean.PAGINATOR_BUTTON.equals(model.getOrigin()); + } + + @Override + public FeatureDefinition getFeature() { + return CoreFeatureDefinition.CORE_FEATURE; + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorButtonPayload.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorButtonPayload.java new file mode 100644 index 000000000..c32cdad86 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorButtonPayload.java @@ -0,0 +1,24 @@ +package dev.sheldan.abstracto.core.model; + +import dev.sheldan.abstracto.core.models.template.button.ButtonPayload; +import dev.sheldan.abstracto.core.templating.model.EmbedConfiguration; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@Builder +public class PaginatorButtonPayload implements ButtonPayload { + private List embedConfigs; + private String paginatorId; + private String exitButtonId; + private String startButtonId; + private String previousButtonId; + private String nextButtonId; + private String lastButtonId; + private Boolean singlePage; + private Long allowedUser; +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorConfiguration.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorConfiguration.java index b5fef715e..ad8bb9442 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorConfiguration.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorConfiguration.java @@ -1,5 +1,7 @@ package dev.sheldan.abstracto.core.model; +import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel; +import dev.sheldan.abstracto.core.templating.model.EmbedConfiguration; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -10,9 +12,14 @@ import java.util.List; @Setter @Builder public class PaginatorConfiguration { - private String headerText; - private List items; + private List embedConfigs; + private String paginatorId; private Long timeoutSeconds; - private Boolean showPageNumbers; - private Boolean useNumberedItems; + private Boolean restrictUser; + private ButtonConfigModel exitButton; + private ButtonConfigModel startButton; + private ButtonConfigModel previousButton; + private ButtonConfigModel nextButton; + private ButtonConfigModel lastButton; + private Boolean singlePage; } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorFooterModel.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorFooterModel.java new file mode 100644 index 000000000..c58ebc311 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorFooterModel.java @@ -0,0 +1,13 @@ +package dev.sheldan.abstracto.core.model; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class PaginatorFooterModel { + private Integer page; + private Integer pageCount; +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorModel.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorModel.java new file mode 100644 index 000000000..c0526d29e --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/PaginatorModel.java @@ -0,0 +1,17 @@ +package dev.sheldan.abstracto.core.model; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class PaginatorModel { + private Object innerModel; + private String exitButtonId; + private String startButtonId; + private String previousButtonId; + private String nextButtonId; + private String lastButtonId; +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java index c97460d8d..11c8fe8f5 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java @@ -307,6 +307,7 @@ public class ChannelServiceBean implements ChannelService { if(messageToSend.getReferencedMessageId() != null) { messageAction = messageAction.referenceById(messageToSend.getReferencedMessageId()); } + messageAction = messageAction.setActionRows(messageToSend.getActionRows()); metricService.incrementCounter(MESSAGE_EDIT_METRIC); return messageAction.submit(); } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java index 879072ecc..f817f06e7 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java @@ -191,6 +191,11 @@ public class MessageServiceBean implements MessageService { return openPrivateChannelForUser(user).thenCompose(privateChannel -> channelService.editMessageInAChannelFuture(messageToSend, privateChannel, messageId).thenApply(message -> null)); } + @Override + public CompletableFuture editMessageInChannel(MessageChannel channel, MessageToSend messageToSend, Long messageId) { + return channelService.editMessageInAChannelFuture(messageToSend, channel, messageId).thenApply(message -> null); + } + @Override public CompletableFuture loadMessageFromCachedMessage(CachedMessage cachedMessage) { return loadMessage(cachedMessage.getServerId(), cachedMessage.getChannelId(), cachedMessage.getMessageId()); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PaginatorServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PaginatorServiceBean.java index 897e78891..117b5bc44 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PaginatorServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PaginatorServiceBean.java @@ -1,19 +1,39 @@ package dev.sheldan.abstracto.core.service; import com.google.gson.Gson; -import com.jagrosh.jdautilities.commons.waiter.EventWaiter; -import com.jagrosh.jdautilities.menu.Paginator; +import dev.sheldan.abstracto.core.model.PaginatorButtonPayload; import dev.sheldan.abstracto.core.model.PaginatorConfiguration; +import dev.sheldan.abstracto.core.model.PaginatorFooterModel; +import dev.sheldan.abstracto.core.model.PaginatorModel; +import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel; +import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService; +import dev.sheldan.abstracto.core.templating.model.EmbedConfiguration; +import dev.sheldan.abstracto.core.templating.model.EmbedFooter; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; import dev.sheldan.abstracto.core.templating.service.TemplateService; -import net.dv8tion.jda.api.entities.MessageEmbed; -import org.apache.commons.lang3.ObjectUtils; +import dev.sheldan.abstracto.core.templating.service.TemplateServiceBean; +import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.scheduling.model.JobParameters; +import dev.sheldan.abstracto.scheduling.service.SchedulerService; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.TextChannel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.concurrent.TimeUnit; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; @Component +@Slf4j public class PaginatorServiceBean implements PaginatorService { @Autowired @@ -28,42 +48,224 @@ public class PaginatorServiceBean implements PaginatorService { @Autowired private MessageService messageService; - @Override - public Paginator createPaginatorFromTemplate(String templateKey, Object model, EventWaiter waiter, Long server) { - String embedConfig = templateService.renderTemplate(templateKey + "_paginator", model); - PaginatorConfiguration configuration = gson.fromJson(embedConfig, PaginatorConfiguration.class); - List items = configuration.getItems(); - int itemsPerPage = findAppropriateCountPerPage(items); + @Autowired + private ComponentService componentService; - return new Paginator.Builder() - .setItemsPerPage(itemsPerPage) - .setText(configuration.getHeaderText()) - .showPageNumbers(ObjectUtils.defaultIfNull(configuration.getShowPageNumbers(), false)) - .setItems(configuration.getItems().toArray(new String[0])) - .useNumberedItems(ObjectUtils.defaultIfNull(configuration.getUseNumberedItems(), false)) - .setEventWaiter(waiter) - .waitOnSinglePage(true) - .setTimeout(ObjectUtils.defaultIfNull(configuration.getTimeoutSeconds(), 120L), TimeUnit.SECONDS) - .setFinalAction(message -> messageService.deleteMessage(message)) + @Autowired + private ComponentPayloadManagementService componentPayloadManagementService; + + @Autowired + private ChannelService channelService; + + @Autowired + private PaginatorServiceBean self; + + @Autowired + private TemplateServiceBean templateServiceBean; + + @Autowired + private SchedulerService schedulerService; + + private static final Map PAGINATORS = new ConcurrentHashMap<>(); + public static final String PAGINATOR_BUTTON = "PAGINATOR_BUTTON"; + public static final String PAGINATOR_FOOTER_TEMPLATE_KEY = "paginator_footer"; + private static final ReentrantLock lock = new ReentrantLock(); + + @Override + public CompletableFuture createPaginatorFromTemplate(String templateKey, Object model, TextChannel textChannel, Long userId) { + Long serverId = textChannel.getGuild().getIdLong(); + String exitButtonId = componentService.generateComponentId(serverId); + String startButtonId = componentService.generateComponentId(serverId); + String previousButtonId = componentService.generateComponentId(serverId); + String nextButtonId = componentService.generateComponentId(serverId); + String lastButtonId = componentService.generateComponentId(serverId); + PaginatorModel wrapperModel = PaginatorModel + .builder() + .exitButtonId(exitButtonId) + .startButtonId(startButtonId) + .previousButtonId(previousButtonId) + .nextButtonId(nextButtonId) + .lastButtonId(lastButtonId) + .innerModel(model) + .build(); + String embedConfig = templateService.renderTemplate(templateKey + "_paginator", wrapperModel, serverId); + PaginatorConfiguration configuration = gson.fromJson(embedConfig, PaginatorConfiguration.class); + log.info("Setting up paginator in channel {} in server {} with {} pages.", textChannel.getIdLong(), + textChannel.getGuild().getIdLong(), configuration.getEmbedConfigs().size()); + setupFooters(configuration); + + configuration.setPaginatorId(componentService.generateComponentId()); + configuration.setSinglePage(configuration.getEmbedConfigs().size() < 2); + PaginatorButtonPayload buttonPayload = getButtonPayload(configuration, exitButtonId, startButtonId, previousButtonId, nextButtonId, lastButtonId); + if(configuration.getRestrictUser() != null && configuration.getRestrictUser()) { + buttonPayload.setAllowedUser(userId); + } + configuration.setExitButton(initializeButton(exitButtonId, buttonPayload)); + if(!configuration.getSinglePage()) { + log.debug("Adding additional buttons for pagination to paginator {}.", configuration.getPaginatorId()); + configuration.setStartButton(initializeButton(startButtonId, buttonPayload)); + configuration.setPreviousButton(initializeButton(previousButtonId, buttonPayload)); + configuration.setNextButton(initializeButton(nextButtonId, buttonPayload)); + configuration.setLastButton(initializeButton(lastButtonId, buttonPayload)); + } + + EmbedConfiguration embedConfiguration = configuration.getEmbedConfigs().get(0); + MessageToSend messageToSend = templateServiceBean.convertEmbedConfigurationToMessageToSend(embedConfiguration); + List> paginatorFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel); + return FutureUtils.toSingleFutureGeneric(paginatorFutures) + .thenAccept(unused -> self.setupButtonPayloads(paginatorFutures.get(0).join(), configuration, serverId, buttonPayload)); + } + + private void setupFooters(PaginatorConfiguration configuration) { + for (int i = 0; i < configuration.getEmbedConfigs().size(); i++) { + PaginatorFooterModel paginatorModel = PaginatorFooterModel + .builder() + .page(i + 1) + .pageCount(configuration.getEmbedConfigs().size()) + .build(); + String footerText = templateService.renderTemplate(PAGINATOR_FOOTER_TEMPLATE_KEY, paginatorModel); + EmbedConfiguration embedConfiguration = configuration.getEmbedConfigs().get(i); + if(embedConfiguration.getFooter() == null) { + embedConfiguration.setFooter(EmbedFooter.builder().text(footerText).build()); + } else { + embedConfiguration.getFooter().setText(footerText); + } + } + } + + public void cleanupPaginatorPayloads(PaginatorButtonPayload configuration) { + List payloadIds = getAllPayloadIdsFromPayload(configuration); + componentPayloadManagementService.deletePayloads(payloadIds); + } + + private List getAllPayloadIdsFromPayload(PaginatorButtonPayload configuration) { + List payloadIds = new ArrayList<>(Arrays.asList(configuration.getExitButtonId())); + if(!configuration.getSinglePage()) { + payloadIds.add(configuration.getStartButtonId()); + payloadIds.add(configuration.getPreviousButtonId()); + payloadIds.add(configuration.getNextButtonId()); + payloadIds.add(configuration.getLastButtonId()); + } + return payloadIds; + } + + + private ButtonConfigModel initializeButton(String buttonId, PaginatorButtonPayload paginatorButtonPayload) { + return ButtonConfigModel + .builder() + .buttonId(buttonId) + .buttonPayload(paginatorButtonPayload) + .payloadType(PaginatorButtonPayload.class) + .origin(PAGINATOR_BUTTON) .build(); } - private int findAppropriateCountPerPage(List items) { - int currentMin = Integer.MAX_VALUE; - // to be sure, because the paginator adds some characters here and there - int carefulMax = MessageEmbed.TEXT_MAX_LENGTH - 50; - for (int i = 0; i < items.size(); i++) { - int count = 0; - int length = 0; - for (String innerItem : items) { - length += innerItem.length(); - if (length > carefulMax) { - currentMin = Math.min(currentMin, count); - break; - } - count++; - } - } - return currentMin; + private PaginatorButtonPayload getButtonPayload(PaginatorConfiguration configuration, String exitButtonId, + String startButtonId, String previousButtonId, + String nextButtonId, String lastButtonId) { + return PaginatorButtonPayload + .builder() + .paginatorId(configuration.getPaginatorId()) + .exitButtonId(exitButtonId) + .startButtonId(startButtonId) + .previousButtonId(previousButtonId) + .nextButtonId(nextButtonId) + .lastButtonId(lastButtonId) + .embedConfigs(configuration.getEmbedConfigs()) + .singlePage(configuration.getSinglePage()) + .build(); } + + public Integer getCurrentPage(String paginatorId) { + return PAGINATORS.get(paginatorId).currentPage; + } + + public PaginatorInfo getPaginatorInfo(String paginatorId) { + return PAGINATORS.get(paginatorId); + } + + public void updateCurrentPage(String paginatorId, Integer newPage, String newAccessorId) { + try { + lock.lock(); + PaginatorInfo paginatorInfo = PAGINATORS.get(paginatorId); + if(paginatorInfo != null) { + paginatorInfo.setCurrentPage(newPage); + paginatorInfo.setLastAccessor(newAccessorId); + } + } catch (Exception exception) { + lock.unlock(); + log.error("Failed to update current page for paginator {} to page {}", paginatorId, newPage, exception); + } + } + + public void schedulePaginationDeletion(String paginatorId, String accessorId) { + PaginatorServiceBean.PaginatorInfo paginatorInfo = PAGINATORS.get(paginatorId); + HashMap parameters = new HashMap<>(); + parameters.put("paginatorId", paginatorId); + parameters.put("accessorId", accessorId); + JobParameters jobParameters = JobParameters + .builder() + .parameters(parameters) + .build(); + Instant targetDate = Instant.now().plus(paginatorInfo.getTimeoutSeconds(), ChronoUnit.SECONDS); + schedulerService.executeJobWithParametersOnce("paginatorCleanupJob", "core", jobParameters, Date.from(targetDate)); + log.debug("Scheduled job to delete the paginator {} in {} seconds.", paginatorId, paginatorInfo.getTimeoutSeconds()); + } + + @Transactional + public void setupButtonPayloads(Message paginatorMessage, PaginatorConfiguration configuration, Long serverId, PaginatorButtonPayload payload) { + savePayload(configuration.getExitButton(), serverId); + if(!configuration.getSinglePage()) { + savePayload(configuration.getStartButton(), serverId); + savePayload(configuration.getPreviousButton(), serverId); + savePayload(configuration.getNextButton(), serverId); + savePayload(configuration.getLastButton(), serverId); + } + + String accessorId = UUID.randomUUID().toString(); + + PaginatorInfo info = PaginatorInfo + .builder() + .currentPage(0) + .serverId(serverId) + .channelId(paginatorMessage.getChannel().getIdLong()) + .messageId(paginatorMessage.getIdLong()) + .timeoutSeconds(configuration.getTimeoutSeconds()) + .paginatorId(configuration.getPaginatorId()) + .payloadIds(getAllPayloadIdsFromPayload(payload)) + .lastAccessor(accessorId) + .build(); + log.debug("We are using the accessor id {} for paginator {} initially.", accessorId, configuration.getPaginatorId()); + PAGINATORS.put(configuration.getPaginatorId(), info); + + schedulePaginationDeletion(configuration.getPaginatorId(), accessorId); + } + + private void savePayload(ButtonConfigModel model, Long serverId) { + componentPayloadManagementService.createPayload(model, serverId); + } + + @Transactional + public void cleanupPaginator(PaginatorInfo paginatorInfo) { + log.info("Cleaning up paginator {} in server {} channel {} message {}.", paginatorInfo.getPaginatorId(), + paginatorInfo.getServerId(), paginatorInfo.getChannelId(), paginatorInfo.getMessageId()); + messageService.deleteMessageInChannelInServer(paginatorInfo.getServerId(), paginatorInfo.getChannelId(), paginatorInfo.getMessageId()); + componentPayloadManagementService.deletePayloads(paginatorInfo.getPayloadIds()); + } + + @Getter + @Builder + public static class PaginatorInfo { + @Setter + private Integer currentPage; + private Long serverId; + private Long channelId; + private Long messageId; + private String paginatorId; + @Setter + private String lastAccessor; + private Long timeoutSeconds; + private List payloadIds; + } + } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java index 0d5f27b84..f3e960c6c 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java @@ -23,6 +23,9 @@ public class ComponentPayloadManagementServiceBean implements ComponentPayloadMa @Autowired private Gson gson; + @Autowired + private ServerManagementService serverManagementService; + @Override public ComponentPayload createPayload(String id, String payload, Class payloadType, String buttonOrigin, AServer server, ComponentType componentType) { ComponentPayload componentPayload = ComponentPayload @@ -43,6 +46,12 @@ public class ComponentPayloadManagementServiceBean implements ComponentPayloadMa return createPayload(buttonConfigModel.getButtonId(), payload, buttonConfigModel.getPayloadType(), buttonConfigModel.getOrigin(), server, ComponentType.BUTTON); } + @Override + public ComponentPayload createPayload(ButtonConfigModel buttonConfigModel, Long serverId) { + AServer server = serverManagementService.loadOrCreate(serverId); + return createPayload(buttonConfigModel, server); + } + @Override public Optional findPayload(String id) { return repository.findById(id); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/service/TemplateServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/service/TemplateServiceBean.java index fedb7d923..6dcce33ea 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/service/TemplateServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/templating/service/TemplateServiceBean.java @@ -82,6 +82,10 @@ public class TemplateServiceBean implements TemplateService { public MessageToSend renderEmbedTemplate(String key, Object model) { String embedConfig = this.renderTemplate(key + "_embed", model); EmbedConfiguration embedConfiguration = gson.fromJson(embedConfig, EmbedConfiguration.class); + return convertEmbedConfigurationToMessageToSend(embedConfiguration); + } + + public MessageToSend convertEmbedConfigurationToMessageToSend(EmbedConfiguration embedConfiguration) { List embedBuilders = new ArrayList<>(); embedBuilders.add(new EmbedBuilder()); if(embedConfiguration.getMetaConfig() != null && diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/collection.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/collection.xml new file mode 100644 index 000000000..29116ec67 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/collection.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/seedData/data.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/seedData/data.xml new file mode 100644 index 000000000..8ce4e0e34 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/seedData/data.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/seedData/paginatorCleanupJob.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/seedData/paginatorCleanupJob.xml new file mode 100644 index 000000000..f45d1bc59 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.3.10/seedData/paginatorCleanupJob.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/core-changeLog.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/core-changeLog.xml index f070a71d5..b0e372b88 100644 --- a/abstracto-application/core/core-impl/src/main/resources/migrations/core-changeLog.xml +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/core-changeLog.xml @@ -21,4 +21,5 @@ + \ No newline at end of file diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java index 3f669a3d7..f42d2dd71 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java @@ -33,6 +33,7 @@ public interface MessageService { CompletableFuture sendMessageToUser(User user, String text); CompletableFuture deleteMessageInChannelWithUser(User user, Long messageId); CompletableFuture editMessageInDMChannel(User user, MessageToSend messageToSend, Long messageId); + CompletableFuture editMessageInChannel(MessageChannel channel, MessageToSend messageToSend, Long messageId); CompletableFuture loadMessageFromCachedMessage(CachedMessage cachedMessage); CompletableFuture loadMessage(Long serverId, Long channelId, Long messageId); CompletableFuture loadMessage(Message message); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/PaginatorService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/PaginatorService.java index 741187992..bba843dc9 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/PaginatorService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/PaginatorService.java @@ -1,9 +1,10 @@ package dev.sheldan.abstracto.core.service; -import com.jagrosh.jdautilities.commons.waiter.EventWaiter; -import com.jagrosh.jdautilities.menu.Paginator; +import net.dv8tion.jda.api.entities.TextChannel; + +import java.util.concurrent.CompletableFuture; public interface PaginatorService { - Paginator createPaginatorFromTemplate(String templateKey, Object model, EventWaiter waiter, Long serverId); + CompletableFuture createPaginatorFromTemplate(String templateKey, Object model, TextChannel textChannel, Long userId); } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementService.java index 920cdf0b1..daf6f0317 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementService.java @@ -11,6 +11,7 @@ import java.util.Optional; public interface ComponentPayloadManagementService { ComponentPayload createPayload(String id, String payload, Class payloadType, String buttonOrigin, AServer server, ComponentType type); ComponentPayload createPayload(ButtonConfigModel buttonConfigModel, AServer server); + ComponentPayload createPayload(ButtonConfigModel buttonConfigModel, Long serverId); Optional findPayload(String id); List findPayloadsOfOriginInServer(String buttonOrigin, AServer server); void deletePayload(String id); diff --git a/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc b/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc index 1009857d3..a2debcb2b 100644 --- a/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc +++ b/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc @@ -123,6 +123,9 @@ Configuring which role to use for muting:: * Usage: `setMuteRole ` * Description: Sets the `role` to be used as the role when applying a mute. This role needs to be muting, which means, if you want it to be effective, this role needs to deny `MESSAGE_WRITE`. The bot does not validate nor require the role to actually mute. Only *one* role can be used as a mute role. +Showing all mutes:: +* Usage: `mutes [member]` +* Description: Shows all the mutes in a paginated matter with buttons to navigate the pages. If `member` is provided, it will only show mutes for this member. === Logging