Skip to content

Commit dbc9cff

Browse files
committed
Track emoji usage in messages and reactions
1 parent 8d2c1b6 commit dbc9cff

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.togetherjava.tjbot.config.FeatureBlacklist;
77
import org.togetherjava.tjbot.config.FeatureBlacklistConfig;
88
import org.togetherjava.tjbot.db.Database;
9+
import org.togetherjava.tjbot.features.analytics.EmojiTrackerListener;
910
import org.togetherjava.tjbot.features.analytics.Metrics;
1011
import org.togetherjava.tjbot.features.basic.MemberCountDisplayRoutine;
1112
import org.togetherjava.tjbot.features.basic.PingCommand;
@@ -159,6 +160,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
159160
// Message receivers
160161
features.add(new TopHelpersMessageListener(database, config));
161162
features.add(new SuggestionsUpDownVoter(config, metrics));
163+
features.add(new EmojiTrackerListener(metrics));
162164
features.add(new ScamBlocker(actionsStore, scamHistoryStore, config, metrics));
163165
features.add(new MediaOnlyChannelListener(config, metrics));
164166
features.add(new FileSharingMessageListener(config, metrics));
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.togetherjava.tjbot.features.analytics;
2+
3+
import net.dv8tion.jda.api.entities.emoji.CustomEmoji;
4+
import net.dv8tion.jda.api.entities.emoji.Emoji;
5+
import net.dv8tion.jda.api.entities.emoji.EmojiUnion;
6+
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
7+
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
8+
9+
import org.togetherjava.tjbot.features.MessageReceiverAdapter;
10+
11+
import java.util.regex.Pattern;
12+
13+
/**
14+
* Listener that tracks emoji usage across all channels for analytics purposes.
15+
* <p>
16+
* Counts emojis used in messages and reactions so admins can see which emojis are unused and should
17+
* be removed.
18+
* <p>
19+
* Custom emojis are tracked by their Discord ID (e.g. {@code emoji-custom-123456789}) rather than
20+
* by name, since emoji names are not unique and may change over time. Animated custom emojis are
21+
* tracked separately (e.g. {@code emoji-custom-animated-123456789}). Unicode emojis are tracked by
22+
* name (e.g. {@code emoji-unicode-thumbsup}).
23+
*/
24+
public final class EmojiTrackerListener extends MessageReceiverAdapter {
25+
private static final Pattern ALL_CHANNELS = Pattern.compile(".*");
26+
27+
private final Metrics metrics;
28+
29+
/**
30+
* Creates a new listener to track emoji usage across all channels.
31+
*
32+
* @param metrics to track emoji usage events
33+
*/
34+
public EmojiTrackerListener(Metrics metrics) {
35+
super(ALL_CHANNELS);
36+
37+
this.metrics = metrics;
38+
}
39+
40+
@Override
41+
public void onMessageReceived(MessageReceivedEvent event) {
42+
if (event.getAuthor().isBot() || event.isWebhookMessage()) {
43+
return;
44+
}
45+
46+
event.getMessage().getMentions().getCustomEmojis().forEach(this::trackCustomEmoji);
47+
}
48+
49+
@Override
50+
public void onMessageReactionAdd(MessageReactionAddEvent event) {
51+
if (event.getUser() != null && event.getUser().isBot()) {
52+
return;
53+
}
54+
55+
trackEmojiUnion(event.getEmoji());
56+
}
57+
58+
private void trackEmojiUnion(EmojiUnion emoji) {
59+
if (emoji.getType() == Emoji.Type.CUSTOM) {
60+
trackCustomEmoji(emoji.asCustom());
61+
} else {
62+
metrics.count("emoji-unicode-" + emoji.asUnicode().getName());
63+
}
64+
}
65+
66+
private void trackCustomEmoji(CustomEmoji emoji) {
67+
String prefix = emoji.isAnimated() ? "emoji-custom-animated-" : "emoji-custom-";
68+
metrics.count(prefix + emoji.getIdLong());
69+
}
70+
}

0 commit comments

Comments
 (0)