Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,26 +90,26 @@ public enum Features {
features.add(new PingCommand());
features.add(new TeXCommand());
features.add(new TagCommand(tagSystem));
features.add(new TagManageCommand(tagSystem, config, modAuditLogWriter));
features.add(new TagManageCommand(tagSystem, modAuditLogWriter));
features.add(new TagsCommand(tagSystem));
features.add(new VcActivityCommand());
features.add(new WarnCommand(actionsStore, config));
features.add(new KickCommand(actionsStore, config));
features.add(new BanCommand(actionsStore, config));
features.add(new UnbanCommand(actionsStore, config));
features.add(new AuditCommand(actionsStore, config));
features.add(new WarnCommand(actionsStore));
features.add(new KickCommand(actionsStore));
features.add(new BanCommand(actionsStore));
features.add(new UnbanCommand(actionsStore));
features.add(new AuditCommand(actionsStore));
features.add(new MuteCommand(actionsStore, config));
features.add(new UnmuteCommand(actionsStore, config));
features.add(new TopHelpersCommand(database, config));
features.add(new TopHelpersCommand(database));
features.add(new RoleSelectCommand());
features.add(new NoteCommand(actionsStore, config));
features.add(new NoteCommand(actionsStore));
features.add(new RemindCommand(database));
features.add(new QuarantineCommand(actionsStore, config));
features.add(new UnquarantineCommand(actionsStore, config));
features.add(new WhoIsCommand());
features.add(new WolframAlphaCommand(config));
features.add(new AskCommand(config, helpSystemHelper));
features.add(new CloseCommand(helpSystemHelper));
features.add(new CloseCommand());
features.add(new ChangeHelpCategoryCommand(config, helpSystemHelper));
features.add(new ChangeHelpTitleCommand(helpSystemHelper));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,6 @@ public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
String title = event.getOption(TITLE_OPTION).getAsString();
String category = event.getOption(CATEGORY_OPTION).getAsString();

if (!handleIsStagingChannel(event)) {
return;
}

if (!handleIsValidTitle(title, event)) {
return;
}
Expand All @@ -104,18 +100,6 @@ public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
}, e -> handleFailure(e, eventHook));
}

private boolean handleIsStagingChannel(@NotNull IReplyCallback event) {
if (helper.isStagingChannelName(event.getChannel().getName())) {
return true;
}

event.reply("Sorry, but this command can only be used in the help staging channel.")
.setEphemeral(true)
.queue();

return false;
}

private boolean handleIsValidTitle(@NotNull CharSequence title, @NotNull IReplyCallback event) {
if (HelpSystemHelper.isTitleValid(title)) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ public ChangeHelpCategoryCommand(@NotNull Config config, @NotNull HelpSystemHelp
public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
String category = event.getOption(CATEGORY_OPTION).getAsString();

if (!helper.handleIsHelpThread(event)) {
return;
}

ThreadChannel helpThread = event.getThreadChannel();
if (helpThread.isArchived()) {
event.reply("This thread is already closed.").setEphemeral(true).queue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public ChangeHelpTitleCommand(@NotNull HelpSystemHelper helper) {
public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
String title = event.getOption(TITLE_OPTION).getAsString();

if (!helper.handleIsHelpThread(event) || !handleIsValidTitle(title, event)) {
if (!handleIsValidTitle(title, event)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,22 @@ public final class CloseCommand extends SlashCommandAdapter {
private static final int COOLDOWN_DURATION_VALUE = 30;
private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES;

private final HelpSystemHelper helper;
private final Cache<Long, Instant> helpThreadIdToLastClose;

/**
* Creates a new instance.
*
* @param helper the helper to use
*/
public CloseCommand(@NotNull HelpSystemHelper helper) {
public CloseCommand() {
super("close", "Close this question thread", SlashCommandVisibility.GUILD);

helpThreadIdToLastClose = Caffeine.newBuilder()
.maximumSize(1_000)
.expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT))
.build();

this.helper = helper;
}

@Override
public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
if (!helper.handleIsHelpThread(event)) {
return;
}

ThreadChannel helpThread = event.getThreadChannel();
if (helpThread.isArchived()) {
event.reply("This thread is already closed.").setEphemeral(true).queue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.MessageAction;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -44,7 +43,6 @@ public final class HelpSystemHelper {
private final String stagingChannelPattern;
private final String categoryRoleSuffix;


/**
* Creates a new instance.
*
Expand Down Expand Up @@ -106,22 +104,6 @@ RestAction<Message> sendExplanationMessage(@NotNull MessageChannel threadChannel
.build();
}

boolean handleIsHelpThread(@NotNull IReplyCallback event) {
if (event.getChannelType() == ChannelType.GUILD_PUBLIC_THREAD) {
ThreadChannel thread = event.getThreadChannel();

if (isOverviewChannelName.test(thread.getParentChannel().getName())) {
return true;
}
}

event.reply("Sorry, but this command can only be used in a help thread.")
.setEphemeral(true)
.queue();

return false;
}

@NotNull
Optional<Role> handleFindRoleForCategory(@NotNull String category, @NotNull Guild guild) {
String roleName = category + categoryRoleSuffix;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@

import org.togetherjava.tjbot.commands.SlashCommandAdapter;
import org.togetherjava.tjbot.commands.SlashCommandVisibility;
import org.togetherjava.tjbot.config.Config;

import java.time.Instant;
import java.time.ZoneOffset;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
Expand All @@ -42,24 +39,20 @@ public final class AuditCommand extends SlashCommandAdapter {
private static final int MAX_PAGE_LENGTH = 25;
private static final String PREVIOUS_BUTTON_LABEL = "⬅";
private static final String NEXT_BUTTON_LABEL = "➡";
private final Predicate<String> hasRequiredRole;
private final ModerationActionsStore actionsStore;

/**
* Constructs an instance.
*
* @param actionsStore used to store actions issued by this command
* @param config the config to use for this
*/
public AuditCommand(@NotNull ModerationActionsStore actionsStore, @NotNull Config config) {
public AuditCommand(@NotNull ModerationActionsStore actionsStore) {
super(COMMAND_NAME, "Lists all moderation actions that have been taken against a user",
SlashCommandVisibility.GUILD);

getData().addOption(OptionType.USER, TARGET_OPTION, "The user who to retrieve actions for",
true);

hasRequiredRole =
Pattern.compile(config.getHeavyModerationRolePattern()).asMatchPredicate();
this.actionsStore = Objects.requireNonNull(actionsStore);
}

Expand Down Expand Up @@ -144,11 +137,10 @@ public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) {
private boolean handleChecks(@NotNull Member bot, @NotNull Member author,
@Nullable Member target, @NotNull IReplyCallback event) {
// Member doesn't exist if attempting to audit a user who is not part of the guild.
if (target != null && !ModerationUtils.handleCanInteractWithTarget(ACTION_VERB, bot, author,
target, event)) {
return false;
if (target == null) {
return true;
}
return ModerationUtils.handleHasAuthorRole(ACTION_VERB, hasRequiredRole, author, event);
return ModerationUtils.handleCanInteractWithTarget(ACTION_VERB, bot, author, target, event);
}

private @NotNull List<List<ActionRecord>> groupActionsByPages(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@
import org.slf4j.LoggerFactory;
import org.togetherjava.tjbot.commands.SlashCommandAdapter;
import org.togetherjava.tjbot.commands.SlashCommandVisibility;
import org.togetherjava.tjbot.config.Config;

import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;

/**
* This command can ban users and optionally remove their messages from the past days. Banning can
Expand All @@ -48,16 +45,14 @@ public final class BanCommand extends SlashCommandAdapter {
@SuppressWarnings("StaticCollection")
private static final List<String> DURATIONS = List.of(ModerationUtils.PERMANENT_DURATION,
"1 hour", "3 hours", "1 day", "2 days", "3 days", "7 days", "30 days");
private final Predicate<String> hasRequiredRole;
private final ModerationActionsStore actionsStore;

/**
* Constructs an instance.
*
* @param actionsStore used to store actions issued by this command
* @param config the config to use for this
*/
public BanCommand(@NotNull ModerationActionsStore actionsStore, @NotNull Config config) {
public BanCommand(@NotNull ModerationActionsStore actionsStore) {
super(COMMAND_NAME, "Bans the given user from the server", SlashCommandVisibility.GUILD);

OptionData durationData = new OptionData(OptionType.STRING, DURATION_OPTION,
Expand All @@ -71,8 +66,6 @@ public BanCommand(@NotNull ModerationActionsStore actionsStore, @NotNull Config
"the amount of days of the message history to delete, none means no messages are deleted.",
true).addChoice("none", 0).addChoice("recent", 1).addChoice("all", 7));

hasRequiredRole =
Pattern.compile(config.getHeavyModerationRolePattern()).asMatchPredicate();
this.actionsStore = Objects.requireNonNull(actionsStore);
}

Expand Down Expand Up @@ -182,9 +175,6 @@ private boolean handleChecks(@NotNull Member bot, @NotNull Member author,
target, event)) {
return false;
}
if (!ModerationUtils.handleHasAuthorRole(ACTION_VERB, hasRequiredRole, author, event)) {
return false;
}
if (!ModerationUtils.handleHasBotPermissions(ACTION_VERB, Permission.BAN_MEMBERS, bot,
guild, event)) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@
import org.slf4j.LoggerFactory;
import org.togetherjava.tjbot.commands.SlashCommandAdapter;
import org.togetherjava.tjbot.commands.SlashCommandVisibility;
import org.togetherjava.tjbot.config.Config;

import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;


/**
* This command can kicks users. Kicking can also be paired with a kick reason. The command will
Expand All @@ -38,22 +34,19 @@ public final class KickCommand extends SlashCommandAdapter {
private static final String REASON_OPTION = "reason";
private static final String COMMAND_NAME = "kick";
private static final String ACTION_VERB = "kick";
private final Predicate<String> hasRequiredRole;
private final ModerationActionsStore actionsStore;

/**
* Constructs an instance.
*
* @param actionsStore used to store actions issued by this command
* @param config the config to use for this
*/
public KickCommand(@NotNull ModerationActionsStore actionsStore, @NotNull Config config) {
public KickCommand(@NotNull ModerationActionsStore actionsStore) {
super(COMMAND_NAME, "Kicks the given user from the server", SlashCommandVisibility.GUILD);

getData().addOption(OptionType.USER, TARGET_OPTION, "The user who you want to kick", true)
.addOption(OptionType.STRING, REASON_OPTION, "Why the user should be kicked", true);

hasRequiredRole = Pattern.compile(config.getSoftModerationRolePattern()).asMatchPredicate();
this.actionsStore = Objects.requireNonNull(actionsStore);
}

Expand Down Expand Up @@ -123,9 +116,6 @@ private boolean handleChecks(@NotNull Member bot, @NotNull Member author,
if (!ModerationUtils.handleCanInteractWithTarget(ACTION_VERB, bot, author, target, event)) {
return false;
}
if (!ModerationUtils.handleHasAuthorRole(ACTION_VERB, hasRequiredRole, author, event)) {
return false;
}
if (!ModerationUtils.handleHasBotPermissions(ACTION_VERB, Permission.KICK_MEMBERS, bot,
guild, event)) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ private static void handleAbsentTarget(@NotNull String actionVerb,
* <li>the target is not member of the guild</li>
* <li>the bot or author do not have enough permissions to interact with the target</li>
* <li>the bot or author do not have enough permissions to interact with the role</li>
* <li>the author does not have the required role for this interaction</li>
* <li>the bot does not have the MANAGE_ROLES permission</li>
* <li>the given reason is too long</li>
* </ul>
Expand All @@ -197,7 +196,6 @@ private static void handleAbsentTarget(@NotNull String actionVerb,
* @param bot the bot executing this interaction
* @param author the author attempting to interact with the target
* @param guild the guild this interaction is executed on
* @param hasRequiredRole a predicate used to identify required roles by their name
* @param reason the reason for this interaction
* @param event the event used to respond to the user
* @return Whether the bot and the author have enough permission
Expand All @@ -206,8 +204,7 @@ private static void handleAbsentTarget(@NotNull String actionVerb,
"squid:S107"})
static boolean handleRoleChangeChecks(@Nullable Role role, @NotNull String actionVerb,
@Nullable Member target, @NotNull Member bot, @NotNull Member author,
@NotNull Guild guild, @NotNull Predicate<? super String> hasRequiredRole,
@NotNull CharSequence reason, @NotNull IReplyCallback event) {
@NotNull Guild guild, @NotNull CharSequence reason, @NotNull IReplyCallback event) {
if (role == null) {
event
.reply("Can not %s the user, unable to find the corresponding role on this server"
Expand All @@ -231,9 +228,6 @@ static boolean handleRoleChangeChecks(@Nullable Role role, @NotNull String actio
if (!handleCanInteractWithRole(bot, author, role, event)) {
return false;
}
if (!handleHasAuthorRole(actionVerb, hasRequiredRole, author, event)) {
return false;
}
if (!handleHasBotPermissions(actionVerb, Permission.MANAGE_ROLES, bot, guild, event)) {
return false;
}
Expand Down Expand Up @@ -267,33 +261,6 @@ static boolean handleHasAuthorPermissions(@NotNull String actionVerb,
return true;
}

/**
* Checks whether the given bot has enough permission to execute the given action. For example
* whether it has enough permissions to ban users.
* <p>
* If not, it will handle the situation and respond to the user.
*
* @param actionVerb the interaction as verb, for example {@code "ban"} or {@code "kick"}
* @param hasRequiredRole a predicate used to identify required roles by their name
* @param author the author attempting to interact with the target
* @param event the event used to respond to the user
* @return Whether the bot has the required permission
*/
@SuppressWarnings("BooleanMethodNameMustStartWithQuestion")
static boolean handleHasAuthorRole(@NotNull String actionVerb,
@NotNull Predicate<? super String> hasRequiredRole, @NotNull Member author,
@NotNull IReplyCallback event) {
if (author.getRoles().stream().map(Role::getName).anyMatch(hasRequiredRole)) {
return true;
}
event
.reply("You can not %s users in this guild since you do not have the required role."
.formatted(actionVerb))
.setEphemeral(true)
.queue();
return false;
}

/**
* Creates a message to be displayed as response to a moderation action.
* <p>
Expand Down
Loading