Skip to content

Commit 64e0d2e

Browse files
committed
feat(coolMessages): implement working version
1 parent f809da4 commit 64e0d2e

File tree

7 files changed

+195
-7
lines changed

7 files changed

+195
-7
lines changed

application/src/main/java/org/togetherjava/tjbot/config/Config.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public final class Config {
4444
private final HelperPruneConfig helperPruneConfig;
4545
private final FeatureBlacklistConfig featureBlacklistConfig;
4646
private final String selectRolesChannelPattern;
47+
private final CoolMessagesBoardConfig coolMessagesConfig;
4748

4849
@SuppressWarnings("ConstructorWithTooManyParameters")
4950
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
@@ -91,7 +92,9 @@ private Config(@JsonProperty(value = "token", required = true) String token,
9192
@JsonProperty(value = "featureBlacklist",
9293
required = true) FeatureBlacklistConfig featureBlacklistConfig,
9394
@JsonProperty(value = "selectRolesChannelPattern",
94-
required = true) String selectRolesChannelPattern) {
95+
required = true) String selectRolesChannelPattern,
96+
@JsonProperty(value = "coolMessagesConfig",
97+
required = true) CoolMessagesBoardConfig coolMessagesConfig) {
9598
this.token = Objects.requireNonNull(token);
9699
this.githubApiKey = Objects.requireNonNull(githubApiKey);
97100
this.databasePath = Objects.requireNonNull(databasePath);
@@ -123,6 +126,7 @@ private Config(@JsonProperty(value = "token", required = true) String token,
123126
this.helperPruneConfig = Objects.requireNonNull(helperPruneConfig);
124127
this.featureBlacklistConfig = Objects.requireNonNull(featureBlacklistConfig);
125128
this.selectRolesChannelPattern = Objects.requireNonNull(selectRolesChannelPattern);
129+
this.coolMessagesConfig = Objects.requireNonNull(coolMessagesConfig);
126130
}
127131

128132
/**
@@ -405,4 +409,13 @@ public FeatureBlacklistConfig getFeatureBlacklistConfig() {
405409
public String getSelectRolesChannelPattern() {
406410
return selectRolesChannelPattern;
407411
}
412+
413+
/**
414+
* The configuration of the cool messages config.
415+
*
416+
* @return configuration of cool messages config
417+
*/
418+
public CoolMessagesBoardConfig getCoolMessagesConfig() {
419+
return coolMessagesConfig;
420+
}
408421
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.togetherjava.tjbot.config;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonRootName;
5+
6+
/**
7+
* Configuration for the cool messages board feature, see
8+
* {@link org.togetherjava.tjbot.features.basic.CoolMessagesBoardManager}.
9+
*/
10+
@JsonRootName("coolMessagesConfig")
11+
public final class CoolMessagesBoardConfig {
12+
private final String boardChannelPattern;
13+
private final int minimumReactions;
14+
15+
private CoolMessagesBoardConfig(
16+
@JsonProperty(value = "minimumReactions", required = true) int minimumReactions,
17+
@JsonProperty(value = "boardChannelPattern",
18+
required = true) String boardChannelPattern) {
19+
this.minimumReactions = minimumReactions;
20+
this.boardChannelPattern = boardChannelPattern;
21+
}
22+
23+
/**
24+
* Gets the minimum amount of reactions needed for a message to be considered as a quote.
25+
*
26+
* @return the minimum amount of reactions
27+
*/
28+
public int getMinimumReactions() {
29+
return minimumReactions;
30+
}
31+
32+
/**
33+
* Gets the REGEX pattern used to identify the quotes text channel
34+
*
35+
* @return the channel name pattern
36+
*/
37+
public String getBoardChannelPattern() {
38+
return boardChannelPattern;
39+
}
40+
}

application/src/main/java/org/togetherjava/tjbot/features/Features.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22

33
import net.dv8tion.jda.api.JDA;
44

5-
import org.togetherjava.tjbot.commands.github.GitHubCommand;
6-
import org.togetherjava.tjbot.commands.github.GitHubReference;
75
import org.togetherjava.tjbot.config.Config;
86
import org.togetherjava.tjbot.config.FeatureBlacklist;
97
import org.togetherjava.tjbot.config.FeatureBlacklistConfig;
108
import org.togetherjava.tjbot.db.Database;
11-
import org.togetherjava.tjbot.features.basic.PingCommand;
12-
import org.togetherjava.tjbot.features.basic.RoleSelectCommand;
13-
import org.togetherjava.tjbot.features.basic.SlashCommandEducator;
14-
import org.togetherjava.tjbot.features.basic.SuggestionsUpDownVoter;
9+
import org.togetherjava.tjbot.features.basic.*;
1510
import org.togetherjava.tjbot.features.bookmarks.BookmarksCommand;
1611
import org.togetherjava.tjbot.features.bookmarks.BookmarksSystem;
1712
import org.togetherjava.tjbot.features.bookmarks.LeftoverBookmarksCleanupRoutine;
@@ -123,6 +118,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
123118
features.add(new CodeMessageManualDetection(codeMessageHandler));
124119
features.add(new SlashCommandEducator());
125120
features.add(new PinnedNotificationRemover(config));
121+
features.add(new CoolMessagesBoardManager(config));
126122

127123
// Event receivers
128124
features.add(new RejoinModerationRoleListener(actionsStore, config));

application/src/main/java/org/togetherjava/tjbot/features/MessageReceiver.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
44
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
55
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
6+
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
67

78
import java.util.regex.Pattern;
89

@@ -56,4 +57,13 @@ public interface MessageReceiver extends Feature {
5657
* message that was deleted
5758
*/
5859
void onMessageDeleted(MessageDeleteEvent event);
60+
61+
/**
62+
* Triggered by the core system whenever a new reaction was added to a message in a text channel
63+
* of a guild the bot has been added to.
64+
*
65+
* @param event the event that triggered this, containing information about the corresponding
66+
* reaction that was added
67+
*/
68+
void onMessageReactionAdd(MessageReactionAddEvent event);
5969
}

application/src/main/java/org/togetherjava/tjbot/features/MessageReceiverAdapter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
44
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
55
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
6+
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
67

78
import java.util.regex.Pattern;
89

@@ -57,4 +58,10 @@ public void onMessageUpdated(MessageUpdateEvent event) {
5758
public void onMessageDeleted(MessageDeleteEvent event) {
5859
// Adapter does not react by default, subclasses may change this behavior
5960
}
61+
62+
@SuppressWarnings("NoopMethodInAbstractClass")
63+
@Override
64+
public void onMessageReactionAdd(MessageReactionAddEvent event) {
65+
// Adapter does not react by default, subclasses may change this behavior
66+
}
6067
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package org.togetherjava.tjbot.features.basic;
2+
3+
import net.dv8tion.jda.api.EmbedBuilder;
4+
import net.dv8tion.jda.api.JDA;
5+
import net.dv8tion.jda.api.entities.Message;
6+
import net.dv8tion.jda.api.entities.MessageEmbed;
7+
import net.dv8tion.jda.api.entities.MessageReaction;
8+
import net.dv8tion.jda.api.entities.User;
9+
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
10+
import net.dv8tion.jda.api.entities.emoji.Emoji;
11+
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
import org.togetherjava.tjbot.config.Config;
16+
import org.togetherjava.tjbot.config.CoolMessagesBoardConfig;
17+
import org.togetherjava.tjbot.features.MessageReceiverAdapter;
18+
19+
import java.awt.*;
20+
import java.util.Collections;
21+
import java.util.Optional;
22+
import java.util.function.Predicate;
23+
import java.util.regex.Pattern;
24+
25+
/**
26+
* Manager for the cool messages board. It appends highly-voted text messages to a separate channel
27+
* where members of the guild can see a list of all of them.
28+
*/
29+
public final class CoolMessagesBoardManager extends MessageReceiverAdapter {
30+
31+
private static final Logger logger = LoggerFactory.getLogger(CoolMessagesBoardManager.class);
32+
private static final Emoji REACT_EMOJI = Emoji.fromUnicode("🌟");
33+
private final Predicate<String> boardChannelNamePredicate;
34+
private final CoolMessagesBoardConfig config;
35+
36+
public CoolMessagesBoardManager(Config config) {
37+
this.config = config.getCoolMessagesConfig();
38+
39+
boardChannelNamePredicate =
40+
Pattern.compile(this.config.getBoardChannelPattern()).asMatchPredicate();
41+
}
42+
43+
@Override
44+
public void onMessageReactionAdd(MessageReactionAddEvent event) {
45+
final MessageReaction messageReaction = event.getReaction();
46+
int originalReactionsCount = messageReaction.hasCount() ? messageReaction.getCount() : 0;
47+
boolean isCoolEmoji = messageReaction.getEmoji().getName().equals(REACT_EMOJI.getName());
48+
long guildId = event.getGuild().getIdLong();
49+
Optional<TextChannel> boardChannel = getBoardChannel(event.getJDA(), guildId);
50+
51+
if (boardChannel.isEmpty()) {
52+
logger.warn("Tried to find the board channel in server {} but was unable to do so",
53+
guildId);
54+
return;
55+
}
56+
57+
if (isCoolEmoji && originalReactionsCount + 1 >= config.getMinimumReactions()) {
58+
event.retrieveMessage()
59+
.queue(message -> insertCoolMessage(boardChannel.orElseThrow(), message),
60+
e -> logger.warn("Tried to retrieve cool message but got: {}",
61+
e.getMessage()));
62+
}
63+
}
64+
65+
/**
66+
* Gets the board text channel where the quotes go to, wrapped in an optional.
67+
*
68+
* @param jda the JDA
69+
* @param guildId the guild ID
70+
* @return the board text channel
71+
*/
72+
private Optional<TextChannel> getBoardChannel(JDA jda, long guildId) {
73+
return jda.getGuildById(guildId)
74+
.getTextChannelCache()
75+
.stream()
76+
.filter(channel -> boardChannelNamePredicate.test(channel.getName()))
77+
.findAny();
78+
}
79+
80+
/**
81+
* Inserts a message to the specified text channel
82+
*/
83+
private static void insertCoolMessage(TextChannel boardChannel, Message message) {
84+
boardChannel.sendMessageEmbeds(Collections.singleton(createQuoteEmbed(message))).queue();
85+
}
86+
87+
/**
88+
* Wraps a text message into a properly formatted quote message used for the board text channel.
89+
*/
90+
private static MessageEmbed createQuoteEmbed(Message message) {
91+
final User author = message.getAuthor();
92+
EmbedBuilder embedBuilder = new EmbedBuilder();
93+
94+
// If the message contains image(s), include the first one
95+
var imageAttachment = message.getAttachments()
96+
.stream()
97+
.parallel()
98+
.filter(Message.Attachment::isImage)
99+
.findAny()
100+
.orElse(null);
101+
102+
if (imageAttachment != null) {
103+
embedBuilder.setThumbnail(imageAttachment.getUrl());
104+
}
105+
106+
return embedBuilder.setDescription(message.getContentDisplay())
107+
.appendDescription("%n%n[Jump to Message](%s)".formatted(message.getJumpUrl()))
108+
.setColor(Color.orange)
109+
.setAuthor(author.getName(), null, author.getAvatarUrl())
110+
.setTimestamp(message.getTimeCreated())
111+
.build();
112+
}
113+
}

application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
1313
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
1414
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
15+
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
1516
import net.dv8tion.jda.api.hooks.ListenerAdapter;
1617
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
1718
import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
@@ -222,6 +223,14 @@ public void onMessageDelete(final MessageDeleteEvent event) {
222223
}
223224
}
224225

226+
@Override
227+
public void onMessageReactionAdd(final MessageReactionAddEvent event) {
228+
if (event.isFromGuild()) {
229+
getMessageReceiversSubscribedTo(event.getChannel())
230+
.forEach(messageReceiver -> messageReceiver.onMessageReactionAdd(event));
231+
}
232+
}
233+
225234
private Stream<MessageReceiver> getMessageReceiversSubscribedTo(Channel channel) {
226235
String channelName = channel.getName();
227236
return channelNameToMessageReceiver.entrySet()

0 commit comments

Comments
 (0)