[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.service.management.AssignableRolePlacePostManagementService;
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.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -20,24 +22,26 @@ public class AssignablePostDeletedListener implements AsyncMessageDeletedListene
@Autowired
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
public FeatureDefinition getFeature() {
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.AssignedRoleUserManagementService;
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.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.ReactionService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -69,69 +70,32 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
@Autowired
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}
* 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
* {@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
* 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 CachedReaction reaction} was added to
* @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
* @param assignablePlacePost The {@link AssignableRolePlacePost post} onto which the {@link MessageReaction reaction} was added to
* @param model The {@link ReactionAddedModel model} containing information about who added which reaction where
*/
private void addAppropriateRoles(CachedReaction cachedReaction, AssignableRolePlacePost assignablePlacePost, ServerUser serverUser, CachedMessage message) {
private void addAppropriateRoles(AssignableRolePlacePost assignablePlacePost, ReactionAddedModel model) {
boolean validReaction = false;
AssignableRolePlace assignableRolePlace = assignablePlacePost.getAssignablePlace();
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (AssignableRole assignableRole : assignablePlacePost.getAssignableRoles()) {
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()) {
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)));
}
Long assignableRoleId = assignableRole.getId();
log.info("User added {} reaction {} and gets assignable role {} in server {}.", serverUser.getUserId(), assignableRole.getEmote().getId(), assignableRoleId, serverUser.getServerId());
CompletableFuture<Void> roleAdditionFuture = assignableRoleServiceBean.assignAssignableRoleToUser(assignableRoleId, serverUser);
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, model.getUserReacting());
futures.add(CompletableFuture.allOf(roleAdditionFuture));
validReaction = true;
@@ -140,11 +104,11 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
}
if(!validReaction) {
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();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
self.updateStoredAssignableRoles(assignableRolePlaceId, serverUser, cachedReaction)
self.updateStoredAssignableRoles(assignableRolePlaceId, model.getUserReacting(), model.getReaction())
).exceptionally(throwable -> {
log.error("Failed to add role or remove emote for assignable role place {}.", assignableRolePlaceId, throwable);
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
* @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 cachedReaction The {@link CachedReaction reaction} wich was added
* @param reaction The {@link CachedReaction reaction} wich was added
*/
@Transactional
public void updateStoredAssignableRoles(Long assignableRolePlaceId, ServerUser serverUser, CachedReaction cachedReaction) {
public void updateStoredAssignableRoles(Long assignableRolePlaceId, ServerUser serverUser, MessageReaction reaction) {
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(assignableRolePlaceId);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser);
if(place.getUniqueRoles()) {
log.trace("Assignable role place {} has unique roles. Deleting all existing references.", assignableRolePlaceId);
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());
assignableRoleServiceBean.addRoleToUser(role.getId(), userInAServer);
@@ -176,4 +140,36 @@ public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
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.management.AssignableRolePlacePostManagementService;
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.models.ServerUser;
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.RoleService;
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.
* 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 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}
* @param model The {@link ReactionRemovedModel model} containing the information which reaction was placed where
*/
@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());
if(messageOptional.isPresent()) {
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(assignablePlacePost.getAssignablePlace().getActive()) {
assignablePlacePost.getAssignableRoles().forEach(assignableRole -> {
if(emoteService.compareCachedEmoteWithAEmote(reactions.getEmote(), assignableRole.getEmote())) {
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), assignableRole.getEmote())) {
Long assignableRoleId = assignableRole.getId();
log.info("Removing assignable role {} for user {} in server {} from assignable role place {}.", assignableRoleId,
userRemoving.getUserId(), userRemoving.getServerId(), assignablePlacePost.getAssignablePlace().getId());
assignableRoleService.fullyRemoveAssignableRoleFromUser(assignableRole, userRemoving).exceptionally(throwable -> {
log.error("Failed to remove assignable role {} from user {} in server {}.", assignableRoleId, userRemoving.getUserId(), userRemoving.getServerId(), throwable);
model.getUserRemoving().getUserId(), model.getServerId(), assignablePlacePost.getAssignablePlace().getId());
assignableRoleService.fullyRemoveAssignableRoleFromUser(assignableRole, model.getUserRemoving()).exceptionally(throwable -> {
log.error("Failed to remove assignable role {} from user {} in server {}.", assignableRoleId, model.getUserRemoving().getUserId(), model.getServerId(), throwable);
return null;
});
}
});
return DefaultListenerResult.PROCESSED;
} 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.RoleManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -95,4 +96,16 @@ public class AssignableRoleManagementServiceBean implements AssignableRoleManage
}
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)
.key(name)
.build();
repository.save(place);
log.info("Creating assignable role place in channel {} on server {}.", channel.getId(), channel.getServer().getId());
return place;
return repository.save(place);
}
@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.database.AEmote;
import dev.sheldan.abstracto.core.models.database.ARole;
import net.dv8tion.jda.api.entities.MessageReaction;
/**
* 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
*/
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;
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.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.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
@@ -27,9 +28,10 @@ public class ExperienceTrackerListener implements AsyncMessageReceivedListener {
private UserInServerManagementService userInServerManagementService;
@Override
public void execute(CachedMessage message) {
AUserInAServer cause = userInServerManagementService.loadOrCreateUser(message.getServerId(), message.getAuthor().getAuthorId());
public DefaultListenerResult execute(MessageReceivedModel model) {
AUserInAServer cause = userInServerManagementService.loadOrCreateUser(model.getServerId(), model.getMessage().getAuthor().getIdLong());
userExperienceService.addExperience(cause);
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -1,10 +1,11 @@
package dev.sheldan.abstracto.experience.listener;
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.sync.jda.JoinListener;
import dev.sheldan.abstracto.core.models.ServerUser;
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.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
@@ -33,18 +34,20 @@ public class JoiningUserRoleListener implements AsyncJoinListener {
private UserInServerManagementService userInServerManagementService;
@Override
public void execute(ServerUser serverUser) {
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser.getServerId(), serverUser.getUserId());
public DefaultListenerResult execute(MemberJoinModel model) {
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(model.getServerId(), model.getJoiningUser().getUserId());
AUserExperience userExperience = userExperienceManagementService.findUserInServer(userInAServer);
Long userInServerId = userInAServer.getUserInServerId();
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 ->
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 {
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

View File

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

View File

@@ -1,12 +1,11 @@
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.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
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.runner.RunWith;
import org.mockito.InjectMocks;
@@ -28,16 +27,25 @@ public class ExperienceTrackerListenerTest {
@Mock
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
public void testExperienceTracking() {
AServer server = Mockito.mock(AServer.class);
AUser user = Mockito.mock(AUser.class);
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
CachedMessage mockedMessage = Mockito.mock(CachedMessage.class);
CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class);
when(mockedMessage.getAuthor()).thenReturn(cachedAuthor);
when(userInServerManagementService.loadOrCreateUser(server.getId(), user.getId())).thenReturn(userInAServer);
testUnit.execute(mockedMessage);
Message mockedMessage = Mockito.mock(Message.class);
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(userInAServer);
when(model.getMessage()).thenReturn(mockedMessage);
when(model.getServerId()).thenReturn(SERVER_ID);
when(mockedMessage.getAuthor()).thenReturn(user);
when(user.getIdLong()).thenReturn(USER_ID);
testUnit.execute(model);
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.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MemberJoinModel;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
@@ -39,6 +40,9 @@ public class JoiningUserRoleListenerTest {
@Mock
private AUserInAServer aUserInAServer;
@Mock
private MemberJoinModel model;
private static final Long SERVER_ID = 1L;
private static final Long USER_ID = 2L;
@@ -46,6 +50,7 @@ public class JoiningUserRoleListenerTest {
public void setup() {
when(serverUser.getServerId()).thenReturn(SERVER_ID);
when(serverUser.getUserId()).thenReturn(USER_ID);
when(model.getJoiningUser()).thenReturn(serverUser);
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, USER_ID)).thenReturn(aUserInAServer);
}
@@ -54,13 +59,13 @@ public class JoiningUserRoleListenerTest {
AUserExperience experience = Mockito.mock(AUserExperience.class);
when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(experience);
when(userExperienceService.syncForSingleUser(experience)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.execute(serverUser);
testUnit.execute(model);
}
@Test
public void testUserWithOutExperienceRejoining() {
when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(null);
testUnit.execute(serverUser);
testUnit.execute(model);
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.ListenerPriority;
import dev.sheldan.abstracto.core.execution.result.ExecutionResult;
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.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
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.MessageService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -59,7 +59,8 @@ public class MessageEmbedListener implements MessageReceivedListener {
.build();
@Override
public MessageReceivedListenerResult execute(Message message) {
public ConsumableListenerResult execute(MessageReceivedModel model) {
Message message = model.getMessage();
String messageRaw = message.getContentRaw();
List<MessageEmbedLink> links = messageEmbedService.getLinksInMessage(messageRaw);
if(!links.isEmpty()) {
@@ -82,12 +83,12 @@ public class MessageEmbedListener implements MessageReceivedListener {
}
if(StringUtils.isBlank(messageRaw) && !links.isEmpty()) {
messageService.deleteMessage(message);
return MessageReceivedListenerResult.DELETED;
return ConsumableListenerResult.DELETED;
}
if(!links.isEmpty()) {
return MessageReceivedListenerResult.PROCESSED;
return ConsumableListenerResult.PROCESSED;
}
return MessageReceivedListenerResult.IGNORED;
return ConsumableListenerResult.IGNORED;
}
@Transactional
@@ -104,8 +105,8 @@ public class MessageEmbedListener implements MessageReceivedListener {
}
@Override
public boolean shouldConsume(Event event, ExecutionResult result) {
return result.equals(MessageReceivedListenerResult.DELETED);
public boolean shouldConsume(Event event, ConsumableListenerResult result) {
return result.equals(ConsumableListenerResult.DELETED);
}
@Override

View File

@@ -1,14 +1,13 @@
package dev.sheldan.abstracto.linkembed.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.MetricService;
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.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService;
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")))
.build();
@Override
public void executeReactionAdded(CachedMessage message, CachedReactions cachedReaction, ServerUser serverUser) {
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(REMOVAL_EMOTE, guildId);
if(emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), aEmote)) {
Optional<EmbeddedMessage> embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId());
public DefaultListenerResult execute(ReactionAddedModel model) {
Long serverId = model.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(REMOVAL_EMOTE, serverId);
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
Long messageId = model.getMessage().getMessageId();
Optional<EmbeddedMessage> embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(messageId);
if(embeddedMessageOptional.isPresent()) {
Long channelId = model.getMessage().getChannelId();
EmbeddedMessage embeddedMessage = embeddedMessageOptional.get();
boolean embeddedUserRemoves = embeddedMessage.getEmbeddedUser().getUserReference().getId().equals(serverUser.getUserId());
boolean embeddingUserRemoves = embeddedMessage.getEmbeddingUser().getUserReference().getId().equals(serverUser.getUserId());
boolean embeddedUserRemoves = embeddedMessage.getEmbeddedUser().getUserReference().getId().equals(model.getUserReacting().getUserId());
boolean embeddingUserRemoves = embeddedMessage.getEmbeddingUser().getUserReference().getId().equals(model.getUserReacting().getUserId());
if(embeddedUserRemoves || embeddingUserRemoves) {
log.info("Removing embed in message {} in channel {} in server {} because of a user reaction.", message.getMessageId(), message.getChannelId(), message.getServerId());
messageService.deleteMessageInChannelInServer(message.getServerId(), message.getChannelId(), message.getMessageId()).thenAccept(aVoid -> {
Optional<EmbeddedMessage> innerOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId());
log.info("Removing embed in message {} in channel {} in server {} because of a user reaction.", messageId, channelId, serverId);
messageService.deleteMessageInChannelInServer(serverId, channelId, messageId).thenAccept(aVoid -> {
Optional<EmbeddedMessage> innerOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(messageId);
innerOptional.ifPresent(value -> messageEmbedPostManagementService.deleteEmbeddedMessage(value));
if(embeddedUserRemoves) {
metricService.incrementCounter(MESSAGE_EMBED_REMOVED_SOURCE);
@@ -83,13 +83,19 @@ public class MessageEmbedRemovalReactionListener implements AsyncReactionAddedLi
});
} else {
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 {
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

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.models.cache.CachedMessage;
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.MessageService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -57,6 +58,9 @@ public class MessageEmbedListenerTest {
@Mock
private TextChannel textChannel;
@Mock
private MessageReceivedModel model;
@Mock
private Guild guild;
@@ -80,7 +84,8 @@ public class MessageEmbedListenerTest {
when(message.getContentRaw()).thenReturn(text);
List<MessageEmbedLink> foundMessageLinks = new ArrayList<>();
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message);
when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message);
}
@@ -116,7 +121,8 @@ public class MessageEmbedListenerTest {
when(message.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message);
when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message);
verify(self, times(0)).embedSingleLink(eq(message), anyLong(), any(CachedMessage.class));
verify(messageCache, times(0)).getMessageFromCache(anyLong(), anyLong(), anyLong());
@@ -148,7 +154,8 @@ public class MessageEmbedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
when(messageEmbedService.getLinksInMessage(completeMessage)).thenReturn(foundMessageLinks);
testUnit.execute(message);
when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message);
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, SECOND_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(secondCachedMessage));
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
testUnit.execute(message);
when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
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, secondCachedMessage);
@@ -210,7 +218,8 @@ public class MessageEmbedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
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.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.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage;
import dev.sheldan.abstracto.linkembed.service.management.MessageEmbedPostManagementService;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@@ -43,10 +43,10 @@ public class MessageEmbedRemovalReactionListenerTest {
private MetricService metricService;
@Mock
private CachedReactions messageReaction;
private MessageReaction messageReaction;
@Mock
private CachedEmote reactionEmote;
private MessageReaction.ReactionEmote reactionEmote;
@Mock
private AUserInAServer embeddingUser;
@@ -63,6 +63,9 @@ public class MessageEmbedRemovalReactionListenerTest {
@Mock
private ServerUser reactingUser;
@Mock
private ReactionAddedModel model;
private static final Long SERVER_ID = 4L;
private static final Long CHANNEL_ID = 5L;
private static final Long MESSAGE_ID = 6L;
@@ -87,7 +90,7 @@ public class MessageEmbedRemovalReactionListenerTest {
when(embeddingAUser.getId()).thenReturn(USER_ID);
when(embeddedAUser.getId()).thenReturn(USER_ID + 1);
when(reactingUser.getUserId()).thenReturn(USER_ID + 2);
executeDeletionTest(embeddingUser, embeddedUser, reactingUser, 0);
executeDeletionTest(embeddingUser, embeddedUser, 0);
}
@Test
@@ -97,7 +100,7 @@ public class MessageEmbedRemovalReactionListenerTest {
when(embeddingUser.getUserReference()).thenReturn(embeddingAUser);
when(embeddingAUser.getId()).thenReturn(USER_ID + 3);
when(reactingUser.getUserId()).thenReturn(USER_ID + 1);
executeDeletionTest(embeddingUser, embeddedUser, reactingUser, 1);
executeDeletionTest(embeddingUser, embeddedUser, 1);
}
@Test
@@ -107,25 +110,28 @@ public class MessageEmbedRemovalReactionListenerTest {
when(embeddingAUser.getId()).thenReturn(USER_ID);
when(embeddedAUser.getId()).thenReturn(USER_ID + 1);
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);
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getChannelId()).thenReturn(CHANNEL_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
AEmote reactedEmote = Mockito.mock(AEmote.class);
when(emoteService.getEmoteOrDefaultEmote(MessageEmbedRemovalReactionListener.REMOVAL_EMOTE, SERVER_ID)).thenReturn(reactedEmote);
when(messageReaction.getEmote()).thenReturn(reactionEmote);
when(emoteService.compareCachedEmoteWithAEmote(reactionEmote, reactedEmote)).thenReturn(true);
when(messageReaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.isReactionEmoteAEmote(reactionEmote, reactedEmote)).thenReturn(true);
EmbeddedMessage message = Mockito.mock(EmbeddedMessage.class);
when(message.getEmbeddingUser()).thenReturn(embeddingUser);
when(message.getEmbeddedUser()).thenReturn(embeddedUser);
when(messageEmbedPostManagementService.findEmbeddedPostByMessageId(MESSAGE_ID)).thenReturn(Optional.of(message));
when(messageService.deleteMessageInChannelInServer(SERVER_ID, CHANNEL_ID, MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(null));
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);
if(wantedDeletions > 0) {
verify(messageEmbedPostManagementService, times(1)).deleteEmbeddedMessage(message);
@@ -134,14 +140,15 @@ public class MessageEmbedRemovalReactionListenerTest {
private void executeRemovalEmoteAddedTest(boolean wasCorrectEmote) {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
ServerUser serverUser = Mockito.mock(ServerUser.class);
AEmote reactedEmote = Mockito.mock(AEmote.class);
when(emoteService.getEmoteOrDefaultEmote(MessageEmbedRemovalReactionListener.REMOVAL_EMOTE, SERVER_ID)).thenReturn(reactedEmote);
when(messageReaction.getEmote()).thenReturn(reactionEmote);
when(emoteService.compareCachedEmoteWithAEmote(reactionEmote, reactedEmote)).thenReturn(wasCorrectEmote);
testUnit.executeReactionAdded(cachedMessage, messageReaction, serverUser);
when(messageReaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.isReactionEmoteAEmote(reactionEmote, reactedEmote)).thenReturn(wasCorrectEmote);
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());
}

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.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.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
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.MessageService;
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";
@Override
public MessageReceivedListenerResult execute(Message message) {
public ConsumableListenerResult execute(MessageReceivedModel model) {
Message message = model.getMessage();
Long serverId = message.getGuild().getIdLong();
Matcher matcher = Message.INVITE_PATTERN.matcher(message.getContentRaw());
ServerUser author = ServerUser.builder().userId(message.getAuthor().getIdLong()).serverId(message.getGuild().getIdLong()).build();
@@ -95,9 +97,9 @@ public class InviteLinkFilterListener implements MessageReceivedListener {
if(sendNotification) {
sendDeletionNotification(codesToTrack, message);
}
return MessageReceivedListenerResult.DELETED;
return ConsumableListenerResult.DELETED;
} else {
return MessageReceivedListenerResult.PROCESSED;
return ConsumableListenerResult.PROCESSED;
}
}

View File

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

View File

@@ -1,9 +1,10 @@
package dev.sheldan.abstracto.moderation.listener;
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.models.ServerUser;
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.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.service.MuteService;
@@ -26,12 +27,13 @@ public class JoinMuteListener implements AsyncJoinListener {
private UserInServerManagementService userInServerManagementService;
@Override
public void execute(ServerUser serverUser) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(serverUser.getServerId(), serverUser.getUserId());
public DefaultListenerResult execute(MemberJoinModel model) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(model.getServerId(), model.getJoiningUser().getUserId());
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);
}
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -1,20 +1,19 @@
package dev.sheldan.abstracto.moderation.listener;
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.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
import dev.sheldan.abstracto.core.service.MemberService;
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.posttarget.LoggingPostTarget;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.HashMap;
@@ -45,23 +44,15 @@ public class LeaveLogger implements AsyncLeaveListener {
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
public FeatureDefinition getFeature() {
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;
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.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -58,10 +60,12 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
private MessageDeleteLogListener self;
@Override
public void execute(CachedMessage messageFromCache) {
memberService.getMemberInServerAsync(messageFromCache.getServerId(), messageFromCache.getAuthor().getAuthorId()).thenAccept(member ->
self.executeListener(messageFromCache, member)
public DefaultListenerResult execute(MessageDeletedModel model) {
CachedMessage message = model.getCachedMessage();
memberService.getMemberInServerAsync(model.getServerId(), message.getAuthor().getAuthorId()).thenAccept(member ->
self.executeListener(message, member)
);
return DefaultListenerResult.PROCESSED;
}
@Transactional

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.moderation.listener;
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.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageTextUpdatedModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
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.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -35,29 +38,26 @@ public class MessageEditedListener implements AsyncMessageTextUpdatedListener {
private ChannelService channelService;
@Override
public void execute(CachedMessage messageBefore, CachedMessage messageAfter) {
if(messageBefore.getContent().equals(messageAfter.getContent())) {
public DefaultListenerResult execute(MessageTextUpdatedModel model) {
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.");
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());
TextChannel textChannel = channelService.getTextChannelFromServer(messageAfter.getServerId(), messageAfter.getChannelId());
MessageEditedLog log = MessageEditedLog
.builder()
.messageAfter(messageAfter)
.messageBefore(messageBefore)
.messageChannel(textChannel)
.guild(textChannel.getGuild())
.member(author)
.build();
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, log, messageBefore.getServerId());
postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.EDIT_LOG, messageBefore.getServerId());
}).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;
});
log.trace("Message {} in channel {} in guild {} was edited.", messageBefore.getMessageId(), messageBefore.getChannelId(), messageBefore.getServerId());
TextChannel textChannel = channelService.getTextChannelFromServer(model.getServerId(), messageBefore.getChannelId());
MessageEditedLog log = MessageEditedLog
.builder()
.messageAfter(messageAfter)
.messageBefore(messageBefore)
.messageChannel(textChannel)
.guild(textChannel.getGuild())
.member(messageAfter.getMember())
.build();
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, log, messageBefore.getServerId());
postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.EDIT_LOG, messageBefore.getServerId());
return DefaultListenerResult.PROCESSED;
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,9 @@
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.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -68,6 +69,9 @@ public class InviteLinkFilterListenerTest {
@Mock
private MetricService metricService;
@Mock
private MessageReceivedModel model;
private static final Long SERVER_ID = 1L;
private static final Long CHANNEL_ID = 2L;
private static final Long USER_ID = 3L;
@@ -79,8 +83,9 @@ public class InviteLinkFilterListenerTest {
public void testExecutionWithNoInvite() {
when(message.getContentRaw()).thenReturn("text");
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
Assert.assertEquals(MessageReceivedListenerResult.PROCESSED, result);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.PROCESSED, result);
}
@Test
@@ -88,8 +93,9 @@ public class InviteLinkFilterListenerTest {
when(message.getContentRaw()).thenReturn(INVITE_LINK);
when(inviteLinkFilterService.isCodeFiltered(eq(INVITE_CODE), any(ServerUser.class))).thenReturn(false);
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
Assert.assertEquals(MessageReceivedListenerResult.PROCESSED, result);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.PROCESSED, result);
}
@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.FILTER_NOTIFICATIONS)).thenReturn(false);
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.DELETED, result);
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.FILTER_NOTIFICATIONS)).thenReturn(false);
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verifyTracking();
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);
setupForNotification();
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
Assert.assertEquals(MessageReceivedListenerResult.DELETED, result);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
Assert.assertEquals(ConsumableListenerResult.DELETED, result);
verifyTracking();
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);
setupForNotification();
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
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));
}
@@ -158,9 +168,10 @@ public class InviteLinkFilterListenerTest {
when(featureModeService.featureModeActive(ModerationFeatureDefinition.INVITE_FILTER, SERVER_ID, InviteFilterMode.FILTER_NOTIFICATIONS)).thenReturn(true);
when(postTargetService.postTargetDefinedInServer(InviteFilterPostTarget.INVITE_DELETE_LOG, SERVER_ID)).thenReturn(false);
setupBasicMessage();
MessageReceivedListenerResult result = testUnit.execute(message);
when(model.getMessage()).thenReturn(message);
ConsumableListenerResult result = testUnit.execute(model);
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(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;
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.PostTargetService;
import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget;
@@ -40,6 +41,8 @@ public class JoinLoggerTest {
@Mock
private Member member;
@Mock
private MemberJoinModel model;
private static final Long SERVER_ID = 1L;
private static final Long USER_ID = 2L;
@@ -48,17 +51,13 @@ public class JoinLoggerTest {
public void testExecute() {
when(serverUser.getUserId()).thenReturn(USER_ID);
when(serverUser.getServerId()).thenReturn(SERVER_ID);
when(model.getMember()).thenReturn(member);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member));
testUnit.execute(serverUser);
verify(self, times(1)).sendJoinLog(serverUser, member);
}
@Test
public void testJoinLog() {
String message = "text";
testUnit.execute(model);
when(serverUser.getServerId()).thenReturn(SERVER_ID);
String message = "text";
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);
}
}

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

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.moderation.listener;
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.PostTargetService;
import dev.sheldan.abstracto.moderation.config.posttarget.LoggingPostTarget;
@@ -42,6 +43,9 @@ public class LeaveLoggerTest {
@Mock
private Member member;
@Mock
private MemberLeaveModel model;
private static final Long SERVER_ID = 1L;
private static final Long USER_ID = 2L;
@@ -50,19 +54,14 @@ public class LeaveLoggerTest {
when(leavingUser.getUserId()).thenReturn(USER_ID);
when(leavingUser.getServerId()).thenReturn(SERVER_ID);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(member));
testUnit.execute(leavingUser);
verify(self, times(1)).executeJoinLogging(leavingUser, member);
when(model.getMember()).thenReturn(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.CachedAuthor;
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.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -86,13 +87,18 @@ public class MessageDeleteLogListenerTest {
@Mock
private Guild guild;
@Mock
private MessageDeletedModel model;
@Test
public void testExecuteListener() {
when(deletedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(deletedMessage.getServerId()).thenReturn(SERVER_ID);
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);
}

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.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageTextUpdatedModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
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 net.dv8tion.jda.api.entities.Guild;
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.Test;
@@ -44,11 +46,13 @@ public class MessageEditedListenerTest {
private MemberService memberService;
@Mock
private CachedMessage messageAfter;
private Message messageAfter;
@Mock
private CachedMessage messageBefore;
private MessageTextUpdatedModel model;
private static final Long SERVER_ID = 4L;
private static final Long CHANNEL_ID = 5L;
private static final Long AUTHOR_ID = 6L;
@@ -56,9 +60,11 @@ public class MessageEditedListenerTest {
@Test
public void testExecuteListenerWithSameContent() {
String content = "text";
when(messageAfter.getContent()).thenReturn(content);
when(messageAfter.getContentRaw()).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());
}
@@ -67,15 +73,12 @@ public class MessageEditedListenerTest {
String content = "text";
String contentAfterwards = "text2";
TextChannel channel = Mockito.mock(TextChannel.class);
when(messageAfter.getContent()).thenReturn(contentAfterwards);
when(messageAfter.getChannelId()).thenReturn(CHANNEL_ID);
when(messageAfter.getContentRaw()).thenReturn(contentAfterwards);
Guild guild = Mockito.mock(Guild.class);
when(channel.getGuild()).thenReturn(guild);
when(messageAfter.getServerId()).thenReturn(SERVER_ID);
Member author = Mockito.mock(Member.class);
CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(messageAfter.getAuthor()).thenReturn(cachedAuthor);
when(messageBefore.getContent()).thenReturn(content);
when(messageBefore.getServerId()).thenReturn(SERVER_ID);
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(memberService.getMemberInServerAsync(SERVER_ID, AUTHOR_ID)).thenReturn(CompletableFuture.completedFuture(author));
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);
MessageEditedLog capturedValue = captor.getValue();
Assert.assertEquals(messageBefore, capturedValue.getMessageBefore());

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import lombok.Getter;
import lombok.Setter;
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"
@@ -16,7 +17,7 @@ public class MessageEditedLog extends UserInitiatedServerContext {
/**
* 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

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.modmail.listener;
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.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
@@ -34,8 +36,9 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
private MemberService memberService;
@Override
public void execute(CachedMessage messageBefore) {
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageBefore.getMessageId());
public DefaultListenerResult execute(MessageDeletedModel model) {
CachedMessage message = model.getCachedMessage();
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(message.getMessageId());
messageOptional.ifPresent(modMailMessage -> {
ModMailThread thread = modMailMessage.getThreadReference();
Long dmMessageId = modMailMessage.getCreatedMessageInDM();
@@ -44,7 +47,7 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
Long channelId = thread.getChannel().getId();
Long serverId = thread.getServer().getId();
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> channelDeletePromise;
if(hasMessageInChannel) {
@@ -53,11 +56,11 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
channelDeletePromise = CompletableFuture.completedFuture(null);
}
CompletableFuture.allOf(dmDeletePromise, channelDeletePromise).whenComplete((unused, throwable) ->
self.removeMessageFromThread(messageBefore.getMessageId())
self.removeMessageFromThread(message.getMessageId())
);
});
});
return DefaultListenerResult.PROCESSED;
}
@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.CommandService;
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.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
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.MemberService;
import dev.sheldan.abstracto.core.service.MessageService;
@@ -62,34 +64,32 @@ public class ModMailMessageEditedListener implements AsyncMessageTextUpdatedList
private ModMailThreadService modMailThreadService;
@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())) {
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());
messageOptional.ifPresent(modMailMessage -> {
log.info("Editing send message {} in channel {} in mod mail thread {} in server {}.", messageBefore.getMessageId(), messageBefore.getChannelId(), modMailMessage.getThreadReference().getId(), messageBefore.getServerId());
String contentStripped = messageAfter.getContent();
String commandName = commandRegistry.getCommandName(contentStripped.substring(0, contentStripped.indexOf(" ")), messageBefore.getServerId());
if(!commandService.doesCommandExist(commandName)) {
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<Member> loadEditingUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getAuthor().getUserReference().getId());
CompletableFuture.allOf(parameterParseFuture, loadTargetUser, loadEditingUser).thenAccept(unused ->
self.updateMessageInThread(loadedMessage, parameterParseFuture.join(), loadTargetUser.join(), loadEditingUser.join())
);
});
if(messageOptional.isPresent()) {
messageOptional.ifPresent(modMailMessage -> {
log.info("Editing send message {} in channel {} in mod mail thread {} in server {}.", messageBefore.getMessageId(), messageBefore.getChannelId(), modMailMessage.getThreadReference().getId(), messageBefore.getServerId());
String contentStripped = message.getContentStripped();
String commandName = commandRegistry.getCommandName(contentStripped.substring(0, contentStripped.indexOf(" ")), messageBefore.getServerId());
if(!commandService.doesCommandExist(commandName)) {
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, message);
CompletableFuture<Member> loadTargetUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId());
CompletableFuture<Member> loadEditingUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getAuthor().getUserReference().getId());
CompletableFuture.allOf(parameterParseFuture, loadTargetUser, loadEditingUser).thenAccept(unused ->
self.updateMessageInThread(message, parameterParseFuture.join(), loadTargetUser.join(), loadEditingUser.join())
);
});
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
@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 {}.",
dmId, channelMessageId, userPostedMessage.getId(), modMailThread.getId(), author.getUserReference().getId(), author.getServerReference().getId());
modMailMessageRepository.save(modMailMessage);
return modMailMessage;
return modMailMessageRepository.save(modMailMessage);
}
@Override

View File

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

View File

@@ -115,8 +115,7 @@ public class ModMailThreadManagementServiceBean implements ModMailThreadManageme
log.info("Create modmail thread in channel {} for user {} in server {}.",
channel.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId());
modMailThreadRepository.save(thread);
return thread;
return modMailThreadRepository.save(thread);
}
@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.AUser;
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.MessageService;
import dev.sheldan.abstracto.modmail.model.database.ModMailMessage;
@@ -60,6 +61,9 @@ public class ModMailMessageDeletedListenerTest {
@Mock
private AChannel channel;
@Mock
private MessageDeletedModel model;
private static final Long DELETED_MESSAGE_ID = 4L;
private static final Long CREATED_MESSAGE_ID_1 = 3L;
private static final Long CREATED_MESSAGE_ID_2 = 5L;
@@ -71,7 +75,8 @@ public class ModMailMessageDeletedListenerTest {
public void testDeleteOutSideOfThread() {
when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_ID);
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());
}
@@ -94,7 +99,8 @@ public class ModMailMessageDeletedListenerTest {
when(targetMember.getUser()).thenReturn(targetUser);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
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(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(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));
testUnit.execute(deletedMessage);
when(model.getCachedMessage()).thenReturn(deletedMessage);
testUnit.execute(model);
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.AUser;
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.MemberService;
import dev.sheldan.abstracto.core.service.MessageService;
@@ -72,7 +73,7 @@ public class ModMailMessageEditedListenerTest {
private CachedMessage messageBefore;
@Mock
private CachedMessage messageAfter;
private Message messageAfter;
@Mock
private Message loadedMessage;
@@ -101,6 +102,9 @@ public class ModMailMessageEditedListenerTest {
@Captor
private ArgumentCaptor<ModMailModeratorReplyModel> replyModelArgumentCaptor;
@Mock
private MessageTextUpdatedModel model;
private static final Long CHANNEL_ID = 5L;
private static final Long MESSAGE_ID = 6L;
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 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
public void testEditOutsideModMailThread() {
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(false);
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());
}
@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
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.getMessageId()).thenReturn(MESSAGE_ID);
when(messageBefore.getServerId()).thenReturn(SERVER_ID);
@@ -154,14 +151,16 @@ public class ModMailMessageEditedListenerTest {
AUser authorUser = Mockito.mock(AUser.class);
when(authorUser.getId()).thenReturn(AUTHOR_USER_ID);
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(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, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
testUnit.executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage);
verify(self, times(1)).updateMessageInThread(loadedMessage, parsedParameters, targetMember, authorMember);
when(model.getAfter()).thenReturn(messageAfter);
when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
}
@Test
@@ -169,6 +168,7 @@ public class ModMailMessageEditedListenerTest {
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(messageBefore.getServerId()).thenReturn(SERVER_ID);
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
@@ -182,14 +182,16 @@ public class ModMailMessageEditedListenerTest {
AUser authorUser = Mockito.mock(AUser.class);
when(authorUser.getId()).thenReturn(AUTHOR_USER_ID);
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(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, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
testUnit.executeMessageUpdatedLogic(messageBefore, messageAfter, loadedMessage);
verify(self, times(1)).updateMessageInThread(loadedMessage, parsedParameters, targetMember, authorMember);
when(model.getAfter()).thenReturn(messageAfter);
when(model.getBefore()).thenReturn(messageBefore);
testUnit.execute(model);
verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
}
@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 {}.",
userToBeReminded.getAUserInAServer().getUserReference().getId(), userToBeReminded.getGuild().getId(), messageId, timeToBeRemindedAt);
reminderRepository.save(reminder);
return reminder;
return reminderRepository.save(reminder);
}
@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;
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.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
@@ -39,13 +40,13 @@ public class RepostEmbedListener implements AsyncMessageEmbeddedListener {
private ChannelService channelService;
@Override
public void execute(GuildMessageEmbedEventModel eventModel) {
public DefaultListenerResult execute(GuildMessageEmbedEventModel eventModel) {
AChannel channel = channelManagementService.loadChannel(eventModel.getChannelId());
if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) {
if(repostManagement.messageEmbedsHaveBeenCovered(eventModel.getMessageId())) {
log.info("The embeds of the message {} in channel {} in server {} have already been covered by repost check -- ignoring.",
eventModel.getMessageId(), eventModel.getChannelId(), eventModel.getServerId());
return;
return DefaultListenerResult.IGNORED;
}
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());
@@ -54,6 +55,7 @@ public class RepostEmbedListener implements AsyncMessageEmbeddedListener {
}
});
}
return DefaultListenerResult.PROCESSED;
}
@Override

View File

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

View File

@@ -172,6 +172,18 @@ public class RepostServiceBean implements RepostService {
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
public String calculateHashForPost(String url, Long serverId) {
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) {
Optional<PostedImage> originalPostOptional = getRepostFor(message, attachment, index);
ServerChannelMessageUser serverChannelMessageUser = ServerChannelMessageUser
@@ -216,6 +236,18 @@ public class RepostServiceBean implements RepostService {
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) {
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());

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,10 @@
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.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.management.RepostCheckChannelGroupManagement;
import org.junit.Test;
@@ -13,35 +16,53 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RepostCheckChannelGroupCreatedListenerTest {
public class RepostCheckAsyncChannelGroupCreatedListenerTest {
@InjectMocks
private RepostCheckChannelGroupCreatedListener testUnit;
private RepostCheckAsyncChannelGroupCreatedListener testUnit;
@Mock
private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Mock
private ChannelGroupManagementService channelGroupManagementService;
@Mock
private AChannelGroup channelGroup;
@Mock
private ChannelGroupCreatedListenerModel model;
@Mock
private ChannelGroupType channelGroupType;
private static final String INCORRECT_TYPE = "incorrectType";
private static final Long CHANNEL_GROUP_ID = 1L;
@Test
public void testChannelGroupCreated() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
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);
}
@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
public void testChannelGroupCreatedIncorrectType() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
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);
}
}

View File

@@ -1,7 +1,10 @@
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.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.management.RepostCheckChannelGroupManagement;
import org.junit.Test;
@@ -13,35 +16,53 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RepostCheckChannelGroupDeletedListenerTest {
public class RepostCheckAsyncChannelGroupDeletedListenerTest {
@InjectMocks
private RepostCheckChannelGroupDeletedListener testUnit;
private RepostCheckAsyncChannelGroupDeletedListener testUnit;
@Mock
private RepostCheckChannelGroupManagement checkChannelGroupManagement;
@Mock
private ChannelGroupManagementService channelGroupManagementService;
@Mock
private AChannelGroup channelGroup;
@Mock
private ChannelGroupType channelGroupType;
@Mock
private ChannelGroupDeletedListenerModel model;
private static final String INCORRECT_TYPE = "incorrectType";
private static final Long CHANNEL_GROUP_ID = 1L;
@Test
public void testChannelGroupDeleted() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
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);
}
@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
public void testChannelGroupDeletedIncorrectType() {
when(channelGroup.getChannelGroupType()).thenReturn(channelGroupType);
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);
}
}

View File

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

View File

@@ -20,8 +20,10 @@ public interface RepostService {
Optional<PostedImage> getRepostFor(Message message, MessageEmbed messageEmbed, Integer embedIndex);
boolean isRepost(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);
void processMessageAttachmentRepostCheck(CachedMessage message);
void processMessageAttachmentRepostCheck(Message message);
void processMessageEmbedsRepostCheck(List<CachedEmbed> embeds, CachedMessage message);
void processMessageEmbedsRepostCheck(List<MessageEmbed> embeds, Message message);
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;
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.MetricService;
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.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
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.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
@@ -36,84 +27,54 @@ import java.util.stream.Collectors;
@Component
@Slf4j
public class StarboardListener implements AsyncReactionAddedListener, AsyncReactionRemovedListener, AsyncReactionClearedListener {
public abstract class StarboardListener {
public static final String FIRST_LEVEL_THRESHOLD_KEY = "starLvl1";
@Autowired
private BotService botService;
protected ConfigManagementService configManagementService;
@Autowired
private ConfigManagementService configManagementService;
protected StarboardService starboardService;
@Autowired
private StarboardService starboardService;
protected StarboardPostManagementService starboardPostManagementService;
@Autowired
private StarboardPostManagementService starboardPostManagementService;
protected StarboardPostReactorManagementService starboardPostReactorManagementService;
@Autowired
private StarboardPostReactorManagementService starboardPostReactorManagementService;
protected UserInServerManagementService userInServerManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
protected EmoteService emoteService;
@Autowired
private EmoteService emoteService;
@Autowired
private MetricService metricService;
protected MetricService metricService;
public static final String STARBOARD_STARS = "starboard.stars";
public static final String STARBOARD_POSTS = "starboard.posts";
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
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "removed")))
.build();
private static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
protected static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
.builder()
.name(STARBOARD_POSTS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.reached")))
.build();
private static final CounterMetric STARBOARD_STARS_THRESHOLD_FELL = CounterMetric
protected static final CounterMetric STARBOARD_STARS_THRESHOLD_FELL = CounterMetric
.builder()
.name(STARBOARD_POSTS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.below")))
.build();
@Override
@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) {
protected void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser userReacting, boolean adding) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
if(reaction != null) {
AUserInAServer author = userInServerManagementService.loadOrCreateUser(message.getServerId(), message.getAuthor().getAuthorId());
List<AUserInAServer> userExceptAuthor = getUsersExcept(reaction.getUsers(), author);
Long starMinimum = getFromConfig(FIRST_LEVEL_THRESHOLD_KEY, message.getServerId());
AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(serverUser);
AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(userReacting);
if (userExceptAuthor.size() >= starMinimum) {
log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.",
message.getMessageId(), message.getChannelId(), message.getServerId());
@@ -122,24 +83,27 @@ public class StarboardListener implements AsyncReactionAddedListener, AsyncReact
} else {
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_REACHED);
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 {
if(starboardPostOptional.isPresent()) {
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);
starboardPostOptional.ifPresent(this::completelyRemoveStarboardPost);
starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
}
}
} else {
if(starboardPostOptional.isPresent()) {
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);
// TODO handle futures correctly
starboardService.updateStarboardPost(starboardPost, message, userExceptAuthor);
@@ -152,62 +116,24 @@ public class StarboardListener implements AsyncReactionAddedListener, AsyncReact
}
}
private void completelyRemoveStarboardPost(StarboardPost starboardPost) {
starboardService.deleteStarboardMessagePost(starboardPost);
starboardPostManagementService.removePost(starboardPost);
protected void completelyRemoveStarboardPost(StarboardPost starboardPost, ServerUser userReacting) {
starboardService.deleteStarboardPost(starboardPost, userReacting);
}
@Override
@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) {
protected Long getFromConfig(String key, Long guildId) {
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 -> {
Optional<AUserInAServer> aUserInAServer = userInServerManagementService.loadUserOptional(serverUser.getServerId(), serverUser.getUserId());
return aUserInAServer.orElse(null);
}).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
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_FELL, "Starboard posts falling below threshold");
}
}

View File

@@ -1,8 +1,10 @@
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.AsyncMessageDeletedListener;
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.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
@@ -20,14 +22,16 @@ public class StarboardPostDeletedListener implements AsyncMessageDeletedListener
private StarboardPostManagementService starboardPostManagementService;
@Override
public void execute(CachedMessage messageBefore) {
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(messageBefore.getMessageId());
public DefaultListenerResult execute(MessageDeletedModel model) {
CachedMessage message = model.getCachedMessage();
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(message.getMessageId());
if(byStarboardPostId.isPresent()) {
StarboardPost post = byStarboardPostId.get();
log.info("Removing starboard post: message {}, channel {}, server {}, because the message was deleted",
post.getPostMessageId(), post.getSourceChannel().getId(), messageBefore.getServerId());
starboardPostManagementService.setStarboardPostIgnored(messageBefore.getMessageId(), true);
post.getPostMessageId(), post.getSourceChannel().getId(), message.getServerId());
starboardPostManagementService.setStarboardPostIgnored(message.getMessageId(), true);
}
return DefaultListenerResult.PROCESSED;
}
@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.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
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.PostTarget;
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.PostTargetManagement;
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.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardPostTarget;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
@@ -90,40 +90,47 @@ public class StarboardServiceBean implements StarboardService {
@Autowired
private StarboardServiceBean self;
@Autowired
private StarboardPostListenerManager starboardPostListenerManager;
@Autowired
private UserService userService;
@Override
public CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) {
Long starredUserId = starredUser.getUserInServerId();
List<Long> userExceptAuthorIds = userExceptAuthor.stream().map(AUserInAServer::getUserInServerId).collect(Collectors.toList());
Long userReactingId = userReacting.getUserReference().getId();
return buildStarboardPostModel(message, userExceptAuthor.size()).thenCompose(starboardPostModel ->
self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel)
self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel, userReactingId)
);
}
@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);
PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardChannelId = starboard.getChannelReference().getId();
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
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));
AChannel starboardChannel = channelManagementService.loadChannel(starboardChannelId);
Message message1 = completableFutures.get(0).join();
Message starboardMessage = completableFutures.get(0).join();
AServerAChannelMessage aServerAChannelMessage = AServerAChannelMessage
.builder()
.messageId(message1.getIdLong())
.messageId(starboardMessage.getIdLong())
.channel(starboardChannel)
.server(starboardChannel.getServer())
.build();
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()) {
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));
starboardPostReactorManagementService.addReactor(starboardPost, user);
});
starboardPostListenerManager.sendStarboardPostCreatedEvent(userReactingId, starboardPost);
}
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<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);
return StarboardPostModel
.builder()
.message(message)
.author(member)
.author(user)
.sourceChannelId(message.getChannelId())
.channel(channel.orElse(null))
.aChannel(aChannel)
.starCount(starCount)
.guild(guild.orElse(null))
.user(user)
.starLevelEmote(starLevelEmote)
.build();
});
@@ -241,6 +246,13 @@ public class StarboardServiceBean implements StarboardService {
.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) {
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 java.time.Instant;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.*;
@Component
@Slf4j
@@ -36,6 +33,7 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
.postMessageId(starredMessage.getMessageId())
.sourceChannel(build)
.ignored(false)
.reactions(new ArrayList<>())
.server(starboardPost.getServer())
.starboardMessageId(starboardPost.getMessageId())
.starboardChannel(starboardPost.getChannel())
@@ -45,8 +43,7 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
starredMessage.getMessageId(), starredMessage.getChannelId(), starredMessage.getServerId(),
starboardPost.getMessageId(), starboardPost.getChannel().getId(), starboardPost.getServer().getId(),
starredUser.getUserReference().getId());
repository.save(post);
return post;
return repository.save(post);
}
@Override
@@ -57,7 +54,6 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
@Override
public void setStarboardPostMessageId(StarboardPost post, Long messageId) {
post.setStarboardMessageId(messageId);
repository.save(post);
}
@Override

View File

@@ -33,8 +33,7 @@ public class StarboardPostReactorManagementServiceBean implements StarboardPostR
.server(user.getServerReference())
.build();
log.info("Persisting the reactor {} for starboard post {} in server {}.", user.getUserReference().getId(), post.getId(), user.getServerReference().getId());
repository.save(reactor);
return reactor;
return repository.save(reactor);
}
@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.models.ServerUser;
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.CachedReactions;
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.management.ConfigManagementService;
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.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;
@@ -22,18 +23,16 @@ 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 StarboardListenerTest {
public class StarAddedListenerTest {
@InjectMocks
private StarboardListener testUnit;
private StarAddedListener testUnit;
@Mock
private ConfigManagementService configManagementService;
@@ -57,7 +56,10 @@ public class StarboardListenerTest {
private MetricService metricService;
@Mock
private CachedReactions cachedReaction;
private MessageReaction reaction;
@Mock
private CachedReactions cachedReactions;
@Mock
private CachedMessage cachedMessage;
@@ -87,11 +89,14 @@ public class StarboardListenerTest {
private StarboardPost post;
@Mock
private CachedEmote cachedEmote;
private MessageReaction.ReactionEmote reactionEmote;
@Mock
private AEmote starEmote;
@Mock
private ReactionAddedModel model;
private static final Long MESSAGE_ID = 5L;
private static final Long SERVER_ID = 6L;
private static final Long AUTHOR_ID = 4L;
@@ -102,7 +107,9 @@ public class StarboardListenerTest {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
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);
}
@@ -110,9 +117,13 @@ public class StarboardListenerTest {
public void testAddingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(false);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing);
when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(false);
when(model.getMessage()).thenReturn(cachedMessage);
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(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
}
@@ -122,8 +133,7 @@ public class StarboardListenerTest {
Long requiredStars = 5L;
setupActingAndAuthor();
executeAddingTest(requiredStars, post);
verify(starboardService, times(1)).deleteStarboardMessagePost(post);
verify(starboardPostManagementService, times(1)).removePost(post);
verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
}
@Test
@@ -154,67 +164,6 @@ public class StarboardListenerTest {
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() {
when(userInServerActing.getUserReference()).thenReturn(userActing);
when(userActing.getId()).thenReturn(USER_ACTING_ID);
@@ -224,57 +173,16 @@ public class StarboardListenerTest {
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) {
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(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(true);
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.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(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
@@ -282,16 +190,21 @@ public class StarboardListenerTest {
AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
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)).getReactionFromMessageByEmote(cachedMessage, starEmote);
}
private void setupWrongEmote(Long serverId, Long authorId, AEmote starEmote) {
when(cachedMessage.getServerId()).thenReturn(serverId);
when(cachedAuthor.getAuthorId()).thenReturn(authorId);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
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.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import org.junit.Test;
@@ -28,9 +29,11 @@ public class StarboardPostDeletedListenerTest {
public void deleteNonStarboardPost() {
Long messageId = 4L;
when(starboardPostManagementService.findByStarboardPostId(messageId)).thenReturn(Optional.empty());
MessageDeletedModel model = Mockito.mock(MessageDeletedModel.class);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getMessageId()).thenReturn(messageId);
testUnit.execute(cachedMessage);
when(model.getCachedMessage()).thenReturn(cachedMessage);
testUnit.execute(model);
verify( starboardPostManagementService, times(0)).setStarboardPostIgnored(messageId, true);
}
@@ -47,7 +50,9 @@ public class StarboardPostDeletedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(serverId);
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);
}

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

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.context.ServerContext;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUser;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
@Getter
@Setter
@SuperBuilder
public class StarboardPostModel extends ServerContext {
private Member author;
private User author;
private TextChannel channel;
private AUser user;
private AChannel aChannel;
private Long sourceChannelId;
private CachedMessage message;
private Integer starCount;
private String starLevelEmote;

View File

@@ -1,5 +1,6 @@
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.database.AUserInAServer;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
@@ -18,4 +19,5 @@ public interface StarboardService {
CompletableFuture<GuildStarStatsModel> retrieveStarStats(Long serverId);
MemberStarStatsModel retrieveStarStatsForMember(Member member);
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.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.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.listener.EmoteCreatedModel;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
@@ -28,24 +28,20 @@ public class CreateTrackedEmoteListener implements AsyncEmoteCreatedListener {
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteCreated(CachedEmote createdEmote) {
log.info("Creating tracked emote {} in server {}.", createdEmote.getServerId(), createdEmote.getEmoteId());
trackedEmoteManagementService.createTrackedEmote(createdEmote);
}
@Override
public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
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.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.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.listener.EmoteDeletedModel;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
@@ -17,7 +17,7 @@ import java.util.Arrays;
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
* feature mode as well.
*/
@@ -28,24 +28,21 @@ public class DeleteTrackedEmoteListener implements AsyncEmoteDeletedListener {
@Autowired
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
public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
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;
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.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.statistic.config.StatisticFeatureDefinition;
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.stereotype.Component;
@@ -27,17 +28,17 @@ public class EmoteTrackingListener implements AsyncMessageReceivedListener {
@Autowired
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
public FeatureDefinition getFeature() {
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.FeatureMode;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteUpdatedListener;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncEmoteNameUpdatedListener;
import dev.sheldan.abstracto.core.models.listener.EmoteNameUpdatedModel;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingMode;
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.
*/
@Component
public class UpdateTrackedEmoteListener implements AsyncEmoteUpdatedListener {
public class UpdateTrackedEmoteNameListener implements AsyncEmoteNameUpdatedListener {
@Autowired
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
public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
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.models.ServerSpecificId;
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.FeatureModeService;
import dev.sheldan.abstracto.core.service.GuildService;
@@ -54,6 +55,9 @@ public class TrackedEmoteServiceBean implements TrackedEmoteService {
@Autowired
private MetricService metricService;
@Autowired
private CacheEntityService cacheEntityService;
public static final String EMOTE_USAGES_TRACKED_METRIC = "emote.usages";
private static final CounterMetric EMOTE_USAGES_TRACKED =
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
@Transactional
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);
}
@Override
public TrackedEmote createTrackedEmote(Emote emote) {
return createTrackedEmote(emote, emote.getGuild());
}
@Override
public TrackedEmote createTrackedEmote(CachedEmote emote) {
AServer server = serverManagementService.loadServer(emote.getServerId());
@@ -109,6 +114,11 @@ public class TrackedEmoteManagementServiceBean implements TrackedEmoteManagement
markAsDeleted(emote);
}
@Override
public void markAsDeleted(Emote emote) {
markAsDeleted(emote.getGuild().getIdLong(), emote.getIdLong());
}
@Override
public void markAsDeleted(TrackedEmote trackedemote) {
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;
import dev.sheldan.abstracto.core.config.ListenerPriority;
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.emote.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -23,14 +23,19 @@ public class CreateTrackedEmoteListenerTest {
@Mock
private TrackedEmoteManagementService trackedEmoteManagementService;
@Mock
private EmoteCreatedModel model;
private static final Long SERVER_ID = 4L;
private static final Long EMOTE_ID = 4L;
@Test
public void testEmoteCreated() {
Long serverId = 4L;
Long emoteId = 5L;
CachedEmote emote = Mockito.mock(CachedEmote.class);
when(emote.getEmoteId()).thenReturn(emoteId);
when(emote.getServerId()).thenReturn(serverId);
testUnit.emoteCreated(emote);
Emote emote = Mockito.mock(Emote.class);
when(emote.getIdLong()).thenReturn(EMOTE_ID);
when(model.getEmote()).thenReturn(emote);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(trackedEmoteManagementService, times(1)).createTrackedEmote(emote);
}
@@ -39,8 +44,4 @@ public class CreateTrackedEmoteListenerTest {
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;
import dev.sheldan.abstracto.core.config.ListenerPriority;
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.emote.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -23,15 +23,20 @@ public class DeleteTrackedEmoteListenerTest {
@Mock
private TrackedEmoteManagementService trackedEmoteManagementService;
@Mock
private EmoteDeletedModel model;
private static final Long SERVER_ID = 4L;
private static final Long EMOTE_ID = 4L;
@Test
public void testEmoteDeleted() {
Long serverId = 4L;
Long emoteId = 5L;
CachedEmote emote = Mockito.mock(CachedEmote.class);
when(emote.getEmoteId()).thenReturn(emoteId);
when(emote.getServerId()).thenReturn(serverId);
testUnit.emoteDeleted(emote);
verify(trackedEmoteManagementService, times(1)).markAsDeleted(serverId, emoteId);
Emote emote = Mockito.mock(Emote.class);
when(emote.getIdLong()).thenReturn(EMOTE_ID);
when(model.getEmote()).thenReturn(emote);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(trackedEmoteManagementService, times(1)).markAsDeleted(SERVER_ID, EMOTE_ID);
}
@Test
@@ -39,8 +44,4 @@ public class DeleteTrackedEmoteListenerTest {
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;
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.statistic.config.StatisticFeatureDefinition;
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.Message;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,13 +33,16 @@ public class EmoteTrackingListenerTest {
private GuildService guildService;
@Mock
private CachedMessage message;
private Message message;
@Mock
private CachedEmote emote1;
private MessageReceivedModel messageReceivedModel;
@Mock
private CachedEmote emote2;
private Emote emote1;
@Mock
private Emote emote2;
@Mock
private Guild guild;
@@ -47,40 +52,43 @@ public class EmoteTrackingListenerTest {
@Test
public void testExecuteOneEmote() {
List<CachedEmote> emotesBag = new ArrayList<>();
List<Emote> emotesBag = new ArrayList<>();
emotesBag.add(emote1);
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);
testUnit.execute(message);
testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote1, guild, 1L);
}
@Test
public void testExecuteOneEmoteMultipleTimes() {
List<CachedEmote> emotesBag = new ArrayList<>();
when(emote1.getEmoteId()).thenReturn(EMOTE_ID);
when(emote2.getEmoteId()).thenReturn(EMOTE_ID);
List<Emote> emotesBag = new ArrayList<>();
when(emote1.getIdLong()).thenReturn(EMOTE_ID);
when(emote2.getIdLong()).thenReturn(EMOTE_ID);
emotesBag.add(emote1);
emotesBag.add(emote2);
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);
testUnit.execute(message);
testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(any(CachedEmote.class), eq(guild), eq(2L));
}
@Test
public void testExecuteMultipleEmotes() {
List<CachedEmote> emotesBag = new ArrayList<>();
when(emote1.getEmoteId()).thenReturn(EMOTE_ID);
when(emote2.getEmoteId()).thenReturn(EMOTE_ID + 1);
List<Emote> emotesBag = new ArrayList<>();
when(emote1.getIdLong()).thenReturn(EMOTE_ID);
when(emote2.getIdLong()).thenReturn(EMOTE_ID + 1);
emotesBag.add(emote1);
emotesBag.add(emote2);
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);
testUnit.execute(message);
testUnit.execute(messageReceivedModel);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote1, guild, 1L);
verify(trackedEmoteService, times(1)).addEmoteToRuntimeStorage(emote2, guild, 1L);
}
@@ -88,8 +96,9 @@ public class EmoteTrackingListenerTest {
@Test
public void testExecuteNoEmote() {
when(message.getEmotes()).thenReturn(new ArrayList<>());
testUnit.execute(message);
verify(trackedEmoteService, times(0)).addEmoteToRuntimeStorage(any(), any(), anyLong());
when(messageReceivedModel.getMessage()).thenReturn(message);
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;
import dev.sheldan.abstracto.core.config.ListenerPriority;
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.emote.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -19,22 +19,26 @@ import static org.mockito.Mockito.*;
public class UpdateTrackedEmoteListenerTest {
@InjectMocks
private UpdateTrackedEmoteListener testUnit;
private UpdateTrackedEmoteNameListener testUnit;
@Mock
private TrackedEmoteManagementService trackedEmoteManagementService;
@Mock
private EmoteNameUpdatedModel model;
@Test
public void testEmoteUpdated() {
Long serverId = 1L;
Long emoteId = 2L;
CachedEmote changedEmote = Mockito.mock(CachedEmote.class);
when(changedEmote.getServerId()).thenReturn(serverId);
when(changedEmote.getEmoteId()).thenReturn(emoteId);
Emote changedEmote = Mockito.mock(Emote.class);
when(changedEmote.getIdLong()).thenReturn(emoteId);
TrackedEmote trackedEmote = Mockito.mock(TrackedEmote.class);
when(trackedEmoteManagementService.loadByEmoteId(emoteId, serverId)).thenReturn(trackedEmote);
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);
}
@@ -43,8 +47,4 @@ public class UpdateTrackedEmoteListenerTest {
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);
/**
* 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
* 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);
/**
* 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}
* @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);
/**
* 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
* @param trackedEmote The {@link TrackedEmote} which should be marked as deleted

View File

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

View File

@@ -16,6 +16,12 @@
<artifactId>core-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
</dependency>
<!-- modules containing commands -->
<dependency>
@@ -24,83 +30,167 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>entertainment-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>entertainment-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>link-embed-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>link-embed-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>remind-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>remind-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>repost-detection-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>repost-detection-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>starboard-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>starboard-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>suggestion-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>suggestion-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</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.service.management.CommandInServerManagementService;
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.listener.ServerCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -14,7 +17,7 @@ import java.util.List;
@Component
@Slf4j
public class CommandConfigListener implements ServerConfigListener {
public class CommandConfigListenerAsync implements AsyncServerCreatedListener {
@Autowired
private List<Command> commandList;
@@ -25,9 +28,13 @@ public class CommandConfigListener implements ServerConfigListener {
@Autowired
private CommandManagementService commandManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public void updateServerConfig(AServer server) {
log.info("Creating command instances for server {}.", server.getId());
public DefaultListenerResult execute(ServerCreatedListenerModel model) {
AServer server = serverManagementService.loadServer(model.getServerId());
log.info("Creating command instances for server {}.", model.getServerId());
commandList.forEach(command -> {
if(command.getConfiguration() != null) {
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());
groupCommandRepository.save(channelGroupCommand);
return channelGroupCommand;
return groupCommandRepository.save(channelGroupCommand);
}
@Override

View File

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

View File

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

View File

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

View File

@@ -1,117 +1,82 @@
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.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
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
private Environment environment;
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";
private ExecutorService executorService;
@Bean(name = "joinListenerExecutor")
public TaskExecutor joinListenerExecutor() {
return setupExecutorFor("joinListener");
return executorService.setupExecutorFor("joinListener");
}
@Bean(name = "leaveListenerExecutor")
public TaskExecutor leaveListenerExecutor() {
return setupExecutorFor("leaveListener");
return executorService.setupExecutorFor("leaveListener");
}
@Bean(name = "messageReceivedExecutor")
public TaskExecutor messageReceivedExecutor() {
return setupExecutorFor("messageReceivedListener");
return executorService.setupExecutorFor("messageReceivedListener");
}
@Bean(name = "messageDeletedExecutor")
public TaskExecutor messageDeletedExecutor() {
return setupExecutorFor("messageReceivedListener");
return executorService.setupExecutorFor("messageReceivedListener");
}
@Bean(name = "messageEmbeddedExecutor")
public TaskExecutor messageEmbeddedExecutor() {
return setupExecutorFor("messageEmbeddedListener");
return executorService.setupExecutorFor("messageEmbeddedListener");
}
@Bean(name = "messageUpdatedExecutor")
public TaskExecutor messageUpdatedExecutor() {
return setupExecutorFor("messageUpdatedListener");
return executorService.setupExecutorFor("messageUpdatedListener");
}
@Bean(name = "privateMessageReceivedExecutor")
public TaskExecutor privateMessageReceivedExecutor() {
return setupExecutorFor("privateMessageReceivedListener");
return executorService.setupExecutorFor("privateMessageReceivedListener");
}
@Bean(name = "emoteCreatedExecutor")
public TaskExecutor emoteCreatedExecutor() {
return setupExecutorFor("emoteCreatedListener");
return executorService.setupExecutorFor("emoteCreatedListener");
}
@Bean(name = "emoteDeletedExecutor")
public TaskExecutor emoteDeletedExecutor() {
return setupExecutorFor("emoteDeletedListener");
return executorService.setupExecutorFor("emoteDeletedListener");
}
@Bean(name = "emoteUpdatedExecutor")
public TaskExecutor emoteUpdatedExecutor() {
return setupExecutorFor("emoteUpdatedListener");
return executorService.setupExecutorFor("emoteUpdatedListener");
}
@Bean(name = "reactionAddedExecutor")
public TaskExecutor reactionAddedExecutor() {
return setupExecutorFor("reactionAddedListener");
return executorService.setupExecutorFor("reactionAddedListener");
}
@Bean(name = "reactionRemovedExecutor")
public TaskExecutor reactionRemovedExecutor() {
return setupExecutorFor("reactionRemovedListener");
return executorService.setupExecutorFor("reactionRemovedListener");
}
@Bean(name = "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.AServer;
import dev.sheldan.abstracto.core.models.listener.AChannelCreatedListenerModel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.TextChannel;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import javax.annotation.Nonnull;
import java.util.List;
@Service
@Component
@Slf4j
public class ChannelListener extends ListenerAdapter {
public class AsyncAChannelCreatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncAChannelCreatedListener> channelListener;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ListenerService listenerService;
@Autowired
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
@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) {
log.info("Handling channel created event. Creating channel {} in server {}", event.getChannel().getIdLong(), event.getGuild().getIdLong());
AServer serverObject = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
log.info("Creating text channel with ID {}.", event.getChannel().getIdLong());
AServer serverObject = serverManagementService.loadOrCreate(event.getChannel().getGuild().getIdLong());
TextChannel createdChannel = event.getChannel();
AChannelType type = AChannelType.getAChannelType(createdChannel.getType());
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