diff --git a/src/main/java/net/discordjug/javabot/systems/moderation/PruneCommand.java b/src/main/java/net/discordjug/javabot/systems/moderation/PruneCommand.java index c467897c6..339178257 100644 --- a/src/main/java/net/discordjug/javabot/systems/moderation/PruneCommand.java +++ b/src/main/java/net/discordjug/javabot/systems/moderation/PruneCommand.java @@ -18,14 +18,18 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; -import java.util.concurrent.TimeUnit; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; +import lombok.extern.slf4j.Slf4j; + /** *

This class represents the /prune command.

* This command will systematically ban users from the server if they match * certain criteria. */ +@Slf4j public class PruneCommand extends ModerateCommand { private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -39,8 +43,6 @@ public PruneCommand(BotConfig botConfig) { .addOption(OptionType.STRING, "pattern", "A regular expression pattern to use, to remove members whose contains a match with the pattern.", false) .addOption(OptionType.STRING, "before", "Remove only users before the given timestamp. Format is yyyy-MM-dd HH:mm:ss, in UTC.", false) .addOption(OptionType.STRING, "after", "Remove only users after the given timestamp. Format is yyyy-MM-dd HH:mm:ss, in UTC.", false) - .addOption(OptionType.STRING, "reason", "The reason for issuing the prune command. This will be provided as the reason for each ban.", false) - .addOption(OptionType.INTEGER, "delete-days-of-history", "The number of days of the banned users' chat history to remove, between 0 and 7. Defaults to 0.", false) ); } @@ -54,36 +56,46 @@ protected ReplyCallbackAction handleModerationCommand(@NotNull SlashCommandInter OptionMapping patternOption = event.getOption("pattern"); OptionMapping beforeOption = event.getOption("before"); OptionMapping afterOption = event.getOption("after"); - OptionMapping reasonOption = event.getOption("reason"); - OptionMapping delDaysOption = event.getOption("delete-days-of-history"); final Pattern pattern = patternOption == null ? null : Pattern.compile(patternOption.getAsString()); final OffsetDateTime before = beforeOption == null ? null : LocalDateTime.parse(beforeOption.getAsString(), TIMESTAMP_FORMATTER).atOffset(ZoneOffset.UTC); final OffsetDateTime after = afterOption == null ? null : LocalDateTime.parse(afterOption.getAsString(), TIMESTAMP_FORMATTER).atOffset(ZoneOffset.UTC); - final int delDays = delDaysOption == null ? 0 : (int) delDaysOption.getAsLong(); - final String reason = reasonOption == null ? null : reasonOption.getAsString(); if (pattern == null && before == null && after == null) { return Responses.warning(event, "At least one filter parameter must be given; cannot remove every user from the server."); } - if (delDays < 0 || delDays > 7) { - return Responses.warning(event, "The number of days of history to delete must not be less than 0, or greater than 7."); - } - if (reason != null && reason.length() > 512) { - return Responses.warning(event, "The reason for the prune cannot be more than 512 characters."); - } - - event.getGuild().loadMembers().onSuccess(members -> { - members.forEach(member -> { - boolean shouldRemove = (pattern == null || pattern.matcher(member.getUser().getName()).find()) && - (before == null || member.getTimeJoined().isBefore(before)) && - (after == null || member.getTimeJoined().isAfter(after)); - if (shouldRemove) { - config.getLogChannel().sendMessage("Removing " + UserUtils.getUserTag(member.getUser()) + " as part of prune.").queue(); - member.ban(delDays, TimeUnit.DAYS).reason(reason).queue(); + String pruneRoleName = "prune-" + LocalDateTime.now(); + event.getGuild().createRole().setName(pruneRoleName).queue(role -> { + event.getGuild().loadMembers().onSuccess(members -> { + AtomicInteger count = new AtomicInteger(0); + members.forEach(member -> { + boolean shouldRemove = (pattern == null || pattern.matcher(member.getUser().getName()).find()) && + (before == null || member.getTimeJoined().isBefore(before)) && + (after == null || member.getTimeJoined().isAfter(after)); + if (shouldRemove) { + if(member.getUser().isBot() || !moderator.canInteract(member) || Checks.hasStaffRole(botConfig, member)) { + config.getLogChannel() + .sendMessage("# WARNING\nPrune by " + moderator.getAsMention() + " would affect " + UserUtils.getUserTag(member.getUser()) + " who is a privileged user! This is likely not intentional.") + .queue(); + return; + } + log.info("Marking " + UserUtils.getUserTag(member.getUser()) + " with " + pruneRoleName +" as part of prune."); + config.getLogChannel() + .sendMessage("Marking " + member.getAsMention() + " (" + UserUtils.getUserTag(member.getUser()) + ") with " + role.getAsMention() +" as part of prune.") + .setAllowedMentions(List.of()) + .queue(); + event.getGuild().addRoleToMember(member, role).queue(); + count.incrementAndGet(); + } + }); + int finalCount = count.get(); + config.getLogChannel().sendMessage("Prune by "+ moderator.getAsMention() +" complete - the role " + pruneRoleName + " is being assigned to " + finalCount + " members.").queue(); + if (finalCount > members.size()/10) { + config.getLogChannel().sendMessage("# WARNING\nThis prune affects a significant portion of all members!").queue(); } }); }); + return Responses.success(event, "Prune Started", "The prune action has started. Please check the log channel for information on the status of the prune."); }