[AB-96] adding ability to edit/delete modmail messages via editing/deleting the original message causing the message,

adding featuremode to modmail to define whether or not there is a separate message posted to the mod mail thread, to see it easier, renaming modmail related tables to singular, adding some necessary methods (caching) to all entities
This commit is contained in:
Sheldan
2020-10-19 23:55:51 +02:00
parent 1b98436736
commit dca98c2953
75 changed files with 952 additions and 592 deletions

View File

@@ -0,0 +1,77 @@
package dev.sheldan.abstracto.modmail.listener;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.listener.MessageDeletedListener;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.config.ModMailFeatures;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import dev.sheldan.abstracto.modmail.service.management.ModMailMessageManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ModMailMessageDeletedListener implements MessageDeletedListener {
@Autowired
private ModMailMessageManagementService modMailMessageManagementService;
@Autowired
private MessageService messageService;
@Autowired
private ModMailMessageDeletedListener self;
@Autowired
private BotService botService;
@Override
public void execute(CachedMessage messageBefore, AServerAChannelAUser authorUser, GuildChannelMember authorMember) {
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageBefore.getMessageId());
messageOptional.ifPresent(modMailMessage -> {
ModMailThread thread = modMailMessage.getThreadReference();
Long dmMessageId = modMailMessage.getCreatedMessageInDM();
boolean hasMessageInChannel = modMailMessage.getCreatedMessageInChannel() != null;
Long channelMessage = modMailMessage.getCreatedMessageInChannel();
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);
botService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(member -> {
CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(member.getUser(), dmMessageId);
CompletableFuture<Void> channelDeletePromise;
if(hasMessageInChannel) {
channelDeletePromise = messageService.deleteMessageInChannelInServer(serverId, channelId, channelMessage);
} else {
channelDeletePromise = CompletableFuture.completedFuture(null);
}
CompletableFuture.allOf(dmDeletePromise, channelDeletePromise).thenAccept(unused ->
self.removeMessageFromThread(messageBefore.getMessageId())
);
});
});
}
@Transactional
public void removeMessageFromThread(Long messageId) {
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageId);
messageOptional.ifPresent(modMailMessage ->
modMailMessageManagementService.deleteMessageFromThread(modMailMessage)
);
}
@Override
public FeatureEnum getFeature() {
return ModMailFeatures.MOD_MAIL;
}
}

View File

@@ -0,0 +1,132 @@
package dev.sheldan.abstracto.modmail.listener;
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.FeatureEnum;
import dev.sheldan.abstracto.core.listener.MessageTextUpdatedListener;
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.service.BotService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.config.ModMailFeatures;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.template.ModMailModeratorReplyModel;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean;
import dev.sheldan.abstracto.modmail.service.management.ModMailMessageManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ModMailMessageEditedListener implements MessageTextUpdatedListener {
public static final String DEFAULT_COMMAND_FOR_MODMAIL_EDIT = "reply";
@Autowired
private ModMailMessageManagementService modMailMessageManagementService;
@Autowired
private CommandService commandService;
@Autowired
private BotService botService;
@Autowired
private TemplateService templateService;
@Autowired
private ModMailMessageEditedListener self;
@Autowired
private ChannelService channelService;
@Autowired
private MessageService messageService;
@Autowired
private CommandRegistry commandRegistry;
@Autowired
private ModMailThreadService modMailThreadService;
@Override
public void execute(CachedMessage messageBefore, Message messageAfter) {
if(!modMailThreadService.isModMailThread(messageBefore.getChannelId())) {
return;
}
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.getContentRaw();
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, messageAfter);
CompletableFuture<Member> loadTargetUser = botService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId());
CompletableFuture<Member> loadEditingUser = botService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getAuthor().getUserReference().getId());
CompletableFuture.allOf(parameterParseFuture, loadTargetUser, loadEditingUser).thenAccept(unused ->
self.updateMessageInThread(messageAfter, parameterParseFuture.join(), loadTargetUser.join(), loadEditingUser.join())
);
});
}
@Transactional
public void updateMessageInThread(Message messageAfter, Parameters parameters, Member targetMember, Member editingUser) {
String newText = (String) parameters.getParameters().get(0);
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(messageAfter.getIdLong());
messageOptional.ifPresent(modMailMessage -> {
FullUserInServer fullThreadUser = FullUserInServer
.builder()
.aUserInAServer(modMailMessage.getThreadReference().getUser())
.member(targetMember)
.build();
ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel
.builder()
.text(newText)
.modMailThread(modMailMessage.getThreadReference())
.postedMessage(messageAfter)
.anonymous(modMailMessage.getAnonymous())
.threadUser(fullThreadUser);
if(modMailMessage.getAnonymous()) {
modMailModeratorReplyModelBuilder.moderator(botService.getBotInGuild(modMailMessage.getThreadReference().getServer()));
} else {
modMailModeratorReplyModelBuilder.moderator(editingUser);
}
ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(ModMailThreadServiceBean.MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY, modMailUserReplyModel);
Long threadId = modMailMessage.getThreadReference().getId();
long serverId = editingUser.getGuild().getIdLong();
if(modMailMessage.getCreatedMessageInChannel() != null) {
AChannel channel = modMailMessage.getThreadReference().getChannel();
log.trace("Editing message {} in mod mail channel {} for thread {} in server {} as well.", modMailMessage.getCreatedMessageInChannel(), channel.getId(), threadId, serverId);
channelService.editMessageInAChannel(messageToSend, channel, modMailMessage.getCreatedMessageInChannel());
}
log.trace("Editing message {} in DM channel with user {} for thread {} in server {}.", modMailMessage.getCreatedMessageInDM(), targetMember.getUser().getIdLong(), threadId, serverId);
messageService.editMessageInDMChannel(targetMember.getUser(), messageToSend, modMailMessage.getCreatedMessageInDM());
});
if(!messageOptional.isPresent()) {
log.warn("Message {} of user {} in channel {} for server {} for thread about user {} could not be found in the mod mail messages when updating the text.",
messageAfter.getIdLong(), editingUser.getIdLong(), messageAfter.getChannel().getIdLong(), messageAfter.getGuild().getIdLong(), targetMember.getIdLong());
}
}
@Override
public FeatureEnum getFeature() {
return ModMailFeatures.MOD_MAIL;
}
}

View File

@@ -8,6 +8,7 @@ import org.springframework.stereotype.Repository;
import javax.persistence.QueryHint;
import java.util.List;
import java.util.Optional;
/**
* Repository to manage the stored {@link ModMailMessage} instances
@@ -16,4 +17,7 @@ import java.util.List;
public interface ModMailMessageRepository extends JpaRepository<ModMailMessage, Long> {
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<ModMailMessage> findByThreadReference(ModMailThread modMailThread);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
Optional<ModMailMessage> findByMessageId(Long messageId);
}

View File

@@ -23,7 +23,7 @@ import java.util.Optional;
public interface ModMailThreadRepository extends JpaRepository<ModMailThread, Long> {
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
ModMailThread findByChannel(AChannel channel);
Optional<ModMailThread> findByChannel(AChannel channel);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<ModMailThread> findByUser(AUserInAServer aUserInAServer);

View File

@@ -38,7 +38,6 @@ public class ModMailMessageServiceBean implements ModMailMessageService {
modMailMessages.forEach(modMailMessage -> {
ServerChannelMessageUser.ServerChannelMessageUserBuilder serverChannelMessageBuilder = ServerChannelMessageUser
.builder()
.messageId(modMailMessage.getMessageId())
.userId(modMailMessage.getAuthor().getUserReference().getId())
.serverId(thread.getServer().getId());
// if its not from a private chat, we need to set channel ID in order to fetch the data
@@ -46,6 +45,9 @@ public class ModMailMessageServiceBean implements ModMailMessageService {
log.trace("Message {} was from DM.", modMailMessage.getMessageId());
serverChannelMessageBuilder
.channelId(modMailMessage.getThreadReference().getChannel().getId());
serverChannelMessageBuilder.messageId(modMailMessage.getCreatedMessageInChannel());
} else {
serverChannelMessageBuilder.messageId(modMailMessage.getCreatedMessageInDM());
}
messageIds.add(serverChannelMessageBuilder.build());
});

View File

@@ -17,6 +17,7 @@ import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.modmail.config.*;
import dev.sheldan.abstracto.modmail.exception.ModMailCategoryIdException;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadChannelNotFound;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadNotFoundException;
import dev.sheldan.abstracto.modmail.models.dto.LoadedModmailThreadMessageList;
import dev.sheldan.abstracto.modmail.models.database.*;
@@ -57,6 +58,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* The template key used for default mod mail exceptions
*/
public static final String MODMAIL_EXCEPTION_GENERIC_TEMPLATE = "modmail_exception_generic";
public static final String MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY = "modmail_staff_message";
@Autowired
private ModMailThreadManagementService modMailThreadManagementService;
@@ -184,7 +186,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
ModMailThread thread = createThreadObject(channel, aUserInAServer);
if(initialMessage != null) {
log.trace("Adding initial message {} to modmail thread in channel {}.", initialMessage.getId(), channel.getId());
modMailMessageManagementService.addMessageToThread(thread, sendMessage, aUserInAServer, false, false);
modMailMessageManagementService.addMessageToThread(thread, null, sendMessage, initialMessage, aUserInAServer, false, false);
}
}
@@ -327,22 +329,22 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
}
@Override
public CompletableFuture<Message> relayMessageToModMailThread(ModMailThread modMailThread, Message message, List<UndoActionInstance> undoActions) {
public CompletableFuture<Message> relayMessageToModMailThread(ModMailThread modMailThread, Message messageFromUser, List<UndoActionInstance> undoActions) {
Long serverId = modMailThread.getServer().getId();
Long channelId = modMailThread.getChannel().getId();
Long modmailThreadId = modMailThread.getId();
log.trace("Relaying message {} to modmail thread {} for user {} to server {}.", message.getId(), modMailThread.getId(), message.getAuthor().getIdLong(), modMailThread.getServer().getId());
return botService.getMemberInServerAsync(modMailThread.getServer().getId(), message.getAuthor().getIdLong()).thenCompose(member -> {
log.trace("Relaying message {} to modmail thread {} for user {} to server {}.", messageFromUser.getId(), modMailThread.getId(), messageFromUser.getAuthor().getIdLong(), modMailThread.getServer().getId());
return botService.getMemberInServerAsync(modMailThread.getServer().getId(), messageFromUser.getAuthor().getIdLong()).thenCompose(member -> {
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(serverId, channelId);
if(textChannelFromServer.isPresent()) {
TextChannel textChannel = textChannelFromServer.get();
return self.sendUserReply(textChannel, modmailThreadId, message, member, true);
return self.sendUserReply(textChannel, modmailThreadId, messageFromUser, member, true);
} else {
log.warn("Closing modmail thread {}, because it seems the channel {} in server {} got deleted.", modmailThreadId, channelId, serverId);
log.warn("Closing mod mail thread {}, because it seems the channel {} in server {} got deleted.", modmailThreadId, channelId, serverId);
// in this case there was no text channel on the server associated with the mod mail thread
// close the existing one, so the user can start a new one
self.closeModMailThreadInDb(modmailThreadId);
return message.getChannel().sendMessage(templateService.renderTemplate("modmail_failed_to_forward_message", new Object())).submit();
return messageFromUser.getChannel().sendMessage(templateService.renderTemplate("modmail_failed_to_forward_message", new Object())).submit();
}
});
@@ -354,12 +356,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* side.
* @param textChannel The {@link TextChannel} in which the {@link ModMailThread} is being handled
* @param modMailThreadId The id of the modmail thread to which the received {@link Message} is a reply to, can be null, if it is null, its the initial message
* @param message The received message from the user
* @param messageFromUser The received message from the user
* @param member The {@link Member} instance from the user the thread is about. It is used as author
* @param modMailThreadExists Whether or not the modmail thread already exists and is persisted.
* @return A {@link CompletableFuture} which resolves when the post processing of the message is completed (adding read notification, and storing messageIDs)
*/
public CompletableFuture<Message> sendUserReply(TextChannel textChannel, Long modMailThreadId, Message message, Member member, boolean modMailThreadExists) {
public CompletableFuture<Message> sendUserReply(TextChannel textChannel, Long modMailThreadId, Message messageFromUser, Member member, boolean modMailThreadExists) {
List<CompletableFuture<Member>> subscriberMemberFutures = new ArrayList<>();
if(modMailThreadExists) {
ModMailThread modMailThread = modMailThreadManagementService.getById(modMailThreadId);
@@ -378,7 +380,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
List<FullUserInServer> subscribers = new ArrayList<>();
ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel
.builder()
.postedMessage(message)
.postedMessage(messageFromUser)
.member(member)
.subscribers(subscribers)
.build();
@@ -387,11 +389,11 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
.thenCompose(aVoid -> {
log.trace("Adding read reaction to initial message for mod mail thread in channel {}.", textChannel.getGuild().getId());
return messageService.addReactionToMessageWithFuture("readReaction", textChannel.getGuild().getIdLong(), message);
return messageService.addReactionToMessageWithFuture("readReaction", textChannel.getGuild().getIdLong(), messageFromUser);
})
.thenApply(aVoid -> {
if(modMailThreadExists) {
self.postProcessSendMessages(textChannel, completableFutures.get(0).join());
self.postProcessSendMessages(textChannel, completableFutures.get(0).join(), messageFromUser);
}
return completableFutures.get(0).join();
});
@@ -403,26 +405,28 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
/**
* This message handles the post processing of the messages received by the user. This includes: saving the messageIDs
* in the database, updating the state of the {@link ModMailThread} and adding the read reaction to the user message
* @param message The actual {@link Message} instance received from the user.
* @param textChannel The channel in which the message
* @param messageInModMailThread The actual {@link Message} instance which was sent to the mod mail thread
* @param messageFromUser The {@link Message} object which was sent from the user
*/
@Transactional
public void postProcessSendMessages(TextChannel textChannel, Message message) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(textChannel.getIdLong());
public void postProcessSendMessages(TextChannel textChannel, Message messageInModMailThread, Message messageFromUser) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByChannelIdOptional(textChannel.getIdLong());
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
log.trace("Adding message {} sent from user to modmail thread {} and setting status to {}.", message.getId(), modMailThread.getId(), ModMailThreadState.USER_REPLIED);
modMailMessageManagementService.addMessageToThread(modMailThread, message, modMailThread.getUser(), false, false);
log.trace("Adding created message {} based on messeage {} sent from user to modmail thread {} and setting status to {}.", messageInModMailThread.getId(), messageFromUser.getId(), modMailThread.getId(), ModMailThreadState.USER_REPLIED);
modMailMessageManagementService.addMessageToThread(modMailThread, null, messageInModMailThread, messageFromUser, modMailThread.getUser(), false, false);
// update the state of the thread
modMailThreadManagementService.setModMailThreadState(modMailThread, ModMailThreadState.USER_REPLIED);
} else {
throw new ModMailThreadNotFoundException(textChannel.getIdLong());
throw new ModMailThreadChannelNotFound();
}
}
@Override
public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions, Member targetMember) {
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", message.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId());
AUserInAServer moderator = userInServerManagementService.loadUser(message.getMember());
public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions, Member targetMember) {
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", replyCommandMessage.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId());
AUserInAServer moderator = userInServerManagementService.loadUser(replyCommandMessage.getMember());
ModMailThread modMailThread = modMailThreadManagementService.getById(modmailThreadId);
FullUserInServer fullThreadUser = FullUserInServer
.builder()
@@ -433,7 +437,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.builder()
.text(text)
.modMailThread(modMailThread)
.postedMessage(message)
.postedMessage(replyCommandMessage)
.anonymous(anonymous)
.threadUser(fullThreadUser);
if(anonymous) {
@@ -445,9 +449,16 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
modMailModeratorReplyModelBuilder.moderator(moderatorMember);
}
ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build();
CompletableFuture<Message> future = messageService.sendEmbedToUserWithMessage(targetMember.getUser(), "modmail_staff_message", modMailUserReplyModel);
return future.thenAccept(sendMessage ->
self.saveSendMessagesAndUpdateState(modmailThreadId, anonymous, moderator, sendMessage)
MessageToSend messageToSend = templateService.renderEmbedTemplate(MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY, modMailUserReplyModel);
CompletableFuture<Message> future = messageService.sendMessageToSendToUser(targetMember.getUser(), messageToSend);
CompletableFuture<Message> sameThreadMessageFuture;
if(featureModeService.featureModeActive(ModMailFeatures.MOD_MAIL, modMailThread.getServer(), ModMailMode.SEPARATE_MESSAGE)) {
sameThreadMessageFuture = channelService.sendMessageToSendToAChannel(messageToSend, modMailThread.getChannel()).get(0);
} else {
sameThreadMessageFuture = CompletableFuture.completedFuture(null);
}
return CompletableFuture.allOf(future, sameThreadMessageFuture).thenAccept(avoid ->
self.saveSendMessagesAndUpdateState(modmailThreadId, anonymous, moderator, future.join(), replyCommandMessage, sameThreadMessageFuture.join())
);
}
@@ -481,6 +492,17 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
}
}
@Override
public boolean isModMailThread(AChannel channel) {
return modMailThreadManagementService.getByChannelOptional(channel).isPresent();
}
@Override
public boolean isModMailThread(Long channelId) {
AChannel channel = channelManagementService.loadChannel(channelId);
return modMailThreadManagementService.getByChannelOptional(channel).isPresent();
}
/**
* This method takes the actively loaded futures, calls the method responsible for logging the messages, and calls the method
* after the logging has been done.
@@ -603,7 +625,13 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
log.info("Logging message {} in modmail thread {}.", loadedMessage.getId(), modMailThreadId);
ModMailMessage modmailMessage = modMailThread.getMessages()
.stream()
.filter(modMailMessage -> modMailMessage.getMessageId().equals(loadedMessage.getIdLong()))
.filter(modMailMessage -> {
if(modMailMessage.getDmChannel()) {
return modMailMessage.getCreatedMessageInDM().equals(loadedMessage.getIdLong());
} else {
return modMailMessage.getCreatedMessageInChannel().equals(loadedMessage.getIdLong());
}
})
.findFirst().orElseThrow(() -> new AbstractoRunTimeException("Could not find desired message in list of messages in thread. This should not happen, as we just retrieved them from the same place."));
Member author = futures.getMemberFuture().join();
ModMailLoggedMessageModel modMailLoggedMessageModel =
@@ -688,12 +716,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* @throws ModMailThreadNotFoundException in case the {@link ModMailThread} is not found by the ID
*/
@Transactional
public void saveSendMessagesAndUpdateState(Long modMailThreadId, Boolean anonymous, AUserInAServer moderator, Message message) {
public void saveSendMessagesAndUpdateState(Long modMailThreadId, Boolean anonymous, AUserInAServer moderator, Message createdMessageInDM, Message replyCommandMessage, Message modMailThreadMessage) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
log.trace("Adding (anonymous: {}) message {} of moderator to modmail thread {} and setting state to {}.", anonymous, message.getId(), modMailThreadId, ModMailThreadState.MOD_REPLIED);
modMailMessageManagementService.addMessageToThread(modMailThread, message, moderator, anonymous, true);
log.trace("Adding (anonymous: {}) message {} of moderator to modmail thread {} and setting state to {}.", anonymous, createdMessageInDM.getId(), modMailThreadId, ModMailThreadState.MOD_REPLIED);
modMailMessageManagementService.addMessageToThread(modMailThread, createdMessageInDM, modMailThreadMessage, replyCommandMessage, moderator, anonymous, true);
modMailThreadManagementService.setModMailThreadState(modMailThread, ModMailThreadState.MOD_REPLIED);
} else {
throw new ModMailThreadNotFoundException(modMailThreadId);

View File

@@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
@@ -19,17 +20,21 @@ public class ModMailMessageManagementServiceBean implements ModMailMessageManage
private ModMailMessageRepository modMailMessageRepository;
@Override
public ModMailMessage addMessageToThread(ModMailThread modMailThread, Message message, AUserInAServer author, Boolean anonymous, Boolean dmChannel) {
public ModMailMessage addMessageToThread(ModMailThread modMailThread, Message createdMessageInDM, Message createdMessageInChannel, Message userPostedMessage, AUserInAServer author, Boolean anonymous, Boolean dmChannel) {
Long dmId = createdMessageInDM != null ? createdMessageInDM.getIdLong() : null;
Long channelMessageId = createdMessageInChannel != null ? createdMessageInChannel.getIdLong() : null;
ModMailMessage modMailMessage = ModMailMessage
.builder()
.author(author)
.messageId(message.getIdLong())
.messageId(userPostedMessage.getIdLong())
.createdMessageInDM(dmId)
.createdMessageInChannel(channelMessageId)
.dmChannel(dmChannel)
.threadReference(modMailThread)
.anonymous(anonymous)
.build();
log.info("Storing modmail thread message {} to modmail thread {} of user {} in server {}.",
message.getId(), modMailThread.getId(), author.getUserReference().getId(), author.getServerReference().getId());
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;
@@ -39,4 +44,14 @@ public class ModMailMessageManagementServiceBean implements ModMailMessageManage
public List<ModMailMessage> getMessagesOfThread(ModMailThread modMailThread) {
return modMailMessageRepository.findByThreadReference(modMailThread);
}
@Override
public Optional<ModMailMessage> getByMessageIdOptional(Long messageId) {
return modMailMessageRepository.findByMessageId(messageId);
}
@Override
public void deleteMessageFromThread(ModMailMessage modMailMessage) {
modMailMessageRepository.delete(modMailMessage);
}
}

View File

@@ -4,6 +4,7 @@ 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.service.management.ChannelManagementService;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadChannelNotFound;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadNotFoundException;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import dev.sheldan.abstracto.modmail.models.database.ModMailThreadState;
@@ -44,9 +45,20 @@ public class ModMailThreadManagementServiceBean implements ModMailThreadManageme
@Override
public ModMailThread getByChannel(AChannel channel) {
return modMailThreadRepository.findByChannel(channel).orElseThrow(ModMailThreadChannelNotFound::new);
}
@Override
public Optional<ModMailThread> getByChannelOptional(AChannel channel) {
return modMailThreadRepository.findByChannel(channel);
}
@Override
public Optional<ModMailThread> getByChannelIdOptional(Long channelId) {
AChannel channel = channelManagementService.loadChannel(channelId);
return getByChannelOptional(channel);
}
@Override
public List<ModMailThread> getThreadByUserAndState(AUserInAServer userInAServer, ModMailThreadState state) {
return modMailThreadRepository.findByUserAndState(userInAServer, state);

View File

@@ -10,10 +10,16 @@
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="modmail_default_feature_mode-insertion">
<insert tableName="default_feature_mode">
<column name="enabled" value="false"/>
<column name="enabled" value="true"/>
<column name="mode" value="log"/>
<column name="feature_id" valueComputed="${modmailFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="default_feature_mode">
<column name="enabled" value="true"/>
<column name="mode" value="threadMessage"/>
<column name="feature_id" valueComputed="${modmailFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -6,10 +6,10 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="modmail_messages-table">
<createTable tableName="modmail_messages">
<changeSet author="Sheldan" id="modmail_message-table">
<createTable tableName="modmail_message">
<column name="message_id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="modmail_messages_pkey"/>
<constraints nullable="false" primaryKey="true" primaryKeyName="modmail_message_pkey"/>
</column>
<column name="anonymous" type="BOOLEAN"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
@@ -22,10 +22,10 @@
</column>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="modmail_messages-fk_modmail_message_thread">
<addForeignKeyConstraint baseColumnNames="thread_reference" baseTableName="modmail_messages" constraintName="fk_modmail_message_thread" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="modmail_thread" validate="true"/>
<changeSet author="Sheldan" id="modmail_message-fk_modmail_message_thread">
<addForeignKeyConstraint baseColumnNames="thread_reference" baseTableName="modmail_message" constraintName="fk_modmail_message_thread" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="modmail_thread" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="modmail_messages-fk_modmail_message_author">
<addForeignKeyConstraint baseColumnNames="modmail_message_author" baseTableName="modmail_messages" constraintName="fk_modmail_message_author" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<changeSet author="Sheldan" id="modmail_message-fk_modmail_message_author">
<addForeignKeyConstraint baseColumnNames="modmail_message_author" baseTableName="modmail_message" constraintName="fk_modmail_message_author" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -6,10 +6,10 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="modmail_roles-table">
<createTable tableName="modmail_roles">
<changeSet author="Sheldan" id="modmail_role-table">
<createTable tableName="modmail_role">
<column autoIncrement="true" name="mod_mail_role_id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="modmail_roles_pkey"/>
<constraints nullable="false" primaryKey="true" primaryKeyName="modmail_role_pkey"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
@@ -21,10 +21,10 @@
</column>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="modmail_roles-fk_modmail_role_role">
<addForeignKeyConstraint baseColumnNames="modmail_role" baseTableName="modmail_roles" constraintName="fk_modmail_role_role" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="role" validate="true"/>
<changeSet author="Sheldan" id="modmail_role-fk_modmail_role_role">
<addForeignKeyConstraint baseColumnNames="modmail_role" baseTableName="modmail_role" constraintName="fk_modmail_role_role" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="role" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="modmail_roles-fk_modmail_role_server">
<addForeignKeyConstraint baseColumnNames="modmail_role_server" baseTableName="modmail_roles" constraintName="fk_modmail_role_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<changeSet author="Sheldan" id="modmail_role-fk_modmail_role_server">
<addForeignKeyConstraint baseColumnNames="modmail_role_server" baseTableName="modmail_role" constraintName="fk_modmail_role_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -6,8 +6,8 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="modmail_threads.xml" relativeToChangelogFile="true"/>
<include file="modmail_messages.xml" relativeToChangelogFile="true"/>
<include file="modmail_thread.xml" relativeToChangelogFile="true"/>
<include file="modmail_message.xml" relativeToChangelogFile="true"/>
<include file="modmail_subscriber.xml" relativeToChangelogFile="true"/>
<include file="modmail_roles.xml" relativeToChangelogFile="true"/>
<include file="modmail_role.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,142 @@
package dev.sheldan.abstracto.modmail.listener;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
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.service.BotService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import dev.sheldan.abstracto.modmail.service.management.ModMailMessageManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
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 java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class ModMailMessageDeletedListenerTest {
@InjectMocks
private ModMailMessageDeletedListener testUnit;
@Mock
private ModMailMessageManagementService modMailMessageManagementService;
@Mock
private MessageService messageService;
@Mock
private ModMailMessageDeletedListener self;
@Mock
private BotService botService;
@Mock
private CachedMessage deletedMessage;
@Mock
private AServerAChannelAUser origin;
@Mock
private GuildChannelMember jdaOrigin;
@Mock
private ModMailMessage modMailMessage;
@Mock
private Member targetMember;
@Mock
private User targetUser;
@Mock
private AServer server;
@Mock
private AChannel channel;
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;
private static final Long USER_ID = 5L;
private static final Long SERVER_ID = 6L;
private static final Long CHANNEL_ID = 9L;
@Test
public void testDeleteOutSideOfThread() {
when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(DELETED_MESSAGE_ID)).thenReturn(Optional.empty());
testUnit.execute(deletedMessage, origin, jdaOrigin);
verify(botService, times(0)).getMemberInServerAsync(anyLong(), anyLong());
}
@Test
public void testDeleteNonDuplicatedMessage() {
when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_ID);
when(deletedMessage.getServerId()).thenReturn(SERVER_ID);
when(modMailMessageManagementService.getByMessageIdOptional(DELETED_MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
ModMailThread thread = Mockito.mock(ModMailThread.class);
AUserInAServer targetUsInAServer = Mockito.mock(AUserInAServer.class);
when(thread.getUser()).thenReturn(targetUsInAServer);
when(thread.getChannel()).thenReturn(channel);
when(thread.getServer()).thenReturn(server);
AUser targetAUser = Mockito.mock(AUser.class);
when(targetUsInAServer.getUserReference()).thenReturn(targetAUser);
when(modMailMessage.getCreatedMessageInChannel()).thenReturn(null);
when(modMailMessage.getCreatedMessageInDM()).thenReturn(CREATED_MESSAGE_ID_2);
when(targetAUser.getId()).thenReturn(USER_ID);
when(modMailMessage.getThreadReference()).thenReturn(thread);
when(targetMember.getUser()).thenReturn(targetUser);
when(botService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(messageService.deleteMessageInChannelWithUser(targetUser, CREATED_MESSAGE_ID_2)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.execute(deletedMessage, origin, jdaOrigin);
verify(messageService, times(0)).deleteMessageInChannelInServer(eq(SERVER_ID), anyLong(), any());
verify(self, times(1)).removeMessageFromThread(DELETED_MESSAGE_ID);
}
@Test
public void testDeleteDuplicatedMessage() {
when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_ID);
when(deletedMessage.getServerId()).thenReturn(SERVER_ID);
when(modMailMessageManagementService.getByMessageIdOptional(DELETED_MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
ModMailThread thread = Mockito.mock(ModMailThread.class);
AUserInAServer targetUsInAServer = Mockito.mock(AUserInAServer.class);
when(thread.getUser()).thenReturn(targetUsInAServer);
when(thread.getChannel()).thenReturn(channel);
when(thread.getServer()).thenReturn(server);
when(server.getId()).thenReturn(SERVER_ID);
when(channel.getId()).thenReturn(CHANNEL_ID);
AUser targetAUser = Mockito.mock(AUser.class);
when(targetUsInAServer.getUserReference()).thenReturn(targetAUser);
when(modMailMessage.getCreatedMessageInChannel()).thenReturn(CREATED_MESSAGE_ID_1);
when(modMailMessage.getCreatedMessageInDM()).thenReturn(CREATED_MESSAGE_ID_2);
when(targetAUser.getId()).thenReturn(USER_ID);
when(modMailMessage.getThreadReference()).thenReturn(thread);
when(targetMember.getUser()).thenReturn(targetUser);
when(botService.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, origin, jdaOrigin);
verify(self, times(1)).removeMessageFromThread(DELETED_MESSAGE_ID);
}
@Test
public void removeMessageFromThread() {
when(modMailMessageManagementService.getByMessageIdOptional(DELETED_MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
testUnit.removeMessageFromThread(DELETED_MESSAGE_ID);
verify(modMailMessageManagementService, times(1)).deleteMessageFromThread(modMailMessage);
}
}

View File

@@ -0,0 +1,273 @@
package dev.sheldan.abstracto.modmail.listener;
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.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.service.BotService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import dev.sheldan.abstracto.modmail.models.template.ModMailModeratorReplyModel;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean;
import dev.sheldan.abstracto.modmail.service.management.ModMailMessageManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.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.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.modmail.listener.ModMailMessageEditedListener.DEFAULT_COMMAND_FOR_MODMAIL_EDIT;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class ModMailMessageEditedListenerTest {
@InjectMocks
private ModMailMessageEditedListener testUnit;
@Mock
private ModMailThreadService modMailThreadService;
@Mock
private ModMailMessageManagementService modMailMessageManagementService;
@Mock
private CommandRegistry commandRegistry;
@Mock
private CommandService commandService;
@Mock
private BotService botService;
@Mock
private TemplateService templateService;
@Mock
private ChannelService channelService;
@Mock
private MessageService messageService;
@Mock
private ModMailMessageEditedListener self;
@Mock
private CachedMessage messageBefore;
@Mock
private Message messageAfter;
@Mock
private ModMailMessage modMailMessage;
@Mock
private Parameters parsedParameters;
@Mock
private Member targetMember;
@Mock
private User targetUser;
@Mock
private MessageToSend messageToSend;
@Mock
private Member authorMember;
@Mock
private Guild guild;
@Captor
private ArgumentCaptor<ModMailModeratorReplyModel> replyModelArgumentCaptor;
private static final Long CHANNEL_ID = 5L;
private static final Long MESSAGE_ID = 6L;
private static final Long CREATED_MESSAGE_ID = 10L;
private static final String NEW_COMMAND_PART = "editedText";
private static final String NEW_PARAM = "param";
private static final String NEW_CONTENT = NEW_COMMAND_PART + " " + NEW_PARAM;
private static final Long SERVER_ID = 4L;
private static final Long USER_ID = 3L;
private static final Long AUTHOR_USER_ID = 9L;
@Test
public void testEditOutsideModMailThread() {
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(false);
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
testUnit.execute(messageBefore, messageAfter);
verify(modMailMessageManagementService, times(0)).getByMessageIdOptional(anyLong());
}
@Test
public void testEditNotTrackedMessage() {
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.empty());
testUnit.execute(messageBefore, messageAfter);
verify(commandRegistry, times(0)).getCommandName(anyString(), anyLong());
}
@Test
public void testEditMessageWithCorrectCommand() {
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(messageBefore.getServerId()).thenReturn(SERVER_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
AUserInAServer targetUsInAServer = Mockito.mock(AUserInAServer.class);
when(thread.getUser()).thenReturn(targetUsInAServer);
AUser targetUser = Mockito.mock(AUser.class);
when(targetUsInAServer.getUserReference()).thenReturn(targetUser);
when(targetUser.getId()).thenReturn(USER_ID);
AUserInAServer authorUserInAServer = Mockito.mock(AUserInAServer.class);
when(modMailMessage.getAuthor()).thenReturn(authorUserInAServer);
AUser authorUser = Mockito.mock(AUser.class);
when(authorUser.getId()).thenReturn(AUTHOR_USER_ID);
when(authorUserInAServer.getUserReference()).thenReturn(authorUser);
when(messageAfter.getContentRaw()).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, messageAfter)).thenReturn(CompletableFuture.completedFuture(parsedParameters));
when(botService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(botService.getMemberInServerAsync(SERVER_ID, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
testUnit.execute(messageBefore, messageAfter);
verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
}
@Test
public void testEditMessageWithInCorrectCommand() {
when(modMailThreadService.isModMailThread(CHANNEL_ID)).thenReturn(true);
when(messageBefore.getChannelId()).thenReturn(CHANNEL_ID);
when(messageBefore.getMessageId()).thenReturn(MESSAGE_ID);
when(messageBefore.getServerId()).thenReturn(SERVER_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
AUserInAServer aUserInAServer = Mockito.mock(AUserInAServer.class);
when(thread.getUser()).thenReturn(aUserInAServer);
AUser user = Mockito.mock(AUser.class);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(user.getId()).thenReturn(USER_ID);
AUserInAServer authorUserInAServer = Mockito.mock(AUserInAServer.class);
when(modMailMessage.getAuthor()).thenReturn(authorUserInAServer);
AUser authorUser = Mockito.mock(AUser.class);
when(authorUser.getId()).thenReturn(AUTHOR_USER_ID);
when(authorUserInAServer.getUserReference()).thenReturn(authorUser);
when(messageAfter.getContentRaw()).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, messageAfter)).thenReturn(CompletableFuture.completedFuture(parsedParameters));
when(botService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember));
when(botService.getMemberInServerAsync(SERVER_ID, AUTHOR_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
testUnit.execute(messageBefore, messageAfter);
verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
}
@Test
public void testUpdateAnonymousMessageInThreadNotDuplicated() {
when(messageAfter.getIdLong()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
when(modMailMessage.getAnonymous()).thenReturn(true);
when(modMailMessage.getCreatedMessageInChannel()).thenReturn(null);
when(modMailMessage.getCreatedMessageInDM()).thenReturn(CREATED_MESSAGE_ID);
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
when(targetMember.getUser()).thenReturn(targetUser);
when(authorMember.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
when(parsedParameters.getParameters()).thenReturn(Arrays.asList(NEW_PARAM));
when(templateService.renderEmbedTemplate(eq(ModMailThreadServiceBean.MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY), replyModelArgumentCaptor.capture())).thenReturn(messageToSend);
testUnit.updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
verify(channelService, times(0)).editMessageInAChannel(eq(messageToSend), any(AChannel.class), anyLong());
verify(messageService, times(1)).editMessageInDMChannel(targetUser, messageToSend, CREATED_MESSAGE_ID);
Assert.assertTrue(replyModelArgumentCaptor.getValue().getAnonymous());
}
@Test
public void testUpdateAnonymousMessageInThreadDuplicated() {
when(messageAfter.getIdLong()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
when(modMailMessage.getAnonymous()).thenReturn(true);
when(modMailMessage.getCreatedMessageInChannel()).thenReturn(CREATED_MESSAGE_ID);
when(modMailMessage.getCreatedMessageInDM()).thenReturn(CREATED_MESSAGE_ID);
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
when(targetMember.getUser()).thenReturn(targetUser);
when(authorMember.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
AChannel channel = Mockito.mock(AChannel.class);
when(thread.getChannel()).thenReturn(channel);
when(channel.getId()).thenReturn(CHANNEL_ID);
when(parsedParameters.getParameters()).thenReturn(Arrays.asList(NEW_PARAM));
when(templateService.renderEmbedTemplate(eq(ModMailThreadServiceBean.MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY), replyModelArgumentCaptor.capture())).thenReturn(messageToSend);
testUnit.updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
verify(channelService, times(1)).editMessageInAChannel(eq(messageToSend), eq(channel), eq(CREATED_MESSAGE_ID));
verify(messageService, times(1)).editMessageInDMChannel(targetUser, messageToSend, CREATED_MESSAGE_ID);
Assert.assertTrue(replyModelArgumentCaptor.getValue().getAnonymous());
}
@Test
public void testUpdateMessageInThreadNotDuplicated() {
when(messageAfter.getIdLong()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
when(modMailMessage.getAnonymous()).thenReturn(false);
when(modMailMessage.getCreatedMessageInChannel()).thenReturn(null);
when(modMailMessage.getCreatedMessageInDM()).thenReturn(CREATED_MESSAGE_ID);
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
when(targetMember.getUser()).thenReturn(targetUser);
when(authorMember.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
when(parsedParameters.getParameters()).thenReturn(Arrays.asList(NEW_PARAM));
when(templateService.renderEmbedTemplate(eq(ModMailThreadServiceBean.MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY), replyModelArgumentCaptor.capture())).thenReturn(messageToSend);
testUnit.updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
verify(channelService, times(0)).editMessageInAChannel(eq(messageToSend), any(AChannel.class), anyLong());
verify(messageService, times(1)).editMessageInDMChannel(targetUser, messageToSend, CREATED_MESSAGE_ID);
Assert.assertFalse(replyModelArgumentCaptor.getValue().getAnonymous());
}
@Test
public void testUpdateMessageInThreadDuplicated() {
when(messageAfter.getIdLong()).thenReturn(MESSAGE_ID);
when(modMailMessageManagementService.getByMessageIdOptional(MESSAGE_ID)).thenReturn(Optional.of(modMailMessage));
when(modMailMessage.getAnonymous()).thenReturn(false);
when(modMailMessage.getCreatedMessageInChannel()).thenReturn(CREATED_MESSAGE_ID);
when(modMailMessage.getCreatedMessageInDM()).thenReturn(CREATED_MESSAGE_ID);
ModMailThread thread = Mockito.mock(ModMailThread.class);
when(modMailMessage.getThreadReference()).thenReturn(thread);
when(targetMember.getUser()).thenReturn(targetUser);
when(authorMember.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
AChannel channel = Mockito.mock(AChannel.class);
when(thread.getChannel()).thenReturn(channel);
when(channel.getId()).thenReturn(CHANNEL_ID);
when(parsedParameters.getParameters()).thenReturn(Arrays.asList(NEW_PARAM));
when(templateService.renderEmbedTemplate(eq(ModMailThreadServiceBean.MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY), replyModelArgumentCaptor.capture())).thenReturn(messageToSend);
testUnit.updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember);
verify(channelService, times(1)).editMessageInAChannel(eq(messageToSend), eq(channel), eq(CREATED_MESSAGE_ID));
verify(messageService, times(1)).editMessageInDMChannel(targetUser, messageToSend, CREATED_MESSAGE_ID);
Assert.assertFalse(replyModelArgumentCaptor.getValue().getAnonymous());
}
}