Skip to content

Commit

Permalink
feat(cool-messages): add primary logic
Browse files Browse the repository at this point in the history
  • Loading branch information
christolis committed Oct 29, 2024
1 parent 9e16276 commit 9389156
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.togetherjava.tjbot.config.FeatureBlacklist;
import org.togetherjava.tjbot.config.FeatureBlacklistConfig;
import org.togetherjava.tjbot.db.Database;
import org.togetherjava.tjbot.features.basic.CoolMessagesBoardManager;
import org.togetherjava.tjbot.features.basic.MemberCountDisplayRoutine;
import org.togetherjava.tjbot.features.basic.PingCommand;
import org.togetherjava.tjbot.features.basic.RoleSelectCommand;
Expand Down Expand Up @@ -150,6 +151,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
features.add(new CodeMessageManualDetection(codeMessageHandler));
features.add(new SlashCommandEducator());
features.add(new PinnedNotificationRemover(config));
features.add(new CoolMessagesBoardManager(config));

// Event receivers
features.add(new RejoinModerationRoleListener(actionsStore, config));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package org.togetherjava.tjbot.features.basic;

import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.togetherjava.tjbot.config.Config;
import org.togetherjava.tjbot.config.CoolMessagesBoardConfig;
import org.togetherjava.tjbot.features.MessageReceiverAdapter;

import java.awt.Color;
import java.util.Collections;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;

/**
* Manager for the cool messages board. It appends highly-voted text messages to a separate channel
* where members of the guild can see a list of all of them.
*/
public final class CoolMessagesBoardManager extends MessageReceiverAdapter {

private static final Logger logger = LoggerFactory.getLogger(CoolMessagesBoardManager.class);
private Emoji coolEmoji;
private final Predicate<String> boardChannelNamePredicate;
private final CoolMessagesBoardConfig config;

public CoolMessagesBoardManager(Config config) {
this.config = config.getCoolMessagesConfig();
this.coolEmoji = Emoji.fromUnicode(this.config.reactionEmoji());

boardChannelNamePredicate =
Pattern.compile(this.config.boardChannelPattern()).asMatchPredicate();
}

@Override
public void onMessageReactionAdd(MessageReactionAddEvent event) {
final MessageReaction messageReaction = event.getReaction();
int originalReactionsCount = messageReaction.hasCount() ? messageReaction.getCount() : 0;
boolean isCoolEmoji = messageReaction.getEmoji().equals(coolEmoji);
long guildId = event.getGuild().getIdLong();
Optional<TextChannel> boardChannel = getBoardChannel(event.getJDA(), guildId);

if (boardChannel.isEmpty()) {
logger.warn(
"Could not find board channel with pattern '{}' in server with ID '{}'. Skipping reaction handling...",
this.config.boardChannelPattern(), guildId);
return;
}

// If the bot has already reacted to this message, then this means that
// the message has been quoted to the cool messages board, so skip it.
if (hasBotReacted(event.getJDA(), messageReaction)) {
return;
}

final int newReactionsCount = originalReactionsCount + 1;
if (isCoolEmoji && newReactionsCount >= config.minimumReactions()) {
event.retrieveMessage()
.queue(message -> message.addReaction(coolEmoji)
.flatMap(v -> insertCoolMessage(boardChannel.get(), message))
.queue(),
e -> logger.warn("Tried to retrieve cool message but got: {}",
e.getMessage()));
}
}

/**
* Gets the board text channel where the quotes go to, wrapped in an optional.
*
* @param jda the JDA
* @param guildId the guild ID
* @return the board text channel
*/
private Optional<TextChannel> getBoardChannel(JDA jda, long guildId) {
return jda.getGuildById(guildId)
.getTextChannelCache()
.stream()
.filter(channel -> boardChannelNamePredicate.test(channel.getName()))
.findAny();
}

/**
* Inserts a message to the specified text channel
*
* @return a {@link MessageCreateAction} of the call to make
*/
private static MessageCreateAction insertCoolMessage(TextChannel boardChannel,
Message message) {
return boardChannel.sendMessageEmbeds(Collections.singleton(createQuoteEmbed(message)));
}

/**
* Wraps a text message into a properly formatted quote message used for the board text channel.
*/
private static MessageEmbed createQuoteEmbed(Message message) {
final User author = message.getAuthor();
EmbedBuilder embedBuilder = new EmbedBuilder();

// If the message contains image(s), include the first one
var firstImageAttachment = message.getAttachments()
.stream()
.parallel()
.filter(Message.Attachment::isImage)
.findAny()
.orElse(null);

if (firstImageAttachment != null) {
embedBuilder.setThumbnail(firstImageAttachment.getUrl());
}

return embedBuilder.setDescription(message.getContentDisplay())
.appendDescription("%n%n[Jump to Message](%s)".formatted(message.getJumpUrl()))
.setColor(Color.orange)
.setAuthor(author.getName(), null, author.getAvatarUrl())
.setTimestamp(message.getTimeCreated())
.build();
}

/**
* Checks a {@link MessageReaction} to see if the bot has reacted to it.
*/
private boolean hasBotReacted(JDA jda, MessageReaction messageReaction) {
if (!coolEmoji.equals(messageReaction.getEmoji())) {
return false;
}

return messageReaction.retrieveUsers()
.parallelStream()
.anyMatch(user -> jda.getSelfUser().getIdLong() == user.getIdLong());
}
}

0 comments on commit 9389156

Please sign in to comment.