From 561dd03035a6be8e64afac15def3f10958127b2a Mon Sep 17 00:00:00 2001 From: SquidXTV Date: Wed, 27 Sep 2023 21:03:26 +0200 Subject: [PATCH 1/3] Update conflicts --- .../tjbot/features/help/HelpSystemHelper.java | 36 ++++++++--- .../help/HelpThreadCreatedListener.java | 61 ++++++++++++++++++- 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index 8604ac631b..88bfd238b9 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -1,10 +1,7 @@ package org.togetherjava.tjbot.features.help; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; @@ -12,6 +9,7 @@ import net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; import net.dv8tion.jda.api.utils.FileUpload; @@ -26,6 +24,7 @@ import org.togetherjava.tjbot.db.generated.tables.records.HelpThreadsRecord; import org.togetherjava.tjbot.features.chatgpt.ChatGptCommand; import org.togetherjava.tjbot.features.chatgpt.ChatGptService; +import org.togetherjava.tjbot.features.componentids.ComponentIdInteractor; import javax.annotation.Nullable; @@ -40,6 +39,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -59,6 +59,7 @@ public final class HelpSystemHelper { private static final String CODE_SYNTAX_EXAMPLE_PATH = "codeSyntaxExample.png"; + private final Predicate hasTagManageRole; private final Predicate isHelpForumName; private final String helpForumPattern; /** @@ -88,6 +89,7 @@ public HelpSystemHelper(Config config, Database database, ChatGptService chatGpt this.database = database; this.chatGptService = chatGptService; + hasTagManageRole = Pattern.compile(config.getTagManageRolePattern()).asMatchPredicate(); helpForumPattern = helpConfig.getHelpForumPattern(); isHelpForumName = Pattern.compile(helpForumPattern).asMatchPredicate(); @@ -161,7 +163,7 @@ private RestAction sendExplanationMessage(GuildMessageChannel threadCha * why the message wasn't used. */ RestAction constructChatGptAttempt(ThreadChannel threadChannel, - String originalQuestion) { + String originalQuestion, ComponentIdInteractor componentIdInteractor) { Optional questionOptional = prepareChatGptQuestion(threadChannel, originalQuestion); Optional chatGPTAnswer; @@ -176,6 +178,7 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, return useChatGptFallbackMessage(threadChannel); } + List ids = new CopyOnWriteArrayList<>(); RestAction message = mentionGuildSlashCommand(threadChannel.getGuild(), ChatGptCommand.COMMAND_NAME) .map(""" @@ -183,10 +186,21 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, In any case, a human is on the way 👍. To continue talking to the AI, you can use \ %s. """::formatted) - .flatMap(threadChannel::sendMessage); - - for (String aiResponse : chatGPTAnswer.get()) { - message = message.map(aiResponse::formatted).flatMap(threadChannel::sendMessage); + .flatMap(threadChannel::sendMessage) + .onSuccess(m -> ids.add(m.getId())); + + String[] answers = chatGPTAnswer.get(); + for (int i = 0; i < answers.length; i++) { + if (i == answers.length - 1) { + message = message.map(answers[i]::formatted) + .flatMap(m -> threadChannel.sendMessage(m) + .addActionRow(Button.danger(componentIdInteractor + .generateComponentId(ids.toArray(String[]::new)), "Dismiss"))); + continue; + } + message = message.map(answers[i]::formatted) + .flatMap(threadChannel::sendMessage) + .onSuccess(m -> ids.add(m.getId())); } return message; @@ -344,6 +358,10 @@ private static ForumTag requireTag(String tagName, ForumChannel forumChannel) { return matchingTags.get(0); } + boolean hasTagManageRole(Member member) { + return member.getRoles().stream().map(Role::getName).anyMatch(hasTagManageRole); + } + boolean isHelpForumName(String channelName) { return isHelpForumName.test(channelName); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java index 210b279779..7e30b12f5c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java @@ -2,18 +2,27 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import net.dv8tion.jda.api.events.channel.ChannelCreateEvent; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.requests.RestAction; import org.togetherjava.tjbot.features.EventReceiver; +import org.togetherjava.tjbot.features.UserInteractionType; +import org.togetherjava.tjbot.features.UserInteractor; +import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator; +import org.togetherjava.tjbot.features.componentids.ComponentIdInteractor; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -22,13 +31,16 @@ * Will for example record thread metadata in the database and send an explanation message to the * user. */ -public final class HelpThreadCreatedListener extends ListenerAdapter implements EventReceiver { +public final class HelpThreadCreatedListener extends ListenerAdapter + implements EventReceiver, UserInteractor { private final HelpSystemHelper helper; private final Cache threadIdToCreatedAtCache = Caffeine.newBuilder() .maximumSize(1_000) .expireAfterAccess(2, TimeUnit.of(ChronoUnit.MINUTES)) .build(); + private final ComponentIdInteractor componentIdInteractor = + new ComponentIdInteractor(getInteractionType(), getName()); /** * Creates a new instance. @@ -78,8 +90,8 @@ private void handleHelpThreadCreated(ThreadChannel threadChannel) { private RestAction createAIResponse(ThreadChannel threadChannel) { RestAction originalQuestion = threadChannel.retrieveMessageById(threadChannel.getIdLong()); - return originalQuestion.flatMap( - message -> helper.constructChatGptAttempt(threadChannel, message.getContentRaw())); + return originalQuestion.flatMap(message -> helper.constructChatGptAttempt(threadChannel, + message.getContentRaw(), componentIdInteractor)); } private RestAction pinOriginalQuestion(ThreadChannel threadChannel) { @@ -112,4 +124,47 @@ private RestAction sendHelperHeadsUp(ThreadChannel threadChannel) { return threadChannel.sendMessage(headsUpWithoutRole) .flatMap(message -> message.editMessage(headsUpWithRole)); } + + @Override + public String getName() { + return "chatpgt-answer"; + } + + @Override + public UserInteractionType getInteractionType() { + return UserInteractionType.OTHER; + } + + @Override + public void acceptComponentIdGenerator(ComponentIdGenerator generator) { + componentIdInteractor.acceptComponentIdGenerator(generator); + } + + @Override + public void onButtonClick(ButtonInteractionEvent event, List args) { + ThreadChannel channel = event.getChannel().asThreadChannel(); + Member interactionUser = event.getMember(); + if (channel.getOwnerIdLong() != interactionUser.getIdLong() + && !helper.hasTagManageRole(interactionUser)) { + event.reply("You do not have permission for this action.").setEphemeral(true).queue(); + return; + } + + RestAction deleteMessages = event.getMessage().delete(); + for (String id : args) { + deleteMessages = deleteMessages.and(channel.deleteMessageById(id)); + } + deleteMessages.queue(); + } + + @Override + public void onSelectMenuSelection(SelectMenuInteractionEvent event, List args) { + throw new UnsupportedOperationException("Not used"); + } + + @Override + public void onModalSubmitted(ModalInteractionEvent event, List args) { + throw new UnsupportedOperationException("Not used"); + } + } From d95085cf6992149a622538863a1d51b14da31112 Mon Sep 17 00:00:00 2001 From: SquidXTV Date: Mon, 2 Oct 2023 20:07:24 +0200 Subject: [PATCH 2/3] Clean up --- .../tjbot/features/help/HelpSystemHelper.java | 22 ++++++++++++------- .../help/HelpThreadCreatedListener.java | 4 +++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index 88bfd238b9..a7738a466c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -188,24 +188,30 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, """::formatted) .flatMap(threadChannel::sendMessage) .onSuccess(m -> ids.add(m.getId())); - String[] answers = chatGPTAnswer.get(); + for (int i = 0; i < answers.length; i++) { + MessageCreateAction messageCreateAction = threadChannel.sendMessage(answers[i]); + if (i == answers.length - 1) { - message = message.map(answers[i]::formatted) - .flatMap(m -> threadChannel.sendMessage(m) - .addActionRow(Button.danger(componentIdInteractor - .generateComponentId(ids.toArray(String[]::new)), "Dismiss"))); + message = message.flatMap(ignored -> messageCreateAction + .addActionRow(generateDismissButton(componentIdInteractor, ids))); continue; } - message = message.map(answers[i]::formatted) - .flatMap(threadChannel::sendMessage) - .onSuccess(m -> ids.add(m.getId())); + + message = message + .flatMap(ignored -> messageCreateAction.onSuccess(m -> ids.add(m.getId()))); } return message; } + private Button generateDismissButton(ComponentIdInteractor componentIdInteractor, + List ids) { + String buttonId = componentIdInteractor.generateComponentId(ids.toArray(String[]::new)); + return Button.danger(buttonId, "Dismiss"); + } + private Optional prepareChatGptQuestion(ThreadChannel threadChannel, String originalQuestion) { String questionTitle = threadChannel.getName(); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java index 7e30b12f5c..8e5db87dc0 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java @@ -23,6 +23,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -142,8 +143,9 @@ public void acceptComponentIdGenerator(ComponentIdGenerator generator) { @Override public void onButtonClick(ButtonInteractionEvent event, List args) { + // This method handles chatgpt's automatic response "dismiss" button ThreadChannel channel = event.getChannel().asThreadChannel(); - Member interactionUser = event.getMember(); + Member interactionUser = Objects.requireNonNull(event.getMember()); if (channel.getOwnerIdLong() != interactionUser.getIdLong() && !helper.hasTagManageRole(interactionUser)) { event.reply("You do not have permission for this action.").setEphemeral(true).queue(); From f404d3b197a41b7f627ad31ede5d4a8fdc0466fc Mon Sep 17 00:00:00 2001 From: SquidXTV Date: Tue, 3 Oct 2023 14:58:59 +0200 Subject: [PATCH 3/3] Requested changes --- .../tjbot/features/help/HelpSystemHelper.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index a7738a466c..9d987954ac 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -188,19 +188,18 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, """::formatted) .flatMap(threadChannel::sendMessage) .onSuccess(m -> ids.add(m.getId())); - String[] answers = chatGPTAnswer.get(); + String[] answers = chatGPTAnswer.orElseThrow(); for (int i = 0; i < answers.length; i++) { - MessageCreateAction messageCreateAction = threadChannel.sendMessage(answers[i]); + MessageCreateAction answer = threadChannel.sendMessage(answers[i]); if (i == answers.length - 1) { - message = message.flatMap(ignored -> messageCreateAction + message = message.flatMap(any -> answer .addActionRow(generateDismissButton(componentIdInteractor, ids))); continue; } - message = message - .flatMap(ignored -> messageCreateAction.onSuccess(m -> ids.add(m.getId()))); + message = message.flatMap(ignored -> answer.onSuccess(m -> ids.add(m.getId()))); } return message;