From 2cb5974e9f88e9dd9b23003c926ac3ae4b7558e0 Mon Sep 17 00:00:00 2001 From: talentedasian Date: Wed, 15 Dec 2021 23:38:19 +0800 Subject: [PATCH 1/7] Draft commit for /modmail slash command. * Add a "guildId" config in Config.json for later use in finding the moderators of the guild. * Initially, the bot response should've been ephemeral but since I was using a selection menu, the way I was disabling it does not work if I set the bot response to ephemeral. --- .../togetherjava/tjbot/commands/Commands.java | 2 + .../commands/modmail/ModmailSlashCommand.java | 91 +++++++++++++++++++ .../org/togetherjava/tjbot/config/Config.java | 9 +- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java index c099717579..429c050a45 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java @@ -7,6 +7,7 @@ import org.togetherjava.tjbot.commands.free.FreeCommand; import org.togetherjava.tjbot.commands.mathcommands.TeXCommand; import org.togetherjava.tjbot.commands.moderation.*; +import org.togetherjava.tjbot.commands.modmail.ModmailSlashCommand; import org.togetherjava.tjbot.commands.tags.TagCommand; import org.togetherjava.tjbot.commands.tags.TagManageCommand; import org.togetherjava.tjbot.commands.tags.TagSystem; @@ -69,6 +70,7 @@ public enum Commands { commands.add(new AuditCommand(actionsStore)); commands.add(new MuteCommand(actionsStore)); commands.add(new UnmuteCommand(actionsStore)); + commands.add(new ModmailSlashCommand()); return commands; } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java new file mode 100644 index 0000000000..582ea4b1e6 --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java @@ -0,0 +1,91 @@ +package org.togetherjava.tjbot.commands.modmail; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent; +import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.selections.SelectOption; +import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu; +import org.jetbrains.annotations.NotNull; +import org.togetherjava.tjbot.commands.SlashCommandAdapter; +import org.togetherjava.tjbot.commands.SlashCommandVisibility; +import org.togetherjava.tjbot.config.Config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ModmailSlashCommand extends SlashCommandAdapter { + + private static final String COMMAND_NAME = "modmail"; + private static final String TARGET_OPTION = "message"; + private static final Config config = Config.getInstance(); + + public ModmailSlashCommand() { + super(COMMAND_NAME, + "sends a message to either a single moderator or on the mod_audit_log channel", + SlashCommandVisibility.GLOBAL); + + getData().addOption(OptionType.STRING, TARGET_OPTION, "The message to send", true); + } + + @Override + public void onSlashCommand(@NotNull SlashCommandEvent event) { + String memberId = event.getUser().getId(); + event.reply(""" + Select the moderator to send message to, or select "All Moderators" to send to + the guild's mod audit channel. + """) + .addActionRow(SelectionMenu.create(generateComponentId(memberId)) + .addOptions(listOfModerators(event.getJDA())) + .build()) + .setEphemeral(false) + .queue(); + } + + @Override + public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List args) { + // Ignore if another user clicked the button + String userId = args.get(0); + if (!userId.equals(Objects.requireNonNull(event.getMember()).getId())) { + event.reply( + "Sorry, but only the user who triggered the command can interact with the menu.") + .setEphemeral(true) + .queue(); + return; + } + + SelectionMenu selectionMenu = event.getSelectionMenu(); + SelectionMenu disabledMenu = selectionMenu.asDisabled(); + if (event.getValues().get(0).equals("all")) { + event.reply("Message now sent to all mods").setEphemeral(true).queue(); + return; + } + event.reply("Message now sent to selected mods").setEphemeral(true).queue(); + + event.getMessage().editMessageComponents(ActionRow.of(disabledMenu)).queue(); + } + + /** + * Creates a list of options containing the moderators for use in the modmail slash command. + * + * @param jda + * @return a list of options in a selection menu + */ + private List listOfModerators(JDA jda) { + List menuChoices = new ArrayList<>(); + + Guild guild = jda.getGuildById(config.getGuildId()); + + guild.findMembersWithRoles(guild.getRolesByName("moderator", true)) + .get() + .stream() + .forEach(mod -> menuChoices.add(SelectOption.of(mod.getEffectiveName(), mod.getId()))); + menuChoices.add(SelectOption.of("All Moderators", "all")); + + return menuChoices; + } + +} diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index 35c077dc13..8dba78b12a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -34,6 +34,7 @@ public final class Config { private final String tagManageRolePattern; private final List freeCommand; + private final long guildId; @SuppressWarnings("ConstructorWithTooManyParameters") @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) @@ -46,7 +47,8 @@ private Config(@JsonProperty("token") String token, @JsonProperty("heavyModerationRolePattern") String heavyModerationRolePattern, @JsonProperty("softModerationRolePattern") String softModerationRolePattern, @JsonProperty("tagManageRolePattern") String tagManageRolePattern, - @JsonProperty("freeCommand") List freeCommand) { + @JsonProperty("freeCommand") List freeCommand, + @JsonProperty("guildId") long guildId) { this.token = token; this.databasePath = databasePath; this.projectWebsite = projectWebsite; @@ -57,6 +59,7 @@ private Config(@JsonProperty("token") String token, this.softModerationRolePattern = softModerationRolePattern; this.tagManageRolePattern = tagManageRolePattern; this.freeCommand = Collections.unmodifiableList(freeCommand); + this.guildId = guildId; } /** @@ -178,4 +181,8 @@ public String getTagManageRolePattern() { public @NotNull Collection getFreeCommandConfig() { return freeCommand; // already unmodifiable } + + public long getGuildId() { + return guildId; + } } From 52744e5fe22e0e905a3a4f8c40183007354a4380 Mon Sep 17 00:00:00 2001 From: talentedasian Date: Thu, 16 Dec 2021 12:06:33 +0800 Subject: [PATCH 2/7] Instead of getting the mods with an expensive query, query it just once and use it for subsequent invocation of the command. --- .../togetherjava/tjbot/commands/Commands.java | 2 +- .../commands/modmail/ModmailSlashCommand.java | 41 +++++++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java index 429c050a45..9919b57ba2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java @@ -70,7 +70,7 @@ public enum Commands { commands.add(new AuditCommand(actionsStore)); commands.add(new MuteCommand(actionsStore)); commands.add(new UnmuteCommand(actionsStore)); - commands.add(new ModmailSlashCommand()); + commands.add(new ModmailSlashCommand(jda)); return commands; } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java index 582ea4b1e6..68561178e3 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java @@ -2,6 +2,7 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent; import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -9,8 +10,11 @@ import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import net.dv8tion.jda.api.interactions.components.selections.SelectionMenu; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.togetherjava.tjbot.commands.SlashCommandAdapter; import org.togetherjava.tjbot.commands.SlashCommandVisibility; +import org.togetherjava.tjbot.commands.free.FreeCommand; import org.togetherjava.tjbot.config.Config; import java.util.ArrayList; @@ -18,17 +22,26 @@ import java.util.Objects; public class ModmailSlashCommand extends SlashCommandAdapter { + private static final Logger logger = LoggerFactory.getLogger(ModmailSlashCommand.class); private static final String COMMAND_NAME = "modmail"; private static final String TARGET_OPTION = "message"; private static final Config config = Config.getInstance(); - public ModmailSlashCommand() { + private static final List mods = new ArrayList<>(); + + private final JDA jda; + + public ModmailSlashCommand(JDA jda) { super(COMMAND_NAME, "sends a message to either a single moderator or on the mod_audit_log channel", SlashCommandVisibility.GLOBAL); getData().addOption(OptionType.STRING, TARGET_OPTION, "The message to send", true); + + this.jda = jda; + + mods.add(SelectOption.of("All Moderators", "all")); } @Override @@ -39,7 +52,7 @@ public void onSlashCommand(@NotNull SlashCommandEvent event) { the guild's mod audit channel. """) .addActionRow(SelectionMenu.create(generateComponentId(memberId)) - .addOptions(listOfModerators(event.getJDA())) + .addOptions(selectionMenuOptions()) .build()) .setEphemeral(false) .queue(); @@ -70,22 +83,24 @@ public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List + * If this method has not yet been called prior to calling this method, it will call an + * expensive query to discord, otherwise, it will return the previous result. * - * @param jda - * @return a list of options in a selection menu + * @return a list of options containing the moderators name to choose from in a selection menu. */ - private List listOfModerators(JDA jda) { - List menuChoices = new ArrayList<>(); - + private List selectionMenuOptions() { Guild guild = jda.getGuildById(config.getGuildId()); - guild.findMembersWithRoles(guild.getRolesByName("moderator", true)) - .get() - .stream() - .forEach(mod -> menuChoices.add(SelectOption.of(mod.getEffectiveName(), mod.getId()))); - menuChoices.add(SelectOption.of("All Moderators", "all")); + // checks if method has been called before. + if (mods.size() == 1) { + guild.findMembersWithRoles(guild.getRolesByName("moderator", true)) + .get() + .stream() + .forEach(mod -> mods.add(SelectOption.of(mod.getEffectiveName(), mod.getId()))); + } - return menuChoices; + return mods; } } From 806bee36446eca14d57f83481e85cdbae9a924d7 Mon Sep 17 00:00:00 2001 From: talentedasian Date: Thu, 16 Dec 2021 12:59:26 +0800 Subject: [PATCH 3/7] Send DM to selected mod. If the selected mod wasn't found, which is highly unlikely, send an ephemeral message and log the unusual behavior --- .../commands/modmail/ModmailSlashCommand.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java index 68561178e3..55f626d9c2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java @@ -3,6 +3,7 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent; import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -17,9 +18,7 @@ import org.togetherjava.tjbot.commands.free.FreeCommand; import org.togetherjava.tjbot.config.Config; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; public class ModmailSlashCommand extends SlashCommandAdapter { private static final Logger logger = LoggerFactory.getLogger(ModmailSlashCommand.class); @@ -28,7 +27,8 @@ public class ModmailSlashCommand extends SlashCommandAdapter { private static final String TARGET_OPTION = "message"; private static final Config config = Config.getInstance(); - private static final List mods = new ArrayList<>(); + private final List mods = new ArrayList<>(); + private final Map modsMap = new HashMap<>(); private final JDA jda; @@ -51,7 +51,8 @@ public void onSlashCommand(@NotNull SlashCommandEvent event) { Select the moderator to send message to, or select "All Moderators" to send to the guild's mod audit channel. """) - .addActionRow(SelectionMenu.create(generateComponentId(memberId)) + .addActionRow(SelectionMenu + .create(generateComponentId(memberId, event.getOption(TARGET_OPTION).getAsString())) .addOptions(selectionMenuOptions()) .build()) .setEphemeral(false) @@ -60,9 +61,11 @@ public void onSlashCommand(@NotNull SlashCommandEvent event) { @Override public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List args) { - // Ignore if another user clicked the button + String message = args.get(1); + // Ignore if another user clicked the button which is only possible when used within the guild. String userId = args.get(0); - if (!userId.equals(Objects.requireNonNull(event.getMember()).getId())) { + if (event.isFromGuild() + && !userId.equals(Objects.requireNonNull(event.getMember()).getId())) { event.reply( "Sorry, but only the user who triggered the command can interact with the menu.") .setEphemeral(true) @@ -70,22 +73,46 @@ public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List channel.sendMessage(message).queue()); + } + /** * Creates a list of options containing the moderators for use in the modmail slash command. *

* If this method has not yet been called prior to calling this method, it will call an * expensive query to discord, otherwise, it will return the previous result. + *

+ * This method also stores the moderators on a map for later use. The map's values are always + * and should be exactly the same with the previous results. * * @return a list of options containing the moderators name to choose from in a selection menu. */ @@ -97,7 +124,11 @@ private List selectionMenuOptions() { guild.findMembersWithRoles(guild.getRolesByName("moderator", true)) .get() .stream() - .forEach(mod -> mods.add(SelectOption.of(mod.getEffectiveName(), mod.getId()))); + .forEach(mod -> { + String modId = mod.getId(); + mods.add(SelectOption.of(mod.getEffectiveName(), modId)); + modsMap.put(modId, mod.getUser()); + }); } return mods; From b563a4cfb772e250b2efc91ae359466fb6cacb6d Mon Sep 17 00:00:00 2001 From: talentedasian Date: Thu, 16 Dec 2021 13:38:31 +0800 Subject: [PATCH 4/7] Reply an ephemeral message to user after sending the message to a mod. This also fixes the failed interaction by discord --- .../commands/modmail/ModmailSlashCommand.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java index 55f626d9c2..6db9669414 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java @@ -2,7 +2,6 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent; import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; @@ -15,7 +14,6 @@ import org.slf4j.LoggerFactory; import org.togetherjava.tjbot.commands.SlashCommandAdapter; import org.togetherjava.tjbot.commands.SlashCommandVisibility; -import org.togetherjava.tjbot.commands.free.FreeCommand; import org.togetherjava.tjbot.config.Config; import java.util.*; @@ -62,7 +60,8 @@ public void onSlashCommand(@NotNull SlashCommandEvent event) { @Override public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List args) { String message = args.get(1); - // Ignore if another user clicked the button which is only possible when used within the guild. + // Ignore if another user clicked the button which is only possible when used within the + // guild. String userId = args.get(0); if (event.isFromGuild() && !userId.equals(Objects.requireNonNull(event.getMember()).getId())) { @@ -78,7 +77,7 @@ public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List channel.sendMessage(message).queue()); + } catch (NullPointerException e) { logger .warn(""" The map storing the moderators is either not in-sync with the list of moderators for the selection menu or @@ -101,8 +103,6 @@ private void sendToMod(String modId, String message, SelectionMenuEvent event) { .setEphemeral(true) .queue(); } - - mod.openPrivateChannel().queue((channel) -> channel.sendMessage(message).queue()); } /** From 7ec6b33bcee1a2bc7c7ec89292bad49a6ed57ae7 Mon Sep 17 00:00:00 2001 From: talentedasian Date: Thu, 16 Dec 2021 15:03:40 +0800 Subject: [PATCH 5/7] Miscellaneous refactors --- .../tjbot/commands/modmail/ModMailUtil.java | 2 + ...lSlashCommand.java => ModmailCommand.java} | 40 ++++++++++++------- .../modmail/ReloadModMailCommand.java | 23 +++++++++++ .../org/togetherjava/tjbot/config/Config.java | 16 +++++--- 4 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java rename application/src/main/java/org/togetherjava/tjbot/commands/modmail/{ModmailSlashCommand.java => ModmailCommand.java} (80%) create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java new file mode 100644 index 0000000000..b471a4a11f --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java @@ -0,0 +1,2 @@ +package org.togetherjava.tjbot.commands.modmail;public class ModMailUtil { +} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java similarity index 80% rename from application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java rename to application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java index 6db9669414..abb007148d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailSlashCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java @@ -30,6 +30,13 @@ public class ModmailSlashCommand extends SlashCommandAdapter { private final JDA jda; + /** + * Creates the reload command, using the given provider as source of truth for the commands to + * reload + * + * @param jda the jda of slash commands to reload when this command is + * triggered + */ public ModmailSlashCommand(JDA jda) { super(COMMAND_NAME, "sends a message to either a single moderator or on the mod_audit_log channel", @@ -88,21 +95,23 @@ public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List channel.sendMessage(message).queue()); - } catch (NullPointerException e) { - logger - .warn(""" - The map storing the moderators is either not in-sync with the list of moderators for the selection menu or - an unknown error has occurred. - """); - - event.reply("The moderator you chose is not on the list of moderators on the guild") - .setEphemeral(true) - .queue(); + return; } + + logger + .warn(""" + The map storing the moderators is either not in-sync with the list of moderators for the selection menu or + an unknown error has occurred. + """); + + event.reply("The moderator you chose is not on the list of moderators on the guild") + .setEphemeral(true) + .queue(); } /** @@ -111,13 +120,14 @@ private void sendToMod(String modId, String message, SelectionMenuEvent event) { * If this method has not yet been called prior to calling this method, it will call an * expensive query to discord, otherwise, it will return the previous result. *

- * This method also stores the moderators on a map for later use. The map's values are always + * This method av lso stores the moderators on a map for later use. The map's values are always * and should be exactly the same with the previous results. * * @return a list of options containing the moderators name to choose from in a selection menu. */ - private List selectionMenuOptions() { - Guild guild = jda.getGuildById(config.getGuildId()); + private @NotNull List selectionMenuOptions() { + Guild guild = Objects.requireNonNull(jda.getGuildById(config.getGuildId()), + "Guild ID is required to use this command"); // checks if method has been called before. if (mods.size() == 1) { diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java new file mode 100644 index 0000000000..f467ee255e --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java @@ -0,0 +1,23 @@ +package org.togetherjava.tjbot.commands.modmail; + +import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import org.jetbrains.annotations.NotNull; +import org.togetherjava.tjbot.commands.SlashCommandAdapter; +import org.togetherjava.tjbot.commands.SlashCommandVisibility; + +public class ReloadModMailCommand extends SlashCommandAdapter { + + /** + * Creates an instance of ReloadMod command. + */ + protected ReloadModMailCommand() { + super("reloadmod", + "Reloads the list of moderators that users can choose to send modmail messages to", + SlashCommandVisibility.GUILD); + } + + @Override + public void onSlashCommand(@NotNull SlashCommandEvent event) { + + } +} diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index 8dba78b12a..a538abd46c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import org.jetbrains.annotations.NotNull; +import org.togetherjava.tjbot.commands.modmail.ModmailSlashCommand; import java.io.IOException; import java.nio.file.Path; @@ -32,9 +33,9 @@ public final class Config { private final String heavyModerationRolePattern; private final String softModerationRolePattern; private final String tagManageRolePattern; + private final String guildId; private final List freeCommand; - private final long guildId; @SuppressWarnings("ConstructorWithTooManyParameters") @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) @@ -47,8 +48,8 @@ private Config(@JsonProperty("token") String token, @JsonProperty("heavyModerationRolePattern") String heavyModerationRolePattern, @JsonProperty("softModerationRolePattern") String softModerationRolePattern, @JsonProperty("tagManageRolePattern") String tagManageRolePattern, - @JsonProperty("freeCommand") List freeCommand, - @JsonProperty("guildId") long guildId) { + @JsonProperty("guildId") String guildId, + @JsonProperty("freeCommand") List freeCommand) { this.token = token; this.databasePath = databasePath; this.projectWebsite = projectWebsite; @@ -58,8 +59,8 @@ private Config(@JsonProperty("token") String token, this.heavyModerationRolePattern = heavyModerationRolePattern; this.softModerationRolePattern = softModerationRolePattern; this.tagManageRolePattern = tagManageRolePattern; - this.freeCommand = Collections.unmodifiableList(freeCommand); this.guildId = guildId; + this.freeCommand = Collections.unmodifiableList(freeCommand); } /** @@ -182,7 +183,12 @@ public String getTagManageRolePattern() { return freeCommand; // already unmodifiable } - public long getGuildId() { + /** + * Gets the id of the guild. See {@link ModmailSlashCommand} for such uses + * + * @return the guildId. + */ + public String getGuildId() { return guildId; } } From 1bfbf42dd5ebc7a2fb9e0a5020633a24822d7bcf Mon Sep 17 00:00:00 2001 From: talentedasian Date: Thu, 16 Dec 2021 23:19:53 +0800 Subject: [PATCH 6/7] * Add /reloadmod command for moderators to reload the list of choices in the /modmail command. * Introduce a utility class for use by other classes when interacting with the ModMailCommand. * Some javadoc. --- .../togetherjava/tjbot/commands/Commands.java | 5 +- .../moderation/ReloadModMailCommand.java | 45 +++++++++++ .../tjbot/commands/modmail/ModMailUtil.java | 79 ++++++++++++++++++- .../commands/modmail/ModmailCommand.java | 49 +++++------- .../modmail/ReloadModMailCommand.java | 23 ------ .../org/togetherjava/tjbot/config/Config.java | 4 +- 6 files changed, 148 insertions(+), 57 deletions(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java index 9919b57ba2..152a1fac84 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java @@ -7,7 +7,7 @@ import org.togetherjava.tjbot.commands.free.FreeCommand; import org.togetherjava.tjbot.commands.mathcommands.TeXCommand; import org.togetherjava.tjbot.commands.moderation.*; -import org.togetherjava.tjbot.commands.modmail.ModmailSlashCommand; +import org.togetherjava.tjbot.commands.modmail.ModmailCommand; import org.togetherjava.tjbot.commands.tags.TagCommand; import org.togetherjava.tjbot.commands.tags.TagManageCommand; import org.togetherjava.tjbot.commands.tags.TagSystem; @@ -70,7 +70,8 @@ public enum Commands { commands.add(new AuditCommand(actionsStore)); commands.add(new MuteCommand(actionsStore)); commands.add(new UnmuteCommand(actionsStore)); - commands.add(new ModmailSlashCommand(jda)); + commands.add(new ModmailCommand(jda)); + commands.add(new ReloadModMailCommand(jda)); return commands; } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java new file mode 100644 index 0000000000..b02293e001 --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java @@ -0,0 +1,45 @@ +package org.togetherjava.tjbot.commands.moderation; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import org.jetbrains.annotations.NotNull; +import org.togetherjava.tjbot.commands.SlashCommandAdapter; +import org.togetherjava.tjbot.commands.SlashCommandVisibility; +import org.togetherjava.tjbot.commands.modmail.ModMailUtil; +import org.togetherjava.tjbot.config.Config; + +import java.util.Objects; + +public class ReloadModMailCommand extends SlashCommandAdapter { + + private final JDA jda; + private static final Config config = Config.getInstance(); + + /** + * Creates an instance of ReloadMod command. + * + * @param jda the JDA instance to find the guild. + */ + public ReloadModMailCommand(@NotNull JDA jda) { + super("reloadmod", "Reloads the list of moderators in the modmail selection menu", + SlashCommandVisibility.GUILD); + + this.jda = Objects.requireNonNull(jda); + } + + @Override + public void onSlashCommand(@NotNull SlashCommandEvent event) { + Guild guild = Objects.requireNonNull(jda.getGuildById(config.getGuildId()), + "A Guild is required to use this command. Perhaps the bot isn't on the guild yet"); + + if (ModMailUtil.doesUserHaveModRole(event.getMember(), guild)) { + event.reply("Only moderators can use this command.").setEphemeral(true).queue(); + return; + } + + ModMailUtil.loadMenuOptions(guild); + event.reply("List of moderators has now been reloaded.").setEphemeral(true).queue(); + } + +} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java index b471a4a11f..aff0fc72cc 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java @@ -1,2 +1,79 @@ -package org.togetherjava.tjbot.commands.modmail;public class ModMailUtil { +package org.togetherjava.tjbot.commands.modmail; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.interactions.components.selections.SelectOption; +import org.jetbrains.annotations.NotNull; +import org.togetherjava.tjbot.config.Config; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +/** + * Utility methods that interact either directly or indirectly with the {@link ModmailCommand}. + */ +public class ModMailUtil { + + public static final Predicate isModRole = + Pattern.compile(Config.getInstance().getHeavyModerationRolePattern()) + .asMatchPredicate(); + + private ModMailUtil() { + throw new UnsupportedOperationException(); + } + + /** + * Clears the list of moderators in the {@link ModmailCommand}. + */ + public static void clearMods() { + ModmailCommand.mods.clear(); + ModmailCommand.modsMap.clear(); + } + + /** + * Finds the moderators on the given guild and stores it for later use. + *

+ * This call is expensive to make thus, it shall only be used preferably once by storing the + * result somewhere or seldomly when moderators want to reload the list of moderators to choose + * from from the {@link ModmailCommand}. + *

+ * The previous result stored by this method will be overwritten if there was any. + */ + public static void loadMenuOptions(@NotNull Guild guild) { + clearMods(); + + Role modRole = getModRole(guild) + .orElseThrow(() -> new IllegalStateException("No moderator role found")); + + guild.findMembersWithRoles(modRole).get().stream().forEach(mod -> { + String modId = mod.getId(); + ModmailCommand.mods.add(SelectOption.of(mod.getEffectiveName(), modId)); + ModmailCommand.modsMap.put(modId, mod.getUser()); + }); + } + + /** + * Gets the moderator role. + * + * @param guild the guild to get the moderator role from + * @return the moderator role, if found + */ + public static @NotNull Optional getModRole(@NotNull Guild guild) { + return guild.getRoles().stream().filter(role -> isModRole.test(role.getName())).findAny(); + } + + /** + * Checks whether the given member is a moderator on the given guild. + * + * @param member the member to check for moderator role. + * @param guild the guild to get the moderator role from. + * @return + */ + public static boolean doesUserHaveModRole(@NotNull Member member, @NotNull Guild guild) { + return member.canInteract(getModRole(guild) + .orElseThrow(() -> new IllegalStateException("No moderator role found"))); + } + } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java index abb007148d..c1cddd123d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java @@ -18,33 +18,32 @@ import java.util.*; -public class ModmailSlashCommand extends SlashCommandAdapter { - private static final Logger logger = LoggerFactory.getLogger(ModmailSlashCommand.class); +public class ModmailCommand extends SlashCommandAdapter { + private static final Logger logger = LoggerFactory.getLogger(ModmailCommand.class); private static final String COMMAND_NAME = "modmail"; private static final String TARGET_OPTION = "message"; private static final Config config = Config.getInstance(); - private final List mods = new ArrayList<>(); - private final Map modsMap = new HashMap<>(); + static final List mods = new ArrayList<>(); + static final Map modsMap = new HashMap<>(); private final JDA jda; /** - * Creates the reload command, using the given provider as source of truth for the commands to - * reload + * Creates an instance of the ModMail command. * - * @param jda the jda of slash commands to reload when this command is - * triggered + * @param jda the {@link JDA} instance of all slash commands to find the target guild or the + * guild where the moderators are. */ - public ModmailSlashCommand(JDA jda) { + public ModmailCommand(@NotNull JDA jda) { super(COMMAND_NAME, "sends a message to either a single moderator or on the mod_audit_log channel", SlashCommandVisibility.GLOBAL); getData().addOption(OptionType.STRING, TARGET_OPTION, "The message to send", true); - this.jda = jda; + this.jda = Objects.requireNonNull(jda); mods.add(SelectOption.of("All Moderators", "all")); } @@ -52,13 +51,19 @@ public ModmailSlashCommand(JDA jda) { @Override public void onSlashCommand(@NotNull SlashCommandEvent event) { String memberId = event.getUser().getId(); + + // checks if selection menu already contains the moderators + if (mods.size() == 1) { + loadMenuOptions(); + } + event.reply(""" Select the moderator to send message to, or select "All Moderators" to send to the guild's mod audit channel. """) .addActionRow(SelectionMenu .create(generateComponentId(memberId, event.getOption(TARGET_OPTION).getAsString())) - .addOptions(selectionMenuOptions()) + .addOptions(mods) .build()) .setEphemeral(false) .queue(); @@ -120,28 +125,14 @@ private void sendToMod(@NotNull String modId, @NotNull String message, * If this method has not yet been called prior to calling this method, it will call an * expensive query to discord, otherwise, it will return the previous result. *

- * This method av lso stores the moderators on a map for later use. The map's values are always + * This method also stores the moderators on a map for later use. The map's values are always * and should be exactly the same with the previous results. - * - * @return a list of options containing the moderators name to choose from in a selection menu. */ - private @NotNull List selectionMenuOptions() { + private void loadMenuOptions() { Guild guild = Objects.requireNonNull(jda.getGuildById(config.getGuildId()), - "Guild ID is required to use this command"); - - // checks if method has been called before. - if (mods.size() == 1) { - guild.findMembersWithRoles(guild.getRolesByName("moderator", true)) - .get() - .stream() - .forEach(mod -> { - String modId = mod.getId(); - mods.add(SelectOption.of(mod.getEffectiveName(), modId)); - modsMap.put(modId, mod.getUser()); - }); - } + "A Guild is required to use this command. Perhaps the bot isn't on the guild yet"); - return mods; + ModMailUtil.loadMenuOptions(guild); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java deleted file mode 100644 index f467ee255e..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ReloadModMailCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.togetherjava.tjbot.commands.modmail; - -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; -import org.jetbrains.annotations.NotNull; -import org.togetherjava.tjbot.commands.SlashCommandAdapter; -import org.togetherjava.tjbot.commands.SlashCommandVisibility; - -public class ReloadModMailCommand extends SlashCommandAdapter { - - /** - * Creates an instance of ReloadMod command. - */ - protected ReloadModMailCommand() { - super("reloadmod", - "Reloads the list of moderators that users can choose to send modmail messages to", - SlashCommandVisibility.GUILD); - } - - @Override - public void onSlashCommand(@NotNull SlashCommandEvent event) { - - } -} diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index a538abd46c..d62c42b768 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import org.jetbrains.annotations.NotNull; -import org.togetherjava.tjbot.commands.modmail.ModmailSlashCommand; +import org.togetherjava.tjbot.commands.modmail.ModmailCommand; import java.io.IOException; import java.nio.file.Path; @@ -184,7 +184,7 @@ public String getTagManageRolePattern() { } /** - * Gets the id of the guild. See {@link ModmailSlashCommand} for such uses + * Gets the id of the guild. See {@link ModmailCommand} for such uses * * @return the guildId. */ From e154d7f5100dba4645ecefaa7e798b1fb624a176 Mon Sep 17 00:00:00 2001 From: talentedasian Date: Fri, 17 Dec 2021 15:04:15 +0800 Subject: [PATCH 7/7] * Completely redo the way how select options are being generated. Replace all loadOptions with the onReady event but still delegating to the ModmailUtils to query discord. * Move ReloadModmailCommand from a separate class to the ModmailCommand class. --- .../togetherjava/tjbot/commands/Commands.java | 6 +- .../moderation/ReloadModMailCommand.java | 45 ------ .../commands/modmail/ModmailCommand.java | 129 +++++++++++------- .../{ModMailUtil.java => ModmailUtil.java} | 56 ++++---- 4 files changed, 117 insertions(+), 119 deletions(-) delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java rename application/src/main/java/org/togetherjava/tjbot/commands/modmail/{ModMailUtil.java => ModmailUtil.java} (52%) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java index 152a1fac84..4f58449d44 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/Commands.java @@ -70,8 +70,10 @@ public enum Commands { commands.add(new AuditCommand(actionsStore)); commands.add(new MuteCommand(actionsStore)); commands.add(new UnmuteCommand(actionsStore)); - commands.add(new ModmailCommand(jda)); - commands.add(new ReloadModMailCommand(jda)); + + ModmailCommand modmailCommand = new ModmailCommand(); + commands.add(modmailCommand); + commands.add(modmailCommand.new ReloadModmailCommand()); return commands; } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java deleted file mode 100644 index b02293e001..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ReloadModMailCommand.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.togetherjava.tjbot.commands.moderation; - -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; -import org.jetbrains.annotations.NotNull; -import org.togetherjava.tjbot.commands.SlashCommandAdapter; -import org.togetherjava.tjbot.commands.SlashCommandVisibility; -import org.togetherjava.tjbot.commands.modmail.ModMailUtil; -import org.togetherjava.tjbot.config.Config; - -import java.util.Objects; - -public class ReloadModMailCommand extends SlashCommandAdapter { - - private final JDA jda; - private static final Config config = Config.getInstance(); - - /** - * Creates an instance of ReloadMod command. - * - * @param jda the JDA instance to find the guild. - */ - public ReloadModMailCommand(@NotNull JDA jda) { - super("reloadmod", "Reloads the list of moderators in the modmail selection menu", - SlashCommandVisibility.GUILD); - - this.jda = Objects.requireNonNull(jda); - } - - @Override - public void onSlashCommand(@NotNull SlashCommandEvent event) { - Guild guild = Objects.requireNonNull(jda.getGuildById(config.getGuildId()), - "A Guild is required to use this command. Perhaps the bot isn't on the guild yet"); - - if (ModMailUtil.doesUserHaveModRole(event.getMember(), guild)) { - event.reply("Only moderators can use this command.").setEphemeral(true).queue(); - return; - } - - ModMailUtil.loadMenuOptions(guild); - event.reply("List of moderators has now been reloaded.").setEphemeral(true).queue(); - } - -} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java index c1cddd123d..463b9ca482 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailCommand.java @@ -1,8 +1,7 @@ package org.togetherjava.tjbot.commands.modmail; -import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.ReadyEvent; import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent; import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -16,8 +15,14 @@ import org.togetherjava.tjbot.commands.SlashCommandVisibility; import org.togetherjava.tjbot.config.Config; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +/** + * Command that either sends a direct message to a moderator or sends the message to the dedicated + * mod audit channel by the moderators. + */ public class ModmailCommand extends SlashCommandAdapter { private static final Logger logger = LoggerFactory.getLogger(ModmailCommand.class); @@ -25,26 +30,18 @@ public class ModmailCommand extends SlashCommandAdapter { private static final String TARGET_OPTION = "message"; private static final Config config = Config.getInstance(); - static final List mods = new ArrayList<>(); - static final Map modsMap = new HashMap<>(); - - private final JDA jda; + final List mods = new ArrayList<>(); /** * Creates an instance of the ModMail command. - * - * @param jda the {@link JDA} instance of all slash commands to find the target guild or the - * guild where the moderators are. */ - public ModmailCommand(@NotNull JDA jda) { + public ModmailCommand() { super(COMMAND_NAME, "sends a message to either a single moderator or on the mod_audit_log channel", SlashCommandVisibility.GLOBAL); getData().addOption(OptionType.STRING, TARGET_OPTION, "The message to send", true); - this.jda = Objects.requireNonNull(jda); - mods.add(SelectOption.of("All Moderators", "all")); } @@ -52,11 +49,6 @@ public ModmailCommand(@NotNull JDA jda) { public void onSlashCommand(@NotNull SlashCommandEvent event) { String memberId = event.getUser().getId(); - // checks if selection menu already contains the moderators - if (mods.size() == 1) { - loadMenuOptions(); - } - event.reply(""" Select the moderator to send message to, or select "All Moderators" to send to the guild's mod audit channel. @@ -72,11 +64,9 @@ public void onSlashCommand(@NotNull SlashCommandEvent event) { @Override public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List args) { String message = args.get(1); - // Ignore if another user clicked the button which is only possible when used within the - // guild. + // Ignore if another user clicked the button. String userId = args.get(0); - if (event.isFromGuild() - && !userId.equals(Objects.requireNonNull(event.getMember()).getId())) { + if (!userId.equals(event.getUser().getId())) { event.reply( "Sorry, but only the user who triggered the command can interact with the menu.") .setEphemeral(true) @@ -94,45 +84,86 @@ public void onSelectionMenu(@NotNull SelectionMenuEvent event, @NotNull List channel.sendMessage(message).queue()); + boolean wasSent = sendToMod(modId, message, event); + if (!wasSent) { + event.reply("The moderator you chose was not found on the guild.") + .setEphemeral(true) + .queue(); + + String modSelectedByUser = event.getSelectedOptions().get(0).getLabel(); + logger.warn(""" + Moderator '{}' chosen by user is not on the guild. Use the /reloadmod command + to update the list of moderators. + """, modSelectedByUser); + return; } - logger - .warn(""" - The map storing the moderators is either not in-sync with the list of moderators for the selection menu or - an unknown error has occurred. - """); + event.reply("Message now sent to selected moderator").setEphemeral(true).queue(); + } - event.reply("The moderator you chose is not on the list of moderators on the guild") - .setEphemeral(true) - .queue(); + /** + * Populates the list of moderators and stores it into a list to avoid querying an expensive + * call to discord everytime the command is used. + * + * @param event the event that triggered this method + */ + @Override + public void onReady(@NotNull ReadyEvent event) { + Guild guild = event.getJDA().getGuildById(config.getGuildId()); + ModmailUtil.listOfMod(guild, mods); + } + + private boolean sendToMod(@NotNull String modId, @NotNull String message, + @NotNull SelectionMenuEvent event) { + // the else is when the user invoked the command not on the context of a guild. + Guild guild = Objects.requireNonNullElse(event.getGuild(), + event.getJDA().getGuildById(config.getGuildId())); + + return !guild.retrieveMemberById(modId) + .submit() + .thenCompose(user -> user.getUser().openPrivateChannel().submit()) + .thenAccept(channel -> channel + .sendMessageEmbeds(ModmailUtil.messageEmbed(event.getUser().getName(), message)) + .queue()) + .whenComplete((v, err) -> { + if (err != null) + err.printStackTrace(); + }) + .isCompletedExceptionally(); } /** - * Creates a list of options containing the moderators for use in the modmail slash command. - *

- * If this method has not yet been called prior to calling this method, it will call an - * expensive query to discord, otherwise, it will return the previous result. + * Reloads the list of moderators to choose from from the {@link ModmailCommand}. *

- * This method also stores the moderators on a map for later use. The map's values are always - * and should be exactly the same with the previous results. + * Only members who have the Moderator role can use this command. */ - private void loadMenuOptions() { - Guild guild = Objects.requireNonNull(jda.getGuildById(config.getGuildId()), - "A Guild is required to use this command. Perhaps the bot isn't on the guild yet"); + public class ReloadModmailCommand extends SlashCommandAdapter { + + private static final String COMMAND_NAME = "reloadmod"; + + public ReloadModmailCommand() { + super(COMMAND_NAME, "reloads the moderators in the modmail command", + SlashCommandVisibility.GUILD); + } + + @Override + public void onSlashCommand(@NotNull SlashCommandEvent event) { + mods.clear(); + ModmailUtil.listOfMod(event.getGuild(), mods); + + if (ModmailUtil.doesUserHaveModRole(event.getMember(), event.getGuild())) { + event.reply("List of moderators has now been updated.").setEphemeral(true).queue(); + return; + } + + event.reply("Only moderators can use this command.").setEphemeral(true).queue(); + } - ModMailUtil.loadMenuOptions(guild); } + } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailUtil.java similarity index 52% rename from application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java rename to application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailUtil.java index aff0fc72cc..dc0c90da40 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModMailUtil.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/modmail/ModmailUtil.java @@ -1,12 +1,16 @@ package org.togetherjava.tjbot.commands.modmail; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import org.jetbrains.annotations.NotNull; import org.togetherjava.tjbot.config.Config; +import java.awt.*; +import java.util.List; import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -14,44 +18,33 @@ /** * Utility methods that interact either directly or indirectly with the {@link ModmailCommand}. */ -public class ModMailUtil { +public class ModmailUtil { public static final Predicate isModRole = Pattern.compile(Config.getInstance().getHeavyModerationRolePattern()) .asMatchPredicate(); - private ModMailUtil() { + private ModmailUtil() { throw new UnsupportedOperationException(); } - /** - * Clears the list of moderators in the {@link ModmailCommand}. - */ - public static void clearMods() { - ModmailCommand.mods.clear(); - ModmailCommand.modsMap.clear(); - } - /** * Finds the moderators on the given guild and stores it for later use. *

- * This call is expensive to make thus, it shall only be used preferably once by storing the - * result somewhere or seldomly when moderators want to reload the list of moderators to choose - * from from the {@link ModmailCommand}. + * This call is expensive, thus, it should only be used preferably once by storing the result + * from the given list or seldomly when moderators want to reload the list of moderators to + * choose from from the {@link ModmailCommand}. *

- * The previous result stored by this method will be overwritten if there was any. + * Since the elements in the given list will not be overwritten, the caller is responsible in + * doing such actions. */ - public static void loadMenuOptions(@NotNull Guild guild) { - clearMods(); - + public static void listOfMod(@NotNull Guild guild, List modsOptions) { Role modRole = getModRole(guild) .orElseThrow(() -> new IllegalStateException("No moderator role found")); - guild.findMembersWithRoles(modRole).get().stream().forEach(mod -> { - String modId = mod.getId(); - ModmailCommand.mods.add(SelectOption.of(mod.getEffectiveName(), modId)); - ModmailCommand.modsMap.put(modId, mod.getUser()); - }); + guild.findMembersWithRoles(modRole) + .onSuccess(mods -> mods.forEach( + mod -> modsOptions.add(SelectOption.of(mod.getEffectiveName(), mod.getId())))); } /** @@ -66,14 +59,31 @@ public static void loadMenuOptions(@NotNull Guild guild) { /** * Checks whether the given member is a moderator on the given guild. + *

+ * See {@link Config#getHeavyModerationRolePattern()}. * * @param member the member to check for moderator role. * @param guild the guild to get the moderator role from. - * @return + * @return true if the member has the role Moderator */ public static boolean doesUserHaveModRole(@NotNull Member member, @NotNull Guild guild) { return member.canInteract(getModRole(guild) .orElseThrow(() -> new IllegalStateException("No moderator role found"))); } + /** + * Creates a color black {@link MessageEmbed} with a non-inline field of the supplied message. + * + * @param user the user who invoked the command. + * @param message the message the user wants to send to to a moderator or the moderators. + * @return returns a {@link MessageEmbed} to send to the moderator. + */ + public static MessageEmbed messageEmbed(@NotNull String user, @NotNull String message) { + return new EmbedBuilder().setAuthor("Modmail Command invoked") + .setColor(Color.BLACK) + .setTitle("Message from user '%s' who used /modmail command".formatted(user)) + .addField("Message", message, false) + .build(); + } + }