From e1a02c0a0115964dfd630b726750d8f23a163362 Mon Sep 17 00:00:00 2001 From: Glabay Date: Sun, 19 Jul 2026 16:33:57 -0300 Subject: [PATCH 1/9] feat: Added Emoji caching with cleanup and TTL --- .../features/basic/QuoteBoardForwarder.java | 109 +++++++++----- .../basic/QuoteBoardForwarderTest.java | 133 ++++++++++++++++++ 2 files changed, 203 insertions(+), 39 deletions(-) create mode 100644 application/src/test/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarderTest.java diff --git a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java index a6cee17024..af60ec9e33 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java @@ -3,21 +3,27 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageReaction; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; -import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.utils.TimeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.togetherjava.tjbot.config.Config; import org.togetherjava.tjbot.config.QuoteBoardConfig; import org.togetherjava.tjbot.features.MessageReceiverAdapter; +import org.togetherjava.tjbot.features.Routine; +import java.time.OffsetDateTime; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -34,9 +40,11 @@ * Key points: - Trigger emoji, minimum vote count and quote-board channel pattern are supplied via * {@code QuoteBoardConfig}. */ -public final class QuoteBoardForwarder extends MessageReceiverAdapter { +public final class QuoteBoardForwarder extends MessageReceiverAdapter implements Routine { private static final Logger logger = LoggerFactory.getLogger(QuoteBoardForwarder.class); + private static final int CLEANUP_INTERVAL_HOURS = 1; + private static final int MAX_MESSAGE_AGE_DAYS = 7; private final Emoji botEmoji; private final Predicate isQuoteBoardChannelName; private final QuoteBoardConfig config; @@ -59,6 +67,13 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) { logger.debug("Received MessageReactionAddEvent: messageId={}, channelId={}, userId={}", event.getMessageId(), event.getChannel().getId(), event.getUserId()); + var messageId = event.getMessageIdLong(); + var messageTime = TimeUtil.getTimeCreated(messageId); + if (messageTime.isBefore(OffsetDateTime.now().minusDays(MAX_MESSAGE_AGE_DAYS))) { + logger.debug("Ignoring reaction as message is older than {} days", MAX_MESSAGE_AGE_DAYS); + return; + } + if (!config.allowChannels().contains(event.getChannel().getName())) { logger.debug("Skipping as reaction occurred in non-whitelisted channel"); return; @@ -82,30 +97,44 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) { return; } - event.retrieveMessage().queue(message -> { - if (hasAlreadyForwardedMessage(message)) { - logger.debug("Message has already been forwarded by the bot. Skipping."); - return; - } + var userId = event.getUserIdLong(); + var emoji = event.getReaction().getEmoji().getAsReactionCode(); + reactions.computeIfAbsent(messageId, _ -> new ConcurrentHashMap<>()) + .computeIfAbsent(emoji, _ -> ConcurrentHashMap.newKeySet()) + .add(userId); - float emojiScore = calculateMessageScore(message.getReactions()); + // check if we've already moved this message... + if (hasBotReactedToMessage(messageId)) { + logger.debug("Message has already been forwarded by the bot. Skipping."); + return; + } + // calculate the overall score of the message reactions + var reactionScore = calcReactionScore(messageId); + if (reactionScore < config.minimumScoreToTrigger()) + return; + + logger.debug("Attempting to forward message to quote board channel: {}", + boardChannel.getName()); - if (emojiScore < config.minimumScoreToTrigger()) { - return; - } + event.retrieveMessage().queue(message -> markAsProcessed(message).flatMap(_ -> message.forwardTo(boardChannel)) + .queue(_ -> logger.debug("Message forwarded to quote board channel: {}", boardChannel.getName()), + e -> logger.warn("Unknown error while attempting to retrieve and forward message for quote-board, message is ignored.", e))); + } - logger.debug("Attempting to forward message to quote board channel: {}", - boardChannel.getName()); + @Override + public Schedule createSchedule() { + return new Schedule(ScheduleMode.FIXED_RATE, 0, CLEANUP_INTERVAL_HOURS, TimeUnit.HOURS); + } - markAsProcessed(message).flatMap(_ -> message.forwardTo(boardChannel)) - .queue(_ -> logger.debug("Message forwarded to quote board channel: {}", - boardChannel.getName()), - e -> logger.warn( - "Unknown error while attempting to retrieve and forward message for quote-board, message is ignored.", - e)); - }); + @Override + public void runRoutine(JDA jda) { + OffsetDateTime cutoff = OffsetDateTime.now().minusDays(MAX_MESSAGE_AGE_DAYS); + reactions.keySet().removeIf(messageId -> TimeUtil.getTimeCreated(messageId).isBefore(cutoff)); } + // MessageId with a Map of Emojis and reacted users + private final Map>> reactions = new ConcurrentHashMap<>(); + private RestAction markAsProcessed(Message message) { return message.addReaction(botEmoji); } @@ -125,7 +154,7 @@ private Optional findQuoteBoardChannel(JDA jda, long guildId) { String.format("Guild with ID '%d' not found.", guildId)); } - List matchingChannels = guild.getTextChannelCache() + List matchingChannels = guild.getTextChannels() .stream() .filter(channel -> isQuoteBoardChannelName.test(channel.getName())) .toList(); @@ -139,26 +168,28 @@ private Optional findQuoteBoardChannel(JDA jda, long guildId) { return matchingChannels.stream().findFirst(); } - /** - * Checks whether the bot has already reacted to the given message with its marker emoji. - */ - private boolean hasAlreadyForwardedMessage(Message message) { - return message.getReactions() - .stream() - .filter(reaction -> botEmoji.equals(reaction.getEmoji())) - .anyMatch(MessageReaction::isSelf); + private boolean hasBotReactedToMessage(Long messageId) { + Map> messageReactions = reactions.get(messageId); + if (messageReactions == null) { + return false; + } + var emojis = messageReactions.keySet(); + // Does the Bot ID in the set of reacted user IDs + return emojis.contains("884898473676271646"); } - private float calculateMessageScore(List reactions) { - return (float) reactions.stream() - .mapToDouble(reaction -> reaction.getCount() * getEmojiScore(reaction.getEmoji())) - .sum(); + private float calcReactionScore(Long messageId) { + var reacts = reactions.get(messageId); + if (reacts == null) { + return 0; + } + var scores = new AtomicReference(0.0F); + reacts.keySet().forEach(emojiCode -> + scores.updateAndGet(v -> v + getEmojiScore(emojiCode))); + return scores.get(); } - private float getEmojiScore(EmojiUnion emoji) { - float defaultScore = config.defaultEmojiScore(); - String reactionCode = emoji.getAsReactionCode(); - - return config.emojiScores().getOrDefault(reactionCode, defaultScore); + private float getEmojiScore(String emojiCode) { + return config.emojiScores().getOrDefault(emojiCode, config.defaultEmojiScore()); } } diff --git a/application/src/test/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarderTest.java b/application/src/test/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarderTest.java new file mode 100644 index 0000000000..b8aa517f80 --- /dev/null +++ b/application/src/test/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarderTest.java @@ -0,0 +1,133 @@ +package org.togetherjava.tjbot.features.basic; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageReaction; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.requests.RestAction; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.togetherjava.tjbot.config.Config; +import org.togetherjava.tjbot.config.QuoteBoardConfig; +import org.togetherjava.tjbot.jda.JdaTester; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.*; + +class QuoteBoardForwarderTest { + private QuoteBoardForwarder forwarder; + private JdaTester jdaTester; + + @BeforeEach + void setUp() { + jdaTester = new JdaTester(); + Config config = mock(Config.class); + when(config.getQuoteBoardConfig()).thenReturn(new QuoteBoardConfig( + 2.0F, + "quotes", + "✅", + 1.0F, + Map.of("⭐", 2.0F), + List.of("general") + )); + forwarder = new QuoteBoardForwarder(config); + } + + private long toSnowflake(Instant instant) { + return (instant.toEpochMilli() - 1420070400000L) << 22; + } + + @Test + void testReactionOnNewMessageTriggers() { + var newTime = Instant.now(); + var newMessageId = toSnowflake(newTime); + var event1 = createMockEvent(newMessageId, 111L, "⭐"); + + RestAction retrieveAction = mock(RestAction.class); + when(event1.retrieveMessage()).thenReturn(retrieveAction); + + forwarder.onMessageReactionAdd(event1); + + verify(event1).retrieveMessage(); + } + + @Test + void testReactionOnOldMessageDoesNotTrigger() { + var oldTime = Instant.now().minus(8, ChronoUnit.DAYS); + var oldMessageId = toSnowflake(oldTime); + var event1 = createMockEvent(oldMessageId, 111L, "⭐"); + var event2 = createMockEvent(oldMessageId, 222L, "⭐"); + + forwarder.onMessageReactionAdd(event1); + forwarder.onMessageReactionAdd(event2); + + verify(event1, never()).retrieveMessage(); + verify(event2, never()).retrieveMessage(); + } + + @Test + void testCleanupRoutine() throws Exception { + var reactions = getReactionsMap(); + var oldTime = Instant.now().minus(8, ChronoUnit.DAYS); + var oldMessageId = toSnowflake(oldTime); + var newTime = Instant.now(); + var newMessageId = toSnowflake(newTime); + + reactions.put(oldMessageId, new java.util.HashMap<>()); + reactions.put(newMessageId, new java.util.HashMap<>()); + + forwarder.runRoutine(jdaTester.getJdaMock()); + + assertFalse(reactions.containsKey(oldMessageId)); + assertTrue(reactions.containsKey(newMessageId)); + } + + private Map>> getReactionsMap() throws Exception { + var field = QuoteBoardForwarder.class.getDeclaredField("reactions"); + field.setAccessible(true); + return (Map>>) field.get(forwarder); + } + + private MessageReactionAddEvent createMockEvent(long messageId, long userId, String emojiCode) { + var event = mock(MessageReactionAddEvent.class); + var channel = mock(MessageChannelUnion.class); + when(channel.getName()).thenReturn("general"); + when(channel.getId()).thenReturn("123"); + when(event.getChannel()).thenReturn(channel); + when(event.getMessageIdLong()).thenReturn(messageId); + when(event.getMessageId()).thenReturn(String.valueOf(messageId)); + + Guild guild = mock(Guild.class); + when(guild.getIdLong()).thenReturn(456L); + when(event.getGuild()).thenReturn(guild); + + var jda = jdaTester.getJdaMock(); + when(event.getJDA()).thenReturn(jda); + when(jda.getGuildById(456L)).thenReturn(guild); + + TextChannel boardChannel = mock(TextChannel.class); + when(boardChannel.getName()).thenReturn("quotes"); + when(boardChannel.getId()).thenReturn("789"); + + when(guild.getTextChannels()).thenReturn(List.of(boardChannel)); + + MessageReaction reaction = mock(MessageReaction.class); + EmojiUnion emoji = mock(EmojiUnion.class); + when(emoji.getAsReactionCode()).thenReturn(emojiCode); + when(reaction.getEmoji()).thenReturn(emoji); + when(event.getReaction()).thenReturn(reaction); + when(event.getUserIdLong()).thenReturn(userId); + + return event; + } +} From ce51c9fee7f6d5d6aedb02dd43c5f339e4ab2794 Mon Sep 17 00:00:00 2001 From: Glabay Date: Mon, 20 Jul 2026 08:23:38 -0300 Subject: [PATCH 2/9] feat: Moved variables to the top of the class --- .../tjbot/features/basic/QuoteBoardForwarder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java index af60ec9e33..b7c7c37640 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java @@ -45,6 +45,9 @@ public final class QuoteBoardForwarder extends MessageReceiverAdapter implements private static final Logger logger = LoggerFactory.getLogger(QuoteBoardForwarder.class); private static final int CLEANUP_INTERVAL_HOURS = 1; private static final int MAX_MESSAGE_AGE_DAYS = 7; + // MessageId with a Map of Emojis and reacted users + // >> + private final Map>> reactions = new ConcurrentHashMap<>(); private final Emoji botEmoji; private final Predicate isQuoteBoardChannelName; private final QuoteBoardConfig config; @@ -132,8 +135,6 @@ public void runRoutine(JDA jda) { reactions.keySet().removeIf(messageId -> TimeUtil.getTimeCreated(messageId).isBefore(cutoff)); } - // MessageId with a Map of Emojis and reacted users - private final Map>> reactions = new ConcurrentHashMap<>(); private RestAction markAsProcessed(Message message) { return message.addReaction(botEmoji); From 8b5dfac640eaa560d87618234202f5c8e91b091e Mon Sep 17 00:00:00 2001 From: Glabay Date: Mon, 20 Jul 2026 08:24:08 -0300 Subject: [PATCH 3/9] feat: Relaced magic numbe string with defined variable --- .../tjbot/features/basic/QuoteBoardForwarder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java index b7c7c37640..c375adaac9 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java @@ -48,6 +48,7 @@ public final class QuoteBoardForwarder extends MessageReceiverAdapter implements // MessageId with a Map of Emojis and reacted users // >> private final Map>> reactions = new ConcurrentHashMap<>(); + private final String TJ_BOT_USER_ID = "884898473676271646"; private final Emoji botEmoji; private final Predicate isQuoteBoardChannelName; private final QuoteBoardConfig config; @@ -175,8 +176,7 @@ private boolean hasBotReactedToMessage(Long messageId) { return false; } var emojis = messageReactions.keySet(); - // Does the Bot ID in the set of reacted user IDs - return emojis.contains("884898473676271646"); + return emojis.contains(TJ_BOT_USER_ID); } private float calcReactionScore(Long messageId) { From 8994725118cd32e4376ea319b00356894b7812be Mon Sep 17 00:00:00 2001 From: Glabay Date: Mon, 20 Jul 2026 08:24:34 -0300 Subject: [PATCH 4/9] feat: removed redundant type --- .../togetherjava/tjbot/features/basic/QuoteBoardForwarder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java index c375adaac9..4e03b07f78 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java @@ -184,7 +184,7 @@ private float calcReactionScore(Long messageId) { if (reacts == null) { return 0; } - var scores = new AtomicReference(0.0F); + var scores = new AtomicReference<>(0.0F); reacts.keySet().forEach(emojiCode -> scores.updateAndGet(v -> v + getEmojiScore(emojiCode))); return scores.get(); From 7a9d6a1ed17377ea561e1c540932de5387b282de Mon Sep 17 00:00:00 2001 From: Glabay Date: Mon, 20 Jul 2026 17:54:14 -0300 Subject: [PATCH 5/9] chore: updated the contact email Signed-off-by: Glabay --- PP.md | 4 ++-- TOS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PP.md b/PP.md index 144f5c848c..fb3ad841f7 100644 --- a/PP.md +++ b/PP.md @@ -99,7 +99,7 @@ Data may be dropped from cache pre-maturely through actions such as removing the ## Removal of Data -Removal of the data can be requested through e-mail at [together.java.tjbot@gmail.com](mailto:together.java.tjbot@gmail.com). +Removal of the data can be requested through e-mail at [contact@togetherjava.org](mailto:contact@togetherjava.org). For security reasons will we ask you to provide us with proof of ownership of the Server, that you wish the data to be removed of. Only a server owner may request removal of data and requesting it will result in the bot being removed from the Server, if still present on it. @@ -121,6 +121,6 @@ This policy is not applicable to any information collected by **bot** instances ## Contact -People may get in contact through e-mail at [together.java.tjbot@gmail.com](mailto:together.java.tjbot@gmail.com), or through **Together Java**'s [official Discord](https://discord.com/invite/XXFUXzK). +People may get in contact through e-mail at [contact@togetherjava.org](mailto:contact@togetherjava.org), or through **Together Java**'s [official Discord](https://discord.com/invite/XXFUXzK). Other ways of support may be provided but are not guaranteed. diff --git a/TOS.md b/TOS.md index 19b3acd584..cce09181c7 100644 --- a/TOS.md +++ b/TOS.md @@ -69,6 +69,6 @@ Any claim related to **Together Java**'s **bot** shall be governed by the laws o ## Contact -People may get in contact through e-mail at [together.java.tjbot@gmail.com](mailto:together.java.tjbot@gmail.com), or through **Together Java**'s [official Discord](https://discord.com/invite/XXFUXzK). +People may get in contact through e-mail at [contact@togetherjava.org](mailto:contact@togetherjava.org), or through **Together Java**'s [official Discord](https://discord.com/invite/XXFUXzK). Other ways of support may be provided but are not guaranteed. From 7dae664ebee45a490684d39fc5de6b64a35733e9 Mon Sep 17 00:00:00 2001 From: Glabay Date: Mon, 20 Jul 2026 22:36:13 -0300 Subject: [PATCH 6/9] chore: Updated JDA from 5.6.1 to 6.5.0 Signed-off-by: Glabay --- application/build.gradle | 4 +- .../config/RoleApplicationSystemConfig.java | 6 +- .../tjbot/features/MessageContextCommand.java | 6 +- .../tjbot/features/SlashCommand.java | 6 +- .../tjbot/features/UserContextCommand.java | 6 +- .../features/basic/QuoteBoardForwarder.java | 28 ++++---- .../features/basic/RoleSelectCommand.java | 20 ++---- .../bookmarks/BookmarksListRemoveHandler.java | 27 ++++---- .../features/chatgpt/ChatGptCommand.java | 13 ++-- .../features/code/CodeMessageHandler.java | 17 ++--- .../componentids/ComponentIdGenerator.java | 4 +- .../componentids/ComponentIdParser.java | 4 +- .../FileSharingMessageListener.java | 5 +- .../tjbot/features/help/HelpSystemHelper.java | 23 ++----- .../features/help/HelpThreadAutoArchiver.java | 9 +-- .../tjbot/features/jshell/JShellCommand.java | 17 +++-- .../features/mathcommands/TeXCommand.java | 11 ++-- .../features/moderation/ReportCommand.java | 29 +++++---- .../moderation/ThisIsScamCommand.java | 18 ++---- .../features/moderation/WhoIsCommand.java | 18 +++--- .../moderation/audit/AuditCommand.java | 11 ++-- .../moderation/modmail/ModMailCommand.java | 23 +++---- .../features/moderation/scam/ScamBlocker.java | 21 ++---- .../tjbot/features/purge/PurgeHelper.java | 7 +- .../features/reminder/ReminderCommand.java | 12 ++-- .../CreateRoleApplicationCommand.java | 20 +++--- .../tjbot/features/system/BotCore.java | 26 +------- .../tjbot/features/tags/TagSystem.java | 4 +- .../TopHelpersAssignmentRoutine.java | 25 +++----- .../features/utils/DiscordClientAction.java | 4 +- .../tjbot/features/utils/MessageUtils.java | 18 +++++- .../basic/QuoteBoardForwarderTest.java | 15 ++--- .../tjbot/features/tags/TagSystemTest.java | 9 +-- .../tjbot/jda/ButtonClickEventBuilder.java | 33 +++++----- .../org/togetherjava/tjbot/jda/JdaTester.java | 64 ++++++------------- 35 files changed, 243 insertions(+), 320 deletions(-) diff --git a/application/build.gradle b/application/build.gradle index 815908a731..4e8aa1f97c 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -46,7 +46,9 @@ dependencies { implementation project(':utils') implementation project(':formatter') - implementation 'net.dv8tion:JDA:5.6.1' + implementation 'net.dv8tion:JDA:6.5.0' + implementation 'net.java.dev.jna:jna:5.16.0' + implementation 'club.minnced:opus-java:1.1.1' implementation 'org.apache.logging.log4j:log4j-core:2.26.0' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j2-impl:2.26.0' diff --git a/application/src/main/java/org/togetherjava/tjbot/config/RoleApplicationSystemConfig.java b/application/src/main/java/org/togetherjava/tjbot/config/RoleApplicationSystemConfig.java index 0979556b58..e40570fe1f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/RoleApplicationSystemConfig.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/RoleApplicationSystemConfig.java @@ -1,7 +1,7 @@ package org.togetherjava.tjbot.config; import com.fasterxml.jackson.annotation.JsonProperty; -import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.components.label.Label; import java.util.Objects; @@ -29,10 +29,10 @@ public record RoleApplicationSystemConfig( Objects.requireNonNull(submissionsChannelPattern); Objects.requireNonNull(defaultQuestion); - if (defaultQuestion.length() > TextInput.MAX_LABEL_LENGTH) { + if (defaultQuestion.length() > Label.LABEL_MAX_LENGTH) { throw new IllegalArgumentException( "defaultQuestion length is too long! Cannot be greater than %d" - .formatted(TextInput.MAX_LABEL_LENGTH)); + .formatted(Label.LABEL_MAX_LENGTH)); } } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/MessageContextCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/MessageContextCommand.java index 83d9df594f..df7f6e2ad5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/MessageContextCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/MessageContextCommand.java @@ -1,12 +1,12 @@ package org.togetherjava.tjbot.features; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator; @@ -49,8 +49,8 @@ public interface MessageContextCommand extends BotCommand { *

* Buttons or menus have to be created with a component ID (see * {@link ComponentInteraction#getComponentId()}, - * {@link net.dv8tion.jda.api.interactions.components.buttons.Button#of(ButtonStyle, String, String)}) - * in a very specific format, otherwise the core system will fail to identify the command that + * {@link net.dv8tion.jda.api.components.buttons.Button#of(ButtonStyle, String, String)}) in a + * very specific format, otherwise the core system will fail to identify the command that * corresponded to the button or menu click event and is unable to route it back. *

* See {@link #acceptComponentIdGenerator(ComponentIdGenerator)} for more info. diff --git a/application/src/main/java/org/togetherjava/tjbot/features/SlashCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/SlashCommand.java index 87cd91e187..51cc12f2fb 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/SlashCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/SlashCommand.java @@ -1,5 +1,6 @@ package org.togetherjava.tjbot.features; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; @@ -10,7 +11,6 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator; @@ -82,8 +82,8 @@ public interface SlashCommand extends BotCommand { *

* Buttons or menus have to be created with a component ID (see * {@link ComponentInteraction#getComponentId()}, - * {@link net.dv8tion.jda.api.interactions.components.buttons.Button#of(ButtonStyle, String, String)}) - * in a very specific format, otherwise the core system will fail to identify the command that + * {@link net.dv8tion.jda.api.components.buttons.Button#of(ButtonStyle, String, String)}) in a + * very specific format, otherwise the core system will fail to identify the command that * corresponded to the button or menu click event and is unable to route it back. *

* See {@link #acceptComponentIdGenerator(ComponentIdGenerator)} for more info on the ID's. diff --git a/application/src/main/java/org/togetherjava/tjbot/features/UserContextCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/UserContextCommand.java index 144c81dbe0..71142a106d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/UserContextCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/UserContextCommand.java @@ -1,12 +1,12 @@ package org.togetherjava.tjbot.features; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator; @@ -50,8 +50,8 @@ public interface UserContextCommand extends BotCommand { *

* Buttons or menus have to be created with a component ID (see * {@link ComponentInteraction#getComponentId()}, - * {@link net.dv8tion.jda.api.interactions.components.buttons.Button#of(ButtonStyle, String, String)}) - * in a very specific format, otherwise the core system will fail to identify the command that + * {@link net.dv8tion.jda.api.components.buttons.Button#of(ButtonStyle, String, String)}) in a + * very specific format, otherwise the core system will fail to identify the command that * corresponded to the button or menu click event and is unable to route it back. *

* See {@link #acceptComponentIdGenerator(ComponentIdGenerator)} for more info. diff --git a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java index 4e03b07f78..ca7dcdea1a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/basic/QuoteBoardForwarder.java @@ -48,7 +48,6 @@ public final class QuoteBoardForwarder extends MessageReceiverAdapter implements // MessageId with a Map of Emojis and reacted users // >> private final Map>> reactions = new ConcurrentHashMap<>(); - private final String TJ_BOT_USER_ID = "884898473676271646"; private final Emoji botEmoji; private final Predicate isQuoteBoardChannelName; private final QuoteBoardConfig config; @@ -74,7 +73,8 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) { var messageId = event.getMessageIdLong(); var messageTime = TimeUtil.getTimeCreated(messageId); if (messageTime.isBefore(OffsetDateTime.now().minusDays(MAX_MESSAGE_AGE_DAYS))) { - logger.debug("Ignoring reaction as message is older than {} days", MAX_MESSAGE_AGE_DAYS); + logger.debug("Ignoring reaction as message is older than {} days", + MAX_MESSAGE_AGE_DAYS); return; } @@ -118,11 +118,15 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) { return; logger.debug("Attempting to forward message to quote board channel: {}", - boardChannel.getName()); - - event.retrieveMessage().queue(message -> markAsProcessed(message).flatMap(_ -> message.forwardTo(boardChannel)) - .queue(_ -> logger.debug("Message forwarded to quote board channel: {}", boardChannel.getName()), - e -> logger.warn("Unknown error while attempting to retrieve and forward message for quote-board, message is ignored.", e))); + boardChannel.getName()); + + event.retrieveMessage() + .queue(message -> markAsProcessed(message).flatMap(_ -> message.forwardTo(boardChannel)) + .queue(_ -> logger.debug("Message forwarded to quote board channel: {}", + boardChannel.getName()), + e -> logger.warn( + "Unknown error while attempting to retrieve and forward message for quote-board, message is ignored.", + e))); } @Override @@ -133,7 +137,8 @@ public Schedule createSchedule() { @Override public void runRoutine(JDA jda) { OffsetDateTime cutoff = OffsetDateTime.now().minusDays(MAX_MESSAGE_AGE_DAYS); - reactions.keySet().removeIf(messageId -> TimeUtil.getTimeCreated(messageId).isBefore(cutoff)); + reactions.keySet() + .removeIf(messageId -> TimeUtil.getTimeCreated(messageId).isBefore(cutoff)); } @@ -176,7 +181,8 @@ private boolean hasBotReactedToMessage(Long messageId) { return false; } var emojis = messageReactions.keySet(); - return emojis.contains(TJ_BOT_USER_ID); + String tjBotUserId = "884898473676271646"; + return emojis.contains(tjBotUserId); } private float calcReactionScore(Long messageId) { @@ -185,8 +191,8 @@ private float calcReactionScore(Long messageId) { return 0; } var scores = new AtomicReference<>(0.0F); - reacts.keySet().forEach(emojiCode -> - scores.updateAndGet(v -> v + getEmojiScore(emojiCode))); + reacts.keySet() + .forEach(emojiCode -> scores.updateAndGet(v -> v + getEmojiScore(emojiCode))); return scores.get(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/basic/RoleSelectCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/basic/RoleSelectCommand.java index 37f2bea2de..e006d78963 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/basic/RoleSelectCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/basic/RoleSelectCommand.java @@ -2,12 +2,10 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.IMentionable; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.RoleIcon; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.selections.SelectOption; +import net.dv8tion.jda.api.components.selections.StringSelectMenu; +import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; @@ -16,8 +14,6 @@ import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.interactions.components.selections.SelectOption; -import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; import org.jetbrains.annotations.Contract; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,11 +23,7 @@ import org.togetherjava.tjbot.features.componentids.Lifespan; import org.togetherjava.tjbot.features.utils.AmbientColors; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -195,7 +187,7 @@ private void sendRoleSelectionMenu(final CommandInteraction event, event.getOption(TITLE_OPTION, "Select your roles:", OptionMapping::getAsString); MessageEmbed embed = createEmbed(title, event.getOption(DESCRIPTION_OPTION).getAsString()); - event.replyEmbeds(embed).addActionRow(menu.build()).queue(); + event.replyEmbeds(embed).setComponents(ActionRow.of(menu.build())).queue(); } private static SelectOption mapToSelectOption(Role role) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/bookmarks/BookmarksListRemoveHandler.java b/application/src/main/java/org/togetherjava/tjbot/features/bookmarks/BookmarksListRemoveHandler.java index 9ebfb31257..af38b7fab9 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/bookmarks/BookmarksListRemoveHandler.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/bookmarks/BookmarksListRemoveHandler.java @@ -2,30 +2,27 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.components.MessageTopLevelComponent; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.selections.SelectMenu; +import net.dv8tion.jda.api.components.selections.SelectOption; +import net.dv8tion.jda.api.components.selections.StringSelectMenu; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; -import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.LayoutComponent; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; -import net.dv8tion.jda.api.interactions.components.selections.SelectOption; -import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; import org.togetherjava.tjbot.db.generated.tables.records.BookmarksRecord; import org.togetherjava.tjbot.features.utils.AmbientColors; import org.togetherjava.tjbot.features.utils.MessageUtils; -import java.awt.Color; -import java.util.ArrayList; -import java.util.Collection; +import java.awt.*; +import java.util.*; import java.util.List; -import java.util.Set; -import java.util.StringJoiner; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; @@ -76,7 +73,7 @@ private void handlePaginatedRequest(GenericCommandInteractionEvent event, MessageEmbed pageEmbed = generatePageEmbed(bookmarks, requestType, 0); - Collection components = new ArrayList<>(); + Collection components = new ArrayList<>(); components.add(generateNavigationComponent(requestType, 0)); if (requestType == RequestType.REMOVE) { @@ -200,7 +197,7 @@ private void updatePagination(ComponentInteraction event, Request request, MessageEmbed pageEmbed = generatePageEmbed(bookmarks, request.type, request.pageToDisplayIndex); - Collection components = new ArrayList<>(); + Collection components = new ArrayList<>(); components.add(generateNavigationComponent(request.type, request.pageToDisplayIndex)); if (request.type == RequestType.REMOVE) { components.addAll(generateRemoveComponents(event.getJDA(), bookmarks, request.type, @@ -210,7 +207,7 @@ private void updatePagination(ComponentInteraction event, Request request, event.editMessageEmbeds(pageEmbed).setComponents(components).queue(); } - private LayoutComponent generateNavigationComponent(RequestType requestType, + private MessageTopLevelComponent generateNavigationComponent(RequestType requestType, int pageToDisplayIndex) { UnaryOperator generateNavigationComponentId = name -> { Request request = new Request(requestType, name, pageToDisplayIndex, Set.of()); @@ -227,7 +224,7 @@ private LayoutComponent generateNavigationComponent(RequestType requestType, return ActionRow.of(buttonPrev, buttonNext); } - private List generateRemoveComponents(JDA jda, + private List generateRemoveComponents(JDA jda, List bookmarks, RequestType requestType, int pageToDisplayIndex, Set bookmarksToRemoveChannelIDs) { List pageEntries = getPageEntries(bookmarks, pageToDisplayIndex); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java index 95af0b3c3b..5076aec272 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java @@ -3,6 +3,9 @@ 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.components.label.Label; +import net.dv8tion.jda.api.components.textinput.TextInput; +import net.dv8tion.jda.api.components.textinput.TextInputStyle; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.SelfUser; import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; @@ -11,9 +14,7 @@ import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.modals.Modal; import org.togetherjava.tjbot.features.CommandVisibility; import org.togetherjava.tjbot.features.SlashCommandAdapter; @@ -105,16 +106,14 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { OptionMapping thinkingOption = event.getOption(THINKING_OPTION); boolean thinkingEnabled = thinkingOption != null && thinkingOption.getAsBoolean(); - TextInput body = TextInput - .create(QUESTION_INPUT, "Ask ChatGPT a question or get help with code", - TextInputStyle.PARAGRAPH) + TextInput body = TextInput.create(QUESTION_INPUT, TextInputStyle.PARAGRAPH) .setPlaceholder("Put your question for ChatGPT here") .setRequiredRange(MIN_MESSAGE_INPUT_LENGTH, MAX_MESSAGE_INPUT_LENGTH) .build(); Modal modal = Modal.create(generateComponentId(Boolean.toString(thinkingEnabled)), "ChatGPT") - .addActionRow(body) + .addComponents(Label.of("Ask ChatGPT a question or get help with code", body)) .build(); event.replyModal(modal).queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java b/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java index 5a50c82ae1..10a499eb0a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java @@ -2,11 +2,12 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.message.MessageDeleteEvent; import net.dv8tion.jda.api.events.message.MessageUpdateEvent; -import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.internal.requests.CompletedRestAction; @@ -26,11 +27,7 @@ import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -128,7 +125,7 @@ private MessageCreateData createCodeReplyMessage(long originalMessageId, } return new MessageCreateBuilder().setContent("Detected code, here are some useful tools:") - .setActionRow(codeActionButtons) + .setComponents(ActionRow.of(codeActionButtons)) .build(); } @@ -188,7 +185,7 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { metrics.count("code_action", Map.of("name", codeAction.getLabel())); return event.getHook() .editOriginalEmbeds(codeAction.apply(code)) - .setActionRow(createButtons(originalMessageId, codeAction)); + .setComponents(ActionRow.of(createButtons(originalMessageId, codeAction))); }) .queue(); } @@ -235,8 +232,8 @@ public void onMessageUpdated(MessageUpdateEvent event) { private Optional getCurrentActionFromCodeReply(Message codeReplyMessage) { // The disabled action is the currently applied action - return codeReplyMessage.getButtons() - .stream() + var buttons = MessageUtils.getButtonsFromMessage(codeReplyMessage); + return buttons.stream() .filter(Button::isDisabled) .map(Button::getLabel) .map(labelToCodeAction::get) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdGenerator.java b/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdGenerator.java index 55d3b460de..a6fea986f7 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdGenerator.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdGenerator.java @@ -1,9 +1,9 @@ package org.togetherjava.tjbot.features.componentids; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import org.togetherjava.tjbot.features.SlashCommand; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdParser.java b/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdParser.java index 251fcec9cb..d5bb0098a6 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdParser.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdParser.java @@ -1,9 +1,9 @@ package org.togetherjava.tjbot.features.componentids; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import java.util.Optional; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java b/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java index bdc4f13c38..6a83a13373 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java @@ -1,5 +1,7 @@ package org.togetherjava.tjbot.features.filesharing; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; @@ -7,7 +9,6 @@ import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import net.dv8tion.jda.api.interactions.components.buttons.Button; import org.kohsuke.github.GHGist; import org.kohsuke.github.GHGistBuilder; import org.kohsuke.github.GitHubBuilder; @@ -196,7 +197,7 @@ private void sendResponse(MessageReceivedEvent event, String url, String gistId) "Delete"); metrics.count("file_sharing-uploaded"); - message.reply(messageContent).setActionRow(gist, delete).queue(); + message.reply(messageContent).setComponents(ActionRow.of(gist, delete)).queue(); } private boolean isHelpThread(MessageReceivedEvent event) { 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 d996613b49..5c04605467 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,19 +1,15 @@ 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.Member; -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.SelfUser; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +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; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; -import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.internal.requests.CompletedRestAction; import org.slf4j.Logger; @@ -32,15 +28,7 @@ import org.togetherjava.tjbot.features.utils.Guilds; import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.StringJoiner; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; @@ -171,7 +159,8 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion, CHAT_GPT_MODEL); return post.flatMap(_ -> threadChannel.sendMessageEmbeds(responseEmbed) - .addActionRow(generateDismissButton(componentIdInteractor, messageId.get()))); + .setComponents( + ActionRow.of(generateDismissButton(componentIdInteractor, messageId.get())))); } /** diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java index a63b5fc83d..3abea92e2d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java @@ -2,6 +2,8 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; @@ -9,7 +11,6 @@ import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.utils.TimeUtil; import org.slf4j.Logger; @@ -182,11 +183,11 @@ private void triggerArchiveFlow(ThreadChannel threadChannel, long authorId, Function> sendEmbedWithMention = member -> threadChannel.sendMessage(member.getAsMention()) .addEmbeds(embed) - .addActionRow(Button.primary(markActiveId, MARK_ACTIVE_LABEL)); + .setComponents(ActionRow.of(Button.primary(markActiveId, MARK_ACTIVE_LABEL))); Supplier> sendEmbedWithoutMention = () -> threadChannel.sendMessageEmbeds(embed) - .addActionRow(Button.primary(markActiveId, MARK_ACTIVE_LABEL)); + .setComponents(ActionRow.of(Button.primary(markActiveId, MARK_ACTIVE_LABEL))); threadChannel.getGuild() .retrieveMemberById(authorId) @@ -216,7 +217,7 @@ private void triggerAuthorIdNotFoundArchiveFlow(ThreadChannel threadChannel, String markActiveId = inactivityInteractor.generateComponentId(MARK_ACTIVE_ID); threadChannel.sendMessageEmbeds(embed) - .addActionRow(Button.primary(markActiveId, MARK_ACTIVE_LABEL)) + .setComponents(ActionRow.of(Button.primary(markActiveId, MARK_ACTIVE_LABEL))) .flatMap(_ -> threadChannel.getManager().setArchived(true)) .queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellCommand.java index f5ebe81f3f..76730f7ade 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellCommand.java @@ -1,6 +1,9 @@ package org.togetherjava.tjbot.features.jshell; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.components.label.Label; +import net.dv8tion.jda.api.components.textinput.TextInput; +import net.dv8tion.jda.api.components.textinput.TextInputStyle; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -11,10 +14,8 @@ import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.interactions.modals.ModalMapping; +import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.utils.FileUpload; import org.togetherjava.tjbot.features.CommandVisibility; @@ -50,7 +51,9 @@ public class JShellCommand extends SlashCommandAdapter { private static final String INCLUDE_STARTUP_SCRIPT_PARAMETER = "include-startup-script"; private static final int MIN_MESSAGE_INPUT_LENGTH = 0; - private static final int MAX_MESSAGE_INPUT_LENGTH = TextInput.MAX_VALUE_LENGTH; + private static final int MAX_MESSAGE_INPUT_LENGTH = Label.LABEL_MAX_LENGTH; // Assuming + // Label.LABEL_MAX_LENGTH + // or similar private static final String MAX_SNIPPETS_FILE_PREFIX = " // Snippet 1000"; private static final String MAX_SNIPPETS_EMBED_PREFIX = "Snippet 10```java\n```"; @@ -133,12 +136,14 @@ private void handleEvalCommand(SlashCommandInteractionEvent event) { private void sendEvalModal(SlashCommandInteractionEvent event, boolean startupScript) { TextInput body = TextInput .create(TEXT_INPUT_PART_ID + (startupScript ? "|" + STARTUP_SCRIPT_PARAMETER : ""), - "Enter code to evaluate.", TextInputStyle.PARAGRAPH) + TextInputStyle.PARAGRAPH) .setPlaceholder("Put your code here.") .setRequiredRange(MIN_MESSAGE_INPUT_LENGTH, MAX_MESSAGE_INPUT_LENGTH) .build(); - Modal modal = Modal.create(generateComponentId(), "JShell").addActionRow(body).build(); + Modal modal = Modal.create(generateComponentId(), "JShell") + .addComponents(Label.of("Enter code to evaluate.", body)) + .build(); event.replyModal(modal).queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/TeXCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/TeXCommand.java index dd0e717f92..8215187fad 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/TeXCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/TeXCommand.java @@ -1,11 +1,12 @@ package org.togetherjava.tjbot.features.mathcommands; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.callbacks.IDeferrableCallback; import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import net.dv8tion.jda.api.utils.FileUpload; import org.scilab.forge.jlatexmath.ParseException; import org.scilab.forge.jlatexmath.TeXConstants; @@ -18,8 +19,7 @@ import javax.imageio.ImageIO; -import java.awt.Color; -import java.awt.Image; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -115,7 +115,8 @@ private void sendImage(IDeferrableCallback event, String userID, Image image) event.getHook() .editOriginalAttachments( FileUpload.fromData(renderedTextImageStream.toByteArray(), "tex.png")) - .setActionRow(Button.of(ButtonStyle.DANGER, generateComponentId(userID), "Delete")) + .setComponents(ActionRow + .of(Button.of(ButtonStyle.DANGER, generateComponentId(userID), "Delete"))) .queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 5fe15a4c6d..3603c9ea81 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -3,6 +3,11 @@ 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.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.label.Label; +import net.dv8tion.jda.api.components.textinput.TextInput; +import net.dv8tion.jda.api.components.textinput.TextInputStyle; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -12,10 +17,7 @@ import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.commands.build.Commands; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; import net.dv8tion.jda.api.utils.Result; import org.slf4j.Logger; @@ -95,18 +97,17 @@ public void onMessageContext(MessageContextInteractionEvent event) { String reportedAuthorAvatarUrl = event.getTarget().getAuthor().getEffectiveAvatarUrl(); String reportedAuthorID = event.getTarget().getAuthor().getId(); - TextInput modalTextInput = TextInput - .create(REPORT_REASON_INPUT_ID, "Anonymous report to the moderators", - TextInputStyle.PARAGRAPH) - .setPlaceholder("Why do you want to report this message?") - .setRequiredRange(3, 200) - .build(); + TextInput modalTextInput = + TextInput.create(REPORT_REASON_INPUT_ID, TextInputStyle.PARAGRAPH) + .setPlaceholder("Why do you want to report this message?") + .setRequiredRange(3, 200) + .build(); String reportModalComponentID = generateComponentId(reportedMessage, reportedMessageID, reportedMessageJumpUrl, reportedMessageChannel, reportedMessageTimestamp, reportedAuthorName, reportedAuthorAvatarUrl, reportedAuthorID); Modal reportModal = Modal.create(reportModalComponentID, "Report this to a moderator") - .addActionRow(modalTextInput) + .addComponents(Label.of("Anonymous report to the moderators", modalTextInput)) .build(); event.replyModal(reportModal).queue(); @@ -181,9 +182,9 @@ private MessageCreateAction createModMessage(String reportReason, .setColor(AmbientColors.MODMAIL) .build(); - MessageCreateAction message = - modMailAuditLog.sendMessageEmbeds(reportedMessageEmbed, reportReasonEmbed) - .addActionRow(Button.link(reportedMessage.jumpUrl, "Go to message")); + MessageCreateAction message = modMailAuditLog + .sendMessageEmbeds(reportedMessageEmbed, reportReasonEmbed) + .setComponents(ActionRow.of(Button.link(reportedMessage.jumpUrl, "Go to message"))); Optional moderatorRole = guild.getRoles() .stream() diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ThisIsScamCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ThisIsScamCommand.java index 3180b61c57..01bad8dcdb 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ThisIsScamCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ThisIsScamCommand.java @@ -4,19 +4,15 @@ import com.github.benmanes.caffeine.cache.Caffeine; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -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.User; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.exceptions.ErrorHandler; import net.dv8tion.jda.api.interactions.commands.build.Commands; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import net.dv8tion.jda.api.requests.ErrorResponse; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; @@ -32,7 +28,7 @@ import org.togetherjava.tjbot.features.utils.MessageUtils; import org.togetherjava.tjbot.logging.LogMarkers; -import java.awt.Color; +import java.awt.*; import java.time.Duration; import java.time.Instant; import java.time.OffsetDateTime; @@ -180,8 +176,8 @@ private MessageCreateAction reportToMods(Message message, TextChannel auditChann String[] args = {String.valueOf(guildId), String.valueOf(authorId)}; return auditChannel.sendMessageEmbeds(reportEmbed) - .addActionRow(Button.success(generateComponentId(args), "Yes"), - Button.danger(generateComponentId(args), "No")); + .setComponents(ActionRow.of(Button.success(generateComponentId(args), "Yes"), + Button.danger(generateComponentId(args), "No"))); } private static String createDescription(Message target) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java index e7f7f4b8e9..a8309afe3a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java @@ -1,11 +1,8 @@ package org.togetherjava.tjbot.features.moderation; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.GuildVoiceState; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.commands.OptionMapping; @@ -18,7 +15,7 @@ import javax.annotation.CheckReturnValue; -import java.awt.Color; +import java.awt.*; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.Collection; @@ -91,7 +88,7 @@ private static ReplyCallbackAction handleWhoIsMember(final IReplyCallback event, final Member member, final User.Profile profile) { User user = member.getUser(); - Color memberColor = member.getColor(); + Color memberColor = member.getColors().getPrimary(); Color effectiveColor = (null == memberColor) ? profile.getAccentColor() : memberColor; String description = userIdentificationToStringItem(user) + voiceStateToStringItem(member) @@ -112,8 +109,8 @@ private static ReplyCallbackAction handleWhoIsMember(final IReplyCallback event, private static ReplyCallbackAction sendEmbedWithProfileAction(final IReplyCallback event, MessageEmbed embed, String userId) { return event.replyEmbeds(embed) - .addActionRow( - DiscordClientAction.General.USER.asLinkButton("Click to see profile!", userId)); + .setComponents(ActionRow.of(DiscordClientAction.General.USER + .asLinkButton("Click to see profile!", userId))); } private static String voiceStateToStringItem(final Member member) { @@ -124,7 +121,8 @@ private static String voiceStateToStringItem(final Member member) { return ""; } - return "\n**In voicechannel:** " + (voiceState.getChannel().getAsMention()); + return "\n**In voicechannel:** " + + (Objects.requireNonNull(voiceState.getChannel()).getAsMention()); } /** diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java index 530fa6e739..55a3c9d114 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java @@ -2,6 +2,8 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -11,7 +13,6 @@ import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.utils.TimeUtil; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; @@ -31,11 +32,7 @@ import java.time.Instant; import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -227,7 +224,7 @@ private > R attachPageTurnButtons( List