From 3e802e38bd388919a1ef0ee217aade962a627620 Mon Sep 17 00:00:00 2001 From: Andrew1031 Date: Fri, 20 Sep 2024 15:32:18 -0700 Subject: [PATCH 1/5] Automatically pin first message in project forum --- .../help/HelpThreadCreatedListener.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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 12ee788fa1..2203c2b8c8 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 @@ -58,15 +58,15 @@ public HelpThreadCreatedListener(HelpSystemHelper helper) { @Override public void onMessageReceived(MessageReceivedEvent event) { + String projectsChannelName = "projects"; if (event.isFromThread()) { ThreadChannel threadChannel = event.getChannel().asThreadChannel(); Channel parentChannel = threadChannel.getParentChannel(); - if (helper.isHelpForumName(parentChannel.getName())) { - int messageCount = threadChannel.getMessageCount(); - if (messageCount > 1 || wasThreadAlreadyHandled(threadChannel.getIdLong())) { - return; - } + boolean isPost = isPostMessage(threadChannel); + if (helper.isHelpForumName(parentChannel.getName()) && isPost) { handleHelpThreadCreated(threadChannel); + } else if (parentChannel.getName().equals(projectsChannelName) && isPost) { + handleProjectThread(event); } } } @@ -90,6 +90,10 @@ private void handleHelpThreadCreated(ThreadChannel threadChannel) { }).queue(); } + private void handleProjectThread(MessageReceivedEvent event) { + event.getMessage().pin().queue(); + } + private static User getMentionedAuthorByMessage(Message message) { return message.getMentions().getUsers().getFirst(); } @@ -98,6 +102,11 @@ private static boolean isPostedBySelfUser(Message message) { return message.getJDA().getSelfUser().equals(message.getAuthor()); } + private boolean isPostMessage(ThreadChannel threadChannel) { + int messageCount = threadChannel.getMessageCount(); + return messageCount <= 1 && !wasThreadAlreadyHandled(threadChannel.getIdLong()); + } + private RestAction createAIResponse(ThreadChannel threadChannel, Message message) { return helper.constructChatGptAttempt(threadChannel, getMessageContent(message), componentIdInteractor); From 80412cb8bf5e59851dea932c3955fa23696afe21 Mon Sep 17 00:00:00 2001 From: Andrew1031 Date: Sun, 13 Oct 2024 20:33:20 -0700 Subject: [PATCH 2/5] Fixes: 1. used config file for "projects" channel name 2. Created new listener "ProjectsThreadCreatedListener.java" under new package name called "projects" --- application/config.json.template | 1 + .../org/togetherjava/tjbot/config/Config.java | 14 +++++ .../togetherjava/tjbot/features/Features.java | 2 + .../help/HelpThreadCreatedListener.java | 19 ++---- .../ProjectsThreadCreatedListener.java | 60 +++++++++++++++++++ .../tjbot/features/projects/package-info.java | 11 ++++ 6 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java create mode 100644 application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java diff --git a/application/config.json.template b/application/config.json.template index a1aec8f470..ec8737824d 100644 --- a/application/config.json.template +++ b/application/config.json.template @@ -6,6 +6,7 @@ "discordGuildInvite": "https://discord.com/invite/XXFUXzK", "modAuditLogChannelPattern": "mod-audit-log", "modMailChannelPattern": "modmail", + "projectsChannelPattern": "projects" "mutedRolePattern": "Muted", "heavyModerationRolePattern": "Moderator", "softModerationRolePattern": "Moderator|Community Ambassador", 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 e819f8e7d1..5d46cae584 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -23,6 +23,7 @@ public final class Config { private final String discordGuildInvite; private final String modAuditLogChannelPattern; private final String modMailChannelPattern; + private final String projectsChannelPattern; private final String mutedRolePattern; private final String heavyModerationRolePattern; private final String softModerationRolePattern; @@ -58,6 +59,8 @@ private Config(@JsonProperty(value = "token", required = true) String token, required = true) String modAuditLogChannelPattern, @JsonProperty(value = "modMailChannelPattern", required = true) String modMailChannelPattern, + @JsonProperty(value = "projectsChannelPattern", + required = true) String projectsChannelPattern, @JsonProperty(value = "mutedRolePattern", required = true) String mutedRolePattern, @JsonProperty(value = "heavyModerationRolePattern", required = true) String heavyModerationRolePattern, @@ -103,6 +106,7 @@ private Config(@JsonProperty(value = "token", required = true) String token, this.discordGuildInvite = Objects.requireNonNull(discordGuildInvite); this.modAuditLogChannelPattern = Objects.requireNonNull(modAuditLogChannelPattern); this.modMailChannelPattern = Objects.requireNonNull(modMailChannelPattern); + this.projectsChannelPattern = Objects.requireNonNull(projectsChannelPattern); this.mutedRolePattern = Objects.requireNonNull(mutedRolePattern); this.heavyModerationRolePattern = Objects.requireNonNull(heavyModerationRolePattern); this.softModerationRolePattern = Objects.requireNonNull(softModerationRolePattern); @@ -170,6 +174,16 @@ public String getModMailChannelPattern() { return modMailChannelPattern; } + /** + * Gets the REGEX pattern used to identify the channel that is supposed to contain information + * about user projects + * + * @return the channel name pattern + */ + public String getProjectsChannelPattern() { + return projectsChannelPattern; + } + /** * Gets the token of the Discord bot to connect this application to. * diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index 893adbc00f..82b1e327e1 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -62,6 +62,7 @@ import org.togetherjava.tjbot.features.moderation.scam.ScamHistoryPurgeRoutine; import org.togetherjava.tjbot.features.moderation.scam.ScamHistoryStore; import org.togetherjava.tjbot.features.moderation.temp.TemporaryModerationRoutine; +import org.togetherjava.tjbot.features.projects.ProjectsThreadCreatedListener; import org.togetherjava.tjbot.features.reminder.RemindRoutine; import org.togetherjava.tjbot.features.reminder.ReminderCommand; import org.togetherjava.tjbot.features.system.BotCore; @@ -157,6 +158,7 @@ public static Collection createFeatures(JDA jda, Database database, Con features.add(new LeftoverBookmarksListener(bookmarksSystem)); features.add(new HelpThreadCreatedListener(helpSystemHelper)); features.add(new HelpThreadLifecycleListener(helpSystemHelper, database)); + features.add(new ProjectsThreadCreatedListener(config)); // Message context commands features.add(new TransferQuestionCommand(config, chatGptService)); 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 2203c2b8c8..12ee788fa1 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 @@ -58,15 +58,15 @@ public HelpThreadCreatedListener(HelpSystemHelper helper) { @Override public void onMessageReceived(MessageReceivedEvent event) { - String projectsChannelName = "projects"; if (event.isFromThread()) { ThreadChannel threadChannel = event.getChannel().asThreadChannel(); Channel parentChannel = threadChannel.getParentChannel(); - boolean isPost = isPostMessage(threadChannel); - if (helper.isHelpForumName(parentChannel.getName()) && isPost) { + if (helper.isHelpForumName(parentChannel.getName())) { + int messageCount = threadChannel.getMessageCount(); + if (messageCount > 1 || wasThreadAlreadyHandled(threadChannel.getIdLong())) { + return; + } handleHelpThreadCreated(threadChannel); - } else if (parentChannel.getName().equals(projectsChannelName) && isPost) { - handleProjectThread(event); } } } @@ -90,10 +90,6 @@ private void handleHelpThreadCreated(ThreadChannel threadChannel) { }).queue(); } - private void handleProjectThread(MessageReceivedEvent event) { - event.getMessage().pin().queue(); - } - private static User getMentionedAuthorByMessage(Message message) { return message.getMentions().getUsers().getFirst(); } @@ -102,11 +98,6 @@ private static boolean isPostedBySelfUser(Message message) { return message.getJDA().getSelfUser().equals(message.getAuthor()); } - private boolean isPostMessage(ThreadChannel threadChannel) { - int messageCount = threadChannel.getMessageCount(); - return messageCount <= 1 && !wasThreadAlreadyHandled(threadChannel.getIdLong()); - } - private RestAction createAIResponse(ThreadChannel threadChannel, Message message) { return helper.constructChatGptAttempt(threadChannel, getMessageContent(message), componentIdInteractor); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java new file mode 100644 index 0000000000..814b97aec8 --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java @@ -0,0 +1,60 @@ +package org.togetherjava.tjbot.features.projects; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import net.dv8tion.jda.api.entities.channel.Channel; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import org.togetherjava.tjbot.config.Config; +import org.togetherjava.tjbot.features.EventReceiver; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; + +/** + * Listens for new threads being created in the "projects" forum and pins the first message. * + * {@link Config#getProjectsChannelPattern()}. + */ +public final class ProjectsThreadCreatedListener extends ListenerAdapter implements EventReceiver { + private final String configProjectsChannelPattern; + private final Cache threadIdToCreatedAtCache = Caffeine.newBuilder() + .maximumSize(1_000) + .expireAfterAccess(2, TimeUnit.of(ChronoUnit.MINUTES)) + .build(); + + public ProjectsThreadCreatedListener(Config config) { + configProjectsChannelPattern = config.getProjectsChannelPattern(); + } + + @Override + public void onMessageReceived(MessageReceivedEvent event) { + if (event.isFromThread()) { + ThreadChannel threadChannel = event.getChannel().asThreadChannel(); + Channel parentChannel = threadChannel.getParentChannel(); + boolean isPost = isPostMessage(threadChannel); + + if (parentChannel.getName().equals(configProjectsChannelPattern) && isPost) { + handleProjectThread(event); + } + } + } + + private boolean wasThreadAlreadyHandled(long threadChannelId) { + Instant now = Instant.now(); + Instant createdAt = threadIdToCreatedAtCache.get(threadChannelId, any -> now); + return createdAt != now; + } + + private boolean isPostMessage(ThreadChannel threadChannel) { + int messageCount = threadChannel.getMessageCount(); + return messageCount <= 1 && !wasThreadAlreadyHandled(threadChannel.getIdLong()); + } + + private void handleProjectThread(MessageReceivedEvent event) { + // Pin the first message in the thread + event.getMessage().pin().queue(); + } +} diff --git a/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java b/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java new file mode 100644 index 0000000000..e2fea3e412 --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java @@ -0,0 +1,11 @@ +/** + * This packages offers all the functionality for the remind-command. The core class is + * {@link org.togetherjava.tjbot.features.projects.ProjectsThreadCreatedListener}. + */ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package org.togetherjava.tjbot.features.projects; + +import org.togetherjava.tjbot.annotations.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; From 87c0e846aa4d91f661ec9a5a2c870e3c19b7a21f Mon Sep 17 00:00:00 2001 From: Andrew1031 Date: Sun, 13 Oct 2024 23:37:17 -0700 Subject: [PATCH 3/5] Fix comments in projects package package-info.java file --- .../org/togetherjava/tjbot/features/projects/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java b/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java index e2fea3e412..614cc24ed9 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/projects/package-info.java @@ -1,5 +1,5 @@ /** - * This packages offers all the functionality for the remind-command. The core class is + * This packages offers all the functionality for the projects channel. The core class is * {@link org.togetherjava.tjbot.features.projects.ProjectsThreadCreatedListener}. */ @MethodsReturnNonnullByDefault From f0f91351ce2beb657f74b21654f249fac4a050f0 Mon Sep 17 00:00:00 2001 From: Andrew1031 Date: Mon, 21 Oct 2024 15:46:39 -0700 Subject: [PATCH 4/5] Suggested changes - More reliable way to verify parent message of channel --- .../projects/ProjectsThreadCreatedListener.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java index 814b97aec8..6ce95d7e4e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java @@ -37,7 +37,7 @@ public void onMessageReceived(MessageReceivedEvent event) { boolean isPost = isPostMessage(threadChannel); if (parentChannel.getName().equals(configProjectsChannelPattern) && isPost) { - handleProjectThread(event); + pinParentMessage(event); } } } @@ -50,11 +50,15 @@ private boolean wasThreadAlreadyHandled(long threadChannelId) { private boolean isPostMessage(ThreadChannel threadChannel) { int messageCount = threadChannel.getMessageCount(); - return messageCount <= 1 && !wasThreadAlreadyHandled(threadChannel.getIdLong()); + if (messageCount <= 1 && !wasThreadAlreadyHandled(threadChannel.getIdLong())) { + return threadChannel.retrieveMessageById(threadChannel.getIdLong()) + .map(message -> message.getIdLong() == threadChannel.getIdLong()) + .complete(); + } + return false; } - private void handleProjectThread(MessageReceivedEvent event) { - // Pin the first message in the thread + private void pinParentMessage(MessageReceivedEvent event) { event.getMessage().pin().queue(); } } From 896345712306defb5ec04a9e13797bef05ffcb49 Mon Sep 17 00:00:00 2001 From: Andrew1031 Date: Tue, 22 Oct 2024 10:16:00 -0700 Subject: [PATCH 5/5] pass the event and thread channel to this method isPostMessage, get the message ID from that MessageRecievedEvent and get thread channel ID from thread channel --- .../features/projects/ProjectsThreadCreatedListener.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java index 6ce95d7e4e..74c961594f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/projects/ProjectsThreadCreatedListener.java @@ -34,7 +34,7 @@ public void onMessageReceived(MessageReceivedEvent event) { if (event.isFromThread()) { ThreadChannel threadChannel = event.getChannel().asThreadChannel(); Channel parentChannel = threadChannel.getParentChannel(); - boolean isPost = isPostMessage(threadChannel); + boolean isPost = isPostMessage(threadChannel, event); if (parentChannel.getName().equals(configProjectsChannelPattern) && isPost) { pinParentMessage(event); @@ -48,12 +48,10 @@ private boolean wasThreadAlreadyHandled(long threadChannelId) { return createdAt != now; } - private boolean isPostMessage(ThreadChannel threadChannel) { + private boolean isPostMessage(ThreadChannel threadChannel, MessageReceivedEvent event) { int messageCount = threadChannel.getMessageCount(); if (messageCount <= 1 && !wasThreadAlreadyHandled(threadChannel.getIdLong())) { - return threadChannel.retrieveMessageById(threadChannel.getIdLong()) - .map(message -> message.getIdLong() == threadChannel.getIdLong()) - .complete(); + return event.getMessageId().equals(threadChannel.getId()); } return false; }