[AB-203] restructuring listeners to use more common code and common interfaces for listeners and models

restructuring entity listener to be async and added models
fixing usage of repository save method
adding interface dependencies to bundle dependency management
This commit is contained in:
Sheldan
2021-03-21 10:58:31 +01:00
parent cfe7786d4d
commit b4e36efafb
241 changed files with 3521 additions and 2049 deletions

View File

@@ -5,8 +5,10 @@ import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost; import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService; import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -20,24 +22,26 @@ public class AssignablePostDeletedListener implements AsyncMessageDeletedListene
@Autowired @Autowired
private AssignableRolePlacePostManagementService service; private AssignableRolePlacePostManagementService service;
/**
* This method deletes one individual {@link AssignableRolePlacePost post}, because its message has been deleted
* @param messageBefore The {@link CachedMessage message} which was deleted
*/
@Override
public void execute(CachedMessage messageBefore) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(messageBefore.getMessageId());
messageOptional.ifPresent(post -> {
AssignableRolePlace assignablePlace = post.getAssignablePlace();
log.info("Post {} has been deleted in server {} in channel {}, we are removing a post from place {}.", post.getId(), messageBefore.getServerId(), messageBefore.getChannelId(), assignablePlace.getKey());
post.getAssignableRoles().forEach(assignableRole -> assignableRole.setAssignableRolePlacePost(null));
assignablePlace.getMessagePosts().remove(post);
});
}
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES; return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
} }
/**
* This method deletes one individual {@link AssignableRolePlacePost post}, because its message has been deleted
* @param model The {@link MessageDeletedModel message} containing the {@link CachedMessage cachedMessage} which was deleted
*/
@Override
public DefaultListenerResult execute(MessageDeletedModel model) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(model.getCachedMessage().getMessageId());
messageOptional.ifPresent(post -> {
AssignableRolePlace assignablePlace = post.getAssignablePlace();
log.info("Post {} has been deleted in server {} in channel {}, we are removing a post from place {}.",
post.getId(), model.getServerId(), model.getCachedMessage().getChannelId(), assignablePlace.getKey());
post.getAssignableRoles().forEach(assignableRole -> assignableRole.setAssignableRolePlacePost(null));
assignablePlace.getMessagePosts().remove(post);
});
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -12,17 +12,18 @@ import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePl
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService; import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService; import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.models.ServerUser; 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.CachedReaction;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.ReactionService; import dev.sheldan.abstracto.core.service.ReactionService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -69,69 +70,32 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
@Autowired @Autowired
private MemberService memberService; private MemberService memberService;
/**
* Determines if the {@link net.dv8tion.jda.api.entities.Message message} a reaction was added to, belongs to a
* {@link AssignableRolePlacePost post}.
* If the {@link AssignableRolePlacePost post} belong to an inactive {@link AssignableRolePlace place} this method
* will automatically remove the reaction, self reactions are ignored. Otherwise the logic according to the configuration
* of the {@link AssignableRolePlace place} will be executed.
* @param message The {@link CachedMessage message} on which a reaction was added
* @param cachedReaction The {@link CachedReaction reaction} which was added
* @param serverUser The {@link ServerUser serverUser} who added the reaction
*/
@Override
public void executeReactionAdded(CachedMessage message, CachedReactions cachedReaction, ServerUser serverUser) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(message.getMessageId());
if(messageOptional.isPresent()) {
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(cachedReaction.getSelf()) {
log.info("Ignoring self reaction on assignable role post in server {}.", message.getServerId());
return;
}
CachedReaction specificReaction = cachedReaction.getReactionForSpecificUser(serverUser);
Long assignableRolePlacePostId = assignablePlacePost.getId();
if(assignablePlacePost.getAssignablePlace().getActive()) {
log.info("User {} added reaction to assignable role place {} in server {}. Handling added event.", serverUser.getUserId(), assignablePlacePost.getId(), serverUser.getServerId());
addAppropriateRoles(specificReaction, assignablePlacePost, serverUser, message);
} else {
reactionService.removeReactionFromMessage(specificReaction, message).exceptionally(throwable -> {
log.error("Failed to remove reaction on place post {} because place is inactive.", assignableRolePlacePostId, throwable);
return null;
});
log.trace("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), serverUser.getServerId());
}
}
}
/** /**
* Iterates over all {@link AssignableRole assignableRoles} of the post and checks which {@link AssignableRole assignableRole} * Iterates over all {@link AssignableRole assignableRoles} of the post and checks which {@link AssignableRole assignableRole}
* is identified by the added {@link CachedReaction reaction}. If there is no valid reaction, the {@link net.dv8tion.jda.api.entities.MessageReaction reaction} * is identified by the added {@link MessageReaction reaction}. If there is no valid reaction, the {@link net.dv8tion.jda.api.entities.MessageReaction reaction}
* will be removed again. In case the {@link AssignableRolePlace place} is configured to have unique roles, this will remove the existing * will be removed again. In case the {@link AssignableRolePlace place} is configured to have unique roles, this will remove the existing
* {@link net.dv8tion.jda.api.entities.MessageReaction reaction} and the assigned {@link net.dv8tion.jda.api.entities.Role role}. * {@link net.dv8tion.jda.api.entities.MessageReaction reaction} and the assigned {@link net.dv8tion.jda.api.entities.Role role}.
* Afterwards the appropriate {@link net.dv8tion.jda.api.entities.Role role} will be added and the update * Afterwards the appropriate {@link net.dv8tion.jda.api.entities.Role role} will be added and the update
* will be stored in the database. * will be stored in the database.
* @param cachedReaction The {@link CachedReaction reaction} which was added * @param assignablePlacePost The {@link AssignableRolePlacePost post} onto which the {@link MessageReaction reaction} was added to
* @param assignablePlacePost The {@link AssignableRolePlacePost post} onto which the {@link CachedReaction reaction} was added to * @param model The {@link ReactionAddedModel model} containing information about who added which reaction where
* @param serverUser The {@link ServerUser serverUser} who added the {@link CachedReaction reaction}
* @param message The {@link CachedMessage message} onto which the {@link net.dv8tion.jda.api.entities.MessageReaction reaction}
* was added
*/ */
private void addAppropriateRoles(CachedReaction cachedReaction, AssignableRolePlacePost assignablePlacePost, ServerUser serverUser, CachedMessage message) { private void addAppropriateRoles(AssignableRolePlacePost assignablePlacePost, ReactionAddedModel model) {
boolean validReaction = false; boolean validReaction = false;
AssignableRolePlace assignableRolePlace = assignablePlacePost.getAssignablePlace(); AssignableRolePlace assignableRolePlace = assignablePlacePost.getAssignablePlace();
List<CompletableFuture<Void>> futures = new ArrayList<>(); List<CompletableFuture<Void>> futures = new ArrayList<>();
for (AssignableRole assignableRole : assignablePlacePost.getAssignableRoles()) { for (AssignableRole assignableRole : assignablePlacePost.getAssignableRoles()) {
log.trace("Checking emote {} if it was reaction for assignable role place.", assignableRole.getEmote().getId()); log.trace("Checking emote {} if it was reaction for assignable role place.", assignableRole.getEmote().getId());
if (emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), assignableRole.getEmote())) { if (emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), assignableRole.getEmote())) {
if(assignableRolePlace.getUniqueRoles()) { if(assignableRolePlace.getUniqueRoles()) {
log.trace("Assignable role place {} has unique roles configured. Removing existing reactions and roles.", assignableRolePlace.getId()); log.trace("Assignable role place {} has unique roles configured. Removing existing reactions and roles.", assignableRolePlace.getId());
Optional<AssignedRoleUser> byUserInServer = assignedRoleUserManagementService.findByUserInServerOptional(serverUser); Optional<AssignedRoleUser> byUserInServer = assignedRoleUserManagementService.findByUserInServerOptional(model.getUserReacting());
byUserInServer.ifPresent(user -> futures.add(assignableRolePlaceService.removeExistingReactionsAndRoles(assignableRolePlace, user))); byUserInServer.ifPresent(user -> futures.add(assignableRolePlaceService.removeExistingReactionsAndRoles(assignableRolePlace, user)));
} }
Long assignableRoleId = assignableRole.getId(); Long assignableRoleId = assignableRole.getId();
log.info("User added {} reaction {} and gets assignable role {} in server {}.", serverUser.getUserId(), assignableRole.getEmote().getId(), assignableRoleId, serverUser.getServerId()); log.info("User added {} reaction {} and gets assignable role {} in server {}.", model.getUserReacting().getUserId(), assignableRole.getEmote().getId(), assignableRoleId, model.getServerId());
CompletableFuture<Void> roleAdditionFuture = assignableRoleServiceBean.assignAssignableRoleToUser(assignableRoleId, serverUser); CompletableFuture<Void> roleAdditionFuture = assignableRoleServiceBean.assignAssignableRoleToUser(assignableRoleId, model.getUserReacting());
futures.add(CompletableFuture.allOf(roleAdditionFuture)); futures.add(CompletableFuture.allOf(roleAdditionFuture));
validReaction = true; validReaction = true;
@@ -140,11 +104,11 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
} }
if(!validReaction) { if(!validReaction) {
log.trace("Reaction was not found in the configuration of assignable role place {}, removing reaction.", assignableRolePlace.getId()); log.trace("Reaction was not found in the configuration of assignable role place {}, removing reaction.", assignableRolePlace.getId());
futures.add(reactionService.removeReaction(message, cachedReaction.getEmote(), serverUser)); futures.add(reactionService.removeReactionFromMessage(model.getReaction(), model.getMessage(), model.getMemberReacting().getUser()));
} }
Long assignableRolePlaceId = assignableRolePlace.getId(); Long assignableRolePlaceId = assignableRolePlace.getId();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
self.updateStoredAssignableRoles(assignableRolePlaceId, serverUser, cachedReaction) self.updateStoredAssignableRoles(assignableRolePlaceId, model.getUserReacting(), model.getReaction())
).exceptionally(throwable -> { ).exceptionally(throwable -> {
log.error("Failed to add role or remove emote for assignable role place {}.", assignableRolePlaceId, throwable); log.error("Failed to add role or remove emote for assignable role place {}.", assignableRolePlaceId, throwable);
return null; return null;
@@ -155,17 +119,17 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
* Persists the {@link AssignableRole role} changes for the user who added a reaction in the database * Persists the {@link AssignableRole role} changes for the user who added a reaction in the database
* @param assignableRolePlaceId The ID of the {@link AssignableRolePlace place} * @param assignableRolePlaceId The ID of the {@link AssignableRolePlace place}
* @param serverUser The {@link ServerUser serverUser} who added the {@link net.dv8tion.jda.api.entities.MessageReaction reaction} * @param serverUser The {@link ServerUser serverUser} who added the {@link net.dv8tion.jda.api.entities.MessageReaction reaction}
* @param cachedReaction The {@link CachedReaction reaction} wich was added * @param reaction The {@link CachedReaction reaction} wich was added
*/ */
@Transactional @Transactional
public void updateStoredAssignableRoles(Long assignableRolePlaceId, ServerUser serverUser, CachedReaction cachedReaction) { public void updateStoredAssignableRoles(Long assignableRolePlaceId, ServerUser serverUser, MessageReaction reaction) {
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(assignableRolePlaceId); AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(assignableRolePlaceId);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser); AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser);
if(place.getUniqueRoles()) { if(place.getUniqueRoles()) {
log.trace("Assignable role place {} has unique roles. Deleting all existing references.", assignableRolePlaceId); log.trace("Assignable role place {} has unique roles. Deleting all existing references.", assignableRolePlaceId);
assignableRoleServiceBean.clearAllRolesOfUserInPlace(place, userInAServer); assignableRoleServiceBean.clearAllRolesOfUserInPlace(place, userInAServer);
} }
AssignableRole role = assignableRoleManagementService.getRoleForReactionEmote(cachedReaction.getEmote(), place); AssignableRole role = assignableRoleManagementService.getRoleForReactionEmote(reaction.getReactionEmote(), place);
log.info("Adding role to assignable role {} to user {} in server {}.", role.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId()); log.info("Adding role to assignable role {} to user {} in server {}.", role.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId());
assignableRoleServiceBean.addRoleToUser(role.getId(), userInAServer); assignableRoleServiceBean.addRoleToUser(role.getId(), userInAServer);
@@ -176,4 +140,36 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES; return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
} }
/**
* Determines if the {@link net.dv8tion.jda.api.entities.Message message} a reaction was added to, belongs to a
* {@link AssignableRolePlacePost post}.
* If the {@link AssignableRolePlacePost post} belong to an inactive {@link AssignableRolePlace place} this method
* will automatically remove the reaction, self reactions are ignored. Otherwise the logic according to the configuration
* of the {@link AssignableRolePlace place} will be executed.
* @param model The {@link ReactionAddedModel message} which contains information about the added reaction
*/
@Override
public DefaultListenerResult execute(ReactionAddedModel model) {
MessageReaction reaction = model.getReaction();
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(model.getMessage().getMessageId());
if(messageOptional.isPresent()) {
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(reaction.isSelf()) {
log.info("Ignoring self reaction on assignable role post in server {}.", model.getServerId());
return DefaultListenerResult.IGNORED;
}
Long assignableRolePlacePostId = assignablePlacePost.getId();
if(assignablePlacePost.getAssignablePlace().getActive()) {
log.info("User {} added reaction to assignable role place {} in server {}. Handling added event.", model.getUserReacting().getUserId(), assignablePlacePost.getId(), model.getServerId());
addAppropriateRoles(assignablePlacePost, model);
} else {
reactionService.removeReactionFromMessage(model.getReaction(), model.getMessage()).exceptionally(throwable -> {
log.error("Failed to remove reaction on place post {} because place is inactive.", assignableRolePlacePostId, throwable);
return null;
});
log.trace("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), model.getServerId());
}
}
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -6,10 +6,10 @@ import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceP
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService; import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService; import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener; 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.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions; import dev.sheldan.abstracto.core.models.listener.ReactionRemovedModel;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.RoleService; import dev.sheldan.abstracto.core.service.RoleService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -45,31 +45,37 @@ public class AssignablePostReactionRemoved implements AsyncReactionRemovedListen
* If the {@link AssignableRolePlacePost post} belong to an inactive {@link AssignableRolePlace place} this method ignores the removal. * If the {@link AssignableRolePlacePost post} belong to an inactive {@link AssignableRolePlace place} this method ignores the removal.
* Otherwise the logic according to the configuration * Otherwise the logic according to the configuration
* of the {@link AssignableRolePlace place} will be executed. * of the {@link AssignableRolePlace place} will be executed.
* @param message The {@link CachedMessage message} on which a reaction was added * @param model The {@link ReactionRemovedModel model} containing the information which reaction was placed where
* @param reactions All the reactions which are currently known to be on the {@link CachedMessage message}
* @param userRemoving The {@link ServerUser serverUser} which removed a {@link net.dv8tion.jda.api.entities.MessageReaction reaction}
*/ */
@Override @Override
public void executeReactionRemoved(CachedMessage message, CachedReactions reactions, ServerUser userRemoving) { public DefaultListenerResult execute(ReactionRemovedModel model) {
CachedMessage message = model.getMessage();
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(message.getMessageId()); Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(message.getMessageId());
if(messageOptional.isPresent()) { if(messageOptional.isPresent()) {
AssignableRolePlacePost assignablePlacePost = messageOptional.get(); AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(assignablePlacePost.getAssignablePlace().getActive()) { if(assignablePlacePost.getAssignablePlace().getActive()) {
assignablePlacePost.getAssignableRoles().forEach(assignableRole -> { assignablePlacePost.getAssignableRoles().forEach(assignableRole -> {
if(emoteService.compareCachedEmoteWithAEmote(reactions.getEmote(), assignableRole.getEmote())) { if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), assignableRole.getEmote())) {
Long assignableRoleId = assignableRole.getId(); Long assignableRoleId = assignableRole.getId();
log.info("Removing assignable role {} for user {} in server {} from assignable role place {}.", assignableRoleId, log.info("Removing assignable role {} for user {} in server {} from assignable role place {}.", assignableRoleId,
userRemoving.getUserId(), userRemoving.getServerId(), assignablePlacePost.getAssignablePlace().getId()); model.getUserRemoving().getUserId(), model.getServerId(), assignablePlacePost.getAssignablePlace().getId());
assignableRoleService.fullyRemoveAssignableRoleFromUser(assignableRole, userRemoving).exceptionally(throwable -> { assignableRoleService.fullyRemoveAssignableRoleFromUser(assignableRole, model.getUserRemoving()).exceptionally(throwable -> {
log.error("Failed to remove assignable role {} from user {} in server {}.", assignableRoleId, userRemoving.getUserId(), userRemoving.getServerId(), throwable); log.error("Failed to remove assignable role {} from user {} in server {}.", assignableRoleId, model.getUserRemoving().getUserId(), model.getServerId(), throwable);
return null; return null;
}); });
} }
}); });
return DefaultListenerResult.PROCESSED;
} else { } else {
log.trace("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), userRemoving.getServerId()); log.trace("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), model.getServerId());
return DefaultListenerResult.PROCESSED;
} }
} else {
return DefaultListenerResult.IGNORED;
} }
} }
} }

View File

@@ -13,6 +13,7 @@ import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService; import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService; import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -95,4 +96,16 @@ public class AssignableRoleManagementServiceBean implements AssignableRoleManage
} }
throw new AbstractoRunTimeException("Role for reaction was not found."); throw new AbstractoRunTimeException("Role for reaction was not found.");
} }
@Override
public AssignableRole getRoleForReactionEmote(MessageReaction.ReactionEmote cachedEmote, AssignableRolePlace assignableRolePlace) {
for (AssignableRolePlacePost post : assignableRolePlace.getMessagePosts()) {
for (AssignableRole assignableRole : post.getAssignableRoles()) {
if (emoteService.isReactionEmoteAEmote(cachedEmote, assignableRole.getEmote())) {
return assignableRole;
}
}
}
throw new AbstractoRunTimeException("Role for reaction was not found.");
}
} }

View File

@@ -28,9 +28,8 @@ public class AssignableRolePlaceManagementServiceBean implements AssignableRoleP
.text(text) .text(text)
.key(name) .key(name)
.build(); .build();
repository.save(place);
log.info("Creating assignable role place in channel {} on server {}.", channel.getId(), channel.getServer().getId()); log.info("Creating assignable role place in channel {} on server {}.", channel.getId(), channel.getServer().getId());
return place; return repository.save(place);
} }
@Override @Override

View File

@@ -6,6 +6,7 @@ import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceP
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.database.AEmote; import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.ARole; import dev.sheldan.abstracto.core.models.database.ARole;
import net.dv8tion.jda.api.entities.MessageReaction;
/** /**
* Management service for the table of {@link AssignableRole assignableRoles} * Management service for the table of {@link AssignableRole assignableRoles}
@@ -64,4 +65,5 @@ public interface AssignableRoleManagementService {
* @return An instance of {@link AssignableRole role} which was in the place and identified by the emote * @return An instance of {@link AssignableRole role} which was in the place and identified by the emote
*/ */
AssignableRole getRoleForReactionEmote(CachedEmote cachedEmote, AssignableRolePlace assignableRolePlace); AssignableRole getRoleForReactionEmote(CachedEmote cachedEmote, AssignableRolePlace assignableRolePlace);
AssignableRole getRoleForReactionEmote(MessageReaction.ReactionEmote cachedEmote, AssignableRolePlace assignableRolePlace);
} }

View File

@@ -1,10 +1,11 @@
package dev.sheldan.abstracto.experience.listener; package dev.sheldan.abstracto.experience.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener; import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
@@ -27,9 +28,10 @@ public class ExperienceTrackerListener implements AsyncMessageReceivedListener {
private UserInServerManagementService userInServerManagementService; private UserInServerManagementService userInServerManagementService;
@Override @Override
public void execute(CachedMessage message) { public DefaultListenerResult execute(MessageReceivedModel model) {
AUserInAServer cause = userInServerManagementService.loadOrCreateUser(message.getServerId(), message.getAuthor().getAuthorId()); AUserInAServer cause = userInServerManagementService.loadOrCreateUser(model.getServerId(), model.getMessage().getAuthor().getIdLong());
userExperienceService.addExperience(cause); userExperienceService.addExperience(cause);
return DefaultListenerResult.PROCESSED;
} }
@Override @Override

View File

@@ -1,10 +1,11 @@
package dev.sheldan.abstracto.experience.listener; package dev.sheldan.abstracto.experience.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncJoinListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncJoinListener;
import dev.sheldan.abstracto.core.listener.sync.jda.JoinListener; import dev.sheldan.abstracto.core.listener.sync.jda.JoinListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.model.database.AUserExperience; import dev.sheldan.abstracto.experience.model.database.AUserExperience;
@@ -33,18 +34,20 @@ public class JoiningUserRoleListener implements AsyncJoinListener {
private UserInServerManagementService userInServerManagementService; private UserInServerManagementService userInServerManagementService;
@Override @Override
public void execute(ServerUser serverUser) { public DefaultListenerResult execute(MemberJoinModel model) {
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser.getServerId(), serverUser.getUserId()); AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(model.getServerId(), model.getJoiningUser().getUserId());
AUserExperience userExperience = userExperienceManagementService.findUserInServer(userInAServer); AUserExperience userExperience = userExperienceManagementService.findUserInServer(userInAServer);
Long userInServerId = userInAServer.getUserInServerId(); Long userInServerId = userInAServer.getUserInServerId();
if(userExperience != null) { if(userExperience != null) {
log.info("User {} joined {} with previous experience. Setting up experience role again (if necessary).", serverUser.getUserId(), serverUser.getServerId()); log.info("User {} joined {} with previous experience. Setting up experience role again (if necessary).", model.getJoiningUser().getUserId(), model.getServerId());
userExperienceService.syncForSingleUser(userExperience).thenAccept(result -> userExperienceService.syncForSingleUser(userExperience).thenAccept(result ->
log.info("Finished re-assigning experience for re-joining user {} in server {}.", userInServerId, serverUser.getServerId()) log.info("Finished re-assigning experience for re-joining user {} in server {}.", userInServerId, model.getServerId())
); );
} else { } else {
log.info("Joined user {} in server {} does not have any previous experience. Not setting up anything.", serverUser.getUserId(), serverUser.getServerId()); log.info("Joined user {} in server {} does not have any previous experience. Not setting up anything.", model.getJoiningUser().getUserId(), model.getServerId());
} }
return DefaultListenerResult.PROCESSED;
} }
@Override @Override

View File

@@ -93,7 +93,7 @@ public class ExperienceRoleManagementServiceBean implements ExperienceRoleManage
.role(role) .role(role)
.build(); .build();
log.trace("Role did not exist. Creating new."); log.trace("Role did not exist. Creating new.");
experienceRole = experienceRoleRepository.save(experienceRole); return experienceRoleRepository.save(experienceRole);
} }
return experienceRole; return experienceRole;
} }

View File

@@ -1,12 +1,11 @@
package dev.sheldan.abstracto.experience.listener; package dev.sheldan.abstracto.experience.listener;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@@ -28,16 +27,25 @@ public class ExperienceTrackerListenerTest {
@Mock @Mock
private UserInServerManagementService userInServerManagementService; private UserInServerManagementService userInServerManagementService;
@Mock
private MessageReceivedModel model;
@Mock
private User user;
private static final Long SERVER_ID = 4L;
private static final Long USER_ID = 5L;
@Test @Test
public void testExperienceTracking() { public void testExperienceTracking() {
AServer server = Mockito.mock(AServer.class);
AUser user = Mockito.mock(AUser.class);
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class); AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
CachedMessage mockedMessage = Mockito.mock(CachedMessage.class); Message mockedMessage = Mockito.mock(Message.class);
CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class); when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(userInAServer);
when(mockedMessage.getAuthor()).thenReturn(cachedAuthor); when(model.getMessage()).thenReturn(mockedMessage);
when(userInServerManagementService.loadOrCreateUser(server.getId(), user.getId())).thenReturn(userInAServer); when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(mockedMessage); when(mockedMessage.getAuthor()).thenReturn(user);
when(user.getIdLong()).thenReturn(USER_ID);
testUnit.execute(model);
verify(userExperienceService, times(1)).addExperience(userInAServer); verify(userExperienceService, times(1)).addExperience(userInAServer);
} }
} }

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.experience.listener;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.model.database.AUserExperience; import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
@@ -39,6 +40,9 @@ public class JoiningUserRoleListenerTest {
@Mock @Mock
private AUserInAServer aUserInAServer; private AUserInAServer aUserInAServer;
@Mock
private MemberJoinModel model;
private static final Long SERVER_ID = 1L; private static final Long SERVER_ID = 1L;
private static final Long USER_ID = 2L; private static final Long USER_ID = 2L;
@@ -46,6 +50,7 @@ public class JoiningUserRoleListenerTest {
public void setup() { public void setup() {
when(serverUser.getServerId()).thenReturn(SERVER_ID); when(serverUser.getServerId()).thenReturn(SERVER_ID);
when(serverUser.getUserId()).thenReturn(USER_ID); when(serverUser.getUserId()).thenReturn(USER_ID);
when(model.getJoiningUser()).thenReturn(serverUser);
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(aUserInAServer); when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(aUserInAServer);
} }
@@ -54,13 +59,13 @@ public class JoiningUserRoleListenerTest {
AUserExperience experience = Mockito.mock(AUserExperience.class); AUserExperience experience = Mockito.mock(AUserExperience.class);
when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(experience); when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(experience);
when(userExperienceService.syncForSingleUser(experience)).thenReturn(CompletableFuture.completedFuture(null)); when(userExperienceService.syncForSingleUser(experience)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.execute(serverUser); testUnit.execute(model);
} }
@Test @Test
public void testUserWithOutExperienceRejoining() { public void testUserWithOutExperienceRejoining() {
when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(null); when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(null);
testUnit.execute(serverUser); testUnit.execute(model);
verify(userExperienceService, times(0)).syncForSingleUser(any()); verify(userExperienceService, times(0)).syncForSingleUser(any());
} }

View File

@@ -2,13 +2,13 @@ package dev.sheldan.abstracto.linkembed.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.execution.result.ExecutionResult; import dev.sheldan.abstracto.core.listener.ConsumableListenerResult;
import dev.sheldan.abstracto.core.execution.result.MessageReceivedListenerResult;
import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener; import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric; import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag; import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.MessageCache; import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -59,7 +59,8 @@ public class MessageEmbedListener implements MessageReceivedListener {
.build(); .build();
@Override @Override
public MessageReceivedListenerResult execute(Message message) { public ConsumableListenerResult execute(MessageReceivedModel model) {
Message message = model.getMessage();
String messageRaw = message.getContentRaw(); String messageRaw = message.getContentRaw();
List<MessageEmbedLink> links = messageEmbedService.getLinksInMessage(messageRaw); List<MessageEmbedLink> links = messageEmbedService.getLinksInMessage(messageRaw);
if(!links.isEmpty()) { if(!links.isEmpty()) {
@@ -82,12 +83,12 @@ public class MessageEmbedListener implements MessageReceivedListener {
} }
if(StringUtils.isBlank(messageRaw) && !links.isEmpty()) { if(StringUtils.isBlank(messageRaw) && !links.isEmpty()) {
messageService.deleteMessage(message); messageService.deleteMessage(message);
return MessageReceivedListenerResult.DELETED; return ConsumableListenerResult.DELETED;
} }
if(!links.isEmpty()) { if(!links.isEmpty()) {
return MessageReceivedListenerResult.PROCESSED; return ConsumableListenerResult.PROCESSED;
} }
return MessageReceivedListenerResult.IGNORED; return ConsumableListenerResult.IGNORED;
} }
@Transactional @Transactional
@@ -104,8 +105,8 @@ public class MessageEmbedListener implements MessageReceivedListener {
} }
@Override @Override
public boolean shouldConsume(Event event, ExecutionResult result) { public boolean shouldConsume(Event event, ConsumableListenerResult result) {
return result.equals(MessageReceivedListenerResult.DELETED); return result.equals(ConsumableListenerResult.DELETED);
} }
@Override @Override

View File

@@ -1,14 +1,13 @@
package dev.sheldan.abstracto.linkembed.listener; package dev.sheldan.abstracto.linkembed.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric; import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag; import dev.sheldan.abstracto.core.metric.service.MetricTag;
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.AEmote;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
@@ -59,21 +58,22 @@ public class MessageEmbedRemovalReactionListener implements AsyncReactionAddedLi
.tagList(Arrays.asList(MetricTag.getTag(MESSAGE_EMBED_ACTION, "removed.source"))) .tagList(Arrays.asList(MetricTag.getTag(MESSAGE_EMBED_ACTION, "removed.source")))
.build(); .build();
@Override @Override
public void executeReactionAdded(CachedMessage message, CachedReactions cachedReaction, ServerUser serverUser) { public DefaultListenerResult execute(ReactionAddedModel model) {
Long guildId = message.getServerId(); Long serverId = model.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(REMOVAL_EMOTE, guildId); AEmote aEmote = emoteService.getEmoteOrDefaultEmote(REMOVAL_EMOTE, serverId);
if(emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), aEmote)) { if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
Optional<EmbeddedMessage> embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId()); Long messageId = model.getMessage().getMessageId();
Optional<EmbeddedMessage> embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(messageId);
if(embeddedMessageOptional.isPresent()) { if(embeddedMessageOptional.isPresent()) {
Long channelId = model.getMessage().getChannelId();
EmbeddedMessage embeddedMessage = embeddedMessageOptional.get(); EmbeddedMessage embeddedMessage = embeddedMessageOptional.get();
boolean embeddedUserRemoves = embeddedMessage.getEmbeddedUser().getUserReference().getId().equals(serverUser.getUserId()); boolean embeddedUserRemoves = embeddedMessage.getEmbeddedUser().getUserReference().getId().equals(model.getUserReacting().getUserId());
boolean embeddingUserRemoves = embeddedMessage.getEmbeddingUser().getUserReference().getId().equals(serverUser.getUserId()); boolean embeddingUserRemoves = embeddedMessage.getEmbeddingUser().getUserReference().getId().equals(model.getUserReacting().getUserId());
if(embeddedUserRemoves || embeddingUserRemoves) { if(embeddedUserRemoves || embeddingUserRemoves) {
log.info("Removing embed in message {} in channel {} in server {} because of a user reaction.", message.getMessageId(), message.getChannelId(), message.getServerId()); log.info("Removing embed in message {} in channel {} in server {} because of a user reaction.", messageId, channelId, serverId);
messageService.deleteMessageInChannelInServer(message.getServerId(), message.getChannelId(), message.getMessageId()).thenAccept(aVoid -> { messageService.deleteMessageInChannelInServer(serverId, channelId, messageId).thenAccept(aVoid -> {
Optional<EmbeddedMessage> innerOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId()); Optional<EmbeddedMessage> innerOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(messageId);
innerOptional.ifPresent(value -> messageEmbedPostManagementService.deleteEmbeddedMessage(value)); innerOptional.ifPresent(value -> messageEmbedPostManagementService.deleteEmbeddedMessage(value));
if(embeddedUserRemoves) { if(embeddedUserRemoves) {
metricService.incrementCounter(MESSAGE_EMBED_REMOVED_SOURCE); metricService.incrementCounter(MESSAGE_EMBED_REMOVED_SOURCE);
@@ -83,13 +83,19 @@ public class MessageEmbedRemovalReactionListener implements AsyncReactionAddedLi
}); });
} else { } else {
log.trace("Somebody besides the original author and the user embedding added the removal reaction to the message {} in channel {} in server {}.", log.trace("Somebody besides the original author and the user embedding added the removal reaction to the message {} in channel {} in server {}.",
message.getMessageId(), message.getChannelId(), message.getServerId()); messageId, channelId, serverId);
return DefaultListenerResult.IGNORED;
} }
} else { } else {
log.trace("Removal emote was placed on a message which was not recognized as an embedded message."); log.trace("Removal emote was placed on a message which was not recognized as an embedded message.");
return DefaultListenerResult.IGNORED;
} }
return DefaultListenerResult.PROCESSED;
} else {
return DefaultListenerResult.IGNORED;
} }
} }
@Override @Override

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.linkembed.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.MessageCache; import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -57,6 +58,9 @@ public class MessageEmbedListenerTest {
@Mock @Mock
private TextChannel textChannel; private TextChannel textChannel;
@Mock
private MessageReceivedModel model;
@Mock @Mock
private Guild guild; private Guild guild;
@@ -80,7 +84,8 @@ public class MessageEmbedListenerTest {
when(message.getContentRaw()).thenReturn(text); when(message.getContentRaw()).thenReturn(text);
List<MessageEmbedLink> foundMessageLinks = new ArrayList<>(); List<MessageEmbedLink> foundMessageLinks = new ArrayList<>();
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks); when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message); when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message); verify(messageService, times(0)).deleteMessage(message);
} }
@@ -116,7 +121,8 @@ public class MessageEmbedListenerTest {
when(message.getMember()).thenReturn(author); when(message.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer); when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks); when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message); when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message); verify(messageService, times(0)).deleteMessage(message);
verify(self, times(0)).embedSingleLink(eq(message), anyLong(), any(CachedMessage.class)); verify(self, times(0)).embedSingleLink(eq(message), anyLong(), any(CachedMessage.class));
verify(messageCache, times(0)).getMessageFromCache(anyLong(), anyLong(), anyLong()); verify(messageCache, times(0)).getMessageFromCache(anyLong(), anyLong(), anyLong());
@@ -148,7 +154,8 @@ public class MessageEmbedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class); CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage)); when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
when(messageEmbedService.getLinksInMessage(completeMessage)).thenReturn(foundMessageLinks); when(messageEmbedService.getLinksInMessage(completeMessage)).thenReturn(foundMessageLinks);
testUnit.execute(message); when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message); verify(messageService, times(0)).deleteMessage(message);
verify(self, times(1)).embedSingleLink(message, USER_IN_SERVER_ID, cachedMessage); verify(self, times(1)).embedSingleLink(message, USER_IN_SERVER_ID, cachedMessage);
} }
@@ -179,7 +186,8 @@ public class MessageEmbedListenerTest {
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage)); when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, SECOND_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(secondCachedMessage)); when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, SECOND_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(secondCachedMessage));
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks); when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message); when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(1)).deleteMessage(message); verify(messageService, times(1)).deleteMessage(message);
verify(self, times(1)).embedSingleLink(message, USER_IN_SERVER_ID, cachedMessage); verify(self, times(1)).embedSingleLink(message, USER_IN_SERVER_ID, cachedMessage);
verify(self, times(1)).embedSingleLink(message, USER_IN_SERVER_ID, secondCachedMessage); verify(self, times(1)).embedSingleLink(message, USER_IN_SERVER_ID, secondCachedMessage);
@@ -210,7 +218,8 @@ public class MessageEmbedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class); CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage)); when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks); when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message); when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
} }
} }

View File

@@ -2,16 +2,16 @@ package dev.sheldan.abstracto.linkembed.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; 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.AEmote;
import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage; import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage;
import dev.sheldan.abstracto.linkembed.service.management.MessageEmbedPostManagementService; import dev.sheldan.abstracto.linkembed.service.management.MessageEmbedPostManagementService;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@@ -43,10 +43,10 @@ public class MessageEmbedRemovalReactionListenerTest {
private MetricService metricService; private MetricService metricService;
@Mock @Mock
private CachedReactions messageReaction; private MessageReaction messageReaction;
@Mock @Mock
private CachedEmote reactionEmote; private MessageReaction.ReactionEmote reactionEmote;
@Mock @Mock
private AUserInAServer embeddingUser; private AUserInAServer embeddingUser;
@@ -63,6 +63,9 @@ public class MessageEmbedRemovalReactionListenerTest {
@Mock @Mock
private ServerUser reactingUser; private ServerUser reactingUser;
@Mock
private ReactionAddedModel model;
private static final Long SERVER_ID = 4L; private static final Long SERVER_ID = 4L;
private static final Long CHANNEL_ID = 5L; private static final Long CHANNEL_ID = 5L;
private static final Long MESSAGE_ID = 6L; private static final Long MESSAGE_ID = 6L;
@@ -87,7 +90,7 @@ public class MessageEmbedRemovalReactionListenerTest {
when(embeddingAUser.getId()).thenReturn(USER_ID); when(embeddingAUser.getId()).thenReturn(USER_ID);
when(embeddedAUser.getId()).thenReturn(USER_ID + 1); when(embeddedAUser.getId()).thenReturn(USER_ID + 1);
when(reactingUser.getUserId()).thenReturn(USER_ID + 2); when(reactingUser.getUserId()).thenReturn(USER_ID + 2);
executeDeletionTest(embeddingUser, embeddedUser, reactingUser, 0); executeDeletionTest(embeddingUser, embeddedUser, 0);
} }
@Test @Test
@@ -97,7 +100,7 @@ public class MessageEmbedRemovalReactionListenerTest {
when(embeddingUser.getUserReference()).thenReturn(embeddingAUser); when(embeddingUser.getUserReference()).thenReturn(embeddingAUser);
when(embeddingAUser.getId()).thenReturn(USER_ID + 3); when(embeddingAUser.getId()).thenReturn(USER_ID + 3);
when(reactingUser.getUserId()).thenReturn(USER_ID + 1); when(reactingUser.getUserId()).thenReturn(USER_ID + 1);
executeDeletionTest(embeddingUser, embeddedUser, reactingUser, 1); executeDeletionTest(embeddingUser, embeddedUser, 1);
} }
@Test @Test
@@ -107,25 +110,28 @@ public class MessageEmbedRemovalReactionListenerTest {
when(embeddingAUser.getId()).thenReturn(USER_ID); when(embeddingAUser.getId()).thenReturn(USER_ID);
when(embeddedAUser.getId()).thenReturn(USER_ID + 1); when(embeddedAUser.getId()).thenReturn(USER_ID + 1);
when(reactingUser.getUserId()).thenReturn(USER_ID); when(reactingUser.getUserId()).thenReturn(USER_ID);
executeDeletionTest(embeddingUser, embeddedUser, reactingUser, 1); executeDeletionTest(embeddingUser, embeddedUser, 1);
} }
private void executeDeletionTest(AUserInAServer embeddingUser, AUserInAServer embeddedUser, ServerUser userAddingReaction, int wantedDeletions) { private void executeDeletionTest(AUserInAServer embeddingUser, AUserInAServer embeddedUser, int wantedDeletions) {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class); CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getChannelId()).thenReturn(CHANNEL_ID); when(cachedMessage.getChannelId()).thenReturn(CHANNEL_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID); when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
AEmote reactedEmote = Mockito.mock(AEmote.class); AEmote reactedEmote = Mockito.mock(AEmote.class);
when(emoteService.getEmoteOrDefaultEmote(MessageEmbedRemovalReactionListener.REMOVAL_EMOTE, SERVER_ID)).thenReturn(reactedEmote); when(emoteService.getEmoteOrDefaultEmote(MessageEmbedRemovalReactionListener.REMOVAL_EMOTE, SERVER_ID)).thenReturn(reactedEmote);
when(messageReaction.getEmote()).thenReturn(reactionEmote); when(messageReaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.compareCachedEmoteWithAEmote(reactionEmote, reactedEmote)).thenReturn(true); when(emoteService.isReactionEmoteAEmote(reactionEmote, reactedEmote)).thenReturn(true);
EmbeddedMessage message = Mockito.mock(EmbeddedMessage.class); EmbeddedMessage message = Mockito.mock(EmbeddedMessage.class);
when(message.getEmbeddingUser()).thenReturn(embeddingUser); when(message.getEmbeddingUser()).thenReturn(embeddingUser);
when(message.getEmbeddedUser()).thenReturn(embeddedUser); when(message.getEmbeddedUser()).thenReturn(embeddedUser);
when(messageEmbedPostManagementService.findEmbeddedPostByMessageId(MESSAGE_ID)).thenReturn(Optional.of(message)); when(messageEmbedPostManagementService.findEmbeddedPostByMessageId(MESSAGE_ID)).thenReturn(Optional.of(message));
when(messageService.deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(null)); when(messageService.deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(null));
when(messageEmbedPostManagementService.findEmbeddedPostByMessageId(MESSAGE_ID)).thenReturn(Optional.of(message)); when(messageEmbedPostManagementService.findEmbeddedPostByMessageId(MESSAGE_ID)).thenReturn(Optional.of(message));
testUnit.executeReactionAdded(cachedMessage, messageReaction, userAddingReaction); when(model.getMessage()).thenReturn(cachedMessage);
when(model.getReaction()).thenReturn(messageReaction);
when(model.getServerId()).thenReturn(SERVER_ID);
when(model.getUserReacting()).thenReturn(reactingUser);
testUnit.execute(model);
verify(messageService, times(wantedDeletions)).deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, MESSAGE_ID); verify(messageService, times(wantedDeletions)).deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, MESSAGE_ID);
if(wantedDeletions > 0) { if(wantedDeletions > 0) {
verify(messageEmbedPostManagementService, times(1)).deleteEmbeddedMessage(message); verify(messageEmbedPostManagementService, times(1)).deleteEmbeddedMessage(message);
@@ -134,14 +140,15 @@ public class MessageEmbedRemovalReactionListenerTest {
private void executeRemovalEmoteAddedTest(boolean wasCorrectEmote) { private void executeRemovalEmoteAddedTest(boolean wasCorrectEmote) {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class); CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID); when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
ServerUser serverUser = Mockito.mock(ServerUser.class);
AEmote reactedEmote = Mockito.mock(AEmote.class); AEmote reactedEmote = Mockito.mock(AEmote.class);
when(emoteService.getEmoteOrDefaultEmote(MessageEmbedRemovalReactionListener.REMOVAL_EMOTE, SERVER_ID)).thenReturn(reactedEmote); when(emoteService.getEmoteOrDefaultEmote(MessageEmbedRemovalReactionListener.REMOVAL_EMOTE, SERVER_ID)).thenReturn(reactedEmote);
when(messageReaction.getEmote()).thenReturn(reactionEmote); when(messageReaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.compareCachedEmoteWithAEmote(reactionEmote, reactedEmote)).thenReturn(wasCorrectEmote); when(emoteService.isReactionEmoteAEmote(reactionEmote, reactedEmote)).thenReturn(wasCorrectEmote);
testUnit.executeReactionAdded(cachedMessage, messageReaction, serverUser); when(model.getMessage()).thenReturn(cachedMessage);
when(model.getReaction()).thenReturn(messageReaction);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessageInChannelInServer(anyLong(), anyLong(), anyLong()); verify(messageService, times(0)).deleteMessageInChannelInServer(anyLong(), anyLong(), anyLong());
} }

View File

@@ -2,12 +2,13 @@ package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.execution.result.MessageReceivedListenerResult; import dev.sheldan.abstracto.core.listener.ConsumableListenerResult;
import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener; import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric; import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag; import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.FeatureModeService; import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -69,7 +70,8 @@ public class InviteLinkFilterListener implements MessageReceivedListener {
public static final String INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY = "invite_link_deleted_notification"; public static final String INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY = "invite_link_deleted_notification";
@Override @Override
public MessageReceivedListenerResult execute(Message message) { public ConsumableListenerResult execute(MessageReceivedModel model) {
Message message = model.getMessage();
Long serverId = message.getGuild().getIdLong(); Long serverId = message.getGuild().getIdLong();
Matcher matcher = Message.INVITE_PATTERN.matcher(message.getContentRaw()); Matcher matcher = Message.INVITE_PATTERN.matcher(message.getContentRaw());
ServerUser author = ServerUser.builder().userId(message.getAuthor().getIdLong()).serverId(message.getGuild().getIdLong()).build(); ServerUser author = ServerUser.builder().userId(message.getAuthor().getIdLong()).serverId(message.getGuild().getIdLong()).build();
@@ -95,9 +97,9 @@ public class InviteLinkFilterListener implements MessageReceivedListener {
if(sendNotification) { if(sendNotification) {
sendDeletionNotification(codesToTrack, message); sendDeletionNotification(codesToTrack, message);
} }
return MessageReceivedListenerResult.DELETED; return ConsumableListenerResult.DELETED;
} else { } else {
return MessageReceivedListenerResult.PROCESSED; return ConsumableListenerResult.PROCESSED;
} }
} }

View File

@@ -1,19 +1,18 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncJoinListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncJoinListener;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget; import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap; import java.util.HashMap;
@@ -42,20 +41,14 @@ public class JoinLogger implements AsyncJoinListener {
} }
@Override @Override
public void execute(ServerUser serverUser) { public DefaultListenerResult execute(MemberJoinModel serverUser) {
log.info("User {} joined server {}.", serverUser.getUserId(), serverUser.getServerId()); HashMap<String, Object> parameters = getUserParameter(serverUser.getMember().getUser());
memberService.getMemberInServerAsync(serverUser.getServerId(), serverUser.getUserId()).thenAccept(member ->
self.sendJoinLog(serverUser, member)
);
}
@Transactional
public void sendJoinLog(ServerUser serverUser, Member member) {
HashMap<String, Object> parameters = getUserParameter(member.getUser());
String text = templateService.renderTemplateWithMap(USER_JOIN_TEMPLATE, parameters, serverUser.getServerId()); String text = templateService.renderTemplateWithMap(USER_JOIN_TEMPLATE, parameters, serverUser.getServerId());
postTargetService.sendTextInPostTarget(text, LoggingPostTarget.JOIN_LOG, serverUser.getServerId()); postTargetService.sendTextInPostTarget(text, LoggingPostTarget.JOIN_LOG, serverUser.getServerId());
return DefaultListenerResult.PROCESSED;
} }
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.LOGGING; return ModerationFeatureDefinition.LOGGING;

View File

@@ -1,9 +1,10 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncJoinListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncJoinListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.service.MuteService; import dev.sheldan.abstracto.moderation.service.MuteService;
@@ -26,12 +27,13 @@ public class JoinMuteListener implements AsyncJoinListener {
private UserInServerManagementService userInServerManagementService; private UserInServerManagementService userInServerManagementService;
@Override @Override
public void execute(ServerUser serverUser) { public DefaultListenerResult execute(MemberJoinModel model) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(serverUser.getServerId(), serverUser.getUserId()); AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(model.getServerId(), model.getJoiningUser().getUserId());
if(muteManagementService.hasActiveMute(aUserInAServer)) { if(muteManagementService.hasActiveMute(aUserInAServer)) {
log.info("Re-muting user {} which joined the server {}, because the mute has not ended yet.", serverUser.getUserId(), serverUser.getServerId()); log.info("Re-muting user {} which joined the server {}, because the mute has not ended yet.", model.getJoiningUser().getUserId(), model.getServerId());
muteService.applyMuteRole(aUserInAServer); muteService.applyMuteRole(aUserInAServer);
} }
return DefaultListenerResult.PROCESSED;
} }
@Override @Override

View File

@@ -1,20 +1,19 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncLeaveListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncLeaveListener;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget; import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.HashMap; import java.util.HashMap;
@@ -45,23 +44,15 @@ public class LeaveLogger implements AsyncLeaveListener {
return parameters; return parameters;
} }
@Override
public void execute(ServerUser serverUser) {
log.info("User {} left server {}.", serverUser.getUserId(), serverUser.getServerId());
memberService.getMemberInServerAsync(serverUser.getServerId(), serverUser.getUserId()).thenAccept(member ->
self.executeJoinLogging(serverUser, member)
);
}
@Transactional
public void executeJoinLogging(ServerUser serverUser, Member member) {
String text = templateService.renderTemplateWithMap(USER_LEAVE_TEMPLATE, getUserParameter(member.getUser()), serverUser.getServerId());
postTargetService.sendTextInPostTarget(text, LoggingPostTarget.LEAVE_LOG, serverUser.getServerId());
}
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.LOGGING; return ModerationFeatureDefinition.LOGGING;
} }
@Override
public DefaultListenerResult execute(MemberLeaveModel model) {
String text = templateService.renderTemplateWithMap(USER_LEAVE_TEMPLATE, getUserParameter(model.getMember().getUser()), model.getLeavingUser().getServerId());
postTargetService.sendTextInPostTarget(text, LoggingPostTarget.LEAVE_LOG, model.getServerId());
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -58,10 +60,12 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
private MessageDeleteLogListener self; private MessageDeleteLogListener self;
@Override @Override
public void execute(CachedMessage messageFromCache) { public DefaultListenerResult execute(MessageDeletedModel model) {
memberService.getMemberInServerAsync(messageFromCache.getServerId(), messageFromCache.getAuthor().getAuthorId()).thenAccept(member -> CachedMessage message = model.getCachedMessage();
self.executeListener(messageFromCache, member) memberService.getMemberInServerAsync(model.getServerId(), message.getAuthor().getAuthorId()).thenAccept(member ->
self.executeListener(message, member)
); );
return DefaultListenerResult.PROCESSED;
} }
@Transactional @Transactional

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageTextUpdatedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageTextUpdatedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageTextUpdatedModel;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -12,6 +14,7 @@ import dev.sheldan.abstracto.moderation.model.template.listener.MessageEditedLog
import dev.sheldan.abstracto.core.templating.model.MessageToSend; import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService; import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -35,29 +38,26 @@ public class MessageEditedListener implements AsyncMessageTextUpdatedListener {
private ChannelService channelService; private ChannelService channelService;
@Override @Override
public void execute(CachedMessage messageBefore, CachedMessage messageAfter) { public DefaultListenerResult execute(MessageTextUpdatedModel model) {
if(messageBefore.getContent().equals(messageAfter.getContent())) { Message messageAfter = model.getAfter();
CachedMessage messageBefore = model.getBefore();
if(messageBefore.getContent().equals(messageAfter.getContentRaw())) {
log.trace("Message content was the same. Possible reason was: message was not in cache."); log.trace("Message content was the same. Possible reason was: message was not in cache.");
return; return DefaultListenerResult.IGNORED;
} }
memberService.getMemberInServerAsync(messageAfter.getServerId(), messageAfter.getAuthor().getAuthorId()).thenAccept(author -> { log.trace("Message {} in channel {} in guild {} was edited.", messageBefore.getMessageId(), messageBefore.getChannelId(), messageBefore.getServerId());
log.trace("Message {} in channel {} in guild {} was edited.", messageBefore.getMessageId(), messageBefore.getChannelId(), messageBefore.getServerId()); TextChannel textChannel = channelService.getTextChannelFromServer(model.getServerId(), messageBefore.getChannelId());
TextChannel textChannel = channelService.getTextChannelFromServer(messageAfter.getServerId(), messageAfter.getChannelId()); MessageEditedLog log = MessageEditedLog
MessageEditedLog log = MessageEditedLog .builder()
.builder() .messageAfter(messageAfter)
.messageAfter(messageAfter) .messageBefore(messageBefore)
.messageBefore(messageBefore) .messageChannel(textChannel)
.messageChannel(textChannel) .guild(textChannel.getGuild())
.guild(textChannel.getGuild()) .member(messageAfter.getMember())
.member(author) .build();
.build(); MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, log, messageBefore.getServerId());
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, log, messageBefore.getServerId()); postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.EDIT_LOG, messageBefore.getServerId());
postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.EDIT_LOG, messageBefore.getServerId()); return DefaultListenerResult.PROCESSED;
}).exceptionally(throwable -> {
log.error("Failed to load member {} for message edited listener in server {} for message {} in channel {}.",
messageAfter.getAuthor().getAuthorId(), messageAfter.getServerId(), messageAfter.getMessageId(), messageAfter.getChannelId(), throwable);
return null;
});
} }
@Override @Override

View File

@@ -44,8 +44,7 @@ public class MuteManagementServiceBean implements MuteManagementService {
.muteId(id) .muteId(id)
.muteEnded(false) .muteEnded(false)
.build(); .build();
muteRepository.save(mute); return muteRepository.save(mute);
return mute;
} }
@Override @Override

View File

@@ -30,8 +30,7 @@ public class MuteRoleManagementServiceBean implements MuteRoleManagementService
.role(role) .role(role)
.roleServer(server) .roleServer(server)
.build(); .build();
muteRoleRepository.save(muteRole); return muteRoleRepository.save(muteRole);
return muteRole;
} }
@Override @Override

View File

@@ -36,8 +36,7 @@ public class UserNoteManagementServiceBean implements UserNoteManagementService
.server(aUserInAServer.getServerReference()) .server(aUserInAServer.getServerReference())
.user(aUserInAServer) .user(aUserInAServer)
.build(); .build();
userNoteRepository.save(newNote); return userNoteRepository.save(newNote);
return newNote;
} }
@Override @Override

View File

@@ -35,8 +35,7 @@ public class WarnManagementServiceBean implements WarnManagementService {
.warnId(warningId) .warnId(warningId)
.decayed(false) .decayed(false)
.build(); .build();
warnRepository.save(warning); return warnRepository.save(warning);
return warning;
} }
@Override @Override

View File

@@ -1,8 +1,9 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.execution.result.MessageReceivedListenerResult; import dev.sheldan.abstracto.core.listener.ConsumableListenerResult;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.FeatureModeService; import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -68,6 +69,9 @@ public class InviteLinkFilterListenerTest {
@Mock @Mock
private MetricService metricService; private MetricService metricService;
@Mock
private MessageReceivedModel model;
private static final Long SERVER_ID = 1L; private static final Long SERVER_ID = 1L;
private static final Long CHANNEL_ID = 2L; private static final Long CHANNEL_ID = 2L;
private static final Long USER_ID = 3L; private static final Long USER_ID = 3L;
@@ -79,8 +83,9 @@ public class InviteLinkFilterListenerTest {
public void testExecutionWithNoInvite() { public void testExecutionWithNoInvite() {
when(message.getContentRaw()).thenReturn("text"); when(message.getContentRaw()).thenReturn("text");
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
Assert.assertEquals(MessageReceivedListenerResult.PROCESSED, result); ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.PROCESSED, result);
} }
@Test @Test
@@ -88,8 +93,9 @@ public class InviteLinkFilterListenerTest {
when(message.getContentRaw()).thenReturn(INVITE_LINK); when(message.getContentRaw()).thenReturn(INVITE_LINK);
when(inviteLinkFilterService.isCodeFiltered(eq(INVITE_CODE), any(ServerUser.class))).thenReturn(false); when(inviteLinkFilterService.isCodeFiltered(eq(INVITE_CODE), any(ServerUser.class))).thenReturn(false);
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
Assert.assertEquals(MessageReceivedListenerResult.PROCESSED, result); ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.PROCESSED, result);
} }
@Test @Test
@@ -100,8 +106,9 @@ public class InviteLinkFilterListenerTest {
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.TRACK_USES)).thenReturn(false); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.TRACK_USES)).thenReturn(false);
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(false); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(false);
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result); ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verify(metricService, times(1)).incrementCounter(any()); verify(metricService, times(1)).incrementCounter(any());
} }
@@ -113,8 +120,9 @@ public class InviteLinkFilterListenerTest {
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.TRACK_USES)).thenReturn(true); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.TRACK_USES)).thenReturn(true);
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(false); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(false);
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result); ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verifyTracking(); verifyTracking();
verify(metricService, times(1)).incrementCounter(any()); verify(metricService, times(1)).incrementCounter(any());
} }
@@ -128,8 +136,9 @@ public class InviteLinkFilterListenerTest {
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true);
setupForNotification(); setupForNotification();
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result); ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verifyTracking(); verifyTracking();
verify(metricService, times(1)).incrementCounter(any()); verify(metricService, times(1)).incrementCounter(any());
} }
@@ -143,9 +152,10 @@ public class InviteLinkFilterListenerTest {
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true);
setupForNotification(); setupForNotification();
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
verify(metricService, times(1)).incrementCounter(any()); verify(metricService, times(1)).incrementCounter(any());
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result); Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verify(inviteLinkFilterService, times(0)).storeFilteredInviteLinkUsage(eq(INVITE_CODE), any(ServerUser.class)); verify(inviteLinkFilterService, times(0)).storeFilteredInviteLinkUsage(eq(INVITE_CODE), any(ServerUser.class));
} }
@@ -158,9 +168,10 @@ public class InviteLinkFilterListenerTest {
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true); when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true);
when(postTargetService.postTargetDefinedInServer(InviteFilterPostTarget.INVITE_DELETE_LOG, SERVER_ID)).thenReturn(false); when(postTargetService.postTargetDefinedInServer(InviteFilterPostTarget.INVITE_DELETE_LOG, SERVER_ID)).thenReturn(false);
setupBasicMessage(); setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message); when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
verify(metricService, times(1)).incrementCounter(any()); verify(metricService, times(1)).incrementCounter(any());
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result); Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verify(inviteLinkFilterService, times(0)).storeFilteredInviteLinkUsage(eq(INVITE_CODE), any(ServerUser.class)); verify(inviteLinkFilterService, times(0)).storeFilteredInviteLinkUsage(eq(INVITE_CODE), any(ServerUser.class));
verify(templateService, times(0)).renderEmbedTemplate(eq(InviteLinkFilterListener.INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY), any(DeletedInvitesNotificationModel.class), eq(SERVER_ID)); verify(templateService, times(0)).renderEmbedTemplate(eq(InviteLinkFilterListener.INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY), any(DeletedInvitesNotificationModel.class), eq(SERVER_ID));
} }

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget; import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget;
@@ -40,6 +41,8 @@ public class JoinLoggerTest {
@Mock @Mock
private Member member; private Member member;
@Mock
private MemberJoinModel model;
private static final Long SERVER_ID = 1L; private static final Long SERVER_ID = 1L;
private static final Long USER_ID = 2L; private static final Long USER_ID = 2L;
@@ -48,17 +51,13 @@ public class JoinLoggerTest {
public void testExecute() { public void testExecute() {
when(serverUser.getUserId()).thenReturn(USER_ID); when(serverUser.getUserId()).thenReturn(USER_ID);
when(serverUser.getServerId()).thenReturn(SERVER_ID); when(serverUser.getServerId()).thenReturn(SERVER_ID);
when(model.getMember()).thenReturn(member);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member)); when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member));
testUnit.execute(serverUser); testUnit.execute(model);
verify(self, times(1)).sendJoinLog(serverUser, member);
}
@Test
public void testJoinLog() {
String message = "text";
when(serverUser.getServerId()).thenReturn(SERVER_ID); when(serverUser.getServerId()).thenReturn(SERVER_ID);
String message = "text";
when(templateService.renderTemplateWithMap(eq(JoinLogger.USER_JOIN_TEMPLATE), any(), eq(SERVER_ID))).thenReturn(message); when(templateService.renderTemplateWithMap(eq(JoinLogger.USER_JOIN_TEMPLATE), any(), eq(SERVER_ID))).thenReturn(message);
testUnit.sendJoinLog(serverUser, member);
verify(postTargetService, times(1)).sendTextInPostTarget(message, LoggingPostTarget.JOIN_LOG, SERVER_ID); verify(postTargetService, times(1)).sendTextInPostTarget(message, LoggingPostTarget.JOIN_LOG, SERVER_ID);
} }
} }

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.service.MuteService; import dev.sheldan.abstracto.moderation.service.MuteService;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService; import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
@@ -42,6 +43,9 @@ public class JoinMuteListenerTest {
@Mock @Mock
private ServerUser serverUser; private ServerUser serverUser;
@Mock
private MemberJoinModel model;
private static final Long SERVER_ID = 3L; private static final Long SERVER_ID = 3L;
private static final Long USER_ID = 4L; private static final Long USER_ID = 4L;
@@ -51,7 +55,8 @@ public class JoinMuteListenerTest {
when(serverUser.getUserId()).thenReturn(USER_ID); when(serverUser.getUserId()).thenReturn(USER_ID);
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(joiningUser); when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(joiningUser);
when(muteManagementService.hasActiveMute(joiningUser)).thenReturn(false); when(muteManagementService.hasActiveMute(joiningUser)).thenReturn(false);
testUnit.execute(serverUser); when(model.getMember()).thenReturn(member);
testUnit.execute(model);
verify(muteService, times(0)).applyMuteRole(joiningUser); verify(muteService, times(0)).applyMuteRole(joiningUser);
} }
@@ -61,7 +66,8 @@ public class JoinMuteListenerTest {
when(serverUser.getUserId()).thenReturn(USER_ID); when(serverUser.getUserId()).thenReturn(USER_ID);
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(joiningUser); when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(joiningUser);
when(muteManagementService.hasActiveMute(joiningUser)).thenReturn(true); when(muteManagementService.hasActiveMute(joiningUser)).thenReturn(true);
testUnit.execute(serverUser); when(model.getMember()).thenReturn(member);
testUnit.execute(model);
verify(muteService, times(1)).applyMuteRole(joiningUser); verify(muteService, times(1)).applyMuteRole(joiningUser);
} }
} }

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.moderation.listener; package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget; import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget;
@@ -42,6 +43,9 @@ public class LeaveLoggerTest {
@Mock @Mock
private Member member; private Member member;
@Mock
private MemberLeaveModel model;
private static final Long SERVER_ID = 1L; private static final Long SERVER_ID = 1L;
private static final Long USER_ID = 2L; private static final Long USER_ID = 2L;
@@ -50,19 +54,14 @@ public class LeaveLoggerTest {
when(leavingUser.getUserId()).thenReturn(USER_ID); when(leavingUser.getUserId()).thenReturn(USER_ID);
when(leavingUser.getServerId()).thenReturn(SERVER_ID); when(leavingUser.getServerId()).thenReturn(SERVER_ID);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member)); when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member));
testUnit.execute(leavingUser); when(model.getMember()).thenReturn(member);
verify(self, times(1)).executeJoinLogging(leavingUser, member); User user = Mockito.mock(User.class);
when(member.getUser()).thenReturn(user);
String message = "text";
when(leavingUser.getServerId()).thenReturn(SERVER_ID);
when(templateService.renderTemplateWithMap(eq(LeaveLogger.USER_LEAVE_TEMPLATE), any(), eq(SERVER_ID))).thenReturn(message);
testUnit.execute(model);
verify(postTargetService, times(1)).sendTextInPostTarget(message, LoggingPostTarget.LEAVE_LOG, SERVER_ID);
} }
@Test
public void executeListener() {
User user = Mockito.mock(User.class);
when(member.getUser()).thenReturn(user);
String message = "text";
when(leavingUser.getServerId()).thenReturn(SERVER_ID);
when(templateService.renderTemplateWithMap(eq(LeaveLogger.USER_LEAVE_TEMPLATE), any(), eq(SERVER_ID))).thenReturn(message);
testUnit.executeJoinLogging(leavingUser, member);
verify(postTargetService, times(1)).sendTextInPostTarget(message, LoggingPostTarget.LEAVE_LOG, SERVER_ID);
}
} }

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.models.cache.CachedAttachment; import dev.sheldan.abstracto.core.models.cache.CachedAttachment;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor; import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -86,13 +87,18 @@ public class MessageDeleteLogListenerTest {
@Mock @Mock
private Guild guild; private Guild guild;
@Mock
private MessageDeletedModel model;
@Test @Test
public void testExecuteListener() { public void testExecuteListener() {
when(deletedMessage.getAuthor()).thenReturn(cachedAuthor); when(deletedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID); when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(deletedMessage.getServerId()).thenReturn(SERVER_ID); when(deletedMessage.getServerId()).thenReturn(SERVER_ID);
when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_ID)).thenReturn(CompletableFuture.completedFuture(member)); when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_ID)).thenReturn(CompletableFuture.completedFuture(member));
testUnit.execute(deletedMessage); when(model.getCachedMessage()).thenReturn(deletedMessage);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(self, times(1)).executeListener(deletedMessage, member); verify(self, times(1)).executeListener(deletedMessage, member);
} }

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor; import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageTextUpdatedModel;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -11,6 +12,7 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService; import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@@ -44,11 +46,13 @@ public class MessageEditedListenerTest {
private MemberService memberService; private MemberService memberService;
@Mock @Mock
private CachedMessage messageAfter; private Message messageAfter;
@Mock @Mock
private CachedMessage messageBefore; private CachedMessage messageBefore;
private MessageTextUpdatedModel model;
private static final Long SERVER_ID = 4L; private static final Long SERVER_ID = 4L;
private static final Long CHANNEL_ID = 5L; private static final Long CHANNEL_ID = 5L;
private static final Long AUTHOR_ID = 6L; private static final Long AUTHOR_ID = 6L;
@@ -56,9 +60,11 @@ public class MessageEditedListenerTest {
@Test @Test
public void testExecuteListenerWithSameContent() { public void testExecuteListenerWithSameContent() {
String content = "text"; String content = "text";
when(messageAfter.getContent()).thenReturn(content); when(messageAfter.getContentRaw()).thenReturn(content);
when(messageBefore.getContent()).thenReturn(content); when(messageBefore.getContent()).thenReturn(content);
testUnit.execute(messageBefore, messageAfter); when(model.getAfter()).thenReturn(messageAfter);
when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(templateService, times(0)).renderEmbedTemplate(eq(MessageEditedListener.MESSAGE_EDITED_TEMPLATE), any()); verify(templateService, times(0)).renderEmbedTemplate(eq(MessageEditedListener.MESSAGE_EDITED_TEMPLATE), any());
} }
@@ -67,15 +73,12 @@ public class MessageEditedListenerTest {
String content = "text"; String content = "text";
String contentAfterwards = "text2"; String contentAfterwards = "text2";
TextChannel channel = Mockito.mock(TextChannel.class); TextChannel channel = Mockito.mock(TextChannel.class);
when(messageAfter.getContent()).thenReturn(contentAfterwards); when(messageAfter.getContentRaw()).thenReturn(contentAfterwards);
when(messageAfter.getChannelId()).thenReturn(CHANNEL_ID);
Guild guild = Mockito.mock(Guild.class); Guild guild = Mockito.mock(Guild.class);
when(channel.getGuild()).thenReturn(guild); when(channel.getGuild()).thenReturn(guild);
when(messageAfter.getServerId()).thenReturn(SERVER_ID);
Member author = Mockito.mock(Member.class); Member author = Mockito.mock(Member.class);
CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class); CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID); when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(messageAfter.getAuthor()).thenReturn(cachedAuthor);
when(messageBefore.getContent()).thenReturn(content); when(messageBefore.getContent()).thenReturn(content);
when(messageBefore.getServerId()).thenReturn(SERVER_ID); when(messageBefore.getServerId()).thenReturn(SERVER_ID);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class); MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
@@ -83,7 +86,9 @@ public class MessageEditedListenerTest {
when(templateService.renderEmbedTemplate(eq(MessageEditedListener.MESSAGE_EDITED_TEMPLATE), captor.capture(), eq(SERVER_ID))).thenReturn(messageToSend); when(templateService.renderEmbedTemplate(eq(MessageEditedListener.MESSAGE_EDITED_TEMPLATE), captor.capture(), eq(SERVER_ID))).thenReturn(messageToSend);
when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_ID)).thenReturn(CompletableFuture.completedFuture(author)); when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_ID)).thenReturn(CompletableFuture.completedFuture(author));
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(channel); when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(channel);
testUnit.execute(messageBefore, messageAfter); when(model.getAfter()).thenReturn(messageAfter);
when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(postTargetService, times(1)).sendEmbedInPostTarget(messageToSend, LoggingPostTarget.EDIT_LOG, SERVER_ID); verify(postTargetService, times(1)).sendEmbedInPostTarget(messageToSend, LoggingPostTarget.EDIT_LOG, SERVER_ID);
MessageEditedLog capturedValue = captor.getValue(); MessageEditedLog capturedValue = captor.getValue();
Assert.assertEquals(messageBefore, capturedValue.getMessageBefore()); Assert.assertEquals(messageBefore, capturedValue.getMessageBefore());

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Message;
/** /**
* Used when rendering the log message when a message was edited. The template is: "message_edited_embed" * Used when rendering the log message when a message was edited. The template is: "message_edited_embed"
@@ -16,7 +17,7 @@ public class MessageEditedLog extends UserInitiatedServerContext {
/** /**
* The {@link CachedMessage} instance which contains the new content of the message * The {@link CachedMessage} instance which contains the new content of the message
*/ */
private CachedMessage messageAfter; private Message messageAfter;
/** /**
* The {@link CachedMessage} which contains the message before the edit was made * The {@link CachedMessage} which contains the message before the edit was made

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.modmail.listener; package dev.sheldan.abstracto.modmail.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
@@ -34,8 +36,9 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
private MemberService memberService; private MemberService memberService;
@Override @Override
public void execute(CachedMessage messageBefore) { public DefaultListenerResult execute(MessageDeletedModel model) {
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageBefore.getMessageId()); CachedMessage message = model.getCachedMessage();
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(message.getMessageId());
messageOptional.ifPresent(modMailMessage -> { messageOptional.ifPresent(modMailMessage -> {
ModMailThread thread = modMailMessage.getThreadReference(); ModMailThread thread = modMailMessage.getThreadReference();
Long dmMessageId = modMailMessage.getCreatedMessageInDM(); Long dmMessageId = modMailMessage.getCreatedMessageInDM();
@@ -44,7 +47,7 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
Long channelId = thread.getChannel().getId(); Long channelId = thread.getChannel().getId();
Long serverId = thread.getServer().getId(); Long serverId = thread.getServer().getId();
log.info("Deleting message for mod mail thread {} in channel {} in server {}.", thread.getId(), channelId, serverId); log.info("Deleting message for mod mail thread {} in channel {} in server {}.", thread.getId(), channelId, serverId);
memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(member -> { memberService.getMemberInServerAsync(model.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(member -> {
CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(member.getUser(), dmMessageId); CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(member.getUser(), dmMessageId);
CompletableFuture<Void> channelDeletePromise; CompletableFuture<Void> channelDeletePromise;
if(hasMessageInChannel) { if(hasMessageInChannel) {
@@ -53,11 +56,11 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
channelDeletePromise = CompletableFuture.completedFuture(null); channelDeletePromise = CompletableFuture.completedFuture(null);
} }
CompletableFuture.allOf(dmDeletePromise, channelDeletePromise).whenComplete((unused, throwable) -> CompletableFuture.allOf(dmDeletePromise, channelDeletePromise).whenComplete((unused, throwable) ->
self.removeMessageFromThread(messageBefore.getMessageId()) self.removeMessageFromThread(message.getMessageId())
); );
}); });
}); });
return DefaultListenerResult.PROCESSED;
} }
@Transactional @Transactional

View File

@@ -4,10 +4,12 @@ import dev.sheldan.abstracto.core.command.config.Parameters;
import dev.sheldan.abstracto.core.command.service.CommandRegistry; import dev.sheldan.abstracto.core.command.service.CommandRegistry;
import dev.sheldan.abstracto.core.command.service.CommandService; import dev.sheldan.abstracto.core.command.service.CommandService;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageTextUpdatedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageTextUpdatedListener;
import dev.sheldan.abstracto.core.models.FullUserInServer; import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.MessageTextUpdatedModel;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
@@ -62,34 +64,32 @@ public class ModMailMessageEditedListener implements AsyncMessageTextUpdatedList
private ModMailThreadService modMailThreadService; private ModMailThreadService modMailThreadService;
@Override @Override
public void execute(CachedMessage messageBefore, CachedMessage messageAfter) { public DefaultListenerResult execute(MessageTextUpdatedModel model) {
CachedMessage messageBefore = model.getBefore();
Message message = model.getAfter();
if(!modMailThreadService.isModMailThread(messageBefore.getChannelId())) { if(!modMailThreadService.isModMailThread(messageBefore.getChannelId())) {
return; return DefaultListenerResult.IGNORED;
} }
messageService.loadMessageFromCachedMessage(messageAfter).thenAccept(loadedMessage ->
self.executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage)
);
}
@Transactional
public void executeMessageUpdatedLogic(CachedMessage messageBefore, CachedMessage messageAfter, Message loadedMessage) {
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageBefore.getMessageId()); Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageBefore.getMessageId());
messageOptional.ifPresent(modMailMessage -> { if(messageOptional.isPresent()) {
log.info("Editing send message {} in channel {} in mod mail thread {} in server {}.", messageBefore.getMessageId(), messageBefore.getChannelId(), modMailMessage.getThreadReference().getId(), messageBefore.getServerId()); messageOptional.ifPresent(modMailMessage -> {
String contentStripped = messageAfter.getContent(); log.info("Editing send message {} in channel {} in mod mail thread {} in server {}.", messageBefore.getMessageId(), messageBefore.getChannelId(), modMailMessage.getThreadReference().getId(), messageBefore.getServerId());
String commandName = commandRegistry.getCommandName(contentStripped.substring(0, contentStripped.indexOf(" ")), messageBefore.getServerId()); String contentStripped = message.getContentStripped();
if(!commandService.doesCommandExist(commandName)) { String commandName = commandRegistry.getCommandName(contentStripped.substring(0, contentStripped.indexOf(" ")), messageBefore.getServerId());
commandName = DEFAULT_COMMAND_FOR_MODMAIL_EDIT; if(!commandService.doesCommandExist(commandName)) {
log.info("Edit did not contain the original command to retrieve the parameters for. Resulting to {}.", DEFAULT_COMMAND_FOR_MODMAIL_EDIT); commandName = DEFAULT_COMMAND_FOR_MODMAIL_EDIT;
} log.info("Edit did not contain the original command to retrieve the parameters for. Resulting to {}.", DEFAULT_COMMAND_FOR_MODMAIL_EDIT);
CompletableFuture<Parameters> parameterParseFuture = commandService.getParametersForCommand(commandName, loadedMessage); }
CompletableFuture<Member> loadTargetUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()); CompletableFuture<Parameters> parameterParseFuture = commandService.getParametersForCommand(commandName, message);
CompletableFuture<Member> loadEditingUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getAuthor().getUserReference().getId()); CompletableFuture<Member> loadTargetUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId());
CompletableFuture.allOf(parameterParseFuture, loadTargetUser, loadEditingUser).thenAccept(unused -> CompletableFuture<Member> loadEditingUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getAuthor().getUserReference().getId());
self.updateMessageInThread(loadedMessage, parameterParseFuture.join(), loadTargetUser.join(), loadEditingUser.join()) CompletableFuture.allOf(parameterParseFuture, loadTargetUser, loadEditingUser).thenAccept(unused ->
); self.updateMessageInThread(message, parameterParseFuture.join(), loadTargetUser.join(), loadEditingUser.join())
}); );
});
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
} }
@Transactional @Transactional

View File

@@ -37,8 +37,7 @@ public class ModMailMessageManagementServiceBean implements ModMailMessageManage
log.info("Storing created message in DM {} with created message in channel {} caused by message {} to modmail thread {} of user {} in server {}.", log.info("Storing created message in DM {} with created message in channel {} caused by message {} to modmail thread {} of user {} in server {}.",
dmId, channelMessageId, userPostedMessage.getId(), modMailThread.getId(), author.getUserReference().getId(), author.getServerReference().getId()); dmId, channelMessageId, userPostedMessage.getId(), modMailThread.getId(), author.getUserReference().getId(), author.getServerReference().getId());
modMailMessageRepository.save(modMailMessage); return modMailMessageRepository.save(modMailMessage);
return modMailMessage;
} }
@Override @Override

View File

@@ -39,8 +39,7 @@ public class ModMailSubscriberManagementServiceBean implements ModMailSubscriber
log.info("Creating subscription for user {} in server {} for modmail thread {}.", log.info("Creating subscription for user {} in server {} for modmail thread {}.",
aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), modMailThread.getId()); aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), modMailThread.getId());
modMailSubscriberRepository.save(subscriber); return modMailSubscriberRepository.save(subscriber);
return subscriber;
} }
@Override @Override

View File

@@ -115,8 +115,7 @@ public class ModMailThreadManagementServiceBean implements ModMailThreadManageme
log.info("Create modmail thread in channel {} for user {} in server {}.", log.info("Create modmail thread in channel {} for user {} in server {}.",
channel.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId()); channel.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId());
modMailThreadRepository.save(thread); return modMailThreadRepository.save(thread);
return thread;
} }
@Override @Override

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.model.database.ModMailMessage; import dev.sheldan.abstracto.modmail.model.database.ModMailMessage;
@@ -60,6 +61,9 @@ public class ModMailMessageDeletedListenerTest {
@Mock @Mock
private AChannel channel; private AChannel channel;
@Mock
private MessageDeletedModel model;
private static final Long DELETED_MESSAGE_ID = 4L; private static final Long DELETED_MESSAGE_ID = 4L;
private static final Long CREATED_MESSAGE_ID_1 = 3L; private static final Long CREATED_MESSAGE_ID_1 = 3L;
private static final Long CREATED_MESSAGE_ID_2 = 5L; private static final Long CREATED_MESSAGE_ID_2 = 5L;
@@ -71,7 +75,8 @@ public class ModMailMessageDeletedListenerTest {
public void testDeleteOutSideOfThread() { public void testDeleteOutSideOfThread() {
when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_ID); when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(DELETED_MESSAGE_ID)).thenReturn(Optional.empty()); when(modMailMessageManagementService.getByMessageIdOptional(DELETED_MESSAGE_ID)).thenReturn(Optional.empty());
testUnit.execute(deletedMessage); when(model.getCachedMessage()).thenReturn(deletedMessage);
testUnit.execute(model);
verify(memberService, times(0)).getMemberInServerAsync(anyLong(), anyLong()); verify(memberService, times(0)).getMemberInServerAsync(anyLong(), anyLong());
} }
@@ -94,7 +99,8 @@ public class ModMailMessageDeletedListenerTest {
when(targetMember.getUser()).thenReturn(targetUser); when(targetMember.getUser()).thenReturn(targetUser);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember)); when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(messageService.deleteMessageInChannelWithUser(targetUser, CREATED_MESSAGE_ID_2)).thenReturn(CompletableFuture.completedFuture(null)); when(messageService.deleteMessageInChannelWithUser(targetUser, CREATED_MESSAGE_ID_2)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.execute(deletedMessage); when(model.getCachedMessage()).thenReturn(deletedMessage);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessageInChannelInServer(eq(SERVER_ID), anyLong(), any()); verify(messageService, times(0)).deleteMessageInChannelInServer(eq(SERVER_ID), anyLong(), any());
verify(self, times(1)).removeMessageFromThread(DELETED_MESSAGE_ID); verify(self, times(1)).removeMessageFromThread(DELETED_MESSAGE_ID);
} }
@@ -121,7 +127,8 @@ public class ModMailMessageDeletedListenerTest {
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember)); when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(messageService.deleteMessageInChannelWithUser(targetUser, CREATED_MESSAGE_ID_2)).thenReturn(CompletableFuture.completedFuture(null)); when(messageService.deleteMessageInChannelWithUser(targetUser, CREATED_MESSAGE_ID_2)).thenReturn(CompletableFuture.completedFuture(null));
when(messageService.deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, CREATED_MESSAGE_ID_1)).thenReturn(CompletableFuture.completedFuture(null)); when(messageService.deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, CREATED_MESSAGE_ID_1)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.execute(deletedMessage); when(model.getCachedMessage()).thenReturn(deletedMessage);
testUnit.execute(model);
verify(self, times(1)).removeMessageFromThread(DELETED_MESSAGE_ID); verify(self, times(1)).removeMessageFromThread(DELETED_MESSAGE_ID);
} }

View File

@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageTextUpdatedModel;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
@@ -72,7 +73,7 @@ public class ModMailMessageEditedListenerTest {
private CachedMessage messageBefore; private CachedMessage messageBefore;
@Mock @Mock
private CachedMessage messageAfter; private Message messageAfter;
@Mock @Mock
private Message loadedMessage; private Message loadedMessage;
@@ -101,6 +102,9 @@ public class ModMailMessageEditedListenerTest {
@Captor @Captor
private ArgumentCaptor<ModMailModeratorReplyModel> replyModelArgumentCaptor; private ArgumentCaptor<ModMailModeratorReplyModel> replyModelArgumentCaptor;
@Mock
private MessageTextUpdatedModel model;
private static final Long CHANNEL_ID = 5L; private static final Long CHANNEL_ID = 5L;
private static final Long MESSAGE_ID = 6L; private static final Long MESSAGE_ID = 6L;
private static final Long CREATED_MESSAGE_ID = 10L; private static final Long CREATED_MESSAGE_ID = 10L;
@@ -111,33 +115,26 @@ public class ModMailMessageEditedListenerTest {
private static final Long USER_ID = 3L; private static final Long USER_ID = 3L;
private static final Long AUTHOR_USER_ID = 9L; private static final Long AUTHOR_USER_ID = 9L;
@Test
public void testMessageLoading() {
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(messageService.loadMessageFromCachedMessage(messageAfter)).thenReturn(CompletableFuture.completedFuture(loadedMessage));
testUnit.execute(messageBefore, messageAfter);
verify(self, times(1)).executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage);
}
@Test @Test
public void testEditOutsideModMailThread() { public void testEditOutsideModMailThread() {
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(false); when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(false);
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID); when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
testUnit.execute(messageBefore, messageAfter); when(model.getAfter()).thenReturn(messageAfter);
when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(modMailMessageManagementService, times(0)).getByMessageIdOptional(anyLong()); verify(modMailMessageManagementService, times(0)).getByMessageIdOptional(anyLong());
} }
@Test
public void testEditNotTrackedMessage() {
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.empty());
testUnit.executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage);
verify(commandRegistry, times(0)).getCommandName(anyString(), anyLong());
}
@Test @Test
public void testEditMessageWithCorrectCommand() { public void testEditMessageWithCorrectCommand() {
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(model.getBefore()).thenReturn(messageBefore);
when(model.getAfter()).thenReturn(messageAfter);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.empty());
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID); when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID); when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(messageBefore.getServerId()).thenReturn(SERVER_ID); when(messageBefore.getServerId()).thenReturn(SERVER_ID);
@@ -154,14 +151,16 @@ public class ModMailMessageEditedListenerTest {
AUser authorUser = Mockito.mock(AUser.class); AUser authorUser = Mockito.mock(AUser.class);
when(authorUser.getId()).thenReturn(AUTHOR_USER_ID); when(authorUser.getId()).thenReturn(AUTHOR_USER_ID);
when(authorUserInAServer.getUserReference()).thenReturn(authorUser); when(authorUserInAServer.getUserReference()).thenReturn(authorUser);
when(messageAfter.getContent()).thenReturn(NEW_CONTENT); when(messageAfter.getContentStripped()).thenReturn(NEW_CONTENT);
when(commandRegistry.getCommandName(NEW_COMMAND_PART, SERVER_ID)).thenReturn(NEW_COMMAND_PART); when(commandRegistry.getCommandName(NEW_COMMAND_PART, SERVER_ID)).thenReturn(NEW_COMMAND_PART);
when(commandService.doesCommandExist(NEW_COMMAND_PART)).thenReturn(true); when(commandService.doesCommandExist(NEW_COMMAND_PART)).thenReturn(true);
when(commandService.getParametersForCommand(NEW_COMMAND_PART, loadedMessage)).thenReturn(CompletableFuture.completedFuture(parsedParameters)); when(commandService.getParametersForCommand(NEW_COMMAND_PART, messageAfter)).thenReturn(CompletableFuture.completedFuture(parsedParameters));
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember)); when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember)); when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
testUnit.executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage); when(model.getAfter()).thenReturn(messageAfter);
verify(self, times(1)).updateMessageInThread(loadedMessage, parsedParameters, targetMember, authorMember); when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
} }
@Test @Test
@@ -169,6 +168,7 @@ public class ModMailMessageEditedListenerTest {
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID); when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID); when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(messageBefore.getServerId()).thenReturn(SERVER_ID); when(messageBefore.getServerId()).thenReturn(SERVER_ID);
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage)); when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
ModMailThread thread = Mockito.mock(ModMailThread.class); ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread); when(modMailMessage.getThreadReference()).thenReturn(thread);
@@ -182,14 +182,16 @@ public class ModMailMessageEditedListenerTest {
AUser authorUser = Mockito.mock(AUser.class); AUser authorUser = Mockito.mock(AUser.class);
when(authorUser.getId()).thenReturn(AUTHOR_USER_ID); when(authorUser.getId()).thenReturn(AUTHOR_USER_ID);
when(authorUserInAServer.getUserReference()).thenReturn(authorUser); when(authorUserInAServer.getUserReference()).thenReturn(authorUser);
when(messageAfter.getContent()).thenReturn(NEW_CONTENT); when(messageAfter.getContentStripped()).thenReturn(NEW_CONTENT);
when(commandRegistry.getCommandName(NEW_COMMAND_PART, SERVER_ID)).thenReturn(NEW_COMMAND_PART); when(commandRegistry.getCommandName(NEW_COMMAND_PART, SERVER_ID)).thenReturn(NEW_COMMAND_PART);
when(commandService.doesCommandExist(NEW_COMMAND_PART)).thenReturn(false); when(commandService.doesCommandExist(NEW_COMMAND_PART)).thenReturn(false);
when(commandService.getParametersForCommand(DEFAULT_COMMAND_FOR_MODMAIL_EDIT, loadedMessage)).thenReturn(CompletableFuture.completedFuture(parsedParameters)); when(commandService.getParametersForCommand(DEFAULT_COMMAND_FOR_MODMAIL_EDIT, messageAfter)).thenReturn(CompletableFuture.completedFuture(parsedParameters));
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember)); when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember)); when(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
testUnit.executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage); when(model.getAfter()).thenReturn(messageAfter);
verify(self, times(1)).updateMessageInThread(loadedMessage, parsedParameters, targetMember, authorMember); when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
} }
@Test @Test

View File

@@ -35,8 +35,7 @@ public class ReminderManagementServiceBean implements ReminderManagementService
log.info("Creating reminder for user {} in server {} in message {} to be reminded at {}.", log.info("Creating reminder for user {} in server {} in message {} to be reminded at {}.",
userToBeReminded.getAUserInAServer().getUserReference().getId(), userToBeReminded.getGuild().getId(), messageId, timeToBeRemindedAt); userToBeReminded.getAUserInAServer().getUserReference().getId(), userToBeReminded.getGuild().getId(), messageId, timeToBeRemindedAt);
reminderRepository.save(reminder); return reminderRepository.save(reminder);
return reminder;
} }
@Override @Override

View File

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.sync.entity.AsyncChannelGroupCreatedListener;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.listener.ChannelGroupCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean;
import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RepostCheckAsyncChannelGroupCreatedListener implements AsyncChannelGroupCreatedListener {
@Autowired
private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Autowired
private ChannelGroupManagementService channelGroupManagementService;
@Override
public DefaultListenerResult execute(ChannelGroupCreatedListenerModel model) {
AChannelGroup channelGroup = channelGroupManagementService.findChannelGroupById(model.getChannelGroupId());
if(channelGroup.getChannelGroupType().getGroupTypeKey().equals(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE)) {
checkChannelGroupManagement.createRepostCheckChannelGroup(channelGroup);
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
}

View File

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.sync.entity.AsyncChannelGroupDeletedListener;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.listener.ChannelGroupDeletedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean;
import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RepostCheckAsyncChannelGroupDeletedListener implements AsyncChannelGroupDeletedListener {
@Autowired
private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Autowired
private ChannelGroupManagementService channelGroupManagementService;
@Override
public DefaultListenerResult execute(ChannelGroupDeletedListenerModel model) {
AChannelGroup channelGroup = channelGroupManagementService.findChannelGroupById(model.getChannelGroupId());
if(channelGroup.getChannelGroupType().getGroupTypeKey().equals(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE)) {
checkChannelGroupManagement.deleteRepostCheckChannelGroup(channelGroup);
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
}

View File

@@ -1,22 +0,0 @@
package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.listener.sync.entity.ChannelGroupCreatedListener;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean;
import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RepostCheckChannelGroupCreatedListener implements ChannelGroupCreatedListener {
@Autowired
private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Override
public void channelGroupCreated(AChannelGroup channelGroup) {
if(channelGroup.getChannelGroupType().getGroupTypeKey().equals(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE)) {
checkChannelGroupManagement.createRepostCheckChannelGroup(channelGroup);
}
}
}

View File

@@ -1,22 +0,0 @@
package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.listener.sync.entity.ChannelGroupDeletedListener;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean;
import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RepostCheckChannelGroupDeletedListener implements ChannelGroupDeletedListener {
@Autowired
private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Override
public void channelGroupDeleted(AChannelGroup channelGroup) {
if(channelGroup.getChannelGroupType().getGroupTypeKey().equals(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE)) {
checkChannelGroupManagement.deleteRepostCheckChannelGroup(channelGroup);
}
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.repostdetection.listener; package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageEmbeddedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageEmbeddedListener;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel; import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
@@ -39,13 +40,13 @@ public class RepostEmbedListener implements AsyncMessageEmbeddedListener {
private ChannelService channelService; private ChannelService channelService;
@Override @Override
public void execute(GuildMessageEmbedEventModel eventModel) { public DefaultListenerResult execute(GuildMessageEmbedEventModel eventModel) {
AChannel channel = channelManagementService.loadChannel(eventModel.getChannelId()); AChannel channel = channelManagementService.loadChannel(eventModel.getChannelId());
if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) { if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) {
if(repostManagement.messageEmbedsHaveBeenCovered(eventModel.getMessageId())) { if(repostManagement.messageEmbedsHaveBeenCovered(eventModel.getMessageId())) {
log.info("The embeds of the message {} in channel {} in server {} have already been covered by repost check -- ignoring.", log.info("The embeds of the message {} in channel {} in server {} have already been covered by repost check -- ignoring.",
eventModel.getMessageId(), eventModel.getChannelId(), eventModel.getServerId()); eventModel.getMessageId(), eventModel.getChannelId(), eventModel.getServerId());
return; return DefaultListenerResult.IGNORED;
} }
channelService.retrieveMessageInChannel(eventModel.getServerId(), eventModel.getChannelId(), eventModel.getMessageId()).thenAccept(message -> { channelService.retrieveMessageInChannel(eventModel.getServerId(), eventModel.getChannelId(), eventModel.getMessageId()).thenAccept(message -> {
List<MessageEmbed> imageEmbeds = eventModel.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList()); List<MessageEmbed> imageEmbeds = eventModel.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList());
@@ -54,6 +55,7 @@ public class RepostEmbedListener implements AsyncMessageEmbeddedListener {
} }
}); });
} }
return DefaultListenerResult.PROCESSED;
} }
@Override @Override

View File

@@ -1,16 +1,18 @@
package dev.sheldan.abstracto.repostdetection.listener; package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener; 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.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService; import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.repostdetection.config.RepostDetectionFeatureDefinition; import dev.sheldan.abstracto.repostdetection.config.RepostDetectionFeatureDefinition;
import dev.sheldan.abstracto.repostdetection.service.RepostCheckChannelService; import dev.sheldan.abstracto.repostdetection.service.RepostCheckChannelService;
import dev.sheldan.abstracto.repostdetection.service.RepostService; import dev.sheldan.abstracto.repostdetection.service.RepostService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.EmbedType; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -32,13 +34,16 @@ public class RepostMessageReceivedListener implements AsyncMessageReceivedListen
private ChannelManagementService channelManagementService; private ChannelManagementService channelManagementService;
@Override @Override
public void execute(CachedMessage message) { public DefaultListenerResult execute(MessageReceivedModel model) {
AChannel channel = channelManagementService.loadChannel(message.getChannelId()); Message message = model.getMessage();
AChannel channel = channelManagementService.loadChannel(message.getTextChannel().getIdLong());
if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) { if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) {
repostService.processMessageAttachmentRepostCheck(message); repostService.processMessageAttachmentRepostCheck(message);
List<CachedEmbed> imageEmbeds = message.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList()); List<MessageEmbed> imageEmbeds = message.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList());
repostService.processMessageEmbedsRepostCheck(imageEmbeds, message); repostService.processMessageEmbedsRepostCheck(imageEmbeds, message);
return DefaultListenerResult.PROCESSED;
} }
return DefaultListenerResult.IGNORED;
} }
@Override @Override

View File

@@ -172,6 +172,18 @@ public class RepostServiceBean implements RepostService {
return checkForDuplicates(serverChannelMessageUser, index, attachment.getProxyUrl()); return checkForDuplicates(serverChannelMessageUser, index, attachment.getProxyUrl());
} }
@Override
public Optional<PostedImage> getRepostFor(Message message, Message.Attachment attachment, Integer index) {
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
.builder()
.serverId(message.getGuild().getIdLong())
.channelId(message.getChannel().getIdLong())
.userId(message.getAuthor().getIdLong())
.messageId(message.getIdLong())
.build();
return checkForDuplicates(serverChannelMessageUser, index, attachment.getProxyUrl());
}
@Override @Override
public String calculateHashForPost(String url, Long serverId) { public String calculateHashForPost(String url, Long serverId) {
File downloadedFile = null; File downloadedFile = null;
@@ -204,6 +216,14 @@ public class RepostServiceBean implements RepostService {
} }
} }
@Override
public void processMessageAttachmentRepostCheck(Message 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(CachedMessage message, CachedAttachment attachment, Integer index, boolean moreRepostsPossible) { private void executeRepostCheckForAttachment(CachedMessage message, CachedAttachment attachment, Integer index, boolean moreRepostsPossible) {
Optional<PostedImage> originalPostOptional = getRepostFor(message, attachment, index); Optional<PostedImage> originalPostOptional = getRepostFor(message, attachment, index);
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
@@ -216,6 +236,18 @@ public class RepostServiceBean implements RepostService {
originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(serverChannelMessageUser, index, moreRepostsPossible, postedImage)); originalPostOptional.ifPresent(postedImage -> markMessageAndPersist(serverChannelMessageUser, index, moreRepostsPossible, postedImage));
} }
private void executeRepostCheckForAttachment(Message message, Message.Attachment attachment, Integer index, boolean moreRepostsPossible) {
Optional<PostedImage> originalPostOptional = getRepostFor(message, attachment, index);
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));
}
private void markMessageAndPersist(ServerChannelMessageUser messageUser, Integer index, boolean moreRepostsPossible, PostedImage originalPost) { 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()); log.info("Detected repost in message embed {} of message {} in channel {} in server {}.", index, messageUser.getMessageId(), messageUser.getChannelId(), messageUser.getServerId());
CompletableFuture<Void> markerFuture = reactionService.addReactionToMessageAsync(REPOST_MARKER_EMOTE_KEY, messageUser.getServerId(), messageUser.getChannelId(), messageUser.getMessageId()); CompletableFuture<Void> markerFuture = reactionService.addReactionToMessageAsync(REPOST_MARKER_EMOTE_KEY, messageUser.getServerId(), messageUser.getChannelId(), messageUser.getMessageId());

View File

@@ -34,8 +34,7 @@ public class PostedImageManagementBean implements PostedImageManagement {
.postedChannel(creation.getChannel()) .postedChannel(creation.getChannel())
.build(); .build();
postedImageRepository.save(post); return postedImageRepository.save(post);
return post;
} }
@Override @Override

View File

@@ -49,8 +49,7 @@ public class RepostCheckChannelGroupManagementBean implements RepostCheckChannel
.id(channelGroup.getId()) .id(channelGroup.getId())
.build(); .build();
repository.save(repostCheckChannelGroup); return repository.save(repostCheckChannelGroup);
return repostCheckChannelGroup;
} }
@Override @Override

View File

@@ -32,8 +32,7 @@ public class RepostManagementServiceBean implements RepostManagementService {
.count(1) .count(1)
.build(); .build();
repostRepository.save(repost); return repostRepository.save(repost);
return repost;
} }
@Override @Override

View File

@@ -1,7 +1,10 @@
package dev.sheldan.abstracto.repostdetection.listener; package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.command.exception.ChannelGroupNotFoundByIdException;
import dev.sheldan.abstracto.core.models.database.AChannelGroup; import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType; import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.models.listener.ChannelGroupCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean; import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean;
import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement; import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement;
import org.junit.Test; import org.junit.Test;
@@ -13,35 +16,53 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class RepostCheckChannelGroupCreatedListenerTest { public class RepostCheckAsyncChannelGroupCreatedListenerTest {
@InjectMocks @InjectMocks
private RepostCheckChannelGroupCreatedListener testUnit; private RepostCheckAsyncChannelGroupCreatedListener testUnit;
@Mock @Mock
private RepostCheckChannelGroupManagement checkChannelGroupManagement; private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Mock
private ChannelGroupManagementService channelGroupManagementService;
@Mock @Mock
private AChannelGroup channelGroup; private AChannelGroup channelGroup;
@Mock
private ChannelGroupCreatedListenerModel model;
@Mock @Mock
private ChannelGroupType channelGroupType; private ChannelGroupType channelGroupType;
private static final String INCORRECT_TYPE = "incorrectType"; private static final String INCORRECT_TYPE = "incorrectType";
private static final Long CHANNEL_GROUP_ID = 1L;
@Test @Test
public void testChannelGroupCreated() { public void testChannelGroupCreated() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType); when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
when(channelGroupType.getGroupTypeKey()).thenReturn(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE); when(channelGroupType.getGroupTypeKey()).thenReturn(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE);
testUnit.channelGroupCreated(channelGroup); when(model.getChannelGroupId()).thenReturn(CHANNEL_GROUP_ID);
when(channelGroupManagementService.findChannelGroupById(CHANNEL_GROUP_ID)).thenReturn(channelGroup);
testUnit.execute(model);
verify(checkChannelGroupManagement, times(1)).createRepostCheckChannelGroup(channelGroup); verify(checkChannelGroupManagement, times(1)).createRepostCheckChannelGroup(channelGroup);
} }
@Test(expected = ChannelGroupNotFoundByIdException.class)
public void testChannelGroupNotExisting() {
when(model.getChannelGroupId()).thenReturn(CHANNEL_GROUP_ID);
when(channelGroupManagementService.findChannelGroupById(CHANNEL_GROUP_ID)).thenThrow(new ChannelGroupNotFoundByIdException());
testUnit.execute(model);
}
@Test @Test
public void testChannelGroupCreatedIncorrectType() { public void testChannelGroupCreatedIncorrectType() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType); when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
when(channelGroupType.getGroupTypeKey()).thenReturn(INCORRECT_TYPE); when(channelGroupType.getGroupTypeKey()).thenReturn(INCORRECT_TYPE);
testUnit.channelGroupCreated(channelGroup); when(model.getChannelGroupId()).thenReturn(CHANNEL_GROUP_ID);
when(channelGroupManagementService.findChannelGroupById(CHANNEL_GROUP_ID)).thenReturn(channelGroup);
testUnit.execute(model);
verify(checkChannelGroupManagement, times(0)).createRepostCheckChannelGroup(channelGroup); verify(checkChannelGroupManagement, times(0)).createRepostCheckChannelGroup(channelGroup);
} }
} }

View File

@@ -1,7 +1,10 @@
package dev.sheldan.abstracto.repostdetection.listener; package dev.sheldan.abstracto.repostdetection.listener;
import dev.sheldan.abstracto.core.command.exception.ChannelGroupNotFoundByIdException;
import dev.sheldan.abstracto.core.models.database.AChannelGroup; import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType; import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.models.listener.ChannelGroupDeletedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean; import dev.sheldan.abstracto.repostdetection.service.RepostServiceBean;
import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement; import dev.sheldan.abstracto.repostdetection.service.management.RepostCheckChannelGroupManagement;
import org.junit.Test; import org.junit.Test;
@@ -13,35 +16,53 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class RepostCheckChannelGroupDeletedListenerTest { public class RepostCheckAsyncChannelGroupDeletedListenerTest {
@InjectMocks @InjectMocks
private RepostCheckChannelGroupDeletedListener testUnit; private RepostCheckAsyncChannelGroupDeletedListener testUnit;
@Mock @Mock
private RepostCheckChannelGroupManagement checkChannelGroupManagement; private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Mock
private ChannelGroupManagementService channelGroupManagementService;
@Mock @Mock
private AChannelGroup channelGroup; private AChannelGroup channelGroup;
@Mock @Mock
private ChannelGroupType channelGroupType; private ChannelGroupType channelGroupType;
@Mock
private ChannelGroupDeletedListenerModel model;
private static final String INCORRECT_TYPE = "incorrectType"; private static final String INCORRECT_TYPE = "incorrectType";
private static final Long CHANNEL_GROUP_ID = 1L;
@Test @Test
public void testChannelGroupDeleted() { public void testChannelGroupDeleted() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType); when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
when(channelGroupType.getGroupTypeKey()).thenReturn(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE); when(channelGroupType.getGroupTypeKey()).thenReturn(RepostServiceBean.REPOST_CHECK_CHANNEL_GROUP_TYPE);
testUnit.channelGroupDeleted(channelGroup); when(model.getChannelGroupId()).thenReturn(CHANNEL_GROUP_ID);
when(channelGroupManagementService.findChannelGroupById(CHANNEL_GROUP_ID)).thenReturn(channelGroup);
testUnit.execute(model);
verify(checkChannelGroupManagement, times(1)).deleteRepostCheckChannelGroup(channelGroup); verify(checkChannelGroupManagement, times(1)).deleteRepostCheckChannelGroup(channelGroup);
} }
@Test(expected = ChannelGroupNotFoundByIdException.class)
public void testChannelGroupNotExisting() {
when(model.getChannelGroupId()).thenReturn(CHANNEL_GROUP_ID);
when(channelGroupManagementService.findChannelGroupById(CHANNEL_GROUP_ID)).thenThrow(new ChannelGroupNotFoundByIdException());
testUnit.execute(model);
}
@Test @Test
public void testChannelGroupDeletedIncorrectType() { public void testChannelGroupDeletedIncorrectType() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType); when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
when(channelGroupType.getGroupTypeKey()).thenReturn(INCORRECT_TYPE); when(channelGroupType.getGroupTypeKey()).thenReturn(INCORRECT_TYPE);
testUnit.channelGroupDeleted(channelGroup); when(model.getChannelGroupId()).thenReturn(CHANNEL_GROUP_ID);
when(channelGroupManagementService.findChannelGroupById(CHANNEL_GROUP_ID)).thenReturn(channelGroup);
testUnit.execute(model);
verify(checkChannelGroupManagement, times(0)).deleteRepostCheckChannelGroup(channelGroup); verify(checkChannelGroupManagement, times(0)).deleteRepostCheckChannelGroup(channelGroup);
} }
} }

View File

@@ -1,12 +1,14 @@
package dev.sheldan.abstracto.repostdetection.listener; package dev.sheldan.abstracto.repostdetection.listener;
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.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService; import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.repostdetection.service.RepostCheckChannelService; import dev.sheldan.abstracto.repostdetection.service.RepostCheckChannelService;
import dev.sheldan.abstracto.repostdetection.service.RepostService; import dev.sheldan.abstracto.repostdetection.service.RepostService;
import net.dv8tion.jda.api.entities.EmbedType; import net.dv8tion.jda.api.entities.EmbedType;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.TextChannel;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -34,27 +36,33 @@ public class RepostMessageReceivedListenerTest {
private RepostService repostService; private RepostService repostService;
@Mock @Mock
private CachedMessage message; private Message message;
@Mock @Mock
private AChannel channel; private AChannel channel;
@Mock
private MessageReceivedModel model;
@Mock
private TextChannel textChannel;
@Captor @Captor
private ArgumentCaptor<List<CachedEmbed>> embedListCaptor; private ArgumentCaptor<List<MessageEmbed>> embedListCaptor;
private static final Long CHANNEL_ID = 4L; private static final Long CHANNEL_ID = 4L;
@Test @Test
public void testExecuteCheckDisabled() { public void testExecuteCheckDisabled() {
setupRepostCheckEnabled(false); setupRepostCheckEnabled(false);
testUnit.execute(message); testUnit.execute(model);
verify(repostService, times(0)).processMessageAttachmentRepostCheck(message); verify(repostService, times(0)).processMessageAttachmentRepostCheck(message);
} }
@Test @Test
public void testExecuteOnlyMessage() { public void testExecuteOnlyMessage() {
setupRepostCheckEnabled(true); setupRepostCheckEnabled(true);
testUnit.execute(message); testUnit.execute(model);
verify(repostService, times(1)).processMessageAttachmentRepostCheck(message); verify(repostService, times(1)).processMessageAttachmentRepostCheck(message);
verify(repostService, times(1)).processMessageEmbedsRepostCheck(embedListCaptor.capture(), eq(message)); verify(repostService, times(1)).processMessageEmbedsRepostCheck(embedListCaptor.capture(), eq(message));
Assert.assertEquals(0, embedListCaptor.getValue().size()); Assert.assertEquals(0, embedListCaptor.getValue().size());
@@ -63,35 +71,38 @@ public class RepostMessageReceivedListenerTest {
@Test @Test
public void testExecuteOnlyMessageOneImageAttachment() { public void testExecuteOnlyMessageOneImageAttachment() {
setupRepostCheckEnabled(true); setupRepostCheckEnabled(true);
CachedEmbed imageEmbed = Mockito.mock(CachedEmbed.class); MessageEmbed imageEmbed = Mockito.mock(MessageEmbed.class);
when(imageEmbed.getType()).thenReturn(EmbedType.IMAGE); when(imageEmbed.getType()).thenReturn(EmbedType.IMAGE);
when(message.getEmbeds()).thenReturn(Arrays.asList(imageEmbed)); when(message.getEmbeds()).thenReturn(Arrays.asList(imageEmbed));
testUnit.execute(message); testUnit.execute(model);
verifySingleEmbed(imageEmbed); verifySingleEmbed(imageEmbed);
} }
@Test @Test
public void testExecuteOnlyMessageTwoEmbedsOneImageAttachment() { public void testExecuteOnlyMessageTwoEmbedsOneImageAttachment() {
setupRepostCheckEnabled(true); setupRepostCheckEnabled(true);
CachedEmbed imageEmbed = Mockito.mock(CachedEmbed.class); MessageEmbed imageEmbed = Mockito.mock(MessageEmbed.class);
when(imageEmbed.getType()).thenReturn(EmbedType.IMAGE); when(imageEmbed.getType()).thenReturn(EmbedType.IMAGE);
CachedEmbed nonImageEmbed = Mockito.mock(CachedEmbed.class); MessageEmbed nonImageEmbed = Mockito.mock(MessageEmbed.class);
when(nonImageEmbed.getType()).thenReturn(EmbedType.LINK); when(nonImageEmbed.getType()).thenReturn(EmbedType.LINK);
when(message.getEmbeds()).thenReturn(Arrays.asList(imageEmbed, nonImageEmbed)); when(message.getEmbeds()).thenReturn(Arrays.asList(imageEmbed, nonImageEmbed));
testUnit.execute(message); when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verifySingleEmbed(imageEmbed); verifySingleEmbed(imageEmbed);
} }
private void setupRepostCheckEnabled(boolean b) { private void setupRepostCheckEnabled(boolean b) {
when(message.getChannelId()).thenReturn(CHANNEL_ID); when(message.getTextChannel()).thenReturn(textChannel);
when(model.getMessage()).thenReturn(message);
when(textChannel.getIdLong()).thenReturn(CHANNEL_ID);
when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(channel); when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(channel);
when(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)).thenReturn(b); when(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)).thenReturn(b);
} }
private void verifySingleEmbed(CachedEmbed imageEmbed) { private void verifySingleEmbed(MessageEmbed imageEmbed) {
verify(repostService, times(1)).processMessageAttachmentRepostCheck(message); verify(repostService, times(1)).processMessageAttachmentRepostCheck(message);
verify(repostService, times(1)).processMessageEmbedsRepostCheck(embedListCaptor.capture(), eq(message)); verify(repostService, times(1)).processMessageEmbedsRepostCheck(embedListCaptor.capture(), eq(message));
List<CachedEmbed> processedEmbeds = embedListCaptor.getValue(); List<MessageEmbed> processedEmbeds = embedListCaptor.getValue();
Assert.assertEquals(1, processedEmbeds.size()); Assert.assertEquals(1, processedEmbeds.size());
Assert.assertEquals(imageEmbed, processedEmbeds.get(0)); Assert.assertEquals(imageEmbed, processedEmbeds.get(0));
} }

View File

@@ -20,8 +20,10 @@ public interface RepostService {
Optional<PostedImage> getRepostFor(Message message, MessageEmbed messageEmbed, Integer embedIndex); Optional<PostedImage> getRepostFor(Message message, MessageEmbed messageEmbed, Integer embedIndex);
boolean isRepost(CachedMessage message, CachedAttachment attachment, Integer index); boolean isRepost(CachedMessage message, CachedAttachment attachment, Integer index);
Optional<PostedImage> getRepostFor(CachedMessage message, CachedAttachment attachment, Integer index); Optional<PostedImage> getRepostFor(CachedMessage message, CachedAttachment attachment, Integer index);
Optional<PostedImage> getRepostFor(Message message, Message.Attachment attachment, Integer index);
String calculateHashForPost(String url, Long serverId); String calculateHashForPost(String url, Long serverId);
void processMessageAttachmentRepostCheck(CachedMessage message); void processMessageAttachmentRepostCheck(CachedMessage message);
void processMessageAttachmentRepostCheck(Message message);
void processMessageEmbedsRepostCheck(List<CachedEmbed> embeds, CachedMessage message); void processMessageEmbedsRepostCheck(List<CachedEmbed> embeds, CachedMessage message);
void processMessageEmbedsRepostCheck(List<MessageEmbed> embeds, Message message); void processMessageEmbedsRepostCheck(List<MessageEmbed> embeds, Message message);
CompletableFuture<List<RepostLeaderboardEntryModel>> retrieveRepostLeaderboard(Guild guild, Integer page); CompletableFuture<List<RepostLeaderboardEntryModel>> retrieveRepostLeaderboard(Guild guild, Integer page);

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.starboard.config;
import dev.sheldan.abstracto.core.service.ExecutorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
@Configuration
public class StarboardListenerConfig {
@Autowired
private ExecutorService executorService;
@Bean(name = "starboardStatusListenerExecutor")
public TaskExecutor joinListenerExecutor() {
return executorService.setupExecutorFor("starboardStatusListener");
}
}

View File

@@ -0,0 +1,59 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Optional;
@Component
@Slf4j
public class StarAddedListener extends StarboardListener implements AsyncReactionAddedListener {
protected static final CounterMetric STARBOARD_STARS_ADDED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "added")))
.build();
@Override
public DefaultListenerResult execute(ReactionAddedModel model) {
if(model.getUserReacting().getUserId().equals(model.getMessage().getAuthor().getAuthorId())) {
return DefaultListenerResult.IGNORED;
}
Long serverId = model.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId);
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_ADDED);
log.info("User {} in server {} reacted with star to put a message {} from channel {} on starboard.",
model.getUserReacting().getUserId(), model.getServerId(), model.getMessage().getMessageId(), model.getMessage().getChannelId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(model.getMessage(), aEmote);
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), model.getUserReacting(), true);
return DefaultListenerResult.PROCESSED;
} else {
return DefaultListenerResult.IGNORED;
}
}
@PostConstruct
@Override
public void postConstruct() {
metricService.registerCounter(STARBOARD_STARS_ADDED, "Star reaction added");
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
}

View File

@@ -0,0 +1,39 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionClearedListener;
import dev.sheldan.abstracto.core.models.listener.ReactionClearedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class StarClearedListener extends StarboardListener implements AsyncReactionClearedListener {
@Override
public DefaultListenerResult execute(ReactionClearedModel model) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(model.getMessage().getMessageId());
if(starboardPostOptional.isPresent()) {
starboardPostOptional.ifPresent(starboardPost -> {
log.info("Reactions on message {} in channel {} in server {} were cleared. Completely deleting the starboard post {}.",
model.getMessage().getMessageId(), model.getMessage().getChannelId(), model.getServerId(), starboardPost.getId());
starboardPostReactorManagementService.removeReactors(starboardPost);
completelyRemoveStarboardPost(starboardPost, null);
});
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
}

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.listener.ReactionRemovedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Optional;
@Component
@Slf4j
public class StarRemovedListener extends StarboardListener implements AsyncReactionRemovedListener {
protected static final CounterMetric STARBOARD_STARS_REMOVED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "removed")))
.build();
@Override
@Transactional
public DefaultListenerResult execute(ReactionRemovedModel model) {
ServerUser userRemoving = model.getUserRemoving();
if(model.getMessage().getAuthor().getAuthorId().equals(userRemoving.getUserId())) {
return DefaultListenerResult.IGNORED;
}
Long guildId = model.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, guildId);
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_REMOVED);
log.info("User {} in server {} removed star reaction from message {} on starboard.",
userRemoving.getUserId(), model.getServerId(), model.getMessage().getMessageId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(model.getMessage(), aEmote);
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), userRemoving, false);
return DefaultListenerResult.PROCESSED;
} else {
return DefaultListenerResult.IGNORED;
}
}
@PostConstruct
@Override
public void postConstruct() {
metricService.registerCounter(STARBOARD_STARS_REMOVED, "Star reaction removed");
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
}

View File

@@ -1,23 +1,15 @@
package dev.sheldan.abstracto.starboard.listener; package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
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.metric.service.CounterMetric; import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag; import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions; 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.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService; import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost; import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService; import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
@@ -25,7 +17,6 @@ import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorMa
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.Arrays; import java.util.Arrays;
@@ -36,84 +27,54 @@ import java.util.stream.Collectors;
@Component @Component
@Slf4j @Slf4j
public class StarboardListener implements AsyncReactionAddedListener, AsyncReactionRemovedListener, AsyncReactionClearedListener { public abstract class StarboardListener {
public static final String FIRST_LEVEL_THRESHOLD_KEY = "starLvl1"; public static final String FIRST_LEVEL_THRESHOLD_KEY = "starLvl1";
@Autowired @Autowired
private BotService botService; protected ConfigManagementService configManagementService;
@Autowired @Autowired
private ConfigManagementService configManagementService; protected StarboardService starboardService;
@Autowired @Autowired
private StarboardService starboardService; protected StarboardPostManagementService starboardPostManagementService;
@Autowired @Autowired
private StarboardPostManagementService starboardPostManagementService; protected StarboardPostReactorManagementService starboardPostReactorManagementService;
@Autowired @Autowired
private StarboardPostReactorManagementService starboardPostReactorManagementService; protected UserInServerManagementService userInServerManagementService;
@Autowired @Autowired
private UserInServerManagementService userInServerManagementService; protected EmoteService emoteService;
@Autowired @Autowired
private EmoteService emoteService; protected MetricService metricService;
@Autowired
private MetricService metricService;
public static final String STARBOARD_STARS = "starboard.stars"; public static final String STARBOARD_STARS = "starboard.stars";
public static final String STARBOARD_POSTS = "starboard.posts"; public static final String STARBOARD_POSTS = "starboard.posts";
public static final String STAR_ACTION = "action"; public static final String STAR_ACTION = "action";
private static final CounterMetric STARBOARD_STARS_ADDED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "added")))
.build();
private static final CounterMetric STARBOARD_STARS_REMOVED = CounterMetric protected static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "removed")))
.build();
private static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
.builder() .builder()
.name(STARBOARD_POSTS) .name(STARBOARD_POSTS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.reached"))) .tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.reached")))
.build(); .build();
private static final CounterMetric STARBOARD_STARS_THRESHOLD_FELL = CounterMetric protected static final CounterMetric STARBOARD_STARS_THRESHOLD_FELL = CounterMetric
.builder() .builder()
.name(STARBOARD_POSTS) .name(STARBOARD_POSTS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.below"))) .tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.below")))
.build(); .build();
@Override protected void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser userReacting, boolean adding) {
@Transactional
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(StarboardFeature.STAR_EMOTE, guildId);
if(emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_ADDED);
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, CachedReactions reaction, ServerUser serverUser, boolean adding) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId()); Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
if(reaction != null) { if(reaction != null) {
AUserInAServer author = userInServerManagementService.loadOrCreateUser(message.getServerId(), message.getAuthor().getAuthorId()); AUserInAServer author = userInServerManagementService.loadOrCreateUser(message.getServerId(), message.getAuthor().getAuthorId());
List<AUserInAServer> userExceptAuthor = getUsersExcept(reaction.getUsers(), author); List<AUserInAServer> userExceptAuthor = getUsersExcept(reaction.getUsers(), author);
Long starMinimum = getFromConfig(FIRST_LEVEL_THRESHOLD_KEY, message.getServerId()); Long starMinimum = getFromConfig(FIRST_LEVEL_THRESHOLD_KEY, message.getServerId());
AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(serverUser); AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(userReacting);
if (userExceptAuthor.size() >= starMinimum) { if (userExceptAuthor.size() >= starMinimum) {
log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.", log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.",
message.getMessageId(), message.getChannelId(), message.getServerId()); message.getMessageId(), message.getChannelId(), message.getServerId());
@@ -122,24 +83,27 @@ public class StarboardListener implements AsyncReactionAddedListener, AsyncReact
} else { } else {
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_REACHED); metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_REACHED);
log.info("Creating starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId()); log.info("Creating starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author); starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author).exceptionally(throwable -> {
log.error("Failed to persist starboard post for message {}.", message.getMessageId(), throwable);
return null;
});
} }
} else { } else {
if(starboardPostOptional.isPresent()) { if(starboardPostOptional.isPresent()) {
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_FELL); metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_FELL);
log.info("Removing starboard post for message {} in channel {} in server {}. It fell under the threshold {}", message.getMessageId(), message.getChannelId(), message.getServerId(), starMinimum); log.info("Removing starboard post for message {} in channel {} in server {}. It fell under the threshold {}", message.getMessageId(), message.getChannelId(), message.getServerId(), starMinimum);
starboardPostOptional.ifPresent(this::completelyRemoveStarboardPost); starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
} }
} }
} else { } else {
if(starboardPostOptional.isPresent()) { if(starboardPostOptional.isPresent()) {
log.info("Removing starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId()); log.info("Removing starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
starboardPostOptional.ifPresent(this::completelyRemoveStarboardPost); starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
} }
} }
} }
private void updateStarboardPost(CachedMessage message, AUserInAServer userReacting, boolean adding, StarboardPost starboardPost, List<AUserInAServer> userExceptAuthor) { protected void updateStarboardPost(CachedMessage message, AUserInAServer userReacting, boolean adding, StarboardPost starboardPost, List<AUserInAServer> userExceptAuthor) {
starboardPost.setIgnored(false); starboardPost.setIgnored(false);
// TODO handle futures correctly // TODO handle futures correctly
starboardService.updateStarboardPost(starboardPost, message, userExceptAuthor); starboardService.updateStarboardPost(starboardPost, message, userExceptAuthor);
@@ -152,62 +116,24 @@ public class StarboardListener implements AsyncReactionAddedListener, AsyncReact
} }
} }
private void completelyRemoveStarboardPost(StarboardPost starboardPost) { protected void completelyRemoveStarboardPost(StarboardPost starboardPost, ServerUser userReacting) {
starboardService.deleteStarboardMessagePost(starboardPost); starboardService.deleteStarboardPost(starboardPost, userReacting);
starboardPostManagementService.removePost(starboardPost);
} }
@Override protected Long getFromConfig(String key, Long guildId) {
@Transactional
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(StarboardFeature.STAR_EMOTE, guildId);
if(emoteService.compareCachedEmoteWithAEmote(removedReaction.getEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_REMOVED);
log.info("User {} in server {} removed star reaction from message {} on starboard.",
userRemoving.getUserId(), message.getServerId(), message.getMessageId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
handleStarboardPostChange(message, reactionOptional.orElse(null), userRemoving, false);
}
}
private Long getFromConfig(String key, Long guildId) {
return configManagementService.loadConfig(guildId, key).getLongValue(); return configManagementService.loadConfig(guildId, key).getLongValue();
} }
private List<AUserInAServer> getUsersExcept(List<ServerUser> users, AUserInAServer author) { protected 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 -> { 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()); Optional<AUserInAServer> aUserInAServer = userInServerManagementService.loadUserOptional(serverUser.getServerId(), serverUser.getUserId());
return aUserInAServer.orElse(null); return aUserInAServer.orElse(null);
}).filter(Objects::nonNull).collect(Collectors.toList()); }).filter(Objects::nonNull).collect(Collectors.toList());
} }
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
@Override
public void executeReactionCleared(CachedMessage message) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
starboardPostOptional.ifPresent(starboardPost -> {
log.info("Reactions on message {} in channel {} in server {} were cleared. Completely deleting the starboard post {}.",
message.getMessageId(), message.getChannelId(), message.getServerId(), starboardPost.getId());
starboardPostReactorManagementService.removeReactors(starboardPost);
completelyRemoveStarboardPost(starboardPost);
});
}
@PostConstruct @PostConstruct
public void postConstruct() { public void postConstruct() {
metricService.registerCounter(STARBOARD_STARS_ADDED, "Star reaction added");
metricService.registerCounter(STARBOARD_STARS_REMOVED, "Star reaction removed");
metricService.registerCounter(STARBOARD_STARS_THRESHOLD_REACHED, "Starboard posts reaching threshold"); metricService.registerCounter(STARBOARD_STARS_THRESHOLD_REACHED, "Starboard posts reaching threshold");
metricService.registerCounter(STARBOARD_STARS_THRESHOLD_FELL, "Starboard posts falling below threshold"); metricService.registerCounter(STARBOARD_STARS_THRESHOLD_FELL, "Starboard posts falling below threshold");
} }
} }

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.starboard.listener; package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition; import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost; import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
@@ -20,14 +22,16 @@ public class StarboardPostDeletedListener implements AsyncMessageDeletedListener
private StarboardPostManagementService starboardPostManagementService; private StarboardPostManagementService starboardPostManagementService;
@Override @Override
public void execute(CachedMessage messageBefore) { public DefaultListenerResult execute(MessageDeletedModel model) {
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(messageBefore.getMessageId()); CachedMessage message = model.getCachedMessage();
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(message.getMessageId());
if(byStarboardPostId.isPresent()) { if(byStarboardPostId.isPresent()) {
StarboardPost post = byStarboardPostId.get(); StarboardPost post = byStarboardPostId.get();
log.info("Removing starboard post: message {}, channel {}, server {}, because the message was deleted", log.info("Removing starboard post: message {}, channel {}, server {}, because the message was deleted",
post.getPostMessageId(), post.getSourceChannel().getId(), messageBefore.getServerId()); post.getPostMessageId(), post.getSourceChannel().getId(), message.getServerId());
starboardPostManagementService.setStarboardPostIgnored(messageBefore.getMessageId(), true); starboardPostManagementService.setStarboardPostIgnored(message.getMessageId(), true);
} }
return DefaultListenerResult.PROCESSED;
} }
@Override @Override

View File

@@ -0,0 +1,98 @@
package dev.sheldan.abstracto.starboard.service;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.starboard.listener.StarboardPostState;
import dev.sheldan.abstracto.starboard.listener.StarboardPostUpdatedListener;
import dev.sheldan.abstracto.starboard.model.StarboardPostUpdatedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
public class StarboardPostListenerManager {
@Autowired(required = false)
private List<StarboardPostUpdatedListener> listeners;
@Autowired
@Qualifier("starboardStatusListenerExecutor")
private TaskExecutor starboardStatusExecutor;
@Autowired
private StarboardPostListenerManager self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ListenerService listenerService;
public void sendStarboardPostCreatedEvent(Long userReactingId, StarboardPost post) {
if(listeners == null || listeners.isEmpty()) {
return;
}
ServerUser userReactingServerUser = ServerUser.builder().serverId(post.getServer().getId()).userId(userReactingId).build();
StarboardPostUpdatedModel model = createStarboardStatusModel(post, userReactingServerUser);
model.setNewState(StarboardPostState.CREATED);
listeners.forEach(listener -> listenerService.executeFeatureAwareListener(listener, model));
}
public void sendStarboardPostDeletedEvent(StarboardPost post, ServerUser userReacting) {
if(listeners == null || listeners.isEmpty()) {
return;
}
StarboardPostUpdatedModel model = createStarboardStatusModel(post, userReacting);
model.setNewState(StarboardPostState.DELETED);
}
private StarboardPostUpdatedModel createStarboardStatusModel(StarboardPost post, ServerUser userReacting) {
Long serverId = post.getServer().getId();
ServerUser starredUser = ServerUser
.builder()
.serverId(serverId)
.userId(post.getAuthor().getUserReference().getId())
.build();
ServerChannelMessage starboardMessagePayLoad = ServerChannelMessage
.builder()
.serverId(serverId)
.channelId(post.getStarboardChannel().getId())
.messageId(post.getStarboardMessageId())
.build();
ServerChannelMessage starredMessage = ServerChannelMessage
.builder()
.serverId(serverId)
.channelId(post.getSourceChannel().getId())
.messageId(post.getPostMessageId())
.build();
List<Long> starrerIds = post.
getReactions()
.stream()
.map(starboardPostReaction -> starboardPostReaction.getReactor().getUserReference().getId())
.collect(Collectors.toList());
return StarboardPostUpdatedModel
.builder()
.lastStarrer(userReacting)
.starredUser(starredUser)
.starboardMessage(starboardMessagePayLoad)
.starredMessage(starredMessage)
.allStarrer(starrerIds)
.build();
}
}

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.starboard.service;
import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException; import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage; import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.database.PostTarget; import dev.sheldan.abstracto.core.models.database.PostTarget;
import dev.sheldan.abstracto.core.models.property.SystemConfigProperty; import dev.sheldan.abstracto.core.models.property.SystemConfigProperty;
@@ -13,9 +13,9 @@ import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService; import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement; import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.templating.model.MessageToSend; import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService; import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.starboard.config.StarboardFeature; import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardPostTarget; import dev.sheldan.abstracto.starboard.config.StarboardPostTarget;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost; import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
@@ -90,40 +90,47 @@ public class StarboardServiceBean implements StarboardService {
@Autowired @Autowired
private StarboardServiceBean self; private StarboardServiceBean self;
@Autowired
private StarboardPostListenerManager starboardPostListenerManager;
@Autowired
private UserService userService;
@Override @Override
public CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) { public CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) {
Long starredUserId = starredUser.getUserInServerId(); Long starredUserId = starredUser.getUserInServerId();
List<Long> userExceptAuthorIds = userExceptAuthor.stream().map(AUserInAServer::getUserInServerId).collect(Collectors.toList()); List<Long> userExceptAuthorIds = userExceptAuthor.stream().map(AUserInAServer::getUserInServerId).collect(Collectors.toList());
Long userReactingId = userReacting.getUserReference().getId();
return buildStarboardPostModel(message, userExceptAuthor.size()).thenCompose(starboardPostModel -> return buildStarboardPostModel(message, userExceptAuthor.size()).thenCompose(starboardPostModel ->
self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel) self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel, userReactingId)
); );
} }
@Transactional @Transactional
public CompletionStage<Void> sendStarboardPostAndStore(CachedMessage message, Long starredUserId, List<Long> userExceptAuthorIds, StarboardPostModel starboardPostModel) { public CompletionStage<Void> sendStarboardPostAndStore(CachedMessage message, Long starredUserId, List<Long> userExceptAuthorIds, StarboardPostModel starboardPostModel, Long userReactingId) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel); MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel);
PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId()); PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId()); List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardChannelId = starboard.getChannelReference().getId(); Long starboardChannelId = starboard.getChannelReference().getId();
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId) self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId, userReactingId)
); );
} }
@Transactional @Transactional
public void persistPost(CachedMessage message, List<Long> userExceptAuthorIds, List<CompletableFuture<Message>> completableFutures, Long starboardChannelId, Long starredUserId) { public void persistPost(CachedMessage message, List<Long> userExceptAuthorIds, List<CompletableFuture<Message>> completableFutures, Long starboardChannelId, Long starredUserId, Long userReactingId) {
AUserInAServer innerStarredUser = userInServerManagementService.loadUserOptional(starredUserId).orElseThrow(() -> new UserInServerNotFoundException(starredUserId)); AUserInAServer innerStarredUser = userInServerManagementService.loadUserOptional(starredUserId).orElseThrow(() -> new UserInServerNotFoundException(starredUserId));
AChannel starboardChannel = channelManagementService.loadChannel(starboardChannelId); AChannel starboardChannel = channelManagementService.loadChannel(starboardChannelId);
Message message1 = completableFutures.get(0).join(); Message starboardMessage = completableFutures.get(0).join();
AServerAChannelMessage aServerAChannelMessage = AServerAChannelMessage AServerAChannelMessage aServerAChannelMessage = AServerAChannelMessage
.builder() .builder()
.messageId(message1.getIdLong()) .messageId(starboardMessage.getIdLong())
.channel(starboardChannel) .channel(starboardChannel)
.server(starboardChannel.getServer()) .server(starboardChannel.getServer())
.build(); .build();
StarboardPost starboardPost = starboardPostManagementService.createStarboardPost(message, innerStarredUser, aServerAChannelMessage); StarboardPost starboardPost = starboardPostManagementService.createStarboardPost(message, innerStarredUser, aServerAChannelMessage);
log.info("Persisting starboard post in channel {} with message {} with {} reactors.", message1.getId(),starboardChannelId, userExceptAuthorIds.size()); log.info("Persisting starboard post in channel {} with message {} with {} reactors.", starboardMessage.getId(),starboardChannelId, userExceptAuthorIds.size());
if(userExceptAuthorIds.isEmpty()) { if(userExceptAuthorIds.isEmpty()) {
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()); 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());
} }
@@ -131,25 +138,23 @@ public class StarboardServiceBean implements StarboardService {
AUserInAServer user = userInServerManagementService.loadUserOptional(aLong).orElseThrow(() -> new UserInServerNotFoundException(aLong)); AUserInAServer user = userInServerManagementService.loadUserOptional(aLong).orElseThrow(() -> new UserInServerNotFoundException(aLong));
starboardPostReactorManagementService.addReactor(starboardPost, user); starboardPostReactorManagementService.addReactor(starboardPost, user);
}); });
starboardPostListenerManager.sendStarboardPostCreatedEvent(userReactingId, starboardPost);
} }
private CompletableFuture<StarboardPostModel> buildStarboardPostModel(CachedMessage message, Integer starCount) { private CompletableFuture<StarboardPostModel> buildStarboardPostModel(CachedMessage message, Integer starCount) {
return memberService.getMemberInServerAsync(message.getServerId(), message.getAuthor().getAuthorId()).thenApply(member -> { return userService.retrieveUserForId(message.getAuthor().getAuthorId()).thenApply(user -> {
Optional<TextChannel> channel = channelService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId()); Optional<TextChannel> channel = channelService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = guildService.getGuildByIdOptional(message.getServerId()); Optional<Guild> guild = guildService.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.getAuthor().getAuthorId()).build();
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount); String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel return StarboardPostModel
.builder() .builder()
.message(message) .message(message)
.author(member) .author(user)
.sourceChannelId(message.getChannelId())
.channel(channel.orElse(null)) .channel(channel.orElse(null))
.aChannel(aChannel)
.starCount(starCount) .starCount(starCount)
.guild(guild.orElse(null)) .guild(guild.orElse(null))
.user(user)
.starLevelEmote(starLevelEmote) .starLevelEmote(starLevelEmote)
.build(); .build();
}); });
@@ -241,6 +246,13 @@ public class StarboardServiceBean implements StarboardService {
.build(); .build();
} }
@Override
public void deleteStarboardPost(StarboardPost starboardPost, ServerUser userReacting) {
deleteStarboardMessagePost(starboardPost);
starboardPostManagementService.removePost(starboardPost);
starboardPostListenerManager.sendStarboardPostDeletedEvent(starboardPost, userReacting);
}
private String getStarboardRankingEmote(Long serverId, Integer position) { private String getStarboardRankingEmote(Long serverId, Integer position) {
return emoteService.getUsableEmoteOrDefault(serverId, buildBadgeName(position)); return emoteService.getUsableEmoteOrDefault(serverId, buildBadgeName(position));
} }

View File

@@ -12,10 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Instant; import java.time.Instant;
import java.util.Collections; import java.util.*;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
@Component @Component
@Slf4j @Slf4j
@@ -36,6 +33,7 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
.postMessageId(starredMessage.getMessageId()) .postMessageId(starredMessage.getMessageId())
.sourceChannel(build) .sourceChannel(build)
.ignored(false) .ignored(false)
.reactions(new ArrayList<>())
.server(starboardPost.getServer()) .server(starboardPost.getServer())
.starboardMessageId(starboardPost.getMessageId()) .starboardMessageId(starboardPost.getMessageId())
.starboardChannel(starboardPost.getChannel()) .starboardChannel(starboardPost.getChannel())
@@ -45,8 +43,7 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
starredMessage.getMessageId(), starredMessage.getChannelId(), starredMessage.getServerId(), starredMessage.getMessageId(), starredMessage.getChannelId(), starredMessage.getServerId(),
starboardPost.getMessageId(), starboardPost.getChannel().getId(), starboardPost.getServer().getId(), starboardPost.getMessageId(), starboardPost.getChannel().getId(), starboardPost.getServer().getId(),
starredUser.getUserReference().getId()); starredUser.getUserReference().getId());
repository.save(post); return repository.save(post);
return post;
} }
@Override @Override
@@ -57,7 +54,6 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
@Override @Override
public void setStarboardPostMessageId(StarboardPost post, Long messageId) { public void setStarboardPostMessageId(StarboardPost post, Long messageId) {
post.setStarboardMessageId(messageId); post.setStarboardMessageId(messageId);
repository.save(post);
} }
@Override @Override

View File

@@ -33,8 +33,7 @@ public class StarboardPostReactorManagementServiceBean implements StarboardPostR
.server(user.getServerReference()) .server(user.getServerReference())
.build(); .build();
log.info("Persisting the reactor {} for starboard post {} in server {}.", user.getUserReference().getId(), post.getId(), user.getServerReference().getId()); log.info("Persisting the reactor {} for starboard post {} in server {}.", user.getUserReference().getId(), post.getId(), user.getServerReference().getId());
repository.save(reactor); return repository.save(reactor);
return reactor;
} }
@Override @Override

View File

@@ -3,10 +3,10 @@ package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor; import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions; import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.*; import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService; import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -15,6 +15,7 @@ import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService; import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@@ -22,18 +23,16 @@ import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class StarboardListenerTest { public class StarAddedListenerTest {
@InjectMocks @InjectMocks
private StarboardListener testUnit; private StarAddedListener testUnit;
@Mock @Mock
private ConfigManagementService configManagementService; private ConfigManagementService configManagementService;
@@ -57,7 +56,10 @@ public class StarboardListenerTest {
private MetricService metricService; private MetricService metricService;
@Mock @Mock
private CachedReactions cachedReaction; private MessageReaction reaction;
@Mock
private CachedReactions cachedReactions;
@Mock @Mock
private CachedMessage cachedMessage; private CachedMessage cachedMessage;
@@ -87,11 +89,14 @@ public class StarboardListenerTest {
private StarboardPost post; private StarboardPost post;
@Mock @Mock
private CachedEmote cachedEmote; private MessageReaction.ReactionEmote reactionEmote;
@Mock @Mock
private AEmote starEmote; private AEmote starEmote;
@Mock
private ReactionAddedModel model;
private static final Long MESSAGE_ID = 5L; private static final Long MESSAGE_ID = 5L;
private static final Long SERVER_ID = 6L; private static final Long SERVER_ID = 6L;
private static final Long AUTHOR_ID = 4L; private static final Long AUTHOR_ID = 4L;
@@ -102,7 +107,9 @@ public class StarboardListenerTest {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID); when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor); when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID); when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing); when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserReacting()).thenReturn(serverUserActing);
testUnit.execute(model);
verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID); verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
} }
@@ -110,9 +117,13 @@ public class StarboardListenerTest {
public void testAddingWrongEmote() { public void testAddingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID); when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote); setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
when(cachedReaction.getEmote()).thenReturn(cachedEmote); when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(false);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(false); when(model.getMessage()).thenReturn(cachedMessage);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing); when(model.getUserReacting()).thenReturn(serverUserActing);
when(model.getServerId()).thenReturn(SERVER_ID);
when(model.getReaction()).thenReturn(reaction);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID); verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote)); verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
} }
@@ -122,8 +133,7 @@ public class StarboardListenerTest {
Long requiredStars = 5L; Long requiredStars = 5L;
setupActingAndAuthor(); setupActingAndAuthor();
executeAddingTest(requiredStars, post); executeAddingTest(requiredStars, post);
verify(starboardService, times(1)).deleteStarboardMessagePost(post); verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
verify(starboardPostManagementService, times(1)).removePost(post);
} }
@Test @Test
@@ -154,67 +164,6 @@ public class StarboardListenerTest {
verify(starboardPostReactorManagementService, times(1)).addReactor(post, userInServerActing); verify(starboardPostReactorManagementService, times(1)).addReactor(post, userInServerActing);
} }
@Test
public void testAuthorRemovingReaction() {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID);
testUnit.executeReactionRemoved(cachedMessage, cachedReaction, serverUserActing);
verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
}
@Test
public void testRemovingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
testUnit.executeReactionRemoved(cachedMessage, cachedReaction, serverUserActing);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
}
@Test
public void testRemoveReactionFromExistingPostBelowThreshold() {
Long requiredStars = 5L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
executeRemovalTest(requiredStars, remainingUsers);
verify(starboardService, times(1)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(1)).removePost(eq(post));
}
@Test
public void testRemoveReactionFromExistingPostAboveThreshold() {
Long requiredStars = 0L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
executeRemovalTest(requiredStars, remainingUsers);
verify(metricService, times(1)).incrementCounter(any());
verify(starboardService, times(0)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(0)).removePost(eq(post));
}
@Test
public void testRemoveReactionFromExistingPostTriggeringThreshold() {
Long requiredStars = 1L;
ArrayList<ServerUser> usersRemaining = new ArrayList<>();
setupActingAndAuthor();
executeRemovalTest(requiredStars, usersRemaining);
verify(metricService, times(2)).incrementCounter(any());
verify(starboardService, times(1)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(1)).removePost(eq(post));
}
@Test
public void testReactionsClearedOnStarredMessage() {
executeClearingTest(Mockito.mock(StarboardPost.class));
}
@Test
public void testReactionsClearedOnNotStarredMessage() {
executeClearingTest(null);
}
private void setupActingAndAuthor() { private void setupActingAndAuthor() {
when(userInServerActing.getUserReference()).thenReturn(userActing); when(userInServerActing.getUserReference()).thenReturn(userActing);
when(userActing.getId()).thenReturn(USER_ACTING_ID); when(userActing.getId()).thenReturn(USER_ACTING_ID);
@@ -224,57 +173,16 @@ public class StarboardListenerTest {
when(aUser.getId()).thenReturn(AUTHOR_ID); when(aUser.getId()).thenReturn(AUTHOR_ID);
} }
private void executeClearingTest(StarboardPost post) {
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
testUnit.executeReactionCleared(cachedMessage);
int callCount = post != null ? 1 : 0;
verify(starboardPostReactorManagementService, times(callCount)).removeReactors(post);
verify(starboardService, times(callCount)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(callCount)).removePost(eq(post));
}
private void executeRemovalTest(Long requiredStars, List<ServerUser> remainingUsers) {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(true);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote);
CachedReactions reaction = Mockito.mock(CachedReactions.class);
when(reaction.getUsers()).thenReturn(remainingUsers);
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(reaction));
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
when(serverUserActing.getServerId()).thenReturn(SERVER_ID);
if(!remainingUsers.isEmpty()) {
when(userInServerManagementService.loadUserOptional(SERVER_ID, USER_ACTING_ID)).thenReturn(Optional.of(userInServerActing));
}
AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig);
testUnit.executeReactionRemoved(cachedMessage, cachedReaction, serverUserActing);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote);
}
private void executeAddingTest(Long requiredStars, StarboardPost postToUse) { private void executeAddingTest(Long requiredStars, StarboardPost postToUse) {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID); when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID); when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID); when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor); when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote); when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(true); when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(true);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote); when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote);
CachedReactions reaction = Mockito.mock(CachedReactions.class);
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID); when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
when(serverUserActing.getServerId()).thenReturn(SERVER_ID); when(serverUserActing.getServerId()).thenReturn(SERVER_ID);
when(reaction.getUsers()).thenReturn(Arrays.asList(serverUserActing));
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(reaction));
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(postToUse)); when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(postToUse));
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer); when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing); when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
@@ -282,16 +190,21 @@ public class StarboardListenerTest {
AConfig starRequirementConfig = Mockito.mock(AConfig.class); AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars); when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig); when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing); when(model.getMessage()).thenReturn(cachedMessage);
when(cachedReactions.getUsers()).thenReturn(Arrays.asList(serverUserActing));
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(cachedReactions));
when(model.getUserReacting()).thenReturn(serverUserActing);
when(model.getReaction()).thenReturn(reaction);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID); verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote); verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote);
} }
private void setupWrongEmote(Long serverId, Long authorId, AEmote starEmote) { private void setupWrongEmote(Long serverId, Long authorId, AEmote starEmote) {
when(cachedMessage.getServerId()).thenReturn(serverId);
when(cachedAuthor.getAuthorId()).thenReturn(authorId); when(cachedAuthor.getAuthorId()).thenReturn(authorId);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor); when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote); when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId)).thenReturn(starEmote); when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId)).thenReturn(starEmote);
} }
} }

View File

@@ -0,0 +1,66 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.ReactionClearedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class StarClearedListenerTest {
@InjectMocks
private StarClearedListener testUnit;
@Mock
private StarboardService starboardService;
@Mock
private StarboardPostManagementService starboardPostManagementService;
@Mock
private StarboardPostReactorManagementService starboardPostReactorManagementService;
@Mock
private MetricService metricService;
@Mock
private CachedMessage cachedMessage;
@Mock
private ReactionClearedModel model;
private static final Long MESSAGE_ID = 5L;
@Test
public void testReactionsClearedOnStarredMessage() {
executeClearingTest(Mockito.mock(StarboardPost.class));
}
@Test
public void testReactionsClearedOnNotStarredMessage() {
executeClearingTest(null);
}
private void executeClearingTest(StarboardPost post) {
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(model.getMessage()).thenReturn(cachedMessage);
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
testUnit.execute(model);
int callCount = post != null ? 1 : 0;
verify(starboardPostReactorManagementService, times(callCount)).removeReactors(post);
verify(starboardService, times(callCount)).deleteStarboardPost(post, null);
}
}

View File

@@ -0,0 +1,210 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.listener.ReactionRemovedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class StarRemovedListenerTest {
@InjectMocks
private StarRemovedListener testUnit;
@Mock
private ConfigManagementService configManagementService;
@Mock
private StarboardService starboardService;
@Mock
private StarboardPostManagementService starboardPostManagementService;
@Mock
private StarboardPostReactorManagementService starboardPostReactorManagementService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private EmoteService emoteService;
@Mock
private MetricService metricService;
@Mock
private MessageReaction reaction;
@Mock
private CachedMessage cachedMessage;
@Mock
private ServerUser serverUserActing;
@Mock
private CachedAuthor cachedAuthor;
@Mock
private AUserInAServer userInServerActing;
@Mock
private AUser userActing;
@Mock
private CachedReactions cachedReactions;
@Mock
private AUserInAServer userInAServer;
@Mock
private AUser aUser;
@Mock
private AServer server;
@Mock
private StarboardPost post;
@Mock
private MessageReaction.ReactionEmote reactionEmote;
@Mock
private AEmote starEmote;
@Mock
private ReactionRemovedModel model;
private static final Long MESSAGE_ID = 5L;
private static final Long SERVER_ID = 6L;
private static final Long AUTHOR_ID = 4L;
private static final Long USER_ACTING_ID = 7L;
@Test
public void testAuthorRemovingReaction() {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserRemoving()).thenReturn(serverUserActing);
testUnit.execute(model);
verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
}
@Test
public void testRemovingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserRemoving()).thenReturn(serverUserActing);
when(model.getReaction()).thenReturn(reaction);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
}
@Test
public void testRemoveReactionFromExistingPostBelowThreshold() {
Long requiredStars = 5L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
executeRemovalTest(requiredStars, remainingUsers);
verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
}
@Test
public void testRemoveReactionFromExistingPostAboveThreshold() {
Long requiredStars = 0L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
executeRemovalTest(requiredStars, remainingUsers);
verify(metricService, times(1)).incrementCounter(any());
verify(starboardService, times(0)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(0)).removePost(eq(post));
}
@Test
public void testRemoveReactionFromExistingPostTriggeringThreshold() {
Long requiredStars = 1L;
ArrayList<ServerUser> usersRemaining = new ArrayList<>();
setupActingAndAuthor();
executeRemovalTest(requiredStars, usersRemaining);
verify(metricService, times(2)).incrementCounter(any());
verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
}
private void setupActingAndAuthor() {
when(userInServerActing.getUserReference()).thenReturn(userActing);
when(userActing.getId()).thenReturn(USER_ACTING_ID);
when(userInAServer.getServerReference()).thenReturn(server);
when(server.getId()).thenReturn(SERVER_ID);
when(userInAServer.getUserReference()).thenReturn(aUser);
when(aUser.getId()).thenReturn(AUTHOR_ID);
}
private void executeRemovalTest(Long requiredStars, List<ServerUser> remainingUsers) {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(true);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote);
when(cachedReactions.getUsers()).thenReturn(remainingUsers);
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(cachedReactions));
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
when(serverUserActing.getServerId()).thenReturn(SERVER_ID);
if(!remainingUsers.isEmpty()) {
when(userInServerManagementService.loadUserOptional(SERVER_ID, USER_ACTING_ID)).thenReturn(Optional.of(userInServerActing));
}
AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserRemoving()).thenReturn(serverUserActing);
when(model.getReaction()).thenReturn(reaction);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote);
}
private void setupWrongEmote(Long serverId, Long authorId, AEmote starEmote) {
when(cachedAuthor.getAuthorId()).thenReturn(authorId);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId)).thenReturn(starEmote);
}
}

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost; import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import org.junit.Test; import org.junit.Test;
@@ -28,9 +29,11 @@ public class StarboardPostDeletedListenerTest {
public void deleteNonStarboardPost() { public void deleteNonStarboardPost() {
Long messageId = 4L; Long messageId = 4L;
when(starboardPostManagementService.findByStarboardPostId(messageId)).thenReturn(Optional.empty()); when(starboardPostManagementService.findByStarboardPostId(messageId)).thenReturn(Optional.empty());
MessageDeletedModel model = Mockito.mock(MessageDeletedModel.class);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class); CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getMessageId()).thenReturn(messageId); when(cachedMessage.getMessageId()).thenReturn(messageId);
testUnit.execute(cachedMessage); when(model.getCachedMessage()).thenReturn(cachedMessage);
testUnit.execute(model);
verify( starboardPostManagementService, times(0)).setStarboardPostIgnored(messageId, true); verify( starboardPostManagementService, times(0)).setStarboardPostIgnored(messageId, true);
} }
@@ -47,7 +50,9 @@ public class StarboardPostDeletedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class); CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(serverId); when(cachedMessage.getServerId()).thenReturn(serverId);
when(cachedMessage.getMessageId()).thenReturn(messageId); when(cachedMessage.getMessageId()).thenReturn(messageId);
testUnit.execute(cachedMessage); MessageDeletedModel model = Mockito.mock(MessageDeletedModel.class);
when(model.getCachedMessage()).thenReturn(cachedMessage);
testUnit.execute(model);
verify( starboardPostManagementService, times(1)).setStarboardPostIgnored(messageId, true); verify( starboardPostManagementService, times(1)).setStarboardPostIgnored(messageId, true);
} }

View File

@@ -20,15 +20,13 @@ import dev.sheldan.abstracto.starboard.model.database.StarboardPostReaction;
import dev.sheldan.abstracto.starboard.model.template.*; import dev.sheldan.abstracto.starboard.model.template.*;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService; import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.*; import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationEventPublisher;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -77,6 +75,12 @@ public class StarboardServiceBeanTest {
@Mock @Mock
private UserInServerManagementService userInServerManagementService; private UserInServerManagementService userInServerManagementService;
@Mock
private ApplicationEventPublisher eventPublisher;
@Mock
private UserService userService;
@Mock @Mock
private MessageService messageService; private MessageService messageService;
@@ -95,6 +99,9 @@ public class StarboardServiceBeanTest {
@Mock @Mock
private TextChannel mockedTextChannel; private TextChannel mockedTextChannel;
@Mock
private User starredJdaUser;
@Mock @Mock
private Member starredMember; private Member starredMember;
@@ -107,6 +114,9 @@ public class StarboardServiceBeanTest {
@Mock @Mock
private AServer server; private AServer server;
@Mock
private AUser aUser;
private static final Long STARRED_USER_ID = 5L; private static final Long STARRED_USER_ID = 5L;
private static final Long STARRED_SERVER_USER_ID = 2L; private static final Long STARRED_SERVER_USER_ID = 2L;
private static final Long SERVER_ID = 6L; private static final Long SERVER_ID = 6L;
@@ -116,6 +126,7 @@ public class StarboardServiceBeanTest {
private static final Long CHANNEL_ID = 10L; private static final Long CHANNEL_ID = 10L;
private static final Long MESSAGE_ID = 11L; private static final Long MESSAGE_ID = 11L;
private static final Long SECOND_MESSAGE_ID = 12L; private static final Long SECOND_MESSAGE_ID = 12L;
private static final Long STARRING_USER_ID = 13L;
@Captor @Captor
private ArgumentCaptor<AUserInAServer> userInAServerArgumentCaptor; private ArgumentCaptor<AUserInAServer> userInAServerArgumentCaptor;
@@ -131,6 +142,8 @@ public class StarboardServiceBeanTest {
AUserInAServer secondUserExceptAuthor = Mockito.mock(AUserInAServer.class); AUserInAServer secondUserExceptAuthor = Mockito.mock(AUserInAServer.class);
userExceptAuthor.add(secondUserExceptAuthor); userExceptAuthor.add(secondUserExceptAuthor);
AUserInAServer userReacting = Mockito.mock(AUserInAServer.class); AUserInAServer userReacting = Mockito.mock(AUserInAServer.class);
when(userReacting.getUserReference()).thenReturn(aUser);
when(aUser.getId()).thenReturn(STARRING_USER_ID);
AUserInAServer starredUser = Mockito.mock(AUserInAServer.class); AUserInAServer starredUser = Mockito.mock(AUserInAServer.class);
when(starredUser.getUserInServerId()).thenReturn(STARRED_SERVER_USER_ID); when(starredUser.getUserInServerId()).thenReturn(STARRED_SERVER_USER_ID);
CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class); CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class);
@@ -139,8 +152,7 @@ public class StarboardServiceBeanTest {
when(message.getAuthor()).thenReturn(cachedAuthor); when(message.getAuthor()).thenReturn(cachedAuthor);
when(message.getServerId()).thenReturn(SERVER_ID); when(message.getServerId()).thenReturn(SERVER_ID);
when(message.getChannelId()).thenReturn(CHANNEL_ID); when(message.getChannelId()).thenReturn(CHANNEL_ID);
Member authorMember = Mockito.mock(Member.class); when(userService.retrieveUserForId(STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredJdaUser));
when(memberService.getMemberInServerAsync(SERVER_ID, STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(mockedTextChannel)); when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(mockedTextChannel));
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guild)); when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guild));
SystemConfigProperty config = Mockito.mock(SystemConfigProperty.class); SystemConfigProperty config = Mockito.mock(SystemConfigProperty.class);
@@ -152,10 +164,11 @@ public class StarboardServiceBeanTest {
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + "2")).thenReturn(config); when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + "2")).thenReturn(config);
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + "3")).thenReturn(config); when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + "3")).thenReturn(config);
when(emoteService.getUsableEmoteOrDefault(SERVER_ID, StarboardFeature.STAR_EMOTE_PREFIX + "2")).thenReturn("b"); when(emoteService.getUsableEmoteOrDefault(SERVER_ID, StarboardFeature.STAR_EMOTE_PREFIX + "2")).thenReturn("b");
when(self.sendStarboardPostAndStore(eq(message), eq(STARRED_SERVER_USER_ID), anyList(), any())).thenReturn(CompletableFuture.completedFuture(null)); when(self.sendStarboardPostAndStore(eq(message), eq(STARRED_SERVER_USER_ID), anyList(), any(), eq(STARRING_USER_ID))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<Void> createPostFuture = testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser); CompletableFuture<Void> createPostFuture = testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser);
createPostFuture.join(); createPostFuture.join();
Assert.assertFalse(createPostFuture.isCompletedExceptionally()); Assert.assertFalse(createPostFuture.isCompletedExceptionally());
verify(self, times(1)).sendStarboardPostAndStore(eq(message), eq(STARRED_SERVER_USER_ID), anyList(), any(), eq(STARRING_USER_ID));
} }
@Test @Test
@@ -170,8 +183,8 @@ public class StarboardServiceBeanTest {
when(postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), SERVER_ID)).thenReturn(postTarget); when(postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), SERVER_ID)).thenReturn(postTarget);
when(postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, SERVER_ID)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null))); when(postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, SERVER_ID)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
ArrayList<Long> userExceptAuthorIds = new ArrayList<>(); ArrayList<Long> userExceptAuthorIds = new ArrayList<>();
testUnit.sendStarboardPostAndStore(message, STARRED_USER_ID, userExceptAuthorIds, model); testUnit.sendStarboardPostAndStore(message, STARRED_USER_ID, userExceptAuthorIds, model, STARRING_USER_ID);
verify(self, times(1)).persistPost(eq(message), eq(userExceptAuthorIds), any(), eq(STARBOARD_CHANNEL_ID), eq(STARRED_USER_ID)); verify(self, times(1)).persistPost(eq(message), eq(userExceptAuthorIds), any(), eq(STARBOARD_CHANNEL_ID), eq(STARRED_USER_ID), eq(STARRING_USER_ID));
} }
@Test @Test
@@ -181,9 +194,13 @@ public class StarboardServiceBeanTest {
CachedMessage message = Mockito.mock(CachedMessage.class); CachedMessage message = Mockito.mock(CachedMessage.class);
List<Long> userExceptAuthorIds = Arrays.asList(FIRST_USER_IN_SERVER_ID, SECOND_USER_IN_SERVER_ID); List<Long> userExceptAuthorIds = Arrays.asList(FIRST_USER_IN_SERVER_ID, SECOND_USER_IN_SERVER_ID);
List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost)); List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost));
when(sendPost.getGuild()).thenReturn(guild);
when(sendPost.getChannel()).thenReturn(mockedTextChannel);
when(sendPost.getIdLong()).thenReturn(MESSAGE_ID);
when(userInServerManagementService.loadUserOptional(STARRED_SERVER_USER_ID)).thenReturn(Optional.of(starredUser)); when(userInServerManagementService.loadUserOptional(STARRED_SERVER_USER_ID)).thenReturn(Optional.of(starredUser));
when(userInServerManagementService.loadUserOptional(FIRST_USER_IN_SERVER_ID)).thenReturn(Optional.of(userReacting)); when(userInServerManagementService.loadUserOptional(FIRST_USER_IN_SERVER_ID)).thenReturn(Optional.of(userReacting));
when(userReacting.getUserInServerId()).thenReturn(FIRST_USER_IN_SERVER_ID); when(userReacting.getUserInServerId()).thenReturn(FIRST_USER_IN_SERVER_ID);
when(userReacting.getUserReference()).thenReturn(aUser);
AChannel channel = Mockito.mock(AChannel.class); AChannel channel = Mockito.mock(AChannel.class);
when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(channel); when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(channel);
StarboardPost post = Mockito.mock(StarboardPost.class); StarboardPost post = Mockito.mock(StarboardPost.class);
@@ -191,7 +208,8 @@ public class StarboardServiceBeanTest {
AUserInAServer secondStarrerUserObj = Mockito.mock(AUserInAServer.class); AUserInAServer secondStarrerUserObj = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadUserOptional(SECOND_USER_IN_SERVER_ID)).thenReturn(Optional.of(secondStarrerUserObj)); when(userInServerManagementService.loadUserOptional(SECOND_USER_IN_SERVER_ID)).thenReturn(Optional.of(secondStarrerUserObj));
when(secondStarrerUserObj.getUserInServerId()).thenReturn(SECOND_USER_IN_SERVER_ID); when(secondStarrerUserObj.getUserInServerId()).thenReturn(SECOND_USER_IN_SERVER_ID);
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, STARRED_SERVER_USER_ID); when(secondStarrerUserObj.getUserReference()).thenReturn(aUser);
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, STARRED_SERVER_USER_ID, STARRING_USER_ID);
verify(starboardPostReactorManagementService, times(2)).addReactor(eq(post), userInAServerArgumentCaptor.capture()); verify(starboardPostReactorManagementService, times(2)).addReactor(eq(post), userInAServerArgumentCaptor.capture());
List<AUserInAServer> addedReactors = userInAServerArgumentCaptor.getAllValues(); List<AUserInAServer> addedReactors = userInAServerArgumentCaptor.getAllValues();
Assert.assertEquals(FIRST_USER_IN_SERVER_ID, addedReactors.get(0).getUserInServerId()); Assert.assertEquals(FIRST_USER_IN_SERVER_ID, addedReactors.get(0).getUserInServerId());
@@ -225,7 +243,7 @@ public class StarboardServiceBeanTest {
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LEVELS_CONFIG_KEY)).thenReturn(config); when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LEVELS_CONFIG_KEY)).thenReturn(config);
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + 1)).thenReturn(config); when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + 1)).thenReturn(config);
when(starboardPostManagementService.findByStarboardPostId(starboardPostId)).thenReturn(Optional.of(post)); when(starboardPostManagementService.findByStarboardPostId(starboardPostId)).thenReturn(Optional.of(post));
when(memberService.getMemberInServerAsync(SERVER_ID, STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredMember)); when(userService.retrieveUserForId(STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredJdaUser));
List<AUserInAServer > userExceptAuthor = new ArrayList<>(); List<AUserInAServer > userExceptAuthor = new ArrayList<>();
CompletableFuture<Void> future = testUnit.updateStarboardPost(post, message, userExceptAuthor); CompletableFuture<Void> future = testUnit.updateStarboardPost(post, message, userExceptAuthor);
future.join(); future.join();
@@ -254,7 +272,7 @@ public class StarboardServiceBeanTest {
CachedMessage message = Mockito.mock(CachedMessage.class); CachedMessage message = Mockito.mock(CachedMessage.class);
List<Long> userExceptAuthorIds = Arrays.asList(FIRST_USER_IN_SERVER_ID); List<Long> userExceptAuthorIds = Arrays.asList(FIRST_USER_IN_SERVER_ID);
List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost)); List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost));
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, SECOND_USER_IN_SERVER_ID); testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, SECOND_USER_IN_SERVER_ID, STARRING_USER_ID);
} }
@Test @Test

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.starboard.listener;
public enum StarboardPostState {
CREATED, UPDATED, UPDATED_CHANGED_THRESHOLD, DELETED
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.FeatureAwareListener;
import dev.sheldan.abstracto.starboard.model.StarboardPostUpdatedModel;
public interface StarboardPostUpdatedListener extends FeatureAwareListener<StarboardPostUpdatedModel, DefaultListenerResult> {
}

View File

@@ -0,0 +1,29 @@
package dev.sheldan.abstracto.starboard.model;
import dev.sheldan.abstracto.core.listener.FeatureAwareListenerModel;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.starboard.listener.StarboardPostState;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class StarboardPostUpdatedModel implements FeatureAwareListenerModel {
private ServerChannelMessage starredMessage;
private ServerChannelMessage starboardMessage;
private ServerUser starredUser;
private ServerUser lastStarrer;
private Long starboardPostId;
private List<Long> allStarrer;
private StarboardPostState newState;
@Override
public Long getServerId() {
return starredUser.getServerId();
}
}

View File

@@ -2,22 +2,19 @@ package dev.sheldan.abstracto.starboard.model.template;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.context.ServerContext; import dev.sheldan.abstracto.core.models.context.ServerContext;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUser;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
@Getter @Getter
@Setter @Setter
@SuperBuilder @SuperBuilder
public class StarboardPostModel extends ServerContext { public class StarboardPostModel extends ServerContext {
private Member author; private User author;
private TextChannel channel; private TextChannel channel;
private AUser user; private Long sourceChannelId;
private AChannel aChannel;
private CachedMessage message; private CachedMessage message;
private Integer starCount; private Integer starCount;
private String starLevelEmote; private String starLevelEmote;

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.starboard.service; package dev.sheldan.abstracto.starboard.service;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost; import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
@@ -18,4 +19,5 @@ public interface StarboardService {
CompletableFuture<GuildStarStatsModel> retrieveStarStats(Long serverId); CompletableFuture<GuildStarStatsModel> retrieveStarStats(Long serverId);
MemberStarStatsModel retrieveStarStatsForMember(Member member); MemberStarStatsModel retrieveStarStatsForMember(Member member);
StarStatsPost fromStarboardPost(StarboardPost starboardPost); StarStatsPost fromStarboardPost(StarboardPost starboardPost);
void deleteStarboardPost(StarboardPost starboardPost, ServerUser userReacting);
} }

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode; import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteCreatedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteCreatedListener;
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.listener.EmoteCreatedModel;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode; import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService; import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
@@ -28,24 +28,20 @@ public class CreateTrackedEmoteListener implements AsyncEmoteCreatedListener {
@Autowired @Autowired
private TrackedEmoteManagementService trackedEmoteManagementService; private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteCreated(CachedEmote createdEmote) {
log.info("Creating tracked emote {} in server {}.", createdEmote.getServerId(), createdEmote.getEmoteId());
trackedEmoteManagementService.createTrackedEmote(createdEmote);
}
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING; return StatisticFeatureDefinition.EMOTE_TRACKING;
} }
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override @Override
public List<FeatureMode> getFeatureModeLimitations() { public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(EmoteTrackingMode.AUTO_TRACK); return Arrays.asList(EmoteTrackingMode.AUTO_TRACK);
} }
@Override
public DefaultListenerResult execute(EmoteCreatedModel model) {
log.info("Creating tracked emote {} in server {}.", model.getServerId(), model.getEmote().getIdLong());
trackedEmoteManagementService.createTrackedEmote(model.getEmote());
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode; import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteDeletedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.listener.EmoteDeletedModel;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode; import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService; import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
@@ -17,7 +17,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
* This listener listens for deleted {@link Emote} in a {@link net.dv8tion.jda.api.entities.Guild} and markes respective * This listener listens for deleted {@link Emote} in a {@link net.dv8tion.jda.api.entities.Guild} and marks respective
* {@link dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote} as deleted, if the EMOTE_TRACKING feature is enabled and the AUTO_TRACK * {@link dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote} as deleted, if the EMOTE_TRACKING feature is enabled and the AUTO_TRACK
* feature mode as well. * feature mode as well.
*/ */
@@ -28,24 +28,21 @@ public class DeleteTrackedEmoteListener implements AsyncEmoteDeletedListener {
@Autowired @Autowired
private TrackedEmoteManagementService trackedEmoteManagementService; private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteDeleted(CachedEmote deletedEmote) {
log.info("Marking tracked emote {} in gild {} as deleted.", deletedEmote.getEmoteId(), deletedEmote.getServerId());
trackedEmoteManagementService.markAsDeleted(deletedEmote.getServerId(), deletedEmote.getEmoteId());
}
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING; return StatisticFeatureDefinition.EMOTE_TRACKING;
} }
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override @Override
public List<FeatureMode> getFeatureModeLimitations() { public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(EmoteTrackingMode.AUTO_TRACK); return Arrays.asList(EmoteTrackingMode.AUTO_TRACK);
} }
@Override
public DefaultListenerResult execute(EmoteDeletedModel model) {
log.info("Marking tracked emote {} in gild {} as deleted.", model.getEmote().getIdLong(), model.getServerId());
trackedEmoteManagementService.markAsDeleted(model.getEmote());
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -1,12 +1,13 @@
package dev.sheldan.abstracto.statistic.emote.listener; package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.GuildService; import dev.sheldan.abstracto.core.service.GuildService;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.service.TrackedEmoteService; import dev.sheldan.abstracto.statistic.emote.service.TrackedEmoteService;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -27,17 +28,17 @@ public class EmoteTrackingListener implements AsyncMessageReceivedListener {
@Autowired @Autowired
private GuildService guildService; private GuildService guildService;
@Override
public void execute(CachedMessage message) {
Map<Long, List<CachedEmote>> collect = message.getEmotes().stream().collect(Collectors.groupingBy(CachedEmote::getEmoteId));
collect.values().forEach(groupedEmotes ->
trackedEmoteService.addEmoteToRuntimeStorage(groupedEmotes.get(0), guildService.getGuildById(message.getServerId()), (long) groupedEmotes.size())
);
}
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING; return StatisticFeatureDefinition.EMOTE_TRACKING;
} }
@Override
public DefaultListenerResult execute(MessageReceivedModel model) {
Map<Long, List<Emote>> collect = model.getMessage().getEmotesBag().stream().collect(Collectors.groupingBy(Emote::getIdLong));
collect.values().forEach(groupedEmotes ->
trackedEmoteService.addEmoteToRuntimeStorage(groupedEmotes.get(0), guildService.getGuildById(model.getServerId()), (long) groupedEmotes.size())
);
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode; import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteUpdatedListener; import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteNameUpdatedListener;
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.listener.EmoteNameUpdatedModel;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode; import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote; import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote;
@@ -20,29 +20,25 @@ import java.util.List;
* if the emote is tracked. This is only executed if the EMOTE_TRACKING feature is enabled,and if the AUTO_TRACK feature mode is enabled. * if the emote is tracked. This is only executed if the EMOTE_TRACKING feature is enabled,and if the AUTO_TRACK feature mode is enabled.
*/ */
@Component @Component
public class UpdateTrackedEmoteListener implements AsyncEmoteUpdatedListener { public class UpdateTrackedEmoteNameListener implements AsyncEmoteNameUpdatedListener {
@Autowired @Autowired
private TrackedEmoteManagementService trackedEmoteManagementService; private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteUpdated(CachedEmote updatedEmote, String oldValue, String newValue) {
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByEmoteId(updatedEmote.getEmoteId(), updatedEmote.getServerId());
trackedEmoteManagementService.changeName(trackedEmote, newValue);
}
@Override @Override
public FeatureDefinition getFeature() { public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING; return StatisticFeatureDefinition.EMOTE_TRACKING;
} }
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override @Override
public List<FeatureMode> getFeatureModeLimitations() { public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(EmoteTrackingMode.AUTO_TRACK); return Arrays.asList(EmoteTrackingMode.AUTO_TRACK);
} }
@Override
public DefaultListenerResult execute(EmoteNameUpdatedModel model) {
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByEmote(model.getEmote());
trackedEmoteManagementService.changeName(trackedEmote, model.getNewValue());
return DefaultListenerResult.PROCESSED;
}
} }

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerSpecificId; import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.service.CacheEntityService;
import dev.sheldan.abstracto.core.service.EmoteService; import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.FeatureModeService; import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.GuildService; import dev.sheldan.abstracto.core.service.GuildService;
@@ -54,6 +55,9 @@ public class TrackedEmoteServiceBean implements TrackedEmoteService {
@Autowired @Autowired
private MetricService metricService; private MetricService metricService;
@Autowired
private CacheEntityService cacheEntityService;
public static final String EMOTE_USAGES_TRACKED_METRIC = "emote.usages"; public static final String EMOTE_USAGES_TRACKED_METRIC = "emote.usages";
private static final CounterMetric EMOTE_USAGES_TRACKED = private static final CounterMetric EMOTE_USAGES_TRACKED =
CounterMetric CounterMetric
@@ -82,6 +86,11 @@ public class TrackedEmoteServiceBean implements TrackedEmoteService {
} }
} }
@Override
public void addEmoteToRuntimeStorage(Emote emote, Guild guild, Long count) {
addEmoteToRuntimeStorage(cacheEntityService.getCachedEmoteFromEmote(emote, guild), guild, count);
}
@Override @Override
@Transactional @Transactional
public void storeEmoteStatistics(Map<Long, List<PersistingEmote>> usagesToStore) { public void storeEmoteStatistics(Map<Long, List<PersistingEmote>> usagesToStore) {

View File

@@ -38,6 +38,11 @@ public class TrackedEmoteManagementServiceBean implements TrackedEmoteManagement
return createTrackedEmote(emote.getIdLong(), emote.getName(), emote.isAnimated(), true, server); return createTrackedEmote(emote.getIdLong(), emote.getName(), emote.isAnimated(), true, server);
} }
@Override
public TrackedEmote createTrackedEmote(Emote emote) {
return createTrackedEmote(emote, emote.getGuild());
}
@Override @Override
public TrackedEmote createTrackedEmote(CachedEmote emote) { public TrackedEmote createTrackedEmote(CachedEmote emote) {
AServer server = serverManagementService.loadServer(emote.getServerId()); AServer server = serverManagementService.loadServer(emote.getServerId());
@@ -109,6 +114,11 @@ public class TrackedEmoteManagementServiceBean implements TrackedEmoteManagement
markAsDeleted(emote); markAsDeleted(emote);
} }
@Override
public void markAsDeleted(Emote emote) {
markAsDeleted(emote.getGuild().getIdLong(), emote.getIdLong());
}
@Override @Override
public void markAsDeleted(TrackedEmote trackedemote) { public void markAsDeleted(TrackedEmote trackedemote) {
log.info("Marking tracked emote {} in server {} as deleted.", trackedemote.getTrackedEmoteId().getId(), trackedemote.getTrackedEmoteId().getServerId()); log.info("Marking tracked emote {} in server {} as deleted.", trackedemote.getTrackedEmoteId().getId(), trackedemote.getTrackedEmoteId().getServerId());

View File

@@ -1,9 +1,9 @@
package dev.sheldan.abstracto.statistic.emote.listener; package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.models.listener.EmoteCreatedModel;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService; import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -23,14 +23,19 @@ public class CreateTrackedEmoteListenerTest {
@Mock @Mock
private TrackedEmoteManagementService trackedEmoteManagementService; private TrackedEmoteManagementService trackedEmoteManagementService;
@Mock
private EmoteCreatedModel model;
private static final Long SERVER_ID = 4L;
private static final Long EMOTE_ID = 4L;
@Test @Test
public void testEmoteCreated() { public void testEmoteCreated() {
Long serverId = 4L; Emote emote = Mockito.mock(Emote.class);
Long emoteId = 5L; when(emote.getIdLong()).thenReturn(EMOTE_ID);
CachedEmote emote = Mockito.mock(CachedEmote.class); when(model.getEmote()).thenReturn(emote);
when(emote.getEmoteId()).thenReturn(emoteId); when(model.getServerId()).thenReturn(SERVER_ID);
when(emote.getServerId()).thenReturn(serverId); testUnit.execute(model);
testUnit.emoteCreated(emote);
verify(trackedEmoteManagementService, times(1)).createTrackedEmote(emote); verify(trackedEmoteManagementService, times(1)).createTrackedEmote(emote);
} }
@@ -39,8 +44,4 @@ public class CreateTrackedEmoteListenerTest {
Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature()); Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature());
} }
@Test
public void testPriority() {
Assert.assertEquals(ListenerPriority.MEDIUM, testUnit.getPriority());
}
} }

View File

@@ -1,9 +1,9 @@
package dev.sheldan.abstracto.statistic.emote.listener; package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.models.listener.EmoteDeletedModel;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService; import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -23,15 +23,20 @@ public class DeleteTrackedEmoteListenerTest {
@Mock @Mock
private TrackedEmoteManagementService trackedEmoteManagementService; private TrackedEmoteManagementService trackedEmoteManagementService;
@Mock
private EmoteDeletedModel model;
private static final Long SERVER_ID = 4L;
private static final Long EMOTE_ID = 4L;
@Test @Test
public void testEmoteDeleted() { public void testEmoteDeleted() {
Long serverId = 4L; Emote emote = Mockito.mock(Emote.class);
Long emoteId = 5L; when(emote.getIdLong()).thenReturn(EMOTE_ID);
CachedEmote emote = Mockito.mock(CachedEmote.class); when(model.getEmote()).thenReturn(emote);
when(emote.getEmoteId()).thenReturn(emoteId); when(model.getServerId()).thenReturn(SERVER_ID);
when(emote.getServerId()).thenReturn(serverId); testUnit.execute(model);
testUnit.emoteDeleted(emote); verify(trackedEmoteManagementService, times(1)).markAsDeleted(SERVER_ID, EMOTE_ID);
verify(trackedEmoteManagementService, times(1)).markAsDeleted(serverId, emoteId);
} }
@Test @Test
@@ -39,8 +44,4 @@ public class DeleteTrackedEmoteListenerTest {
Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature()); Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature());
} }
@Test
public void testPriority() {
Assert.assertEquals(ListenerPriority.MEDIUM, testUnit.getPriority());
}
} }

View File

@@ -1,11 +1,13 @@
package dev.sheldan.abstracto.statistic.emote.listener; package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.models.cache.CachedEmote; import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.GuildService; import dev.sheldan.abstracto.core.service.GuildService;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.service.TrackedEmoteService; import dev.sheldan.abstracto.statistic.emote.service.TrackedEmoteService;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -31,13 +33,16 @@ public class EmoteTrackingListenerTest {
private GuildService guildService; private GuildService guildService;
@Mock @Mock
private CachedMessage message; private Message message;
@Mock @Mock
private CachedEmote emote1; private MessageReceivedModel messageReceivedModel;
@Mock @Mock
private CachedEmote emote2; private Emote emote1;
@Mock
private Emote emote2;
@Mock @Mock
private Guild guild; private Guild guild;
@@ -47,40 +52,43 @@ public class EmoteTrackingListenerTest {
@Test @Test
public void testExecuteOneEmote() { public void testExecuteOneEmote() {
List<CachedEmote> emotesBag = new ArrayList<>(); List<Emote> emotesBag = new ArrayList<>();
emotesBag.add(emote1); emotesBag.add(emote1);
when(guildService.getGuildById(SERVER_ID)).thenReturn(guild); when(guildService.getGuildById(SERVER_ID)).thenReturn(guild);
when(message.getServerId()).thenReturn(SERVER_ID); when(messageReceivedModel.getMessage()).thenReturn(message);
when(messageReceivedModel.getServerId()).thenReturn(SERVER_ID);
when(message.getEmotes()).thenReturn(emotesBag); when(message.getEmotes()).thenReturn(emotesBag);
testUnit.execute(message); testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote1, guild, 1L); verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote1, guild, 1L);
} }
@Test @Test
public void testExecuteOneEmoteMultipleTimes() { public void testExecuteOneEmoteMultipleTimes() {
List<CachedEmote> emotesBag = new ArrayList<>(); List<Emote> emotesBag = new ArrayList<>();
when(emote1.getEmoteId()).thenReturn(EMOTE_ID); when(emote1.getIdLong()).thenReturn(EMOTE_ID);
when(emote2.getEmoteId()).thenReturn(EMOTE_ID); when(emote2.getIdLong()).thenReturn(EMOTE_ID);
emotesBag.add(emote1); emotesBag.add(emote1);
emotesBag.add(emote2); emotesBag.add(emote2);
when(guildService.getGuildById(SERVER_ID)).thenReturn(guild); when(guildService.getGuildById(SERVER_ID)).thenReturn(guild);
when(message.getServerId()).thenReturn(SERVER_ID); when(messageReceivedModel.getServerId()).thenReturn(SERVER_ID);
when(messageReceivedModel.getMessage()).thenReturn(message);
when(message.getEmotes()).thenReturn(emotesBag); when(message.getEmotes()).thenReturn(emotesBag);
testUnit.execute(message); testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(any(CachedEmote.class), eq(guild), eq(2L)); verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(any(CachedEmote.class), eq(guild), eq(2L));
} }
@Test @Test
public void testExecuteMultipleEmotes() { public void testExecuteMultipleEmotes() {
List<CachedEmote> emotesBag = new ArrayList<>(); List<Emote> emotesBag = new ArrayList<>();
when(emote1.getEmoteId()).thenReturn(EMOTE_ID); when(emote1.getIdLong()).thenReturn(EMOTE_ID);
when(emote2.getEmoteId()).thenReturn(EMOTE_ID + 1); when(emote2.getIdLong()).thenReturn(EMOTE_ID + 1);
emotesBag.add(emote1); emotesBag.add(emote1);
emotesBag.add(emote2); emotesBag.add(emote2);
when(guildService.getGuildById(SERVER_ID)).thenReturn(guild); when(guildService.getGuildById(SERVER_ID)).thenReturn(guild);
when(message.getServerId()).thenReturn(SERVER_ID); when(messageReceivedModel.getServerId()).thenReturn(SERVER_ID);
when(messageReceivedModel.getMessage()).thenReturn(message);
when(message.getEmotes()).thenReturn(emotesBag); when(message.getEmotes()).thenReturn(emotesBag);
testUnit.execute(message); testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote1, guild, 1L); verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote1, guild, 1L);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote2, guild, 1L); verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote2, guild, 1L);
} }
@@ -88,8 +96,9 @@ public class EmoteTrackingListenerTest {
@Test @Test
public void testExecuteNoEmote() { public void testExecuteNoEmote() {
when(message.getEmotes()).thenReturn(new ArrayList<>()); when(message.getEmotes()).thenReturn(new ArrayList<>());
testUnit.execute(message); when(messageReceivedModel.getMessage()).thenReturn(message);
verify(trackedEmoteService, times(0)).addEmoteToRuntimeStorage(any(), any(), anyLong()); testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(0)).addEmoteToRuntimeStorage(any(Emote.class), any(), anyLong());
} }

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.statistic.emote.listener; package dev.sheldan.abstracto.statistic.emote.listener;
import dev.sheldan.abstracto.core.config.ListenerPriority; import dev.sheldan.abstracto.core.models.listener.EmoteNameUpdatedModel;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition; import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote; import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService; import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -19,22 +19,26 @@ import static org.mockito.Mockito.*;
public class UpdateTrackedEmoteListenerTest { public class UpdateTrackedEmoteListenerTest {
@InjectMocks @InjectMocks
private UpdateTrackedEmoteListener testUnit; private UpdateTrackedEmoteNameListener testUnit;
@Mock @Mock
private TrackedEmoteManagementService trackedEmoteManagementService; private TrackedEmoteManagementService trackedEmoteManagementService;
@Mock
private EmoteNameUpdatedModel model;
@Test @Test
public void testEmoteUpdated() { public void testEmoteUpdated() {
Long serverId = 1L; Long serverId = 1L;
Long emoteId = 2L; Long emoteId = 2L;
CachedEmote changedEmote = Mockito.mock(CachedEmote.class); Emote changedEmote = Mockito.mock(Emote.class);
when(changedEmote.getServerId()).thenReturn(serverId); when(changedEmote.getIdLong()).thenReturn(emoteId);
when(changedEmote.getEmoteId()).thenReturn(emoteId);
TrackedEmote trackedEmote = Mockito.mock(TrackedEmote.class); TrackedEmote trackedEmote = Mockito.mock(TrackedEmote.class);
when(trackedEmoteManagementService.loadByEmoteId(emoteId, serverId)).thenReturn(trackedEmote); when(trackedEmoteManagementService.loadByEmoteId(emoteId, serverId)).thenReturn(trackedEmote);
String newValue = "AFTER"; String newValue = "AFTER";
testUnit.emoteUpdated(changedEmote, "BEFORE", newValue); when(model.getEmote()).thenReturn(changedEmote);
when(model.getNewValue()).thenReturn(newValue);
testUnit.execute(model);
verify(trackedEmoteManagementService, times(1)).changeName(trackedEmote, newValue); verify(trackedEmoteManagementService, times(1)).changeName(trackedEmote, newValue);
} }
@@ -43,8 +47,4 @@ public class UpdateTrackedEmoteListenerTest {
Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature()); Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature());
} }
@Test
public void testPriority() {
Assert.assertEquals(ListenerPriority.MEDIUM, testUnit.getPriority());
}
} }

View File

@@ -31,6 +31,14 @@ public interface TrackedEmoteService {
*/ */
void addEmoteToRuntimeStorage(CachedEmote emote, Guild guild, Long count); void addEmoteToRuntimeStorage(CachedEmote emote, Guild guild, Long count);
/**
* Adds the given {@link Emote} with the given amount to the runtime storage for the given {@link Guild}
* @param emote The {@link Emote} to add to the runtime storage
* @param guild The {@link Guild} in which the {@link Emote} was used and in which the usage should be added
* @param count The amount of times which the {@link Emote} has been used and should be reflected in the runtime storage
*/
void addEmoteToRuntimeStorage(Emote emote, Guild guild, Long count);
/** /**
* Takes the given map of server_ids with the list of {@link PersistingEmote} and stores the objects in the database * Takes the given map of server_ids with the list of {@link PersistingEmote} and stores the objects in the database
* Non existing {@link TrackedEmote} for the server will be created. Depending on the feature mode external emotes will be created. * Non existing {@link TrackedEmote} for the server will be created. Depending on the feature mode external emotes will be created.

View File

@@ -34,6 +34,15 @@ public interface TrackedEmoteManagementService {
*/ */
TrackedEmote createTrackedEmote(Emote emote, Guild guild); TrackedEmote createTrackedEmote(Emote emote, Guild guild);
/**
* Creates and persists a {@link TrackedEmote} for which tracking is enabled based on the given {@link Emote}.
* The emote used here must contain a {@link Guild guild} instance, emotes created from {@link net.dv8tion.jda.api.entities.Message messages}
* do not.
* @param emote The {@link Emote} to be used to create a {@link TrackedEmote}
* @return The created {@link TrackedEmote} instance in the database
*/
TrackedEmote createTrackedEmote(Emote emote);
/** /**
* Creates and persists a {@link TrackedEmote} for which tracking is enabled based on the given {@link Emote} and {@link Guild} * Creates and persists a {@link TrackedEmote} for which tracking is enabled based on the given {@link Emote} and {@link Guild}
* @param emote The {@link CachedEmote} to be used to create a {@link TrackedEmote} * @param emote The {@link CachedEmote} to be used to create a {@link TrackedEmote}
@@ -106,6 +115,14 @@ public interface TrackedEmoteManagementService {
*/ */
void markAsDeleted(Long serverId, Long emoteId); void markAsDeleted(Long serverId, Long emoteId);
/**
* Marks the {@link Emote emote} as deleted in the database. This {@link Emote emote} must
* not come from a {@link net.dv8tion.jda.api.entities.Message message}, because then the {@link Guild guild}
* is null.
* @throws TrackedEmoteNotFoundException if no {@link TrackedEmote} with the given IDs can be found
*/
void markAsDeleted(Emote emote);
/** /**
* Marks the given {@link TrackedEmote} as deleted * Marks the given {@link TrackedEmote} as deleted
* @param trackedEmote The {@link TrackedEmote} which should be marked as deleted * @param trackedEmote The {@link TrackedEmote} which should be marked as deleted

View File

@@ -56,8 +56,7 @@ public class SuggestionManagementServiceBean implements SuggestionManagementServ
.build(); .build();
log.info("Persisting suggestion {} at message {} in channel {} on server {} from user {}.", log.info("Persisting suggestion {} at message {} in channel {} on server {} from user {}.",
suggestionId, message.getId(), channelId, message.getGuild().getId(), suggester.getUserReference().getId()); suggestionId, message.getId(), channelId, message.getGuild().getId(), suggester.getUserReference().getId());
suggestionRepository.save(suggestion); return suggestionRepository.save(suggestion);
return suggestion;
} }
@Override @Override

View File

@@ -16,6 +16,12 @@
<artifactId>core-impl</artifactId> <artifactId>core-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
</dependency>
<!-- modules containing commands --> <!-- modules containing commands -->
<dependency> <dependency>
@@ -24,83 +30,167 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail-impl</artifactId> <artifactId>modmail-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility-impl</artifactId> <artifactId>utility-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking-impl</artifactId> <artifactId>experience-tracking-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId> <groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-impl</artifactId> <artifactId>scheduling-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles-impl</artifactId> <artifactId>assignable-roles-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic-impl</artifactId> <artifactId>statistic-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.core</groupId> <groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-impl</artifactId> <artifactId>metrics-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>entertainment-impl</artifactId> <artifactId>entertainment-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>entertainment-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>link-embed-impl</artifactId> <artifactId>link-embed-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>link-embed-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>remind-impl</artifactId> <artifactId>remind-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>remind-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>repost-detection-impl</artifactId> <artifactId>repost-detection-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>repost-detection-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>starboard-impl</artifactId> <artifactId>starboard-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>starboard-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>suggestion-impl</artifactId> <artifactId>suggestion-impl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>suggestion-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -4,8 +4,11 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.models.database.ACommand; import dev.sheldan.abstracto.core.command.models.database.ACommand;
import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService; import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService;
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService; import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
import dev.sheldan.abstracto.core.listener.sync.entity.ServerConfigListener; import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.entity.AsyncServerCreatedListener;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.listener.ServerCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -14,7 +17,7 @@ import java.util.List;
@Component @Component
@Slf4j @Slf4j
public class CommandConfigListener implements ServerConfigListener { public class CommandConfigListenerAsync implements AsyncServerCreatedListener {
@Autowired @Autowired
private List<Command> commandList; private List<Command> commandList;
@@ -25,9 +28,13 @@ public class CommandConfigListener implements ServerConfigListener {
@Autowired @Autowired
private CommandManagementService commandManagementService; private CommandManagementService commandManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override @Override
public void updateServerConfig(AServer server) { public DefaultListenerResult execute(ServerCreatedListenerModel model) {
log.info("Creating command instances for server {}.", server.getId()); AServer server = serverManagementService.loadServer(model.getServerId());
log.info("Creating command instances for server {}.", model.getServerId());
commandList.forEach(command -> { commandList.forEach(command -> {
if(command.getConfiguration() != null) { if(command.getConfiguration() != null) {
ACommand aCommand = commandManagementService.findCommandByName(command.getConfiguration().getName()); ACommand aCommand = commandManagementService.findCommandByName(command.getConfiguration().getName());
@@ -36,5 +43,6 @@ public class CommandConfigListener implements ServerConfigListener {
} }
} }
}); });
return DefaultListenerResult.PROCESSED;
} }
} }

View File

@@ -40,8 +40,7 @@ public class ChannelGroupCommandManagementServiceBean implements ChannelGroupCom
log.info("Creating command {} in group {}.", command.getName(), group.getId()); log.info("Creating command {} in group {}.", command.getName(), group.getId());
groupCommandRepository.save(channelGroupCommand); return groupCommandRepository.save(channelGroupCommand);
return channelGroupCommand;
} }
@Override @Override

View File

@@ -24,9 +24,8 @@ public class CommandInServerManagementServiceBean implements CommandInServerMana
.serverReference(server) .serverReference(server)
.restricted(false) .restricted(false)
.build(); .build();
repository.save(commandInAServer);
log.info("Creating command {} in server {}.", command.getName(), server.getId()); log.info("Creating command {} in server {}.", command.getName(), server.getId());
return commandInAServer; return repository.save(commandInAServer);
} }
@Override @Override

View File

@@ -40,8 +40,7 @@ public class CommandManagementServiceBean implements CommandManagementService {
.feature(feature) .feature(feature)
.build(); .build();
log.info("Creating creating command {} in module {} with feature {}.", name, module.getName(), feature.getKey()); log.info("Creating creating command {} in module {} with feature {}.", name, module.getName(), feature.getKey());
commandRepository.save(command); return commandRepository.save(command);
return command;
} }
@Override @Override

View File

@@ -19,9 +19,8 @@ public class ModuleManagementServiceBean implements ModuleManagementService {
builder() builder()
.name(name) .name(name)
.build(); .build();
moduleRepository.save(module);
log.info("Creating module {}.", name); log.info("Creating module {}.", name);
return module; return moduleRepository.save(module);
} }
@Override @Override

View File

@@ -1,117 +1,82 @@
package dev.sheldan.abstracto.core.config; package dev.sheldan.abstracto.core.config;
import dev.sheldan.abstracto.core.service.ExecutorService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor; import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration @Configuration
public class ListenerExecutorConfig { public class ListenerExecutorConfig {
@Value("${abstracto.listener.default.maxPoolSize}")
private Integer defaultMaxPoolSize;
@Value("${abstracto.listener.default.corePoolSize}")
private Integer defaultCorePoolSize;
@Value("${abstracto.listener.default.keepAliveSeconds}")
private Integer defaultKeepAliveSeconds;
@Autowired @Autowired
private Environment environment; private ExecutorService executorService;
private static final String LISTENER_PREFIX = "abstracto.listener.";
private static final String LISTENER_MAX_POOL_SIZE = "maxPoolSize";
private static final String LISTENER_CORE_POOL_SIZE = "corePoolSize";
private static final String LISTENER_KEEP_ALIVE_SECONDS = "keepAliveSeconds";
@Bean(name = "joinListenerExecutor") @Bean(name = "joinListenerExecutor")
public TaskExecutor joinListenerExecutor() { public TaskExecutor joinListenerExecutor() {
return setupExecutorFor("joinListener"); return executorService.setupExecutorFor("joinListener");
} }
@Bean(name = "leaveListenerExecutor") @Bean(name = "leaveListenerExecutor")
public TaskExecutor leaveListenerExecutor() { public TaskExecutor leaveListenerExecutor() {
return setupExecutorFor("leaveListener"); return executorService.setupExecutorFor("leaveListener");
} }
@Bean(name = "messageReceivedExecutor") @Bean(name = "messageReceivedExecutor")
public TaskExecutor messageReceivedExecutor() { public TaskExecutor messageReceivedExecutor() {
return setupExecutorFor("messageReceivedListener"); return executorService.setupExecutorFor("messageReceivedListener");
} }
@Bean(name = "messageDeletedExecutor") @Bean(name = "messageDeletedExecutor")
public TaskExecutor messageDeletedExecutor() { public TaskExecutor messageDeletedExecutor() {
return setupExecutorFor("messageReceivedListener"); return executorService.setupExecutorFor("messageReceivedListener");
} }
@Bean(name = "messageEmbeddedExecutor") @Bean(name = "messageEmbeddedExecutor")
public TaskExecutor messageEmbeddedExecutor() { public TaskExecutor messageEmbeddedExecutor() {
return setupExecutorFor("messageEmbeddedListener"); return executorService.setupExecutorFor("messageEmbeddedListener");
} }
@Bean(name = "messageUpdatedExecutor") @Bean(name = "messageUpdatedExecutor")
public TaskExecutor messageUpdatedExecutor() { public TaskExecutor messageUpdatedExecutor() {
return setupExecutorFor("messageUpdatedListener"); return executorService.setupExecutorFor("messageUpdatedListener");
} }
@Bean(name = "privateMessageReceivedExecutor") @Bean(name = "privateMessageReceivedExecutor")
public TaskExecutor privateMessageReceivedExecutor() { public TaskExecutor privateMessageReceivedExecutor() {
return setupExecutorFor("privateMessageReceivedListener"); return executorService.setupExecutorFor("privateMessageReceivedListener");
} }
@Bean(name = "emoteCreatedExecutor") @Bean(name = "emoteCreatedExecutor")
public TaskExecutor emoteCreatedExecutor() { public TaskExecutor emoteCreatedExecutor() {
return setupExecutorFor("emoteCreatedListener"); return executorService.setupExecutorFor("emoteCreatedListener");
} }
@Bean(name = "emoteDeletedExecutor") @Bean(name = "emoteDeletedExecutor")
public TaskExecutor emoteDeletedExecutor() { public TaskExecutor emoteDeletedExecutor() {
return setupExecutorFor("emoteDeletedListener"); return executorService.setupExecutorFor("emoteDeletedListener");
} }
@Bean(name = "emoteUpdatedExecutor") @Bean(name = "emoteUpdatedExecutor")
public TaskExecutor emoteUpdatedExecutor() { public TaskExecutor emoteUpdatedExecutor() {
return setupExecutorFor("emoteUpdatedListener"); return executorService.setupExecutorFor("emoteUpdatedListener");
} }
@Bean(name = "reactionAddedExecutor") @Bean(name = "reactionAddedExecutor")
public TaskExecutor reactionAddedExecutor() { public TaskExecutor reactionAddedExecutor() {
return setupExecutorFor("reactionAddedListener"); return executorService.setupExecutorFor("reactionAddedListener");
} }
@Bean(name = "reactionRemovedExecutor") @Bean(name = "reactionRemovedExecutor")
public TaskExecutor reactionRemovedExecutor() { public TaskExecutor reactionRemovedExecutor() {
return setupExecutorFor("reactionRemovedListener"); return executorService.setupExecutorFor("reactionRemovedListener");
} }
@Bean(name = "reactionClearedExecutor") @Bean(name = "reactionClearedExecutor")
public TaskExecutor reactionClearedExecutor() { public TaskExecutor reactionClearedExecutor() {
return setupExecutorFor("reactionClearedListener"); return executorService.setupExecutorFor("reactionClearedListener");
} }
public ThreadPoolTaskExecutor setupExecutorFor(String listenerName) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
Integer maxPoolSize = getPropertyValueInteger(listenerName, LISTENER_MAX_POOL_SIZE, defaultMaxPoolSize.toString());
Integer corePoolSize = getPropertyValueInteger(listenerName, LISTENER_CORE_POOL_SIZE, defaultCorePoolSize.toString());
Integer keepAliveSeconds = getPropertyValueInteger(listenerName, LISTENER_KEEP_ALIVE_SECONDS, defaultKeepAliveSeconds.toString());
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(listenerName + "-task-executor-thread");
executor.initialize();
return executor;
}
public String getPropertyValue(String listenerName, String key, String defaultValue) {
return environment.getProperty(LISTENER_PREFIX + listenerName + "." + key, defaultValue);
}
public Integer getPropertyValueInteger(String listenerName, String key, String defaultValue) {
return Integer.parseInt(getPropertyValue(listenerName, key, defaultValue));
}
} }

View File

@@ -1,47 +1,51 @@
package dev.sheldan.abstracto.core.listener.sync.jda; package dev.sheldan.abstracto.core.listener.async.entity;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.database.AChannelType; import dev.sheldan.abstracto.core.models.database.AChannelType;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.listener.AChannelCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService; import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.channel.text.TextChannelCreateEvent; import net.dv8tion.jda.api.events.channel.text.TextChannelCreateEvent;
import net.dv8tion.jda.api.events.channel.text.TextChannelDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.List;
@Service @Component
@Slf4j @Slf4j
public class ChannelListener extends ListenerAdapter { public class AsyncAChannelCreatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncAChannelCreatedListener> channelListener;
@Autowired @Autowired
private ServerManagementService serverManagementService; private ServerManagementService serverManagementService;
@Autowired
private ListenerService listenerService;
@Autowired @Autowired
private ChannelManagementService channelManagementService; private ChannelManagementService channelManagementService;
// TODO move this to a separate sync listener, which implements a a generic channel listener, in order to provide
// the channel listener for other implementations
@Override @Override
@Transactional(isolation = Isolation.SERIALIZABLE)
public void onTextChannelDelete(@Nonnull TextChannelDeleteEvent event) {
log.info("Handling channel delete event. Marking channel {} as deleted in server {}", event.getChannel().getIdLong(), event.getGuild().getIdLong());
channelManagementService.markAsDeleted(event.getChannel().getIdLong());
}
@Override
@Transactional(isolation = Isolation.SERIALIZABLE)
public void onTextChannelCreate(@Nonnull TextChannelCreateEvent event) { public void onTextChannelCreate(@Nonnull TextChannelCreateEvent event) {
log.info("Handling channel created event. Creating channel {} in server {}", event.getChannel().getIdLong(), event.getGuild().getIdLong()); log.info("Creating text channel with ID {}.", event.getChannel().getIdLong());
AServer serverObject = serverManagementService.loadOrCreate(event.getGuild().getIdLong()); AServer serverObject = serverManagementService.loadOrCreate(event.getChannel().getGuild().getIdLong());
TextChannel createdChannel = event.getChannel(); TextChannel createdChannel = event.getChannel();
AChannelType type = AChannelType.getAChannelType(createdChannel.getType()); AChannelType type = AChannelType.getAChannelType(createdChannel.getType());
channelManagementService.createChannel(createdChannel.getIdLong(), type, serverObject); channelManagementService.createChannel(createdChannel.getIdLong(), type, serverObject);
} }
@TransactionalEventListener
public void executeServerCreationListener(AChannelCreatedListenerModel model) {
if(channelListener == null) return;
channelListener.forEach(serverCreatedListener -> listenerService.executeListener(serverCreatedListener, model));
}
} }

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.core.listener.async.entity;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.listener.AChannelDeletedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.channel.text.TextChannelDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import javax.annotation.Nonnull;
import java.util.List;
@Component
@Slf4j
public class AsyncAChannelDeletedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncAChannelDeletedListener> channelDeletedListeners;
@Autowired
private ListenerService listenerService;
@Autowired
private ChannelManagementService channelManagementService;
@Override
public void onTextChannelDelete(@Nonnull TextChannelDeleteEvent event) {
channelManagementService.markAsDeleted(event.getChannel().getIdLong());
}
@TransactionalEventListener
public void executeServerCreationListener(AChannelDeletedListenerModel model) {
if(channelDeletedListeners == null) return;
channelDeletedListeners.forEach(serverCreatedListener -> listenerService.executeListener(serverCreatedListener, model));
}
}

View File

@@ -0,0 +1,46 @@
package dev.sheldan.abstracto.core.listener.async.entity;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.listener.ARoleCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.role.RoleCreateEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import javax.annotation.Nonnull;
import java.util.List;
@Component
@Slf4j
public class AsyncARoleCreatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncARoleCreatedListener> roleCreatedListeners;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ListenerService listenerService;
@Autowired
private RoleManagementService roleManagementService;
@Override
public void onRoleCreate(@Nonnull RoleCreateEvent event) {
AServer server = serverManagementService.loadServer(event.getGuild());
roleManagementService.createRole(event.getRole().getIdLong(), server);
}
@TransactionalEventListener
public void executeServerCreationListener(ARoleCreatedListenerModel model) {
if(roleCreatedListeners == null) return;
roleCreatedListeners.forEach(asyncServerCreatedListener -> listenerService.executeListener(asyncServerCreatedListener, model));
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.core.listener.async.entity;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.listener.ARoleDeletedListenerModel;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.role.RoleDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import javax.annotation.Nonnull;
import java.util.List;
@Component
@Slf4j
public class AsyncARoleDeletedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncARoleDeletedListener> roleDeletedListeners;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private ListenerService listenerService;
@Override
public void onRoleDelete(@Nonnull RoleDeleteEvent event) {
roleManagementService.markDeleted(event.getRole().getIdLong());
}
@TransactionalEventListener
public void executeServerCreationListener(ARoleDeletedListenerModel model) {
if(roleDeletedListeners == null) return;
roleDeletedListeners.forEach(serverCreatedListener -> listenerService.executeListener(serverCreatedListener, model));
}
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.core.listener.async.entity;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.listener.sync.entity.AsyncChannelGroupCreatedListener;
import dev.sheldan.abstracto.core.models.listener.ChannelGroupCreatedListenerModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import java.util.List;
@Component
public class AsyncChannelGroupCreatedListenerManager {
@Autowired(required = false)
private List<AsyncChannelGroupCreatedListener> listener;
@Autowired
private ListenerService listenerService;
@TransactionalEventListener
public void executeListener(ChannelGroupCreatedListenerModel createdGroup){
listener.forEach(asyncChannelGroupCreatedListener ->
listenerService.executeListener(asyncChannelGroupCreatedListener, createdGroup)
);
}
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.core.listener.async.entity;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.listener.sync.entity.AsyncChannelGroupDeletedListener;
import dev.sheldan.abstracto.core.models.listener.ChannelGroupDeletedListenerModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import java.util.List;
@Component
public class AsyncChannelGroupDeletedListenerManager {
@Autowired(required = false)
private List<AsyncChannelGroupDeletedListener> listener;
@Autowired
private ListenerService listenerService;
@TransactionalEventListener
public void executeListener(ChannelGroupDeletedListenerModel model){
listener.forEach(channelGroupCreatedListener ->
listenerService.executeListener(channelGroupCreatedListener, model)
);
}
}

Some files were not shown because too many files have changed in this diff Show More