[AB-154] split up private and guild message received handler, split handlers into async and sync handlers

adapting the tests and improving tests to reduce usage of MockUtils
adding some util methods to message bean
extending cache for cached messages
enabling to build cached messages from messages in DM channels (they are not part of the message cache)
splitting multiple listeners to different beans, for better overview (emote updated)
adding convenience service for reactions specifically
split cached reaction and cached reactions, singular only contains one user, while the later contains all users
fixing liquibase configuration for assigned role user
fixing assignable role not having a transaction
moved caching update a bit earlier in various methods
fixing bug that a manual unmute caused duplicate unmute notification
fixing short scheduled unmute not checking the new mute state
limiting parameters for roll
This commit is contained in:
Sheldan
2020-12-20 19:21:24 +01:00
parent 69aa82e26e
commit fb3ed69650
200 changed files with 4253 additions and 1813 deletions

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
@@ -19,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -60,8 +62,8 @@ public class Roll extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("high").type(Integer.class).templated(true).optional(true).build());
parameters.add(Parameter.builder().name("low").type(Integer.class).templated(true).optional(true).build());
parameters.add(Parameter.builder().name("high").type(Integer.class).templated(true).validators(Arrays.asList(MinIntegerValueValidator.min(2L))).optional(true).build());
parameters.add(Parameter.builder().name("low").type(Integer.class).templated(true).validators(Arrays.asList(MinIntegerValueValidator.min(0L))).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("roll")

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.utility.config;
import dev.sheldan.abstracto.core.listener.ServerConfigListener;
import dev.sheldan.abstracto.core.listener.sync.entity.ServerConfigListener;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;

View File

@@ -2,7 +2,9 @@ package dev.sheldan.abstracto.utility.listener.embed;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.MessageReceivedListener;
import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener;
import dev.sheldan.abstracto.core.execution.result.ExecutionResult;
import dev.sheldan.abstracto.core.execution.result.MessageReceivedListenerResult;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -11,6 +13,7 @@ import dev.sheldan.abstracto.utility.models.MessageEmbedLink;
import dev.sheldan.abstracto.utility.service.MessageEmbedService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.Event;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -36,7 +39,7 @@ public class MessageEmbedListener implements MessageReceivedListener {
private MessageEmbedListener self;
@Override
public void execute(Message message) {
public MessageReceivedListenerResult execute(Message message) {
String messageRaw = message.getContentRaw();
List<MessageEmbedLink> links = messageEmbedService.getLinksInMessage(messageRaw);
if(!links.isEmpty()) {
@@ -59,7 +62,12 @@ public class MessageEmbedListener implements MessageReceivedListener {
}
if(StringUtils.isBlank(messageRaw) && !links.isEmpty()) {
message.delete().queue();
return MessageReceivedListenerResult.DELETED;
}
if(!links.isEmpty()) {
return MessageReceivedListenerResult.PROCESSED;
}
return MessageReceivedListenerResult.IGNORED;
}
@Transactional
@@ -73,6 +81,11 @@ public class MessageEmbedListener implements MessageReceivedListener {
});
}
@Override
public boolean shouldConsume(Event event, ExecutionResult result) {
return result.equals(MessageReceivedListenerResult.DELETED);
}
@Override
public FeatureEnum getFeature() {
return UtilityFeature.LINK_EMBEDS;

View File

@@ -1,12 +1,11 @@
package dev.sheldan.abstracto.utility.listener.embed;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.ReactedAddedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService;
@@ -14,8 +13,6 @@ import dev.sheldan.abstracto.utility.config.features.UtilityFeature;
import dev.sheldan.abstracto.utility.models.database.EmbeddedMessage;
import dev.sheldan.abstracto.utility.service.management.MessageEmbedPostManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -23,7 +20,7 @@ import java.util.Optional;
@Component
@Slf4j
public class MessageEmbedRemovalReactionListener implements ReactedAddedListener {
public class MessageEmbedRemovalReactionListener implements AsyncReactionAddedListener {
public static final String REMOVAL_EMOTE = "removeEmbed";
@@ -39,19 +36,16 @@ public class MessageEmbedRemovalReactionListener implements ReactedAddedListener
@Autowired
private EmoteService emoteService;
@Override
public void executeReactionAdded(CachedMessage message, GuildMessageReactionAddEvent event, AUserInAServer userAdding) {
public void executeReactionAdded(CachedMessage message, CachedReactions cachedReaction, ServerUser serverUser) {
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(REMOVAL_EMOTE, guildId);
MessageReaction.ReactionEmote reactionEmote = event.getReactionEmote();
if(emoteService.isReactionEmoteAEmote(reactionEmote, aEmote)) {
if(emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), aEmote)) {
Optional<EmbeddedMessage> embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId());
if(embeddedMessageOptional.isPresent()) {
EmbeddedMessage embeddedMessage = embeddedMessageOptional.get();
AUser userReacting = userAdding.getUserReference();
if(embeddedMessage.getEmbeddedUser().getUserReference().getId().equals(userReacting.getId())
|| embeddedMessage.getEmbeddingUser().getUserReference().getId().equals(userReacting.getId())
if(embeddedMessage.getEmbeddedUser().getUserReference().getId().equals(serverUser.getUserId())
|| embeddedMessage.getEmbeddingUser().getUserReference().getId().equals(serverUser.getUserId())
) {
log.info("Removing embed in message {} in channel {} in server {} because of a user reaction.", message.getMessageId(), message.getChannelId(), message.getServerId());
messageService.deleteMessageInChannelInServer(message.getServerId(), message.getChannelId(), message.getMessageId()).thenAccept(aVoid ->{
@@ -74,8 +68,4 @@ public class MessageEmbedRemovalReactionListener implements ReactedAddedListener
return UtilityFeature.LINK_EMBEDS;
}
@Override
public Integer getPriority() {
return ListenerPriority.HIGH;
}
}

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.utility.listener.repost;
import dev.sheldan.abstracto.core.listener.entity.ChannelGroupCreatedListener;
import dev.sheldan.abstracto.core.listener.sync.entity.ChannelGroupCreatedListener;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.utility.service.RepostServiceBean;
import dev.sheldan.abstracto.utility.service.management.RepostCheckChannelGroupManagement;

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.utility.listener.repost;
import dev.sheldan.abstracto.core.listener.entity.ChannelGroupDeletedListener;
import dev.sheldan.abstracto.core.listener.sync.entity.ChannelGroupDeletedListener;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.utility.service.RepostServiceBean;
import dev.sheldan.abstracto.utility.service.management.RepostCheckChannelGroupManagement;

View File

@@ -1,9 +1,11 @@
package dev.sheldan.abstracto.utility.listener.repost;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.MessageEmbeddedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageEmbeddedListener;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.utility.config.features.UtilityFeature;
import dev.sheldan.abstracto.utility.service.RepostCheckChannelService;
import dev.sheldan.abstracto.utility.service.RepostService;
@@ -19,7 +21,7 @@ import java.util.stream.Collectors;
@Component
@Slf4j
public class RepostEmbedListener implements MessageEmbeddedListener {
public class RepostEmbedListener implements AsyncMessageEmbeddedListener {
@Autowired
private RepostCheckChannelService repostCheckChannelService;
@@ -30,15 +32,22 @@ public class RepostEmbedListener implements MessageEmbeddedListener {
@Autowired
private PostedImageManagement repostManagement;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private BotService botService;
@Override
public void execute(GuildMessageEmbedEventModel eventModel) {
if(repostCheckChannelService.duplicateCheckEnabledForChannel(eventModel.getChannel())) {
AChannel channel = channelManagementService.loadChannel(eventModel.getChannelId());
if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) {
if(repostManagement.messageEmbedsHaveBeenCovered(eventModel.getMessageId())) {
log.info("The embeds of the message {} in channel {} in server {} have already been covered by repost check -- ignoring.",
eventModel.getMessageId(), eventModel.getChannel().getIdLong(), eventModel.getChannel().getGuild().getIdLong());
eventModel.getMessageId(), eventModel.getChannelId(), eventModel.getServerId());
return;
}
eventModel.getChannel().retrieveMessageById(eventModel.getMessageId()).queue(message -> {
botService.getTextChannelFromServer(eventModel.getServerId(), eventModel.getChannelId()).retrieveMessageById(eventModel.getMessageId()).queue(message -> {
List<MessageEmbed> imageEmbeds = eventModel.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList());
repostService.processMessageEmbedsRepostCheck(imageEmbeds, message);
});
@@ -50,9 +59,4 @@ public class RepostEmbedListener implements MessageEmbeddedListener {
return UtilityFeature.REPOST_DETECTION;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -1,15 +1,16 @@
package dev.sheldan.abstracto.utility.listener.repost;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.MessageReceivedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
import dev.sheldan.abstracto.core.models.cache.CachedEmbed;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.utility.config.features.UtilityFeature;
import dev.sheldan.abstracto.utility.service.RepostCheckChannelService;
import dev.sheldan.abstracto.utility.service.RepostService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.EmbedType;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -19,7 +20,7 @@ import java.util.stream.Collectors;
@Component
@Slf4j
public class RepostMessageReceivedListener implements MessageReceivedListener {
public class RepostMessageReceivedListener implements AsyncMessageReceivedListener {
@Autowired
private RepostCheckChannelService repostCheckChannelService;
@@ -27,11 +28,15 @@ public class RepostMessageReceivedListener implements MessageReceivedListener {
@Autowired
private RepostService repostService;
@Autowired
private ChannelManagementService channelManagementService;
@Override
public void execute(Message message) {
if(repostCheckChannelService.duplicateCheckEnabledForChannel(message.getTextChannel())) {
public void execute(CachedMessage message) {
AChannel channel = channelManagementService.loadChannel(message.getChannelId());
if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) {
repostService.processMessageAttachmentRepostCheck(message);
List<MessageEmbed> imageEmbeds = message.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList());
List<CachedEmbed> imageEmbeds = message.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList());
repostService.processMessageEmbedsRepostCheck(imageEmbeds, message);
}
}
@@ -41,8 +46,4 @@ public class RepostMessageReceivedListener implements MessageReceivedListener {
return UtilityFeature.REPOST_DETECTION;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -1,12 +1,12 @@
package dev.sheldan.abstracto.utility.listener.starboard;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.ReactedAddedListener;
import dev.sheldan.abstracto.core.listener.ReactedRemovedListener;
import dev.sheldan.abstracto.core.listener.ReactionClearedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionClearedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
@@ -19,9 +19,6 @@ import dev.sheldan.abstracto.utility.service.StarboardService;
import dev.sheldan.abstracto.utility.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.utility.service.management.StarboardPostReactorManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -33,7 +30,7 @@ import java.util.stream.Collectors;
@Component
@Slf4j
public class StarboardListener implements ReactedAddedListener, ReactedRemovedListener, ReactionClearedListener {
public class StarboardListener implements AsyncReactionAddedListener, AsyncReactionRemovedListener, AsyncReactionClearedListener {
public static final String STAR_EMOTE = "star";
public static final String FIRST_LEVEL_THRESHOLD_KEY = "starLvl1";
@@ -61,34 +58,34 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
@Override
@Transactional
public void executeReactionAdded(CachedMessage message, GuildMessageReactionAddEvent addedReaction, AUserInAServer userAdding) {
if(userAdding.getUserReference().getId().equals(message.getAuthorId())) {
public void executeReactionAdded(CachedMessage message, CachedReactions cachedReaction, ServerUser serverUser) {
if(serverUser.getUserId().equals(message.getAuthor().getAuthorId())) {
return;
}
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(STAR_EMOTE, guildId);
MessageReaction.ReactionEmote reactionEmote = addedReaction.getReactionEmote();
if(emoteService.isReactionEmoteAEmote(reactionEmote, aEmote)) {
log.info("User {} in server {} reacted with star to put a message {} from channel {} on starboard.", userAdding.getUserReference().getId(), userAdding.getServerReference().getId(), message.getMessageId(), message.getChannelId());
Optional<CachedReaction> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
handleStarboardPostChange(message, reactionOptional.orElse(null), userAdding, true);
if(emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), aEmote)) {
log.info("User {} in server {} reacted with star to put a message {} from channel {} on starboard.", serverUser.getUserId(), message.getServerId(), message.getMessageId(), message.getChannelId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
handleStarboardPostChange(message, reactionOptional.orElse(null), serverUser, true);
}
}
private void handleStarboardPostChange(CachedMessage message, CachedReaction reaction, AUserInAServer userReacting, boolean adding) {
private void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser serverUser, boolean adding) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
if(reaction != null) {
AUserInAServer author = userInServerManagementService.loadUser(message.getServerId(), message.getAuthorId());
List<AUserInAServer> userExceptAuthor = getUsersExcept(reaction.getUserInServersIds(), author);
AUserInAServer author = userInServerManagementService.loadUser(message.getServerId(), message.getAuthor().getAuthorId());
List<AUserInAServer> userExceptAuthor = getUsersExcept(reaction.getUsers(), author);
Long starMinimum = getFromConfig(FIRST_LEVEL_THRESHOLD_KEY, message.getServerId());
AUserInAServer userAddingReaction = userInServerManagementService.loadUser(serverUser);
if (userExceptAuthor.size() >= starMinimum) {
log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.",
message.getMessageId(), message.getChannelId(), message.getServerId());
if(starboardPostOptional.isPresent()) {
updateStarboardPost(message, userReacting, adding, starboardPostOptional.get(), userExceptAuthor);
updateStarboardPost(message, userAddingReaction, adding, starboardPostOptional.get(), userExceptAuthor);
} else {
log.info("Creating starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
starboardService.createStarboardPost(message, userExceptAuthor, userReacting, author);
starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author);
}
} else {
if(starboardPostOptional.isPresent()) {
@@ -124,17 +121,16 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
@Override
@Transactional
public void executeReactionRemoved(CachedMessage message, GuildMessageReactionRemoveEvent removedReaction, AUserInAServer userRemoving) {
if(message.getAuthorId().equals(userRemoving.getUserReference().getId())) {
public void executeReactionRemoved(CachedMessage message, CachedReactions removedReaction, ServerUser userRemoving) {
if(message.getAuthor().getAuthorId().equals(userRemoving.getUserId())) {
return;
}
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(STAR_EMOTE, guildId);
MessageReaction.ReactionEmote reactionEmote = removedReaction.getReactionEmote();
if(emoteService.isReactionEmoteAEmote(reactionEmote, aEmote)) {
if(emoteService.compareCachedEmoteWithAEmote(removedReaction.getEmote(), aEmote)) {
log.info("User {} in server {} removed star reaction from message {} on starboard.",
userRemoving.getUserReference().getId(), userRemoving.getServerReference().getId(), message.getMessageId());
Optional<CachedReaction> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
userRemoving.getUserId(), message.getServerId(), message.getMessageId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
handleStarboardPostChange(message, reactionOptional.orElse(null), userRemoving, false);
}
}
@@ -143,9 +139,9 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
return configManagementService.loadConfig(guildId, key).getLongValue();
}
private List<AUserInAServer> getUsersExcept(List<Long> users, AUserInAServer author) {
return users.stream().filter(user -> !user.equals(author.getUserInServerId())).map(aLong -> {
Optional<AUserInAServer> aUserInAServer = userInServerManagementService.loadUserConditional(aLong);
private List<AUserInAServer> getUsersExcept(List<ServerUser> users, AUserInAServer author) {
return users.stream().filter(user -> !(user.getServerId().equals(author.getServerReference().getId()) && user.getUserId().equals(author.getUserReference().getId()))).map(serverUser -> {
Optional<AUserInAServer> aUserInAServer = userInServerManagementService.loadUserOptional(serverUser.getServerId(), serverUser.getUserId());
return aUserInAServer.orElse(null);
}).filter(Objects::nonNull).collect(Collectors.toList());
}
@@ -167,8 +163,4 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
});
}
@Override
public Integer getPriority() {
return ListenerPriority.HIGH;
}
}

View File

@@ -1,10 +1,7 @@
package dev.sheldan.abstracto.utility.listener.starboard;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.MessageDeletedListener;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.utility.config.features.UtilityFeature;
import dev.sheldan.abstracto.utility.models.database.StarboardPost;
@@ -17,18 +14,18 @@ import java.util.Optional;
@Component
@Slf4j
public class StarboardPostDeletedListener implements MessageDeletedListener {
public class StarboardPostDeletedListener implements AsyncMessageDeletedListener {
@Autowired
private StarboardPostManagementService starboardPostManagementService;
@Override
public void execute(CachedMessage messageBefore, AServerAChannelAUser authorUser, GuildChannelMember authorMember) {
public void execute(CachedMessage messageBefore) {
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(messageBefore.getMessageId());
if(byStarboardPostId.isPresent()) {
StarboardPost post = byStarboardPostId.get();
log.info("Removing starboard post: message {}, channel {}, server {}, because the message was deleted",
post.getPostMessageId(), post.getSourceChanel().getId(), post.getAuthor().getUserReference().getId());
post.getPostMessageId(), post.getSourceChanel().getId(), messageBefore.getServerId());
starboardPostManagementService.setStarboardPostIgnored(messageBefore.getMessageId(), true);
}
}
@@ -38,8 +35,4 @@ public class StarboardPostDeletedListener implements MessageDeletedListener {
return UtilityFeature.STARBOARD;
}
@Override
public Integer getPriority() {
return ListenerPriority.HIGH;
}
}

View File

@@ -111,7 +111,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
@Override
@Transactional
public CompletableFuture<Void> embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage) {
Optional<AUserInAServer> causeOpt = userInServerManagementService.loadUserConditional(userEmbeddingUserInServerId);
Optional<AUserInAServer> causeOpt = userInServerManagementService.loadUserOptional(userEmbeddingUserInServerId);
if(causeOpt.isPresent()) {
return buildTemplateParameter(embeddingMessage, cachedMessage).thenCompose(messageEmbeddedModel ->
self.sendEmbeddingMessage(cachedMessage, target, userEmbeddingUserInServerId, messageEmbeddedModel)
@@ -145,7 +145,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
}
private CompletableFuture<MessageEmbeddedModel> buildTemplateParameter(Message message, CachedMessage embeddedMessage) {
return botService.getMemberInServerAsync(embeddedMessage.getServerId(), embeddedMessage.getAuthorId()).thenApply(member ->
return botService.getMemberInServerAsync(embeddedMessage.getServerId(), embeddedMessage.getAuthor().getAuthorId()).thenApply(member ->
self.loadMessageEmbedModel(message, embeddedMessage, member)
);
}

View File

@@ -1,6 +1,10 @@
package dev.sheldan.abstracto.utility.service;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.ServerChannelMessageUser;
import dev.sheldan.abstracto.core.models.cache.CachedAttachment;
import dev.sheldan.abstracto.core.models.cache.CachedEmbed;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.FeatureModeService;
@@ -85,10 +89,31 @@ public class RepostServiceBean implements RepostService {
public static final String REPOST_MARKER_EMOTE_KEY = "repostMarker";
@Override
public boolean isRepost(Message message, MessageEmbed messageEmbed, Integer index) {
public boolean isRepost(CachedMessage message, CachedEmbed messageEmbed, Integer index) {
return getRepostFor(message, messageEmbed, index).isPresent();
}
@Override
public Optional<PostedImage> getRepostFor(CachedMessage message, CachedEmbed messageEmbed, Integer embedIndex) {
if(messageEmbed.getCachedThumbnail() == null && messageEmbed.getCachedImageInfo() == null) {
return Optional.empty();
}
String urlToUse = null;
if(messageEmbed.getCachedThumbnail() != null) {
urlToUse = messageEmbed.getCachedThumbnail().getProxyUrl();
} else if (messageEmbed.getCachedImageInfo() != null) {
urlToUse = messageEmbed.getCachedImageInfo().getProxyUrl();
}
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getServerId())
.channelId(message.getChannelId())
.userId(message.getAuthor().getAuthorId())
.messageId(message.getMessageId())
.build();
return checkForDuplicates(serverChannelMessageUser, EMBEDDED_LINK_POSITION_START_INDEX + embedIndex, urlToUse);
}
@Override
public Optional<PostedImage> getRepostFor(Message message, MessageEmbed messageEmbed, Integer embedIndex) {
if(messageEmbed.getThumbnail() == null && messageEmbed.getImage() == null) {
@@ -100,38 +125,52 @@ public class RepostServiceBean implements RepostService {
} else if (messageEmbed.getImage() != null) {
urlToUse = messageEmbed.getImage().getProxyUrl();
}
return checkForDuplicates(message, EMBEDDED_LINK_POSITION_START_INDEX + embedIndex, urlToUse);
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getGuild().getIdLong())
.channelId(message.getChannel().getIdLong())
.userId(message.getAuthor().getIdLong())
.messageId(message.getIdLong())
.build();
return checkForDuplicates(serverChannelMessageUser, EMBEDDED_LINK_POSITION_START_INDEX + embedIndex, urlToUse);
}
private Optional<PostedImage> checkForDuplicates(Message message, Integer index, String fileUrl) {
String fileHash = calculateHashForPost(fileUrl, message.getGuild().getIdLong());
AServer aServer = serverManagementService.loadServer(message.getGuild().getIdLong());
private Optional<PostedImage> checkForDuplicates(ServerChannelMessageUser serverChannelMessageUser, Integer index, String fileUrl) {
String fileHash = calculateHashForPost(fileUrl, serverChannelMessageUser.getServerId());
AServer aServer = serverManagementService.loadServer(serverChannelMessageUser.getServerId());
Optional<PostedImage> potentialRepost = postedImageManagement.getPostWithHash(fileHash, aServer);
if(potentialRepost.isPresent()) {
PostedImage existingRepost = potentialRepost.get();
return existingRepost.getPostId().getMessageId() != message.getIdLong() ? Optional.of(existingRepost) : Optional.empty();
return !existingRepost.getPostId().getMessageId().equals(serverChannelMessageUser.getMessageId()) ? Optional.of(existingRepost) : Optional.empty();
} else {
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(message.getMember());
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(serverChannelMessageUser.getServerId(), serverChannelMessageUser.getUserId());
AServerAChannelAUser cause = AServerAChannelAUser
.builder()
.aUserInAServer(aUserInAServer)
.channel(channelManagementService.loadChannel(message.getTextChannel().getIdLong()))
.channel(channelManagementService.loadChannel(serverChannelMessageUser.getChannelId()))
.guild(aServer)
.user(aUserInAServer.getUserReference())
.build();
postedImageManagement.createPost(cause, message, fileHash, index);
postedImageManagement.createPost(cause, serverChannelMessageUser.getMessageId(), fileHash, index);
return Optional.empty();
}
}
@Override
public boolean isRepost(Message message, Message.Attachment attachment, Integer index) {
public boolean isRepost(CachedMessage message, CachedAttachment attachment, Integer index) {
return getRepostFor(message, attachment, index).isPresent();
}
@Override
public Optional<PostedImage> getRepostFor(Message message, Message.Attachment attachment, Integer index) {
return checkForDuplicates(message, index, attachment.getProxyUrl());
public Optional<PostedImage> getRepostFor(CachedMessage message, CachedAttachment attachment, Integer index) {
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getServerId())
.channelId(message.getChannelId())
.userId(message.getAuthor().getAuthorId())
.messageId(message.getMessageId())
.build();
return checkForDuplicates(serverChannelMessageUser, index, attachment.getProxyUrl());
}
@Override
@@ -159,31 +198,38 @@ public class RepostServiceBean implements RepostService {
}
@Override
public void processMessageAttachmentRepostCheck(Message message) {
public void processMessageAttachmentRepostCheck(CachedMessage message) {
boolean canThereBeMultipleReposts = message.getAttachments().size() > 1;
for (int imageIndex = 0; imageIndex < message.getAttachments().size(); imageIndex++) {
executeRepostCheckForAttachment(message, message.getAttachments().get(imageIndex), imageIndex, canThereBeMultipleReposts);
}
}
private void executeRepostCheckForAttachment(Message message, Message.Attachment attachment, Integer index, boolean moreRepostsPossible) {
private void executeRepostCheckForAttachment(CachedMessage message, CachedAttachment attachment, Integer index, boolean moreRepostsPossible) {
Optional<PostedImage> originalPostOptional = getRepostFor(message, attachment, index);
originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(message, index, moreRepostsPossible, postedImage));
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getServerId())
.channelId(message.getChannelId())
.userId(message.getAuthor().getAuthorId())
.messageId(message.getMessageId())
.build();
originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(serverChannelMessageUser, index, moreRepostsPossible, postedImage));
}
private void markMessageAndPersist(Message message, Integer index, boolean moreRepostsPossible, PostedImage originalPost) {
log.info("Detected repost in message embed {} of message {} in channel {} in server {}.", index, message.getIdLong(), message.getTextChannel().getIdLong(), message.getGuild().getIdLong());
CompletableFuture<Void> markerFuture = messageService.addReactionToMessageWithFuture(REPOST_MARKER_EMOTE_KEY, message.getGuild().getIdLong(), message);
private void markMessageAndPersist(ServerChannelMessageUser messageUser, Integer index, boolean moreRepostsPossible, PostedImage originalPost) {
log.info("Detected repost in message embed {} of message {} in channel {} in server {}.", index, messageUser.getMessageId(), messageUser.getChannelId(), messageUser.getServerId());
CompletableFuture<Void> markerFuture = messageService.addReactionToMessageWithFuture(REPOST_MARKER_EMOTE_KEY, messageUser.getServerId(), messageUser.getChannelId(), messageUser.getMessageId());
CompletableFuture<Void> counterFuture;
if (moreRepostsPossible) {
counterFuture = messageService.addDefaultReactionToMessageAsync(NUMBER_EMOJI.get(index), message);
counterFuture = messageService.addDefaultReactionToMessageAsync(NUMBER_EMOJI.get(index), messageUser.getServerId(), messageUser.getChannelId(), messageUser.getMessageId());
} else {
counterFuture = CompletableFuture.completedFuture(null);
}
Long messageId = originalPost.getPostId().getMessageId();
Integer position = originalPost.getPostId().getPosition();
Long serverId = message.getGuild().getIdLong();
Long userId = message.getAuthor().getIdLong();
Long serverId = messageUser.getServerId();
Long userId = messageUser.getUserId();
CompletableFuture.allOf(markerFuture, counterFuture).thenAccept(unused ->
self.persistRepost(messageId, position, serverId, userId)
);
@@ -202,6 +248,14 @@ public class RepostServiceBean implements RepostService {
}
}
@Override
public void processMessageEmbedsRepostCheck(List<CachedEmbed> embeds, CachedMessage message) {
boolean canThereBeMultipleReposts = embeds.size() > 1 || !message.getAttachments().isEmpty();
for (int imageIndex = 0; imageIndex < embeds.size(); imageIndex++) {
executeRepostCheckForMessageEmbed(message, embeds.get(imageIndex), imageIndex + message.getAttachments().size(), canThereBeMultipleReposts);
}
}
@Override
public void processMessageEmbedsRepostCheck(List<MessageEmbed> embeds, Message message) {
boolean canThereBeMultipleReposts = embeds.size() > 1 || !message.getAttachments().isEmpty();
@@ -228,8 +282,27 @@ public class RepostServiceBean implements RepostService {
repostManagementService.deleteRepostsFromServer(server);
}
private void executeRepostCheckForMessageEmbed(CachedMessage message, CachedEmbed messageEmbed, Integer index, boolean moreRepostsPossible) {
Optional<PostedImage> originalPostOptional = getRepostFor(message, messageEmbed, index);
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getServerId())
.channelId(message.getChannelId())
.userId(message.getAuthor().getAuthorId())
.messageId(message.getMessageId())
.build();
originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(serverChannelMessageUser, index, moreRepostsPossible, postedImage));
}
private void executeRepostCheckForMessageEmbed(Message message, MessageEmbed messageEmbed, Integer index, boolean moreRepostsPossible) {
Optional<PostedImage> originalPostOptional = getRepostFor(message, messageEmbed, index);
originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(message, index, moreRepostsPossible, postedImage));
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getGuild().getIdLong())
.channelId(message.getChannel().getIdLong())
.userId(message.getAuthor().getIdLong())
.messageId(message.getIdLong())
.build();
originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(serverChannelMessageUser, index, moreRepostsPossible, postedImage));
}
}

View File

@@ -104,7 +104,7 @@ public class StarboardServiceBean implements StarboardService {
@Transactional
public void persistPost(CachedMessage message, List<Long> userExceptAuthorIds, List<CompletableFuture<Message>> completableFutures, Long starboardChannelId, Long starredUserId) {
AUserInAServer innerStarredUser = userInServerManagementService.loadUserConditional(starredUserId).orElseThrow(() -> new UserInServerNotFoundException(starredUserId));
AUserInAServer innerStarredUser = userInServerManagementService.loadUserOptional(starredUserId).orElseThrow(() -> new UserInServerNotFoundException(starredUserId));
AChannel starboardChannel = channelManagementService.loadChannel(starboardChannelId);
Message message1 = completableFutures.get(0).join();
AServerAChannelMessage aServerAChannelMessage = AServerAChannelMessage
@@ -119,17 +119,18 @@ public class StarboardServiceBean implements StarboardService {
log.warn("There are no user ids except the author for the reactions in post {} in guild {} for message {} in channel {}.", starboardPost.getId(), message.getChannelId(), message.getMessageId(), message.getChannelId());
}
userExceptAuthorIds.forEach(aLong -> {
AUserInAServer user = userInServerManagementService.loadUserConditional(aLong).orElseThrow(() -> new UserInServerNotFoundException(aLong));
AUserInAServer user = userInServerManagementService.loadUserOptional(aLong).orElseThrow(() -> new UserInServerNotFoundException(aLong));
starboardPostReactorManagementService.addReactor(starboardPost, user);
});
}
private CompletableFuture<StarboardPostModel> buildStarboardPostModel(CachedMessage message, Integer starCount) {
return botService.getMemberInServerAsync(message.getServerId(), message.getAuthorId()).thenApply(member -> {
return botService.getMemberInServerAsync(message.getServerId(), message.getAuthor().getAuthorId()).thenApply(member -> {
Optional<TextChannel> channel = botService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = botService.getGuildByIdOptional(message.getServerId());
// TODO use model objects instead of building entity models
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthorId()).build();
AUser user = AUser.builder().id(message.getAuthor().getAuthorId()).build();
AServer server = AServer.builder().id(message.getServerId()).build();
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel

View File

@@ -44,7 +44,7 @@ public class MessageEmbedPostManagementServiceBean implements MessageEmbedPostMa
}
AChannel embeddingChannel = channelManagementService.loadChannel(messageContainingEmbed.getChannel().getIdLong());
AChannel embeddedChannel = channelManagementService.loadChannel(embeddedMessage.getChannelId());
AUserInAServer embeddedAuthor = userInServerManagementService.loadUser(embeddedMessage.getServerId(), embeddedMessage.getAuthorId());
AUserInAServer embeddedAuthor = userInServerManagementService.loadUser(embeddedMessage.getServerId(), embeddedMessage.getAuthor().getAuthorId());
EmbeddedMessage messageEmbedPost = EmbeddedMessage
.builder()
.embeddedMessageId(embeddedMessage.getMessageId())
@@ -58,8 +58,8 @@ public class MessageEmbedPostManagementServiceBean implements MessageEmbedPostMa
.build();
log.info("Saving embedded post: message {} by user {} in channel {} in server {} embedded message {} by user {} in channel {} in server {}.",
messageContainingEmbed.getIdLong(), embeddingUser.getUserReference().getId(), embeddingChannel.getId(), embeddingUser.getServerReference().getId(),
embeddedMessage.getMessageId(), embeddedMessage.getAuthorId(), embeddedMessage.getChannelId(), embeddedMessage.getServerId());
messageContainingEmbed.getIdLong(), messageContainingEmbed.getAuthor().getIdLong(), embeddingChannel.getId(), messageContainingEmbed.getChannel().getIdLong(),
embeddedMessage.getMessageId(), embeddedMessage.getAuthor().getAuthorId(), embeddedMessage.getChannelId(), embeddedMessage.getServerId());
embeddedMessageRepository.save(messageEmbedPost);
}

View File

@@ -8,7 +8,6 @@ import dev.sheldan.abstracto.utility.models.database.PostedImage;
import dev.sheldan.abstracto.utility.models.database.embed.PostIdentifier;
import dev.sheldan.abstracto.utility.repository.PostedImageRepository;
import dev.sheldan.abstracto.utility.service.RepostServiceBean;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,11 +24,11 @@ public class PostedImageManagementBean implements PostedImageManagement {
private RepostCheckChannelGroupManagement checkChannelBean;
@Override
public PostedImage createPost(AServerAChannelAUser creation, Message source, String hash, Integer index) {
public PostedImage createPost(AServerAChannelAUser creation, Long messageId, String hash, Integer index) {
PostedImage post = PostedImage
.builder()
.imageHash(hash)
.postId(new PostIdentifier(source.getIdLong(), index))
.postId(new PostIdentifier(messageId, index))
.poster(creation.getAUserInAServer())
.server(creation.getGuild())
.postedChannel(creation.getChannel())