From 30168aad940d30ca6fd86620533aa83a982885fd Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 18 Sep 2022 16:43:40 +0530 Subject: [PATCH 01/11] grouped help thread commands --- .../togetherjava/tjbot/commands/Features.java | 4 +- .../help/ChangeHelpCategoryCommand.java | 128 ------------ .../commands/help/ChangeHelpTitleCommand.java | 107 ---------- .../tjbot/commands/help/CloseCommand.java | 75 ------- .../commands/help/HelpThreadCommand.java | 192 ++++++++++++++++++ 5 files changed, 193 insertions(+), 313 deletions(-) delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpCategoryCommand.java delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/help/CloseCommand.java create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java index 02603d25ba..f05b626572 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java @@ -125,9 +125,7 @@ public static Collection createFeatures(JDA jda, Database database, Con features.add(new WhoIsCommand()); features.add(new WolframAlphaCommand(config)); features.add(new AskCommand(config, helpSystemHelper)); - features.add(new CloseCommand()); - features.add(new ChangeHelpCategoryCommand(config, helpSystemHelper)); - features.add(new ChangeHelpTitleCommand(helpSystemHelper)); + features.add(new HelpThreadCommand(config, helpSystemHelper)); // Mixtures features.add(new HelpThreadOverviewUpdater(config, helpSystemHelper)); diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpCategoryCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpCategoryCommand.java deleted file mode 100644 index 9235ba053d..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpCategoryCommand.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.togetherjava.tjbot.commands.help; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.ThreadChannel; -import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.interactions.InteractionHook; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.requests.RestAction; -import org.togetherjava.tjbot.commands.CommandVisibility; -import org.togetherjava.tjbot.commands.SlashCommandAdapter; -import org.togetherjava.tjbot.config.Config; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Locale; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * Implements the {@code /change-help-category} command, which is able to change the category of a - * help thread. - *

- * This is either used for threads that do not have categories yet (as created by - * {@link ImplicitAskListener}), or simply to adjust the category afterwards. - *

- * Changing the category will invite all helpers interested into the corresponding category to the - * question thread. - */ -public final class ChangeHelpCategoryCommand extends SlashCommandAdapter { - private static final String CATEGORY_OPTION = "category"; - - private static final int COOLDOWN_DURATION_VALUE = 30; - private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES; - - private final HelpSystemHelper helper; - private final Cache helpThreadIdToLastCategoryChange; - - /** - * Creates a new instance. - * - * @param config the config to use - * @param helper the helper to use - */ - public ChangeHelpCategoryCommand(Config config, HelpSystemHelper helper) { - super("change-help-category", "changes the category of a help thread", - CommandVisibility.GUILD); - - OptionData category = new OptionData(OptionType.STRING, CATEGORY_OPTION, - "select what describes the question the best", true); - config.getHelpSystem() - .getCategories() - .forEach(categoryText -> category.addChoice(categoryText, categoryText)); - - getData().addOptions(category); - - helpThreadIdToLastCategoryChange = Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); - - this.helper = helper; - } - - @Override - public void onSlashCommand(SlashCommandInteractionEvent event) { - String category = event.getOption(CATEGORY_OPTION).getAsString(); - - ThreadChannel helpThread = event.getThreadChannel(); - if (helpThread.isArchived()) { - event.reply("This thread is already closed.").setEphemeral(true).queue(); - return; - } - - if (isHelpThreadOnCooldown(helpThread)) { - event - .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( - COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT.toString().toLowerCase(Locale.US))) - .setEphemeral(true) - .queue(); - return; - } - helpThreadIdToLastCategoryChange.put(helpThread.getIdLong(), Instant.now()); - - event.deferReply().queue(); - - helper.renameChannelToCategory(helpThread, category) - .flatMap(any -> sendCategoryChangedMessage(helpThread.getGuild(), event.getHook(), - helpThread, category)) - .queue(); - } - - private RestAction sendCategoryChangedMessage(Guild guild, InteractionHook hook, - ThreadChannel helpThread, String category) { - String changedContent = "Changed the category to **%s**.".formatted(category); - var action = hook.editOriginal(changedContent); - - Optional helperRole = helper.handleFindRoleForCategory(category, guild); - if (helperRole.isEmpty()) { - return action; - } - - // We want to invite all members of a role, but without hard-pinging them. However, - // manually inviting them is cumbersome and can hit rate limits. - // Instead, we abuse the fact that a role-ping through an edit will not hard-ping users, - // but still invite them to a thread. - String headsUpPattern = "%splease have a look, thanks."; - String headsUpWithoutRole = headsUpPattern.formatted(""); - String headsUpWithRole = - headsUpPattern.formatted(helperRole.orElseThrow().getAsMention() + " "); - return action.flatMap(any -> helpThread.sendMessage(headsUpWithoutRole) - .flatMap(message -> message.editMessage(headsUpWithRole))); - } - - private boolean isHelpThreadOnCooldown(ThreadChannel helpThread) { - return Optional - .ofNullable(helpThreadIdToLastCategoryChange.getIfPresent(helpThread.getIdLong())) - .map(lastCategoryChange -> lastCategoryChange.plus(COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT)) - .filter(Instant.now()::isBefore) - .isPresent(); - } -} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java deleted file mode 100644 index 1510d54f2b..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.togetherjava.tjbot.commands.help; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import net.dv8tion.jda.api.entities.ThreadChannel; -import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import org.togetherjava.tjbot.commands.CommandVisibility; -import org.togetherjava.tjbot.commands.SlashCommandAdapter; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Locale; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MAX; -import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MIN; - -/** - * Implements the {@code /change-help-title} command, which is able to change the title of a help - * thread. - *

- * This is to adjust a bad title in hindsight, for example if it was automatically created by - * {@link ImplicitAskListener}. - */ -public final class ChangeHelpTitleCommand extends SlashCommandAdapter { - private static final String TITLE_OPTION = "title"; - - private static final int COOLDOWN_DURATION_VALUE = 30; - private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES; - - private final HelpSystemHelper helper; - private final Cache helpThreadIdToLastTitleChange; - - /** - * Creates a new instance. - * - * @param helper the helper to use - */ - public ChangeHelpTitleCommand(HelpSystemHelper helper) { - super("change-help-title", "changes the title of a help thread", CommandVisibility.GUILD); - - getData().addOption(OptionType.STRING, TITLE_OPTION, "short and to the point", true); - - helpThreadIdToLastTitleChange = Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); - - this.helper = helper; - } - - @Override - public void onSlashCommand(SlashCommandInteractionEvent event) { - String title = event.getOption(TITLE_OPTION).getAsString(); - - if (!handleIsValidTitle(title, event)) { - return; - } - - ThreadChannel helpThread = event.getThreadChannel(); - if (helpThread.isArchived()) { - event.reply("This thread is already closed.").setEphemeral(true).queue(); - return; - } - - if (isHelpThreadOnCooldown(helpThread)) { - event - .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( - COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT.toString().toLowerCase(Locale.US))) - .setEphemeral(true) - .queue(); - return; - } - helpThreadIdToLastTitleChange.put(helpThread.getIdLong(), Instant.now()); - - helper.renameChannelToTitle(helpThread, title) - .flatMap(any -> event.reply("Changed the title to **%s**.".formatted(title))) - .queue(); - } - - private boolean isHelpThreadOnCooldown(ThreadChannel helpThread) { - return Optional - .ofNullable(helpThreadIdToLastTitleChange.getIfPresent(helpThread.getIdLong())) - .map(lastCategoryChange -> lastCategoryChange.plus(COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT)) - .filter(Instant.now()::isBefore) - .isPresent(); - } - - private boolean handleIsValidTitle(CharSequence title, IReplyCallback event) { - if (HelpSystemHelper.isTitleValid(title)) { - return true; - } - - event.reply( - "Sorry, but the title length (after removal of special characters) has to be between %d and %d." - .formatted(TITLE_COMPACT_LENGTH_MIN, TITLE_COMPACT_LENGTH_MAX)) - .setEphemeral(true) - .queue(); - - return false; - } -} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/CloseCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/CloseCommand.java deleted file mode 100644 index c50b14d1a7..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/CloseCommand.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.togetherjava.tjbot.commands.help; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.ThreadChannel; -import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import org.togetherjava.tjbot.commands.CommandVisibility; -import org.togetherjava.tjbot.commands.SlashCommandAdapter; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Locale; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * Implements the {@code /close} command to close question threads. - *

- * Can be used in active (non-archived) question threads. Will close, i.e. archive, the thread upon - * use. Meant to be used once a question has been resolved. - */ -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 Cache helpThreadIdToLastClose; - - /** - * Creates a new instance. - */ - public CloseCommand() { - super("close", "Close this question thread", CommandVisibility.GUILD); - - helpThreadIdToLastClose = Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); - } - - @Override - public void onSlashCommand(SlashCommandInteractionEvent event) { - ThreadChannel helpThread = event.getThreadChannel(); - if (helpThread.isArchived()) { - event.reply("This thread is already closed.").setEphemeral(true).queue(); - return; - } - - if (isHelpThreadOnCooldown(helpThread)) { - event - .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( - COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT.toString().toLowerCase(Locale.US))) - .setEphemeral(true) - .queue(); - return; - } - helpThreadIdToLastClose.put(helpThread.getIdLong(), Instant.now()); - - MessageEmbed embed = new EmbedBuilder().setDescription("Closed the thread.") - .setColor(HelpSystemHelper.AMBIENT_COLOR) - .build(); - - event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); - } - - private boolean isHelpThreadOnCooldown(ThreadChannel helpThread) { - return Optional.ofNullable(helpThreadIdToLastClose.getIfPresent(helpThread.getIdLong())) - .map(lastCategoryChange -> lastCategoryChange.plus(COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT)) - .filter(Instant.now()::isBefore) - .isPresent(); - } -} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java new file mode 100644 index 0000000000..ae1063088b --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -0,0 +1,192 @@ +package org.togetherjava.tjbot.commands.help; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionHook; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.WebhookMessageUpdateAction; +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.temporal.ChronoUnit; +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MAX; +import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MIN; + +/** + * Implements the {@code /help-thread} command, which are contains special command for help threads + * only + */ +public final class HelpThreadCommand 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 helpThreadIdToLastCategoryChange = Caffeine.newBuilder() + .maximumSize(1_000) + .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) + .build(); + private final Cache helpThreadIdToLastTitleChange = Caffeine.newBuilder() + .maximumSize(1_000) + .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) + .build(); + private final Cache helpThreadIdToLastClose = Caffeine.newBuilder() + .maximumSize(1_000) + .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) + .build(); + + /** + * Creates a new instance. + * + * @param config the config to use + * @param helper the helper to use + */ + public HelpThreadCommand(Config config, HelpSystemHelper helper) { + super("help-thread", "Help thread specific commands", SlashCommandVisibility.GUILD); + + OptionData category = new OptionData(OptionType.STRING, "category", "new category", true); + config.getHelpSystem() + .getCategories() + .forEach(categoryText -> category.addChoice(categoryText, categoryText)); + + getData().addSubcommandGroups( + new SubcommandGroupData("change", "Change the details of this help thread") + .addSubcommands( + new SubcommandData("category", + "Change the category of this help thread").addOptions(category), + new SubcommandData("title", "Change the title of this help thread") + .addOption(OptionType.STRING, "title", "new title", true))); + + getData().addSubcommands(new SubcommandData("close", "Close this help thread")); + + this.helper = helper; + } + + @Override + public void onSlashCommand(SlashCommandInteractionEvent event) { + ThreadChannel helpThread = event.getThreadChannel(); + if (helpThread.isArchived()) { + event.reply("This thread is already closed.").setEphemeral(true).queue(); + return; + } + + boolean isOnCooldown = false; + + switch (event.getSubcommandName()) { + case "category" -> { + if (isHelpThreadOnCooldown(helpThreadIdToLastCategoryChange, helpThread)) { + isOnCooldown = true; + break; + } + changeCategory(event, helpThread); + } + case "title" -> { + if (isHelpThreadOnCooldown(helpThreadIdToLastTitleChange, helpThread)) { + isOnCooldown = true; + break; + } + String title = event.getOption("title").getAsString(); + + if (!HelpSystemHelper.isTitleValid(title)) { + event.reply( + "Sorry, but the title length (after removal of special characters) has to be between %d and %d." + .formatted(TITLE_COMPACT_LENGTH_MIN, TITLE_COMPACT_LENGTH_MAX)) + .setEphemeral(true) + .queue(); + } + + changeTitle(event, helpThread); + } + case "close" -> { + if (isHelpThreadOnCooldown(helpThreadIdToLastClose, helpThread)) { + isOnCooldown = true; + break; + } + close(event, helpThread); + } + } + + if (isOnCooldown) { + event + .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( + COOLDOWN_DURATION_VALUE, + COOLDOWN_DURATION_UNIT.toString().toLowerCase(Locale.US))) + .setEphemeral(true) + .queue(); + } + } + + private boolean isHelpThreadOnCooldown(Cache helpThreadIdToLastAction, + ThreadChannel helpThread) { + return Optional.ofNullable(helpThreadIdToLastAction.getIfPresent(helpThread.getIdLong())) + .map(lastAction -> lastAction.plus(COOLDOWN_DURATION_VALUE, COOLDOWN_DURATION_UNIT)) + .filter(Instant.now()::isBefore) + .isPresent(); + } + + private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + String category = event.getOption("category").getAsString(); + + helpThreadIdToLastCategoryChange.put(helpThread.getIdLong(), Instant.now()); + + event.deferReply().queue(); + + helper.renameChannelToCategory(helpThread, category) + .flatMap(any -> sendCategoryChangedMessage(helpThread.getGuild(), event.getHook(), + helpThread, category)) + .queue(); + } + + private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + String title = event.getOption("title").getAsString(); + + helpThreadIdToLastTitleChange.put(helpThread.getIdLong(), Instant.now()); + + helper.renameChannelToTitle(helpThread, title) + .flatMap(any -> event.reply("Changed the title to **%s**.".formatted(title))) + .queue(); + } + + private void close(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + helpThreadIdToLastClose.put(helpThread.getIdLong(), Instant.now()); + + MessageEmbed embed = new EmbedBuilder().setDescription("Closed the thread.") + .setColor(HelpSystemHelper.AMBIENT_COLOR) + .build(); + + event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); + } + + private RestAction sendCategoryChangedMessage(Guild guild, InteractionHook hook, + ThreadChannel helpThread, String category) { + String changedContent = "Changed the category to **%s**.".formatted(category); + WebhookMessageUpdateAction action = hook.editOriginal(changedContent); + + Optional helperRole = helper.handleFindRoleForCategory(category, guild); + if (helperRole.isEmpty()) { + return action; + } + + // We want to invite all members of a role, but without hard-pinging them. However, + // manually inviting them is cumbersome and can hit rate limits. + // Instead, we abuse the fact that a role-ping through an edit will not hard-ping users, + // but still invite them to a thread. + String headsUpPattern = "%s please have a look, thanks."; + String headsUpWithoutRole = headsUpPattern.formatted(""); + String headsUpWithRole = headsUpPattern.formatted(helperRole.orElseThrow().getAsMention()); + return action.flatMap(any -> helpThread.sendMessage(headsUpWithoutRole) + .flatMap(message -> message.editMessage(headsUpWithRole))); + } +} From b3defd75e02de05aa30a1b8508062443ef3fc470 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 18 Sep 2022 16:56:21 +0530 Subject: [PATCH 02/11] fixed non compliant code --- .../commands/help/HelpThreadCommand.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index ae1063088b..4de198b4d3 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -32,6 +32,9 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final int COOLDOWN_DURATION_VALUE = 30; private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES; + private static final String CATEGORY = "category"; + private static final String TITLE = "title"; + private static final String CLOSE = "close"; private final HelpSystemHelper helper; private final Cache helpThreadIdToLastCategoryChange = Caffeine.newBuilder() @@ -56,7 +59,7 @@ public final class HelpThreadCommand extends SlashCommandAdapter { public HelpThreadCommand(Config config, HelpSystemHelper helper) { super("help-thread", "Help thread specific commands", SlashCommandVisibility.GUILD); - OptionData category = new OptionData(OptionType.STRING, "category", "new category", true); + OptionData category = new OptionData(OptionType.STRING, CATEGORY, "new category", true); config.getHelpSystem() .getCategories() .forEach(categoryText -> category.addChoice(categoryText, categoryText)); @@ -64,12 +67,12 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { getData().addSubcommandGroups( new SubcommandGroupData("change", "Change the details of this help thread") .addSubcommands( - new SubcommandData("category", - "Change the category of this help thread").addOptions(category), - new SubcommandData("title", "Change the title of this help thread") + new SubcommandData(CATEGORY, "Change the category of this help thread") + .addOptions(category), + new SubcommandData(TITLE, "Change the title of this help thread") .addOption(OptionType.STRING, "title", "new title", true))); - getData().addSubcommands(new SubcommandData("close", "Close this help thread")); + getData().addSubcommands(new SubcommandData(CLOSE, "Close this help thread")); this.helper = helper; } @@ -85,14 +88,14 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { boolean isOnCooldown = false; switch (event.getSubcommandName()) { - case "category" -> { + case CATEGORY -> { if (isHelpThreadOnCooldown(helpThreadIdToLastCategoryChange, helpThread)) { isOnCooldown = true; break; } changeCategory(event, helpThread); } - case "title" -> { + case TITLE -> { if (isHelpThreadOnCooldown(helpThreadIdToLastTitleChange, helpThread)) { isOnCooldown = true; break; @@ -109,13 +112,15 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { changeTitle(event, helpThread); } - case "close" -> { + case CLOSE -> { if (isHelpThreadOnCooldown(helpThreadIdToLastClose, helpThread)) { isOnCooldown = true; break; } close(event, helpThread); } + default -> { + } } if (isOnCooldown) { @@ -137,7 +142,7 @@ private boolean isHelpThreadOnCooldown(Cache helpThreadIdToLastAc } private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - String category = event.getOption("category").getAsString(); + String category = event.getOption(CATEGORY).getAsString(); helpThreadIdToLastCategoryChange.put(helpThread.getIdLong(), Instant.now()); @@ -150,7 +155,7 @@ private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel he } private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - String title = event.getOption("title").getAsString(); + String title = event.getOption(TITLE).getAsString(); helpThreadIdToLastTitleChange.put(helpThread.getIdLong(), Instant.now()); From a903cfe6d1e1461a3f080403fee951b2f5a23902 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 18 Sep 2022 17:02:02 +0530 Subject: [PATCH 03/11] fixed non compliant code --- .../togetherjava/tjbot/commands/help/HelpThreadCommand.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index 4de198b4d3..e5ab8ea690 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -70,7 +70,7 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { new SubcommandData(CATEGORY, "Change the category of this help thread") .addOptions(category), new SubcommandData(TITLE, "Change the title of this help thread") - .addOption(OptionType.STRING, "title", "new title", true))); + .addOption(OptionType.STRING, TITLE, "new title", true))); getData().addSubcommands(new SubcommandData(CLOSE, "Close this help thread")); @@ -100,7 +100,7 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { isOnCooldown = true; break; } - String title = event.getOption("title").getAsString(); + String title = event.getOption(TITLE).getAsString(); if (!HelpSystemHelper.isTitleValid(title)) { event.reply( @@ -120,6 +120,7 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { close(event, helpThread); } default -> { + // This can never be the case } } From 3bc592d0e13ea9d4ab1a3da304174a127cdb6891 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 21 Sep 2022 00:31:37 +0530 Subject: [PATCH 04/11] DRY --- .../commands/help/HelpThreadCommand.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index e5ab8ea690..ca592ccf20 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -21,6 +21,7 @@ import java.util.Locale; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MAX; import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MIN; @@ -36,19 +37,15 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final String TITLE = "title"; private static final String CLOSE = "close"; + private static final Supplier> newCaffeine = () -> Caffeine.newBuilder() + .maximumSize(1_000) + .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) + .build(); + private final HelpSystemHelper helper; - private final Cache helpThreadIdToLastCategoryChange = Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); - private final Cache helpThreadIdToLastTitleChange = Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); - private final Cache helpThreadIdToLastClose = Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); + private final Cache helpThreadIdToLastCategoryChange = newCaffeine.get(); + private final Cache helpThreadIdToLastTitleChange = newCaffeine.get(); + private final Cache helpThreadIdToLastClose = newCaffeine.get(); /** * Creates a new instance. From 72e1cda231c15b62c79d69ff78aa1905d8da0b35 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 21 Sep 2022 15:14:13 +0530 Subject: [PATCH 05/11] spotless --- .../togetherjava/tjbot/commands/help/HelpThreadCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index ca592ccf20..74572a2722 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -38,9 +38,9 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final String CLOSE = "close"; private static final Supplier> newCaffeine = () -> Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); + .maximumSize(1_000) + .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) + .build(); private final HelpSystemHelper helper; private final Cache helpThreadIdToLastCategoryChange = newCaffeine.get(); From e1a1c3f7b5e4cfdb9616903ff528b08624e8b0a3 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Fri, 23 Sep 2022 13:38:17 +0530 Subject: [PATCH 06/11] added bookmark command --- .../commands/help/HelpThreadCommand.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index 74572a2722..062f5b9e77 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -36,6 +36,7 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final String CATEGORY = "category"; private static final String TITLE = "title"; private static final String CLOSE = "close"; + private static final String BOOKMARK = "bookmark"; private static final Supplier> newCaffeine = () -> Caffeine.newBuilder() .maximumSize(1_000) @@ -69,7 +70,8 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { new SubcommandData(TITLE, "Change the title of this help thread") .addOption(OptionType.STRING, TITLE, "new title", true))); - getData().addSubcommands(new SubcommandData(CLOSE, "Close this help thread")); + getData().addSubcommands(new SubcommandData(CLOSE, "Close this help thread"), + new SubcommandData(BOOKMARK, "Sends a dm linking thins help thread")); this.helper = helper; } @@ -116,6 +118,9 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { } close(event, helpThread); } + case BOOKMARK -> { + bookmark(event, helpThread); + } default -> { // This can never be the case } @@ -172,6 +177,18 @@ private void close(SlashCommandInteractionEvent event, ThreadChannel helpThread) event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); } + private void bookmark(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + event.getUser() + .openPrivateChannel() + .flatMap(channel -> channel.sendMessage(String.format("<#%s>", helpThread.getIdLong()))) + .queue(); + + event.reply( + "An attempt has made to send a link of this help thread to your DMs. Check your inbox") + .setEphemeral(true) + .queue(); + } + private RestAction sendCategoryChangedMessage(Guild guild, InteractionHook hook, ThreadChannel helpThread, String category) { String changedContent = "Changed the category to **%s**.".formatted(category); From 9194507e453d5c56ea8b66caf30973c88a92ea0f Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 24 Sep 2022 13:50:56 +0530 Subject: [PATCH 07/11] organized code --- .../commands/help/HelpThreadCommand.java | 127 +++++++----------- 1 file changed, 50 insertions(+), 77 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index 062f5b9e77..973a255ef7 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -33,10 +33,9 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final int COOLDOWN_DURATION_VALUE = 30; private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES; - private static final String CATEGORY = "category"; - private static final String TITLE = "title"; - private static final String CLOSE = "close"; - private static final String BOOKMARK = "bookmark"; + private static final String CATEGORY_SUBCOMMAND = "category"; + private static final String TITLE_SUBCOMMAND = "title"; + private static final String CLOSE_SUBCOMMAND = "close"; private static final Supplier> newCaffeine = () -> Caffeine.newBuilder() .maximumSize(1_000) @@ -57,21 +56,20 @@ public final class HelpThreadCommand extends SlashCommandAdapter { public HelpThreadCommand(Config config, HelpSystemHelper helper) { super("help-thread", "Help thread specific commands", SlashCommandVisibility.GUILD); - OptionData category = new OptionData(OptionType.STRING, CATEGORY, "new category", true); + OptionData category = + new OptionData(OptionType.STRING, CATEGORY_SUBCOMMAND, "new category", true); config.getHelpSystem() .getCategories() .forEach(categoryText -> category.addChoice(categoryText, categoryText)); - getData().addSubcommandGroups( - new SubcommandGroupData("change", "Change the details of this help thread") - .addSubcommands( - new SubcommandData(CATEGORY, "Change the category of this help thread") - .addOptions(category), - new SubcommandData(TITLE, "Change the title of this help thread") - .addOption(OptionType.STRING, TITLE, "new title", true))); + getData().addSubcommandGroups(new SubcommandGroupData("change", + "Change the details of this help thread").addSubcommands( + new SubcommandData(CATEGORY_SUBCOMMAND, + "Change the category of this help thread").addOptions(category), + new SubcommandData(TITLE_SUBCOMMAND, "Change the title of this help thread") + .addOption(OptionType.STRING, TITLE_SUBCOMMAND, "new title", true))); - getData().addSubcommands(new SubcommandData(CLOSE, "Close this help thread"), - new SubcommandData(BOOKMARK, "Sends a dm linking thins help thread")); + getData().addSubcommands(new SubcommandData(CLOSE_SUBCOMMAND, "Close this help thread")); this.helper = helper; } @@ -79,61 +77,15 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { @Override public void onSlashCommand(SlashCommandInteractionEvent event) { ThreadChannel helpThread = event.getThreadChannel(); - if (helpThread.isArchived()) { - event.reply("This thread is already closed.").setEphemeral(true).queue(); - return; - } - - boolean isOnCooldown = false; switch (event.getSubcommandName()) { - case CATEGORY -> { - if (isHelpThreadOnCooldown(helpThreadIdToLastCategoryChange, helpThread)) { - isOnCooldown = true; - break; - } - changeCategory(event, helpThread); - } - case TITLE -> { - if (isHelpThreadOnCooldown(helpThreadIdToLastTitleChange, helpThread)) { - isOnCooldown = true; - break; - } - String title = event.getOption(TITLE).getAsString(); - - if (!HelpSystemHelper.isTitleValid(title)) { - event.reply( - "Sorry, but the title length (after removal of special characters) has to be between %d and %d." - .formatted(TITLE_COMPACT_LENGTH_MIN, TITLE_COMPACT_LENGTH_MAX)) - .setEphemeral(true) - .queue(); - } - - changeTitle(event, helpThread); - } - case CLOSE -> { - if (isHelpThreadOnCooldown(helpThreadIdToLastClose, helpThread)) { - isOnCooldown = true; - break; - } - close(event, helpThread); - } - case BOOKMARK -> { - bookmark(event, helpThread); - } + case CATEGORY_SUBCOMMAND -> changeCategory(event, helpThread); + case TITLE_SUBCOMMAND -> changeTitle(event, helpThread); + case CLOSE_SUBCOMMAND -> close(event, helpThread); default -> { // This can never be the case } } - - if (isOnCooldown) { - event - .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( - COOLDOWN_DURATION_VALUE, - COOLDOWN_DURATION_UNIT.toString().toLowerCase(Locale.US))) - .setEphemeral(true) - .queue(); - } } private boolean isHelpThreadOnCooldown(Cache helpThreadIdToLastAction, @@ -144,8 +96,22 @@ private boolean isHelpThreadOnCooldown(Cache helpThreadIdToLastAc .isPresent(); } + private void sendCooldownMessage(SlashCommandInteractionEvent event) { + event + .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( + COOLDOWN_DURATION_VALUE, + COOLDOWN_DURATION_UNIT.toString().toLowerCase(Locale.US))) + .setEphemeral(true) + .queue(); + } + private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - String category = event.getOption(CATEGORY).getAsString(); + if (isHelpThreadOnCooldown(helpThreadIdToLastCategoryChange, helpThread)) { + sendCooldownMessage(event); + return; + } + + String category = event.getOption(CATEGORY_SUBCOMMAND).getAsString(); helpThreadIdToLastCategoryChange.put(helpThread.getIdLong(), Instant.now()); @@ -158,7 +124,21 @@ private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel he } private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - String title = event.getOption(TITLE).getAsString(); + if (isHelpThreadOnCooldown(helpThreadIdToLastTitleChange, helpThread)) { + sendCooldownMessage(event); + return; + } + + String title = event.getOption(TITLE_SUBCOMMAND).getAsString(); + + if (!HelpSystemHelper.isTitleValid(title)) { + event.reply( + "Sorry, but the title length (after removal of special characters) has to be between %d and %d." + .formatted(TITLE_COMPACT_LENGTH_MIN, TITLE_COMPACT_LENGTH_MAX)) + .setEphemeral(true) + .queue(); + return; + } helpThreadIdToLastTitleChange.put(helpThread.getIdLong(), Instant.now()); @@ -168,6 +148,11 @@ private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpT } private void close(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + if (isHelpThreadOnCooldown(helpThreadIdToLastClose, helpThread)) { + sendCooldownMessage(event); + return; + } + helpThreadIdToLastClose.put(helpThread.getIdLong(), Instant.now()); MessageEmbed embed = new EmbedBuilder().setDescription("Closed the thread.") @@ -177,18 +162,6 @@ private void close(SlashCommandInteractionEvent event, ThreadChannel helpThread) event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); } - private void bookmark(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - event.getUser() - .openPrivateChannel() - .flatMap(channel -> channel.sendMessage(String.format("<#%s>", helpThread.getIdLong()))) - .queue(); - - event.reply( - "An attempt has made to send a link of this help thread to your DMs. Check your inbox") - .setEphemeral(true) - .queue(); - } - private RestAction sendCategoryChangedMessage(Guild guild, InteractionHook hook, ThreadChannel helpThread, String category) { String changedContent = "Changed the category to **%s**.".formatted(category); From 8829d3e0ff3a6193f5615a21d59e0d016a56ecf9 Mon Sep 17 00:00:00 2001 From: Zabuzard Date: Wed, 28 Sep 2022 13:52:38 +0200 Subject: [PATCH 08/11] Fixed issues with duplication, CR (Zab) --- .../commands/help/HelpThreadCommand.java | 191 +++++++++++------- 1 file changed, 122 insertions(+), 69 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index 973a255ef7..7b92f75864 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -18,10 +18,12 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Locale; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MAX; import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MIN; @@ -33,19 +35,14 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final int COOLDOWN_DURATION_VALUE = 30; private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES; - private static final String CATEGORY_SUBCOMMAND = "category"; - private static final String TITLE_SUBCOMMAND = "title"; - private static final String CLOSE_SUBCOMMAND = "close"; - - private static final Supplier> newCaffeine = () -> Caffeine.newBuilder() - .maximumSize(1_000) - .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) - .build(); + private static final String CHANGE_CATEGORY_SUBCOMMAND = "category"; + private static final String CHANGE_CATEGORY_OPTION = "category"; + private static final String CHANGE_TITLE_OPTION = "title"; + private static final String CHANGE_TITLE_SUBCOMMAND = "title"; private final HelpSystemHelper helper; - private final Cache helpThreadIdToLastCategoryChange = newCaffeine.get(); - private final Cache helpThreadIdToLastTitleChange = newCaffeine.get(); - private final Cache helpThreadIdToLastClose = newCaffeine.get(); + private final Map> subcommandToCooldownCache; + private final Map> subcommandToEventHandler; /** * Creates a new instance. @@ -56,46 +53,75 @@ public final class HelpThreadCommand extends SlashCommandAdapter { public HelpThreadCommand(Config config, HelpSystemHelper helper) { super("help-thread", "Help thread specific commands", SlashCommandVisibility.GUILD); - OptionData category = - new OptionData(OptionType.STRING, CATEGORY_SUBCOMMAND, "new category", true); + OptionData categoryChoices = + new OptionData(OptionType.STRING, CHANGE_CATEGORY_OPTION, "new category", true); config.getHelpSystem() .getCategories() - .forEach(categoryText -> category.addChoice(categoryText, categoryText)); + .forEach(categoryText -> categoryChoices.addChoice(categoryText, categoryText)); + + SubcommandData changeCategory = + Subcommand.CHANGE_CATEGORY.toSubcommandData().addOptions(categoryChoices); + + SubcommandData changeTitle = Subcommand.CHANGE_TITLE.toSubcommandData() + .addOption(OptionType.STRING, CHANGE_TITLE_OPTION, "new title", true); - getData().addSubcommandGroups(new SubcommandGroupData("change", - "Change the details of this help thread").addSubcommands( - new SubcommandData(CATEGORY_SUBCOMMAND, - "Change the category of this help thread").addOptions(category), - new SubcommandData(TITLE_SUBCOMMAND, "Change the title of this help thread") - .addOption(OptionType.STRING, TITLE_SUBCOMMAND, "new title", true))); + SubcommandGroupData changeCommands = + new SubcommandGroupData("change", "Change the details of this help thread") + .addSubcommands(changeCategory, changeTitle); + getData().addSubcommandGroups(changeCommands); - getData().addSubcommands(new SubcommandData(CLOSE_SUBCOMMAND, "Close this help thread")); + getData().addSubcommands(Subcommand.CLOSE.toSubcommandData()); this.helper = helper; + + Supplier> createCooldownCache = () -> Caffeine.newBuilder() + .maximumSize(1_000) + .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) + .build(); + subcommandToCooldownCache = new EnumMap<>(Arrays.stream(Subcommand.values()) + .filter(Subcommand::hasCooldown) + .collect(Collectors.toMap(Function.identity(), any -> createCooldownCache.get()))); + subcommandToEventHandler = new EnumMap<>( + Map.of(Subcommand.CHANGE_CATEGORY, this::changeCategory, Subcommand.CHANGE_TITLE, + this::changeTitle, Subcommand.CLOSE, this::closeThread)); } @Override public void onSlashCommand(SlashCommandInteractionEvent event) { ThreadChannel helpThread = event.getThreadChannel(); - switch (event.getSubcommandName()) { - case CATEGORY_SUBCOMMAND -> changeCategory(event, helpThread); - case TITLE_SUBCOMMAND -> changeTitle(event, helpThread); - case CLOSE_SUBCOMMAND -> close(event, helpThread); - default -> { - // This can never be the case - } + Subcommand invokedSubcommand = Arrays.stream(Subcommand.values()) + .filter(subcommand -> subcommand.getCommandName().equals(event.getSubcommandName())) + .findAny() + .orElseThrow(); + + if (invokedSubcommand.hasCooldown() + && isHelpThreadOnCooldown(invokedSubcommand, helpThread)) { + sendCooldownMessage(event); + return; } + + subcommandToEventHandler.get(invokedSubcommand).accept(event, helpThread); } - private boolean isHelpThreadOnCooldown(Cache helpThreadIdToLastAction, - ThreadChannel helpThread) { + private boolean isHelpThreadOnCooldown(Subcommand subcommand, ThreadChannel helpThread) { + Cache helpThreadIdToLastAction = requireCooldownCache(subcommand); return Optional.ofNullable(helpThreadIdToLastAction.getIfPresent(helpThread.getIdLong())) .map(lastAction -> lastAction.plus(COOLDOWN_DURATION_VALUE, COOLDOWN_DURATION_UNIT)) .filter(Instant.now()::isBefore) .isPresent(); } + private Cache requireCooldownCache(Subcommand subcommand) { + if (!subcommand.hasCooldown()) { + throw new IllegalArgumentException( + "Must only be used with subcommands that do have cooldown, but " + subcommand + + " was given."); + } + + return subcommandToCooldownCache.get(subcommand); + } + private void sendCooldownMessage(SlashCommandInteractionEvent event) { event .reply("Please wait a bit, this command can only be used once per %d %s.".formatted( @@ -106,16 +132,10 @@ private void sendCooldownMessage(SlashCommandInteractionEvent event) { } private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - if (isHelpThreadOnCooldown(helpThreadIdToLastCategoryChange, helpThread)) { - sendCooldownMessage(event); - return; - } - - String category = event.getOption(CATEGORY_SUBCOMMAND).getAsString(); - - helpThreadIdToLastCategoryChange.put(helpThread.getIdLong(), Instant.now()); + String category = event.getOption(CHANGE_CATEGORY_OPTION).getAsString(); event.deferReply().queue(); + refreshCooldownFor(Subcommand.CHANGE_CATEGORY, helpThread); helper.renameChannelToCategory(helpThread, category) .flatMap(any -> sendCategoryChangedMessage(helpThread.getGuild(), event.getHook(), @@ -123,13 +143,34 @@ private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel he .queue(); } - private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - if (isHelpThreadOnCooldown(helpThreadIdToLastTitleChange, helpThread)) { - sendCooldownMessage(event); - return; + private void refreshCooldownFor(Subcommand subcommand, ThreadChannel helpThread) { + Cache helpThreadIdToLastAction = requireCooldownCache(subcommand); + helpThreadIdToLastAction.put(helpThread.getIdLong(), Instant.now()); + } + + private RestAction sendCategoryChangedMessage(Guild guild, InteractionHook hook, + ThreadChannel helpThread, String category) { + String changedContent = "Changed the category to **%s**.".formatted(category); + WebhookMessageUpdateAction action = hook.editOriginal(changedContent); + + Optional helperRole = helper.handleFindRoleForCategory(category, guild); + if (helperRole.isEmpty()) { + return action; } - String title = event.getOption(TITLE_SUBCOMMAND).getAsString(); + // We want to invite all members of a role, but without hard-pinging them. However, + // manually inviting them is cumbersome and can hit rate limits. + // Instead, we abuse the fact that a role-ping through an edit will not hard-ping users, + // but still invite them to a thread. + String headsUpPattern = "%s please have a look, thanks."; + String headsUpWithoutRole = headsUpPattern.formatted(""); + String headsUpWithRole = headsUpPattern.formatted(helperRole.orElseThrow().getAsMention()); + return action.flatMap(any -> helpThread.sendMessage(headsUpWithoutRole) + .flatMap(message -> message.editMessage(headsUpWithRole))); + } + + private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + String title = event.getOption(CHANGE_TITLE_OPTION).getAsString(); if (!HelpSystemHelper.isTitleValid(title)) { event.reply( @@ -140,20 +181,15 @@ private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpT return; } - helpThreadIdToLastTitleChange.put(helpThread.getIdLong(), Instant.now()); + refreshCooldownFor(Subcommand.CHANGE_TITLE, helpThread); helper.renameChannelToTitle(helpThread, title) .flatMap(any -> event.reply("Changed the title to **%s**.".formatted(title))) .queue(); } - private void close(SlashCommandInteractionEvent event, ThreadChannel helpThread) { - if (isHelpThreadOnCooldown(helpThreadIdToLastClose, helpThread)) { - sendCooldownMessage(event); - return; - } - - helpThreadIdToLastClose.put(helpThread.getIdLong(), Instant.now()); + private void closeThread(SlashCommandInteractionEvent event, ThreadChannel helpThread) { + refreshCooldownFor(Subcommand.CLOSE, helpThread); MessageEmbed embed = new EmbedBuilder().setDescription("Closed the thread.") .setColor(HelpSystemHelper.AMBIENT_COLOR) @@ -162,24 +198,41 @@ private void close(SlashCommandInteractionEvent event, ThreadChannel helpThread) event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); } - private RestAction sendCategoryChangedMessage(Guild guild, InteractionHook hook, - ThreadChannel helpThread, String category) { - String changedContent = "Changed the category to **%s**.".formatted(category); - WebhookMessageUpdateAction action = hook.editOriginal(changedContent); + enum Subcommand { + CHANGE_CATEGORY(CHANGE_CATEGORY_SUBCOMMAND, "Change the category of this help thread", + Cooldown.YES), + CHANGE_TITLE(CHANGE_TITLE_SUBCOMMAND, "Change the title of this help thread", Cooldown.YES), + CLOSE("close", "Close this help thread", Cooldown.YES); - Optional helperRole = helper.handleFindRoleForCategory(category, guild); - if (helperRole.isEmpty()) { - return action; + private final String commandName; + private final String description; + private final Cooldown cooldown; + + Subcommand(String commandName, String description, Cooldown cooldown) { + this.commandName = commandName; + this.description = description; + this.cooldown = cooldown; } - // We want to invite all members of a role, but without hard-pinging them. However, - // manually inviting them is cumbersome and can hit rate limits. - // Instead, we abuse the fact that a role-ping through an edit will not hard-ping users, - // but still invite them to a thread. - String headsUpPattern = "%s please have a look, thanks."; - String headsUpWithoutRole = headsUpPattern.formatted(""); - String headsUpWithRole = headsUpPattern.formatted(helperRole.orElseThrow().getAsMention()); - return action.flatMap(any -> helpThread.sendMessage(headsUpWithoutRole) - .flatMap(message -> message.editMessage(headsUpWithRole))); + String getCommandName() { + return commandName; + } + + String getDescription() { + return description; + } + + boolean hasCooldown() { + return cooldown == Cooldown.YES; + } + + SubcommandData toSubcommandData() { + return new SubcommandData(commandName, description); + } + } + + enum Cooldown { + YES, + NO } } From 2141a621bc6b5a6ec58e153355e28a8b5cb7e8f8 Mon Sep 17 00:00:00 2001 From: Zabuzard Date: Wed, 28 Sep 2022 13:53:19 +0200 Subject: [PATCH 09/11] Adjusted command mentions --- .../togetherjava/tjbot/commands/help/HelpSystemHelper.java | 4 ++-- .../togetherjava/tjbot/commands/help/ImplicitAskListener.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java index 81261f921b..59f4a54a25 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java @@ -108,7 +108,7 @@ RestAction sendExplanationMessage(MessageChannel threadChannel) { **provide details**, context, more code, examples and maybe some screenshots. \ With enough info, someone knows the answer for sure."""), HelpSystemHelper.embedWith( - "Don't forget to close your thread using the command **/close** when your question has been answered, thanks.")); + "Don't forget to close your thread using the command **/help-thread close** when your question has been answered, thanks.")); MessageAction action = threadChannel.sendMessage(message); if (useCodeSyntaxExampleImage) { @@ -293,7 +293,7 @@ private void executeUncategorizedAdviceCheck(long threadChannelId, long authorId MessageEmbed embed = HelpSystemHelper.embedWith( """ Hey there 👋 You have to select a category for your help thread, otherwise nobody can see your question. - Please use the `/change-help-category` slash-command and pick what fits best, thanks 🙂 + Please use the `/help-thread change category` slash-command and pick what fits best, thanks 🙂 """); Message message = new MessageBuilder(author.getAsMention()).setEmbeds(embed).build(); diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/ImplicitAskListener.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/ImplicitAskListener.java index 46086246cb..629d022128 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/ImplicitAskListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/ImplicitAskListener.java @@ -185,7 +185,7 @@ private static MessageAction sendInitialMessage(ThreadChannel threadChannel, """ %s has a question about '**%s**' and will send the details now. - Please use `/change-help-category` to greatly increase the visibility of the question.""" + Please use `/help-thread change category` to greatly increase the visibility of the question.""" .formatted(author, title)).setEmbeds(embed).build(); return threadChannel.sendMessage(threadMessage); From 01a226aa25599d875d621543b48f12cf76b11bbf Mon Sep 17 00:00:00 2001 From: Zabuzard Date: Fri, 30 Sep 2022 12:20:33 +0200 Subject: [PATCH 10/11] added extra map to improve subcommand lookup * and added a helper to simplify `Arrays.stream(Subcommand.values())` fluff --- .../tjbot/commands/help/HelpThreadCommand.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index 7b92f75864..5611a7e496 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -24,6 +24,7 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MAX; import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MIN; @@ -41,6 +42,7 @@ public final class HelpThreadCommand extends SlashCommandAdapter { private static final String CHANGE_TITLE_SUBCOMMAND = "title"; private final HelpSystemHelper helper; + private final Map nameToSubcommand; private final Map> subcommandToCooldownCache; private final Map> subcommandToEventHandler; @@ -78,7 +80,9 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { .maximumSize(1_000) .expireAfterAccess(COOLDOWN_DURATION_VALUE, TimeUnit.of(COOLDOWN_DURATION_UNIT)) .build(); - subcommandToCooldownCache = new EnumMap<>(Arrays.stream(Subcommand.values()) + nameToSubcommand = streamSubcommands() + .collect(Collectors.toMap(Subcommand::getCommandName, Function.identity())); + subcommandToCooldownCache = new EnumMap<>(streamSubcommands() .filter(Subcommand::hasCooldown) .collect(Collectors.toMap(Function.identity(), any -> createCooldownCache.get()))); subcommandToEventHandler = new EnumMap<>( @@ -90,10 +94,8 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { public void onSlashCommand(SlashCommandInteractionEvent event) { ThreadChannel helpThread = event.getThreadChannel(); - Subcommand invokedSubcommand = Arrays.stream(Subcommand.values()) - .filter(subcommand -> subcommand.getCommandName().equals(event.getSubcommandName())) - .findAny() - .orElseThrow(); + Subcommand invokedSubcommand = + Objects.requireNonNull(nameToSubcommand.get(event.getSubcommandName())); if (invokedSubcommand.hasCooldown() && isHelpThreadOnCooldown(invokedSubcommand, helpThread)) { @@ -198,6 +200,10 @@ private void closeThread(SlashCommandInteractionEvent event, ThreadChannel helpT event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); } + private static Stream streamSubcommands() { + return Arrays.stream(Subcommand.values()); + } + enum Subcommand { CHANGE_CATEGORY(CHANGE_CATEGORY_SUBCOMMAND, "Change the category of this help thread", Cooldown.YES), From 0d0c72210f59fa002c9e3bd002db2433124b3570 Mon Sep 17 00:00:00 2001 From: Zabuzard Date: Fri, 30 Sep 2022 15:10:10 +0200 Subject: [PATCH 11/11] Fixed issue after rebase --- .../togetherjava/tjbot/commands/help/HelpThreadCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java index 5611a7e496..04c9b78cb5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpThreadCommand.java @@ -12,8 +12,8 @@ import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.WebhookMessageUpdateAction; +import org.togetherjava.tjbot.commands.CommandVisibility; import org.togetherjava.tjbot.commands.SlashCommandAdapter; -import org.togetherjava.tjbot.commands.SlashCommandVisibility; import org.togetherjava.tjbot.config.Config; import java.time.Instant; @@ -53,7 +53,7 @@ public final class HelpThreadCommand extends SlashCommandAdapter { * @param helper the helper to use */ public HelpThreadCommand(Config config, HelpSystemHelper helper) { - super("help-thread", "Help thread specific commands", SlashCommandVisibility.GUILD); + super("help-thread", "Help thread specific commands", CommandVisibility.GUILD); OptionData categoryChoices = new OptionData(OptionType.STRING, CHANGE_CATEGORY_OPTION, "new category", true);