diff --git a/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/service/MessageEmbedServiceBean.java b/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/service/MessageEmbedServiceBean.java index 545ed74a4..88efc22d0 100644 --- a/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/service/MessageEmbedServiceBean.java +++ b/abstracto-application/abstracto-modules/link-embed/link-embed-impl/src/main/java/dev/sheldan/abstracto/linkembed/service/MessageEmbedServiceBean.java @@ -173,13 +173,28 @@ public class MessageEmbedServiceBean implements MessageEmbedService { CompletableFutureList reactionFutureList = new CompletableFutureList<>(reactionMessageFutures); CompletableFutureList buttonFutureList = new CompletableFutureList<>(buttonMessageFutures); return reactionFutureList.getMainFuture() - .handle((unused, throwable) -> self.removeReactions(reactionFutureList.getObjects())) + .handle((unused, throwable) -> { + if(throwable != null) { + log.warn("Embedded messages reaction message loading failed.", throwable); + } + return self.removeReactions(reactionFutureList.getObjects()); + }) .thenCompose(Function.identity()) .thenCompose(unused -> buttonFutureList.getMainFuture()) - .handle((unused, throwable) -> self.removeButtons(buttonFutureList.getObjects())) + .handle((unused, throwable) -> { + if(throwable != null) { + log.warn("Embedded messages button message loading failed.", throwable); + } + return self.removeButtons(buttonFutureList.getObjects()); + }) // deleting the messages from db regardless of exceptions, at most the reaction remains .thenCompose(Function.identity()) - .whenComplete((unused, throwable) -> self.deleteEmbeddedMessages(embeddedMessagesHandled)) + .whenComplete((unused, throwable) -> { + if(throwable != null) { + log.warn("Embedded message button clearing failed.", throwable); + } + self.deleteEmbeddedMessages(embeddedMessagesHandled); + }) .exceptionally(throwable -> { log.error("Failed to clean up embedded messages.", throwable); return null; diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/SoftBan.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/SoftBan.java new file mode 100644 index 000000000..9e1866b77 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/command/SoftBan.java @@ -0,0 +1,78 @@ +package dev.sheldan.abstracto.moderation.command; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.condition.CommandCondition; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.EffectConfig; +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.config.FeatureDefinition; +import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition; +import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; +import dev.sheldan.abstracto.moderation.service.BanService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static dev.sheldan.abstracto.moderation.service.BanService.BAN_EFFECT_KEY; + +@Component +@Slf4j +public class SoftBan extends AbstractConditionableCommand { + + @Autowired + private BanService banService; + + @Override + public CompletableFuture executeAsync(CommandContext commandContext) { + List parameters = commandContext.getParameters().getParameters(); + User user = (User) parameters.get(0); + Duration delDays = Duration.ofDays(7); + if(parameters.size() > 1) { + delDays = (Duration) parameters.get(1); + } + return banService.softBanUser(commandContext.getGuild(), user, delDays) + .thenApply(unused -> CommandResult.fromSuccess()); + } + + @Override + public CommandConfiguration getConfiguration() { + List parameters = new ArrayList<>(); + parameters.add(Parameter.builder().name("user").templated(true).type(User.class).build()); + parameters.add(Parameter.builder().name("delDays").templated(true).type(Duration.class).optional(true).build()); + HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); + List effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(BAN_EFFECT_KEY).build()); + return CommandConfiguration.builder() + .name("softBan") + .module(ModerationModuleDefinition.MODERATION) + .templated(true) + .async(true) + .effects(effectConfig) + .supportsEmbedException(true) + .causesReaction(true) + .parameters(parameters) + .help(helpInfo) + .build(); + } + + @Override + public FeatureDefinition getFeature() { + return ModerationFeatureDefinition.MODERATION; + } + + @Override + public List getConditions() { + List conditions = super.getConditions(); + conditions.add(immuneUserCondition); + return conditions; + } +} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java index 06dbf61e7..313ccbe7f 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java @@ -16,6 +16,7 @@ import net.dv8tion.jda.api.entities.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.time.Duration; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -58,7 +59,7 @@ public class BanServiceBean implements BanService { .commandMessage(message) .reason(reason) .build(); - CompletableFuture banFuture = banUser(member.getGuild(), member.getUser(), reason); + CompletableFuture banFuture = banUser(member.getGuild(), member.getUser(), 0, reason); CompletableFuture messageFuture = sendBanLogMessage(banLog, member.getGuild().getIdLong(), BAN_LOG_TEMPLATE); return CompletableFuture.allOf(banFuture, messageFuture); } @@ -81,7 +82,7 @@ public class BanServiceBean implements BanService { .thenAccept(message1 -> log.info("Notified about not being able to send ban notification in server {} and channel {} based on message {} from user {}." , message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong(), message.getAuthor().getIdLong())); } - CompletableFuture banFuture = banUser(guild, user, reason); + CompletableFuture banFuture = banUser(guild, user, 0, reason); CompletableFuture messageFuture = sendBanLogMessage(banLog, guild.getIdLong(), BAN_LOG_TEMPLATE); CompletableFuture.allOf(banFuture, messageFuture) .thenAccept(unused1 -> returningFuture.complete(null)) @@ -111,10 +112,29 @@ public class BanServiceBean implements BanService { .bannedUser(user) .unBanningMember(unBanningMember) .build(); - return unBanUser(guild, user) + return unbanUser(guild, user) .thenCompose(unused -> self.sendUnBanLogMessage(banLog, guild.getIdLong(), UN_BAN_LOG_TEMPLATE)); } + @Override + public CompletableFuture banUser(Guild guild, User user, Integer deletionDays, String reason) { + log.info("Banning user {} in guild {}.", user.getIdLong(), guild.getId()); + return guild.ban(user, deletionDays, reason).submit(); + } + + @Override + public CompletableFuture unbanUser(Guild guild, User user) { + log.info("Unbanning user {} in guild {}.", user.getIdLong(), guild.getId()); + return guild.unban(user).submit(); + } + + @Override + public CompletableFuture softBanUser(Guild guild, User user, Duration delDays) { + Long days = delDays.toDays(); + return banUser(guild, user, days.intValue(), "") + .thenCompose(unused -> unbanUser(guild, user)); + } + public CompletableFuture sendBanLogMessage(BanLog banLog, Long guildId, String template) { CompletableFuture completableFuture; MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId); @@ -132,14 +152,4 @@ public class BanServiceBean implements BanService { completableFuture = FutureUtils.toSingleFutureGeneric(notificationFutures); return completableFuture; } - - private CompletableFuture banUser(Guild guild, User user, String reason) { - log.info("Banning user {} in guild {}.", user.getIdLong(), guild.getId()); - return guild.ban(user, 0, reason).submit(); - } - - private CompletableFuture unBanUser(Guild guild, User user) { - log.info("Unbanning user {} in guild {}.", user.getIdLong(), guild.getId()); - return guild.unban(user).submit(); - } } diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.4/collection.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.4/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.4/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.4/seedData/command.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.4/seedData/command.xml new file mode 100644 index 000000000..c4023d442 --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.4/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.4/seedData/data.xml b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/resources/migrations/1.3.4/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.4/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 2d7765c17..0f157a153 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 @@ -10,4 +10,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/BanTest.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/command/BanTest.java index 87e2d7f3c..94309b79b 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/command/BanTest.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/test/java/dev/sheldan/abstracto/moderation/command/BanTest.java @@ -31,36 +31,16 @@ public class BanTest { @Mock private BanService banService; - @Mock - private TemplateService templateService; - @Captor private ArgumentCaptor banLogModelCaptor; - private static final String REASON = "reason"; - private static final Long SERVER_ID = 1L; - @Mock private User bannedMember; - @Test - public void testBanWithDefaultReason() { - CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(bannedMember)); - when(parameters.getGuild().getIdLong()).thenReturn(SERVER_ID); - when(templateService.renderSimpleTemplate(Ban.BAN_DEFAULT_REASON_TEMPLATE, SERVER_ID)).thenReturn(REASON); - when(banService.banUser(eq(bannedMember), eq(REASON), banLogModelCaptor.capture(), any(Message.class))).thenReturn(CompletableFuture.completedFuture(null)); - CompletableFuture result = testUnit.executeAsync(parameters); - Member banningMember = banLogModelCaptor.getValue(); - Assert.assertEquals(parameters.getAuthor(), banningMember); - CommandTestUtilities.checkSuccessfulCompletionAsync(result); - } - @Test public void testBanWithReason() { String customReason = "reason2"; CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(bannedMember, customReason)); - when(parameters.getGuild().getIdLong()).thenReturn(SERVER_ID); - when(templateService.renderSimpleTemplate(Ban.BAN_DEFAULT_REASON_TEMPLATE, SERVER_ID)).thenReturn(REASON); when(banService.banUser(eq(bannedMember), eq(customReason), banLogModelCaptor.capture(), any(Message.class))).thenReturn(CompletableFuture.completedFuture(null)); CompletableFuture result = testUnit.executeAsync(parameters); Member banningMember = banLogModelCaptor.getValue(); diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/BanService.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/BanService.java index 1d36509b4..9fe77a55e 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/BanService.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/BanService.java @@ -1,9 +1,11 @@ package dev.sheldan.abstracto.moderation.service; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; +import java.time.Duration; import java.util.concurrent.CompletableFuture; public interface BanService { @@ -11,4 +13,7 @@ public interface BanService { CompletableFuture banMember(Member member, String reason, Member banningMember, Message message); CompletableFuture banUser(User user, String reason, Member banningMember, Message message); CompletableFuture unBanUser(User user, Member unBanningUser); + CompletableFuture banUser(Guild guild, User user, Integer deletionDays, String reason); + CompletableFuture unbanUser(Guild guild, User user); + CompletableFuture softBanUser(Guild guild, User user, Duration delDays); } 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 f9ee64e19..97731c660 100644 --- a/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc +++ b/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc @@ -34,6 +34,9 @@ Purging messages in a channel:: * Description: Deletes the last `messageCount` messages in the current channel. If a `member` is provided as parameter, only the messages by this member will be deleted. The deletion of this messages will *not* be logged by the logging mechanism. The messages to be deleted need to be from within the last 2 weeks, but there is no limit on how much messages can be deleted besides that. While the command is ongoing, a status update message will be shown indicating how far the command is. This message will be deleted after the command is done. +Deleting all messages and kicking a member:: +* Usage `softBan [delDays]` +* Description: Bans the given `user` and deletes the messages for the given time period in `delDays`. This duration must be in days and can be at most 7 days. When no duration is provided 7 days are used. This command automatically unbans the user afterwards. === Warning