From 336c3d0bd8fe832b7ccc76e0453a0b5a708698dd Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Fri, 31 May 2024 00:54:32 +0200 Subject: [PATCH] [AB-xxx] adding modmail support for ban appeals refactoring modmail to use user objects instead of member in various places --- .../modmail/modmail-impl/pom.xml | 6 + .../abstracto/modmail/command/AnonReply.java | 8 +- .../abstracto/modmail/command/Close.java | 8 +- .../abstracto/modmail/command/Contact.java | 30 +- .../modmail/command/DenyModmailAppeal.java | 121 ++++++++ .../abstracto/modmail/command/Reply.java | 9 +- .../ModMailInitialButtonListener.java | 14 +- .../ModMailMessageDeletedListener.java | 7 +- .../ModMailMessageEditedListener.java | 29 +- .../listener/ModMailMessageListener.java | 3 - .../service/ModMailRoleServiceBean.java | 3 - .../service/ModMailThreadServiceBean.java | 228 ++++++++------ .../migrations/1.5.37/collection.xml | 6 + .../migrations/1.5.37/seedData/command.xml | 16 + .../migrations/1.5.37/seedData/data.xml | 6 + .../migrations/modMail-changeLog.xml | 1 + .../main/resources/modmail-config.properties | 9 +- .../ModMailMessageDeletedListenerTest.java | 141 --------- .../ModMailMessageEditedListenerTest.java | 283 ------------------ .../modmail/config/ModMailFeatureConfig.java | 17 +- .../abstracto/modmail/config/ModMailMode.java | 5 +- .../modmail/model/ClosingContext.java | 1 - .../template/ContactNotificationModel.java | 4 +- .../template/ModMailModeratorReplyModel.java | 7 +- .../template/ModMailNotificationModel.java | 8 +- .../model/template/ModMailThreaderHeader.java | 10 +- .../model/template/ModMailUserReplyModel.java | 6 +- .../modmail/service/ModMailThreadService.java | 15 +- .../models/template/display/UserDisplay.java | 3 + 29 files changed, 386 insertions(+), 618 deletions(-) create mode 100644 abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/DenyModmailAppeal.java create mode 100644 abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/collection.xml create mode 100644 abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/command.xml create mode 100644 abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/data.xml delete mode 100644 abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListenerTest.java delete mode 100644 abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListenerTest.java diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/pom.xml b/abstracto-application/abstracto-modules/modmail/modmail-impl/pom.xml index e43fb1c6d..5aeee0e0f 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/pom.xml +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/pom.xml @@ -38,6 +38,12 @@ ${project.version} + + dev.sheldan.abstracto.modules + moderation-int + ${project.version} + + dev.sheldan.abstracto.core metrics-int diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/AnonReply.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/AnonReply.java index f0c711f1f..1c8c7d3bd 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/AnonReply.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/AnonReply.java @@ -8,7 +8,7 @@ import dev.sheldan.abstracto.core.command.config.Parameter; import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.config.FeatureDefinition; -import dev.sheldan.abstracto.core.service.MemberService; +import dev.sheldan.abstracto.core.service.UserService; import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException; @@ -40,7 +40,7 @@ public class AnonReply extends AbstractConditionableCommand { private ModMailThreadManagementService modMailThreadManagementService; @Autowired - private MemberService memberService; + private UserService userService; @Override public CompletableFuture executeAsync(CommandContext commandContext) { @@ -52,8 +52,8 @@ public class AnonReply extends AbstractConditionableCommand { throw new ModMailThreadClosedException(); } Long threadId = modMailThread.getId(); - return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member -> - modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), true, member) + return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user -> + modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), true, user, commandContext.getGuild()) ).thenApply(aVoid -> CommandResult.fromSuccess()); } diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Close.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Close.java index a67b52ab2..74d0ae38a 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Close.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Close.java @@ -81,7 +81,6 @@ public class Close extends AbstractConditionableCommand { .builder() .closingMember(commandContext.getAuthor()) .notifyUser(true) - .channel(commandContext.getChannel()) .log(true) .note(note) .build(); @@ -112,19 +111,18 @@ public class Close extends AbstractConditionableCommand { ClosingContext context = ClosingContext .builder() .closingMember(event.getMember()) - .channel(event.getChannel()) .notifyUser(!silently) .log(log) .note(note) .build(); return interactionService.replyEmbed(CLOSE_RESPONSE, event) - .thenCompose(interactionHook -> self.closeThread(context)) + .thenCompose(interactionHook -> self.closeThread(context, event.getChannelIdLong())) .thenApply(aVoid -> CommandResult.fromIgnored()); } @Transactional - public CompletableFuture closeThread(ClosingContext closingContext) { - ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(closingContext.getChannel().getIdLong()); + public CompletableFuture closeThread(ClosingContext closingContext, Long channelId) { + ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(channelId); if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) { throw new ModMailThreadClosedException(); } diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Contact.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Contact.java index e95b77889..f71f91cf0 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Contact.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Contact.java @@ -25,6 +25,7 @@ import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementS import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; @@ -44,7 +45,7 @@ import java.util.concurrent.CompletableFuture; @Slf4j public class Contact extends AbstractConditionableCommand { - private static final String CONTACT_PARAMETER = "contact"; + private static final String COMMAND_NAME = "contact"; private static final String USER_PARMETER = "user"; private static final String MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE = "modmail_thread_already_exists"; private static final String CONTACT_RESPONSE = "contact_response"; @@ -87,24 +88,21 @@ public class Contact extends AbstractConditionableCommand { List> futures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel()); return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored()); } else { - return modMailThreadService.createModMailThreadForUser(targetUser, null, false, commandContext.getUndoActions()) - .thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser, unused, commandContext.getChannel())) + return modMailThreadService.createModMailThreadForUser(targetUser.getUser(), targetUser.getGuild(), null, false, commandContext.getUndoActions()) + .thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser.getUser(), unused, commandContext.getChannel())) .thenApply(aVoid -> CommandResult.fromSuccess()); } } @Override public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { - Member member = slashCommandParameterService.getCommandOption(USER_PARMETER, event, Member.class); - if(!member.getGuild().equals(event.getGuild())) { - throw new EntityGuildMismatchException(); - } - AUserInAServer user = userManagementService.loadOrCreateUser(member); + User user = slashCommandParameterService.getCommandOption(USER_PARMETER, event, User.class); + AUserInAServer userInAServer = userManagementService.loadOrCreateUser(user.getIdLong(), event.getGuild().getIdLong()); // if this AUserInAServer already has an open thread, we should instead post a message // containing a link to the channel, instead of opening a new one - if(modMailThreadManagementService.hasOpenModMailThreadForUser(user)) { - log.info("Modmail thread for user {} in server {} already exists. Notifying user {}.", event.getMember().getId(), event.getGuild().getId(), user.getUserReference().getId()); - ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(user); + if(modMailThreadManagementService.hasOpenModMailThreadForUser(userInAServer)) { + log.info("Modmail thread for userInAServer {} in server {} already exists. Notifying userInAServer {}.", event.getMember().getId(), event.getGuild().getId(), userInAServer.getUserReference().getId()); + ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(userInAServer); ModMailThreadExistsModel model = ModMailThreadExistsModel .builder() .existingModMailThread(existingThread) @@ -114,9 +112,9 @@ public class Contact extends AbstractConditionableCommand { .thenApply(interactionHook -> CommandResult.fromSuccess()); } else { CompletableFuture response = interactionService.replyEmbed(CONTACT_RESPONSE, event); - CompletableFuture threadFuture = modMailThreadService.createModMailThreadForUser(member, null, false, new ArrayList<>()); + CompletableFuture threadFuture = modMailThreadService.createModMailThreadForUser(user, event.getGuild(), null, false, new ArrayList<>()); return CompletableFuture.allOf(response, threadFuture) - .thenCompose(unused -> modMailThreadService.sendContactNotification(member, threadFuture.join(), response.join())) + .thenCompose(unused -> modMailThreadService.sendContactNotification(user, threadFuture.join(), response.join())) .thenApply(o -> CommandResult.fromSuccess()); } } @@ -126,7 +124,7 @@ public class Contact extends AbstractConditionableCommand { Parameter responseText = Parameter .builder() .name(USER_PARMETER) - .type(Member.class) + .type(User.class) .templated(true) .build(); List parameters = Arrays.asList(responseText); @@ -139,11 +137,11 @@ public class Contact extends AbstractConditionableCommand { .builder() .enabled(true) .rootCommandName(ModMailSlashCommandNames.MODMAIL) - .commandName(CONTACT_PARAMETER) + .commandName(COMMAND_NAME) .build(); return CommandConfiguration.builder() - .name(CONTACT_PARAMETER) + .name(COMMAND_NAME) .module(ModMailModuleDefinition.MODMAIL) .parameters(parameters) .slashCommandConfig(slashCommandConfig) diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/DenyModmailAppeal.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/DenyModmailAppeal.java new file mode 100644 index 000000000..7b1189a5b --- /dev/null +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/DenyModmailAppeal.java @@ -0,0 +1,121 @@ +package dev.sheldan.abstracto.modmail.command; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.condition.CommandCondition; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.FeatureMode; +import dev.sheldan.abstracto.core.interaction.InteractionService; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; +import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; +import dev.sheldan.abstracto.modmail.config.ModMailMode; +import dev.sheldan.abstracto.modmail.config.ModMailSlashCommandNames; +import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException; +import dev.sheldan.abstracto.modmail.model.database.ModMailThread; +import dev.sheldan.abstracto.modmail.model.database.ModMailThreadState; +import dev.sheldan.abstracto.modmail.service.ModMailThreadService; +import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionHook; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@Component +@Slf4j +public class DenyModmailAppeal extends AbstractConditionableCommand { + + private static final String COMMAND_NAME = "denyappeal"; + private static final String FULL_COMMAND_NAME = "denyModmailAppeal"; + private static final String REASON_PARAMETER = "reason"; + + private static final String RESPONSE_TEMPLATE = "denyModmailAppeal_response"; + + @Autowired + private ModMailContextCondition requiresModMailCondition; + + @Autowired + private ModMailThreadManagementService modMailThreadManagementService; + + @Autowired + private ModMailThreadService modMailThreadService; + + @Autowired + private SlashCommandParameterService slashCommandParameterService; + + @Autowired + private InteractionService interactionService; + + @Override + public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { + ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(event.getChannel().getIdLong()); + if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) { + throw new ModMailThreadClosedException(); + } + String reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class); + CompletableFuture response = interactionService.replyEmbed(RESPONSE_TEMPLATE, event); + CompletableFuture threadFuture = modMailThreadService.rejectAppeal(modMailThread, reason, event.getMember()); + return CompletableFuture.allOf(response, threadFuture) + .thenApply(unused -> CommandResult.fromSuccess()); + } + + @Override + public FeatureDefinition getFeature() { + return ModMailFeatureDefinition.MOD_MAIL; + } + + @Override + public CommandConfiguration getConfiguration() { + Parameter responseText = Parameter + .builder() + .name(REASON_PARAMETER) + .type(String.class) + .templated(true) + .build(); + + List parameters = Arrays.asList(responseText); + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .build(); + + SlashCommandConfig slashCommandConfig = SlashCommandConfig + .builder() + .enabled(true) + .rootCommandName(ModMailSlashCommandNames.MODMAIL) + .commandName(COMMAND_NAME) + .build(); + + return CommandConfiguration.builder() + .name(FULL_COMMAND_NAME) + .module(ModMailModuleDefinition.MODMAIL) + .parameters(parameters) + .slashCommandConfig(slashCommandConfig) + .help(helpInfo) + .slashCommandOnly(true) + .supportsEmbedException(true) + .templated(true) + .build(); + } + + @Override + public List getConditions() { + List conditions = super.getConditions(); + conditions.add(requiresModMailCondition); + return conditions; + } + + @Override + public List getFeatureModeLimitations() { + return List.of(ModMailMode.MOD_MAIL_APPEALS); + } +} diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Reply.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Reply.java index c536b070b..fee6523ff 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Reply.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/command/Reply.java @@ -9,6 +9,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.service.MemberService; +import dev.sheldan.abstracto.core.service.UserService; import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException; @@ -39,7 +40,7 @@ public class Reply extends AbstractConditionableCommand { private ModMailThreadManagementService modMailThreadManagementService; @Autowired - private MemberService memberService; + private UserService userService; @Override public CompletableFuture executeAsync(CommandContext commandContext) { @@ -50,8 +51,8 @@ public class Reply extends AbstractConditionableCommand { throw new ModMailThreadClosedException(); } Long threadId = modMailThread.getId(); - return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member -> - modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), false, member) + return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user -> + modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), false, user, commandContext.getGuild()) ).thenApply(aVoid -> CommandResult.fromSuccess()); } @@ -65,7 +66,7 @@ public class Reply extends AbstractConditionableCommand { .optional(true) .templated(true) .build(); - List parameters = Arrays.asList(responseText); + List parameters = List.of(responseText); HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); return CommandConfiguration.builder() .name("reply") diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailInitialButtonListener.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailInitialButtonListener.java index 42d255ec8..d401e5532 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailInitialButtonListener.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailInitialButtonListener.java @@ -14,6 +14,7 @@ import dev.sheldan.abstracto.modmail.model.dto.ServiceChoicesPayload; import dev.sheldan.abstracto.modmail.service.ModMailThreadService; import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean; import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Guild; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -25,7 +26,7 @@ import java.util.ArrayList; public class ModMailInitialButtonListener implements ButtonClickedListener { @Autowired - private MemberService memberService; + private GuildService guildService; @Autowired private ModMailThreadService modMailThreadService; @@ -51,20 +52,19 @@ public class ModMailInitialButtonListener implements ButtonClickedListener { Long userId = choices.getUserId(); log.debug("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerId(), userId); ArrayList undoActions = new ArrayList<>(); - memberService.getMemberInServerAsync(chosenServer.getServerId(), userId) - .thenCompose(member -> channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId()) + Guild guild = guildService.getGuildById(chosenServer.getServerId()); + channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId()) .thenCompose(originalMessage -> { try { - return modMailThreadService.createModMailThreadForUser(member, originalMessage, true, undoActions); + return modMailThreadService.createModMailThreadForUser(model.getEvent().getUser(), guild, originalMessage, true, undoActions); } catch (Exception ex) { log.error("Failed to setup thread correctly", ex); undoActionService.performActions(undoActions); return null; } }) - .thenAccept(unused -> self.cleanup(model))) - .exceptionally(throwable -> { - log.error("Failed to setup thread correctly", throwable); + .thenAccept(unused -> self.cleanup(model)).exceptionally(throwable -> { + log.warn("Failed to setup modmail thread."); undoActionService.performActions(undoActions); return null; }); diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListener.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListener.java index 9ef2690a9..d68ded485 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListener.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListener.java @@ -7,6 +7,7 @@ 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.core.service.UserService; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.model.database.ModMailMessage; import dev.sheldan.abstracto.modmail.model.database.ModMailThread; @@ -33,7 +34,7 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene private ModMailMessageDeletedListener self; @Autowired - private MemberService memberService; + private UserService userService; @Override public DefaultListenerResult execute(MessageDeletedModel model) { @@ -47,8 +48,8 @@ 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(model.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(member -> { - CompletableFuture dmDeletePromise = messageService.deleteMessageInChannelWithUser(member.getUser(), dmMessageId); + userService.retrieveUserForId(modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(user -> { + CompletableFuture dmDeletePromise = messageService.deleteMessageInChannelWithUser(user, dmMessageId); CompletableFuture channelDeletePromise; if(hasMessageInChannel) { channelDeletePromise = messageService.deleteMessageInChannelInServer(serverId, channelId, channelMessage); diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListener.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListener.java index 37d77d564..4249c4151 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListener.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListener.java @@ -10,9 +10,11 @@ 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.MessageUpdatedModel; +import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MessageService; +import dev.sheldan.abstracto.core.service.UserService; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.model.database.ModMailMessage; import dev.sheldan.abstracto.modmail.model.template.ModMailModeratorReplyModel; @@ -24,6 +26,7 @@ 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.Message; +import net.dv8tion.jda.api.entities.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -45,9 +48,6 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener @Autowired private CommandService commandService; - @Autowired - private MemberService memberService; - @Autowired private TemplateService templateService; @@ -66,6 +66,12 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener @Autowired private ModMailThreadService modMailThreadService; + @Autowired + private MemberService memberService; + + @Autowired + private UserService userService; + @Override public DefaultListenerResult execute(MessageUpdatedModel model) { CachedMessage messageBefore = model.getBefore(); @@ -85,7 +91,7 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener log.info("Edit did not contain the original command to retrieve the parameters for. Resulting to {}.", DEFAULT_COMMAND_FOR_MODMAIL_EDIT); } CompletableFuture parameterParseFuture = commandService.getParametersForCommand(commandName, message); - CompletableFuture loadTargetUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()); + CompletableFuture loadTargetUser = userService.retrieveUserForId(modMailMessage.getThreadReference().getUser().getUserReference().getId()); CompletableFuture 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()) @@ -100,15 +106,10 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener } @Transactional - public void updateMessageInThread(Message loadedMessage, Parameters parameters, Member targetMember, Member editingUser) { + public void updateMessageInThread(Message loadedMessage, Parameters parameters, User user, Member editingUser) { String newText = (String) parameters.getParameters().get(0); Optional messageOptional = modMailMessageManagementService.getByMessageIdOptional(loadedMessage.getIdLong()); messageOptional.ifPresent(modMailMessage -> { - FullUserInServer fullThreadUser = FullUserInServer - .builder() - .aUserInAServer(modMailMessage.getThreadReference().getUser()) - .member(targetMember) - .build(); List imageUrls = loadedMessage .getAttachments() .stream() @@ -128,7 +129,7 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener .attachedImageUrls(imageUrls) .remainingAttachments(otherAttachments) .anonymous(modMailMessage.getAnonymous()) - .threadUser(fullThreadUser); + .userDisplay(UserDisplay.fromUser(user)); if(modMailMessage.getAnonymous()) { modMailModeratorReplyModelBuilder.moderator(memberService.getBotInGuild(modMailMessage.getThreadReference().getServer())); } else { @@ -143,13 +144,13 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener log.debug("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.debug("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()); + log.debug("Editing message {} in DM channel with user {} for thread {} in server {}.", modMailMessage.getCreatedMessageInDM(), user.getIdLong(), threadId, serverId); + messageService.editMessageInDMChannel(user, 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.", - loadedMessage.getIdLong(), editingUser.getIdLong(), loadedMessage.getChannel().getIdLong(), loadedMessage.getGuild().getIdLong(), targetMember.getIdLong()); + loadedMessage.getIdLong(), editingUser.getIdLong(), loadedMessage.getChannel().getIdLong(), loadedMessage.getGuild().getIdLong(), user.getIdLong()); } } diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageListener.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageListener.java index b0b826765..abfc3036c 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageListener.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageListener.java @@ -36,9 +36,6 @@ public class ModMailMessageListener implements PrivateMessageReceivedListener { @Autowired private UserManagementService userManagementService; - @Autowired - private UserInServerManagementService userInServerManagementService; - @Override @Transactional public void execute(Message message) { diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailRoleServiceBean.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailRoleServiceBean.java index 77c5df592..feadf74b0 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailRoleServiceBean.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailRoleServiceBean.java @@ -19,9 +19,6 @@ public class ModMailRoleServiceBean implements ModMailRoleService { @Autowired private CommandService commandService; - @Autowired - private FeatureManagementService featureManagementService; - @Override public void addRoleToModMailRoles(ARole role) { log.info("Adding role {} to modmail roles in server {}.", role.getId(), role.getServer().getId()); diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadServiceBean.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadServiceBean.java index 8f5a8ae77..b4660b28f 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadServiceBean.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadServiceBean.java @@ -8,10 +8,7 @@ import dev.sheldan.abstracto.core.interaction.InteractionService; 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.FeatureValidationResult; -import dev.sheldan.abstracto.core.models.FullGuild; -import dev.sheldan.abstracto.core.models.FullUserInServer; -import dev.sheldan.abstracto.core.models.UndoActionInstance; +import dev.sheldan.abstracto.core.models.*; import dev.sheldan.abstracto.core.models.database.*; import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import dev.sheldan.abstracto.core.service.*; @@ -20,6 +17,7 @@ import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.utils.CompletableFutureList; import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.moderation.service.BanService; import dev.sheldan.abstracto.modmail.config.ModMailFeatureConfig; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.config.ModMailMode; @@ -52,6 +50,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; +import java.time.Duration; import java.time.Instant; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -112,6 +111,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService { @Autowired private GuildService guildService; + @Autowired + private BanService banService; + @Autowired private MessageService messageService; @@ -188,22 +190,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService { public static final String MODMAIL_INITIAL_ORIGIN = "modmailInitial"; @Override - public CompletableFuture createModMailThreadForUser(Member member, Message initialMessage, boolean userInitiated, List undoActions) { - Long serverId = member.getGuild().getIdLong(); - User user = member.getUser(); - AServer server = serverManagementService.loadServer(member.getGuild().getIdLong()); + public CompletableFuture createModMailThreadForUser(User user, Guild guild, Message initialMessage, boolean userInitiated, List undoActions) { + Long serverId = guild.getIdLong(); + AServer server = serverManagementService.loadServer(serverId); metricService.incrementCounter(MODMAIL_THREAD_CREATED_COUNTER); ModMailChannelNameModel model = ModMailChannelNameModel .builder() .serverId(serverId) - .userId(member.getIdLong()) + .userId(user.getIdLong()) .randomText(RandomStringUtils.randomAlphanumeric(25)) .uuid(UUID.randomUUID().toString()) .currentDate(Instant.now()) .build(); String channelName = templateService.renderTemplate(TEXT_CHANNEL_NAME_TEMPLATE_KEY, model, serverId); if (featureModeService.featureModeActive(ModMailFeatureDefinition.MOD_MAIL, serverId, ModMailMode.THREAD_CONTAINER)) { - MessageToSend notificationMessageToSend = getModmailNotificationMessageToSend(member, null, serverId, false); + MessageToSend notificationMessageToSend = getModmailNotificationMessageToSend(user, null, serverId, false); Optional modmailContainerOptional = postTargetService.getPostTargetChannel(ModMailPostTargets.MOD_MAIL_CONTAINER, serverId); if(modmailContainerOptional.isEmpty()) { throw new AbstractoRunTimeException("Modmail thread container not setup."); @@ -219,7 +220,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { .thenCompose(unused -> channelService.createThreadWithStarterMessage(textChannel, channelName, notificationMessage.get(0).join().getIdLong())) .thenCompose(threadChannel -> { undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, threadChannel.getIdLong())); - return self.performModMailThreadSetup(member, initialMessage, threadChannel, userInitiated, undoActions) + return self.performModMailThreadSetup(user, initialMessage, threadChannel, userInitiated, undoActions) .thenCompose(unused -> CompletableFuture.completedFuture(threadChannel)); }); } else { @@ -228,7 +229,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { CompletableFuture textChannelFuture = channelService.createTextChannel(channelName, server, categoryId); return textChannelFuture.thenCompose(channel -> { undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, channel.getIdLong())); - return self.performModMailThreadSetup(member, initialMessage, channel, userInitiated, undoActions) + return self.performModMailThreadSetup(user, initialMessage, channel, userInitiated, undoActions) .thenCompose(unused -> CompletableFuture.completedFuture(channel)); }); } @@ -236,21 +237,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService { @Transactional @Override - public CompletableFuture sendContactNotification(Member member, MessageChannel messageChannel, MessageChannel feedBackChannel) { + public CompletableFuture sendContactNotification(User user, MessageChannel messageChannel, MessageChannel feedBackChannel) { ContactNotificationModel model = ContactNotificationModel .builder() .createdChannel(messageChannel) - .targetMember(member) + .userDisplay(UserDisplay.fromUser(user)) .build(); return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(MODMAIL_THREAD_CREATED_TEMPLATE_KEY, model, feedBackChannel)); } @Override - public CompletableFuture sendContactNotification(Member member, MessageChannel createdMessageChannel, InteractionHook interactionHook) { + public CompletableFuture sendContactNotification(User user, MessageChannel createdMessageChannel, InteractionHook interactionHook) { ContactNotificationModel model = ContactNotificationModel .builder() .createdChannel(createdMessageChannel) - .targetMember(member) + .userDisplay(UserDisplay.fromUser(user)) .build(); return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(MODMAIL_THREAD_CREATED_TEMPLATE_KEY, model, interactionHook)); } @@ -258,7 +259,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { /** * This method is responsible for creating the instance in the database, sending the header in the newly created text channel and forwarding the initial message * by the user (if any), after this is complete, this method executes the method to perform the mod mail notification. - * @param member The {@link Member} for which a {@link ModMailThread} is being created + * @param user The {@link User} for which a {@link ModMailThread} is being created * @param initialMessage The {@link Message} which was sent by the user to open a thread, this is null, if the thread was opened via a command * @param channel The created {@link TextChannel} in which the mod mail thread is dealt with * @param userInitiated Whether the thread was initiated by a member @@ -266,33 +267,33 @@ public class ModMailThreadServiceBean implements ModMailThreadService { * @return A {@link CompletableFuture future} which completes when the setup is done */ @Transactional - public CompletableFuture performModMailThreadSetup(Member member, Message initialMessage, GuildMessageChannel channel, boolean userInitiated, List undoActions) { - log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), member.getId(), channel.getGuild().getId(), userInitiated); - CompletableFuture headerFuture = sendModMailHeader(channel, member); + public CompletableFuture performModMailThreadSetup(User user, Message initialMessage, GuildMessageChannel channel, boolean userInitiated, List undoActions) { + log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), user.getId(), channel.getGuild().getId(), userInitiated); + CompletableFuture headerFuture = sendModMailHeader(channel, user); CompletableFuture userReplyMessage; if(initialMessage != null){ - log.info("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), member.getId(), channel.getId()); - userReplyMessage = self.sendUserReply(channel, 0L, initialMessage, member, false); + log.info("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), user.getId(), channel.getId()); + userReplyMessage = self.sendUserReply(channel, 0L, initialMessage, false); } else { log.info("No initial message to send."); userReplyMessage = CompletableFuture.completedFuture(null); } CompletableFuture notificationFuture; if (userInitiated) { - notificationFuture = self.sendModMailNotification(member, channel); + notificationFuture = self.sendModMailNotification(user, channel); } else { notificationFuture = CompletableFuture.completedFuture(null); } return CompletableFuture.allOf(headerFuture, notificationFuture, userReplyMessage).thenAccept(aVoid -> { undoActions.clear(); - self.setupModMailThreadInDB(initialMessage, channel, member, userReplyMessage.join()); + self.setupModMailThreadInDB(initialMessage, channel, user, userReplyMessage.join()); }); } @Transactional - public void setupModMailThreadInDB(Message initialMessage, GuildMessageChannel channel, Member member, Message sendMessage) { + public void setupModMailThreadInDB(Message initialMessage, GuildMessageChannel channel, User user, Message sendMessage) { log.info("Persisting info about modmail thread {} in database.", channel.getIdLong()); - AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member); + AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(channel.getGuild().getIdLong(), user.getIdLong()); ModMailThread thread = createThreadObject(channel, aUserInAServer); if(initialMessage != null) { log.debug("Adding initial message {} to modmail thread in channel {}.", initialMessage.getId(), channel.getId()); @@ -302,19 +303,19 @@ public class ModMailThreadServiceBean implements ModMailThreadService { /** * Sends the message containing the pings to notify the staff members to handle the opened {@link ModMailThread} - * @param member The {@link FullUserInServer} which opened the thread + * @param user The {@link FullUserInServer} which opened the thread * @param channel The created {@link GuildMessageChannel} in which the mod mail thread is dealt with * @return A {@link CompletableFuture future} which completes when the notification has been sent */ @Transactional - public CompletableFuture sendModMailNotification(Member member, GuildMessageChannel channel) { - Long serverId = member.getGuild().getIdLong(); - MessageToSend messageToSend = getModmailNotificationMessageToSend(member, channel, serverId, true); + public CompletableFuture sendModMailNotification(User user, GuildMessageChannel channel) { + Long serverId = channel.getGuild().getIdLong(); + MessageToSend messageToSend = getModmailNotificationMessageToSend(user, channel, serverId, true); return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModMailPostTargets.MOD_MAIL_PING, serverId)); } - private MessageToSend getModmailNotificationMessageToSend(Member member, GuildMessageChannel channel, Long serverId, boolean pingRole) { - log.info("Sending modmail notification for new modmail thread about user {} in server {}.", member.getId(), serverId); + private MessageToSend getModmailNotificationMessageToSend(User user, GuildMessageChannel channel, Long serverId, boolean pingRole) { + log.info("Sending modmail notification for new modmail thread about user {} in server {}.", user.getId(), serverId); AServer server = serverManagementService.loadServer(serverId); List rolesToPing; if(pingRole) { @@ -322,10 +323,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService { } else { rolesToPing = new ArrayList<>(); } - log.debug("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), member.getId(), serverId); + log.debug("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), user.getId(), serverId); ModMailNotificationModel modMailNotificationModel = ModMailNotificationModel .builder() - .member(member) + .userDisplay(UserDisplay.fromUser(user)) .roles(rolesToPing) .channel(channel) .build(); @@ -370,24 +371,57 @@ public class ModMailThreadServiceBean implements ModMailThreadService { if(!servers.isEmpty()) { log.info("There are {} shared servers between user and the bot.", servers.size()); List availableGuilds = new ArrayList<>(); + Set alreadyConsideredServers = new HashSet<>(); for (AServer server : servers) { // only take the servers in which mod mail is actually enabled, would not make much sense to make the // other servers available - if (featureFlagService.isFeatureEnabled(modMailFeatureConfig, server)) { - FullGuild guild = FullGuild - .builder() - .guild(guildService.getGuildById(server.getId())) - .server(server) - .build(); + boolean possibleForModmail = featureFlagService.isFeatureEnabled(modMailFeatureConfig, server); + if (possibleForModmail) { + Guild guild = guildService.getGuildById(server.getId()); ServerChoice serverChoice = ServerChoice .builder() - .serverId(guild.getGuild().getIdLong()) - .serverName(guild.getGuild().getName()) + .serverId(guild.getIdLong()) + .serverName(guild.getName()) + .build(); + availableGuilds.add(serverChoice); + } + alreadyConsideredServers.add(server.getId()); + } + + List restOfKnownServers = serverManagementService.getAllServers() + .stream() + .filter(server -> alreadyConsideredServers.contains(server.getId())) + .toList(); + for (AServer server : restOfKnownServers) { + boolean possibleForModmail = false; + Long actualServerId = 0L; + Long potentialMainServer = configService.getLongValue(ModMailFeatureConfig.MOD_MAIL_APPEAL_SERVER, server.getId()); // what _other_ server is the appeal server + if(potentialMainServer != 0) { + if(featureModeService.featureModeActive(ModMailFeatureDefinition.MOD_MAIL, potentialMainServer, ModMailMode.MOD_MAIL_APPEALS)) { + Long configuredAppealServerId = configService.getLongValue(ModMailFeatureConfig.MOD_MAIL_APPEAL_SERVER, potentialMainServer); + if(configuredAppealServerId != 0 && configuredAppealServerId.equals(server.getId())) { // if the other server has set the current server as the appeal config + Guild otherGuild = guildService.getGuildById(potentialMainServer); + if(otherGuild != null) { // check if we are part of that server + possibleForModmail = true; + actualServerId = potentialMainServer; + log.info("Server {} was available, because it is using server {} as a mod mail appeal server.", server.getId(), otherGuild.getIdLong()); + } + } + } else { + log.info("Server {} has set the appeal server {}, but that server does not have mod mail appeals enabled.", server.getId(), potentialMainServer); + } + } + if(possibleForModmail) { + Guild guild = guildService.getGuildById(actualServerId); + ServerChoice serverChoice = ServerChoice + .builder() + .serverId(guild.getIdLong()) + .serverName(guild.getName()) .build(); availableGuilds.add(serverChoice); } } - log.info("There were {} shared servers found which have modmail enabled.", availableGuilds.size()); + log.info("There were {} available servers found.", availableGuilds.size()); // if more than 1 server is available, show a choice dialog ArrayList undoActions = new ArrayList<>(); if(availableGuilds.size() > 1) { @@ -415,20 +449,15 @@ public class ModMailThreadServiceBean implements ModMailThreadService { } else if(availableGuilds.size() == 1) { // if exactly one server is available, open the thread directly Long chosenServerId = availableGuilds.get(0).getServerId(); + Guild guild = guildService.getGuildById(chosenServerId); log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", initialMessage.getAuthor().getId(), chosenServerId); - memberService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member -> { - try { - return self.createModMailThreadForUser(member, initialMessage, true, undoActions).thenApply(messageChannel -> null); - } catch (Exception exception) { - CompletableFuture future = new CompletableFuture<>(); - future.completeExceptionally(exception); - return future; - } - }).exceptionally(throwable -> { - log.error("Failed to setup thread correctly", throwable); - undoActionService.performActions(undoActions); - return null; - }); + createModMailThreadForUser(initialMessage.getAuthor(), guild , initialMessage, true, undoActions) + .thenAccept(messageChannel -> { + log.info("Setup modmail thread for user {} in guild {}.", initialMessage.getAuthor().getIdLong(), guild.getIdLong()); + }).exceptionally(throwable -> { + log.error("Failed to setup modmail channel in guild {} for user {}.", guild.getIdLong(), initialMessage.getAuthor().getIdLong(), throwable); + return null; + }); } else { log.info("No server available to open a modmail thread in."); // in case there is no server available, send an error message @@ -451,18 +480,17 @@ public class ModMailThreadServiceBean implements ModMailThreadService { * Method used to send the header of a newly created mod mail thread. This message contains information about * the user which the thread is about * @param channel The {@link GuildMessageChannel} in which the mod mail thread is present in - * @param member The {@link Member} which the {@link ModMailThread} is about + * @param user The {@link User} which the {@link ModMailThread} is about */ - private CompletableFuture sendModMailHeader(GuildMessageChannel channel, Member member) { + private CompletableFuture sendModMailHeader(GuildMessageChannel channel, User user) { log.debug("Sending modmail thread header for tread in channel {} on server {}.", channel.getIdLong(), channel.getGuild().getId()); - AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member); + AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(channel.getGuild().getIdLong(), user.getIdLong()); ModMailThread latestThread = modMailThreadManagementService.getLatestModMailThread(aUserInAServer); List oldThreads = modMailThreadManagementService.getModMailThreadForUser(aUserInAServer); ModMailThreaderHeader header = ModMailThreaderHeader .builder() - .member(member) + .userDisplay(UserDisplay.fromUser(user)) .latestModMailThread(latestThread) - .memberJoinDate(member.getTimeJoined().toInstant()) .pastModMailThreadCount((long)oldThreads.size()) .build(); List> messages = channelService.sendEmbedTemplateInTextChannelList("modmail_thread_header", header, channel); @@ -476,18 +504,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService { Long modmailThreadId = modMailThread.getId(); metricService.incrementCounter(MDOMAIL_THREAD_MESSAGE_RECEIVED); log.debug("Relaying message {} to modmail thread {} for user {} to server {}.", messageFromUser.getId(), modMailThread.getId(), messageFromUser.getAuthor().getIdLong(), modMailThread.getServer().getId()); - return memberService.getMemberInServerAsync(modMailThread.getServer().getId(), messageFromUser.getAuthor().getIdLong()).thenCompose(member -> - self.relayMessage(messageFromUser, serverId, channelId, modmailThreadId, member) - ); - - } - - @Transactional - public CompletableFuture relayMessage(Message messageFromUser, Long serverId, Long channelId, Long modmailThreadId, Member member) { Optional textChannelFromServer = channelService.getMessageChannelFromServerOptional(serverId, channelId); if(textChannelFromServer.isPresent()) { GuildMessageChannel guildMessageChannel = textChannelFromServer.get(); - return self.sendUserReply(guildMessageChannel, modmailThreadId, messageFromUser, member, true); + return self.sendUserReply(guildMessageChannel, modmailThreadId, messageFromUser, true); } else { 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 @@ -505,11 +525,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService { * @param messageChannel The {@link GuildMessageChannel} 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 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 the modmail thread already exists and is persisted. * @return A {@link CompletableFuture} which resolves when the postprocessing of the message is completed (adding read notification, and storing messageIDs) */ - public CompletableFuture sendUserReply(GuildMessageChannel messageChannel, Long modMailThreadId, Message messageFromUser, Member member, boolean modMailThreadExists) { + public CompletableFuture sendUserReply(GuildMessageChannel messageChannel, Long modMailThreadId, Message messageFromUser, boolean modMailThreadExists) { List> subscriberMemberFutures = new ArrayList<>(); if(modMailThreadExists) { ModMailThread modMailThread = modMailThreadManagementService.getById(modMailThreadId); @@ -550,7 +569,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel .builder() .postedMessage(messageFromUser) - .member(member) + .userDisplay(UserDisplay.fromUser(messageFromUser.getAuthor())) .attachedImageUrls(imageUrls) .remainingAttachments(otherAttachments) .subscribers(subscribers) @@ -604,21 +623,16 @@ public class ModMailThreadServiceBean implements ModMailThreadService { @Override @Transactional - public CompletableFuture loadExecutingMemberAndRelay(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, Member targetMember) { - log.info("Relaying message {} to user {} in modmail thread {} on server {}.", replyCommandMessage.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId()); + public CompletableFuture loadExecutingMemberAndRelay(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, User user, Guild guild) { + log.info("Relaying message {} to user {} in modmail thread {} on server {}.", replyCommandMessage.getId(), user.getId(), modmailThreadId, guild.getId()); return memberService.getMemberInServerAsync(replyCommandMessage.getGuild().getIdLong(), replyCommandMessage.getAuthor().getIdLong()) - .thenCompose(executingMember -> self.relayMessageToDm(modmailThreadId, text, replyCommandMessage, anonymous, targetMember, executingMember)); + .thenCompose(executingMember -> self.relayMessageToDm(modmailThreadId, text, replyCommandMessage, anonymous, user, executingMember)); } @Transactional - public CompletableFuture relayMessageToDm(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, Member targetMember, Member executingMember) { + public CompletableFuture relayMessageToDm(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, User user, Member executingMember) { metricService.incrementCounter(MDOMAIL_THREAD_MESSAGE_SENT); ModMailThread modMailThread = modMailThreadManagementService.getById(modmailThreadId); - FullUserInServer fullThreadUser = FullUserInServer - .builder() - .aUserInAServer(modMailThread.getUser()) - .member(targetMember) - .build(); List imageUrls = replyCommandMessage .getAttachments() .stream() @@ -638,7 +652,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { .remainingAttachments(otherAttachments) .attachedImageUrls(imageUrls) .anonymous(anonymous) - .threadUser(fullThreadUser); + .userDisplay(UserDisplay.fromUser(user)); if(anonymous) { log.debug("Message is sent anonymous."); modMailModeratorReplyModelBuilder.moderator(memberService.getBotInGuild(modMailThread.getServer())); @@ -647,7 +661,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { } ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build(); MessageToSend messageToSend = templateService.renderEmbedTemplate(MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY, modMailUserReplyModel, modMailThread.getServer().getId()); - CompletableFuture future = messageService.sendMessageToSendToUser(targetMember.getUser(), messageToSend); + CompletableFuture future = messageService.sendMessageToSendToUser(user, messageToSend); CompletableFuture sameThreadMessageFuture; if(featureModeService.featureModeActive(ModMailFeatureDefinition.MOD_MAIL, modMailThread.getServer(), ModMailMode.SEPARATE_MESSAGE)) { sameThreadMessageFuture = channelService.sendMessageEmbedToSendToAChannel(messageToSend, modMailThread.getChannel()).get(0); @@ -679,24 +693,24 @@ public class ModMailThreadServiceBean implements ModMailThreadService { log.info("Archiving thread {} for modmail thread closing.", modMailThread.getChannel().getId()); return loadUserAndSendClosingHeader(modMailThread, closingConfig) .thenCompose(unused -> channelService.archiveThreadChannel(threadChannel)) - .thenCompose(unused -> memberService.getMemberInServerAsync(serverId, userId)) - .thenAccept(member -> self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), member, undoActions)); + .thenCompose(unused -> userService.retrieveUserForId(userId)) + .thenCompose(user -> self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), user, undoActions)); } else { if(closingConfig.getLog()) { if(!modMailMessages.isEmpty()) { return modMailMessageService.loadModMailMessages(modMailMessages) - .thenAccept(loadedModmailThreadMessages -> self.logMessagesToModMailLog(closingConfig, modMailThreadId, undoActions, loadedModmailThreadMessages, serverId, userId)); + .thenCompose(loadedModmailThreadMessages -> self.logMessagesToModMailLog(closingConfig, modMailThreadId, undoActions, loadedModmailThreadMessages, serverId, userId)); } else { log.info("Modmail thread {} in server {} has no messages. Only logging header.", modMailThreadId, serverId); return loadUserAndSendClosingHeader(modMailThread, closingConfig) - .thenAccept(unused -> memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member -> - self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), member, undoActions) + .thenCompose(unused -> userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user -> + self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), user, undoActions) )); } } else { log.debug("Not logging modmail thread {}.", modMailThreadId); - return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member -> - self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), member, undoActions) + return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user -> + self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), user, undoActions) ); } } @@ -736,8 +750,8 @@ public class ModMailThreadServiceBean implements ModMailThreadService { undoActions.add(UndoActionInstance.getMessageDeleteAction(message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong())); } }); - return memberService.getMemberInServerAsync(serverId, userId).thenCompose(member -> - self.afterSuccessfulLog(modMailThreadId, closingContext.getNotifyUser(), member, undoActions) + return userService.retrieveUserForId(userId).thenCompose(user -> + self.afterSuccessfulLog(modMailThreadId, closingContext.getNotifyUser(), user, undoActions) ).exceptionally(throwable -> { log.warn("Failed to retrieve member for closing the modmail thread. Closing without member information.", throwable); self.afterSuccessfulLog(modMailThreadId, false, null, undoActions); @@ -756,12 +770,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService { * @param modMailThreadId The ID of the {@link ModMailThread} which is being closed. * @param notifyUser Whether the user should be notified * @param undoActions The list of {@link UndoActionInstance} to execute in case of exceptions - * @param modMailThreaduser The {@link Member member} for which the {@link ModMailThread thread} was for + * @param modMailThreaduser The {@link User member} for which the {@link ModMailThread thread} was for * @throws ModMailThreadNotFoundException in case the {@link ModMailThread} is not found by the ID * @return A {@link CompletableFuture future} which completes after the messages have been logged */ @Transactional - public CompletableFuture afterSuccessfulLog(Long modMailThreadId, Boolean notifyUser, Member modMailThreaduser, List undoActions) { + public CompletableFuture afterSuccessfulLog(Long modMailThreadId, Boolean notifyUser, User modMailThreaduser, List undoActions) { log.debug("Mod mail logging for thread {} has completed. Starting post logging activities.", modMailThreadId); Optional modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId); if(modMailThreadOpt.isPresent()) { @@ -771,7 +785,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService { HashMap closingMessage = new HashMap<>(); String defaultValue = templateService.renderSimpleTemplate("modmail_closing_user_message_description"); closingMessage.put("closingMessage", configService.getStringValue(MOD_MAIL_CLOSING_TEXT_SYSTEM_CONFIG_KEY, modMailThread.getServer().getId(), defaultValue)); - return messageService.sendEmbedToUser(modMailThreaduser.getUser(), "modmail_closing_user_message", closingMessage).thenAccept(message -> + return messageService.sendEmbedToUser(modMailThreaduser, "modmail_closing_user_message", closingMessage).thenCompose(message -> self.deleteChannelAndClose(modMailThreadId, undoActions) ).exceptionally(throwable -> { self.deleteChannelAndClose(modMailThreadId, undoActions); @@ -1002,6 +1016,28 @@ public class ModMailThreadServiceBean implements ModMailThreadService { } } + @Transactional + public CompletableFuture banUserFromAppealServer(Long mainServerId, Long userId, String reason) { + Long configuredAppealServerId = configService.getLongValue(ModMailFeatureConfig.MOD_MAIL_APPEAL_SERVER, mainServerId); + Guild appealGuild = guildService.getGuildById(configuredAppealServerId); + return banService.banUser(appealGuild, ServerUser.fromId(configuredAppealServerId, userId), Duration.ZERO, reason); + } + + @Override + public CompletableFuture rejectAppeal(ModMailThread modMailThread, String reason, Member memberPerforming) { + ClosingContext context = ClosingContext + .builder() + .closingMember(memberPerforming) + .notifyUser(true) + .log(true) + .note(reason) + .build(); + Long mainServerId = modMailThread.getServer().getId(); + Long userToBanId = modMailThread.getUser().getUserReference().getId(); + return closeModMailThread(modMailThread, context, new ArrayList<>()) + .thenCompose((nul) -> self.banUserFromAppealServer(mainServerId, userToBanId , reason)); + } + @PostConstruct public void postConstruct() { metricService.registerCounter(MODMAIL_THREAD_CREATED_COUNTER, "Mod mail threads created"); diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/collection.xml b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/collection.xml new file mode 100644 index 000000000..1bbc30fef --- /dev/null +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/collection.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/command.xml b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/command.xml new file mode 100644 index 000000000..ddced2699 --- /dev/null +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/command.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/data.xml b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/data.xml new file mode 100644 index 000000000..e18fc4181 --- /dev/null +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/1.5.37/seedData/data.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/modMail-changeLog.xml b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/modMail-changeLog.xml index 8aee5ff15..7148dbca8 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/modMail-changeLog.xml +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/migrations/modMail-changeLog.xml @@ -3,4 +3,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" > + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/modmail-config.properties b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/modmail-config.properties index 657a9c230..ba76cda53 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/modmail-config.properties +++ b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/main/resources/modmail-config.properties @@ -21,4 +21,11 @@ abstracto.featureModes.threadContainer.enabled=false abstracto.featureModes.threadMessage.featureName=modmail abstracto.featureModes.threadMessage.mode=threadMessage -abstracto.featureModes.threadMessage.enabled=true \ No newline at end of file +abstracto.featureModes.threadMessage.enabled=true + +abstracto.featureModes.modMailAppeals.featureName=modmail +abstracto.featureModes.modMailAppeals.mode=modMailAppeals +abstracto.featureModes.modMailAppeals.enabled=false + +abstracto.systemConfigs.modMailAppealServer.name=modMailAppealServer +abstracto.systemConfigs.modMailAppealServer.longValue=0 \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListenerTest.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListenerTest.java deleted file mode 100644 index cdc1b7b73..000000000 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageDeletedListenerTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package dev.sheldan.abstracto.modmail.listener; - -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.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; -import dev.sheldan.abstracto.modmail.model.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 MemberService memberService; - - @Mock - private CachedMessage deletedMessage; - - @Mock - private ModMailMessage modMailMessage; - - @Mock - private Member targetMember; - - @Mock - private User targetUser; - - @Mock - private AServer server; - - @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; - 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()); - when(model.getCachedMessage()).thenReturn(deletedMessage); - testUnit.execute(model); - verify(memberService, times(0)).getMemberInServerAsync(anyLong(), anyLong()); - } - - @Test - public void testDeleteNonDuplicatedMessage() { - when(deletedMessage.getMessageId()).thenReturn(DELETED_MESSAGE_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(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(targetMember)); - when(messageService.deleteMessageInChannelWithUser(targetUser, CREATED_MESSAGE_ID_2)).thenReturn(CompletableFuture.completedFuture(null)); - when(model.getCachedMessage()).thenReturn(deletedMessage); - when(model.getServerId()).thenReturn(SERVER_ID); - testUnit.execute(model); - 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(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(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)); - when(model.getServerId()).thenReturn(SERVER_ID); - when(model.getCachedMessage()).thenReturn(deletedMessage); - testUnit.execute(model); - 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); - } -} \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListenerTest.java b/abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListenerTest.java deleted file mode 100644 index 9de139e9a..000000000 --- a/abstracto-application/abstracto-modules/modmail/modmail-impl/src/test/java/dev/sheldan/abstracto/modmail/listener/ModMailMessageEditedListenerTest.java +++ /dev/null @@ -1,283 +0,0 @@ -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.models.listener.MessageUpdatedModel; -import dev.sheldan.abstracto.core.service.ChannelService; -import dev.sheldan.abstracto.core.service.MemberService; -import dev.sheldan.abstracto.core.service.MessageService; -import dev.sheldan.abstracto.modmail.model.database.ModMailMessage; -import dev.sheldan.abstracto.modmail.model.database.ModMailThread; -import dev.sheldan.abstracto.modmail.model.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.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.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 MemberService memberService; - - @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 Message loadedMessage; - - @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 replyModelArgumentCaptor; - - @Mock - private MessageUpdatedModel model; - - 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); - when(model.getAfter()).thenReturn(messageAfter); - when(model.getBefore()).thenReturn(messageBefore); - testUnit.execute(model); - verify(modMailMessageManagementService, times(0)).getByMessageIdOptional(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); - 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.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, 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)); - when(model.getAfter()).thenReturn(messageAfter); - when(model.getBefore()).thenReturn(messageBefore); - testUnit.execute(model); - verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember); - } - - @Test - public void testEditMessageWithInCorrectCommand() { - 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); - 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.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, 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)); - when(model.getAfter()).thenReturn(messageAfter); - when(model.getBefore()).thenReturn(messageBefore); - testUnit.execute(model); - verify(self, times(1)).updateMessageInThread(messageAfter, parsedParameters, targetMember, authorMember); - } - - @Test - public void testUpdateAnonymousMessageInThreadNotSentToModMailThreadChannel() { - when(loadedMessage.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(), eq(SERVER_ID))).thenReturn(messageToSend); - testUnit.updateMessageInThread(loadedMessage, 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 testUpdateAnonymousMessageInThreadAlsoSendToModMailThreadChannel() { - when(loadedMessage.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(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(), eq(SERVER_ID))).thenReturn(messageToSend); - testUnit.updateMessageInThread(loadedMessage, 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(loadedMessage.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(), eq(SERVER_ID))).thenReturn(messageToSend); - testUnit.updateMessageInThread(loadedMessage, 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(loadedMessage.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(), eq(SERVER_ID))).thenReturn(messageToSend); - testUnit.updateMessageInThread(loadedMessage, 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()); - } -} \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailFeatureConfig.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailFeatureConfig.java index e980d8e02..e66d6742a 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailFeatureConfig.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailFeatureConfig.java @@ -21,6 +21,7 @@ import java.util.List; public class ModMailFeatureConfig implements FeatureConfig { public static final String MOD_MAIL_CLOSING_TEXT_SYSTEM_CONFIG_KEY = "modMailClosingText"; + public static final String MOD_MAIL_APPEAL_SERVER = "modMailAppealServer"; @Autowired private ModMailFeatureValidator modMailFeatureValidator; @@ -34,27 +35,33 @@ public class ModMailFeatureConfig implements FeatureConfig { @Override public List getRequiredPostTargets() { - return Arrays.asList(ModMailPostTargets.MOD_MAIL_PING, ModMailPostTargets.MOD_MAIL_LOG, ModMailPostTargets.MOD_MAIL_CONTAINER); + return List.of(ModMailPostTargets.MOD_MAIL_PING, + ModMailPostTargets.MOD_MAIL_LOG, + ModMailPostTargets.MOD_MAIL_CONTAINER); } @Override public List getAdditionalFeatureValidators() { - return Arrays.asList(modMailFeatureValidator); + return List.of(modMailFeatureValidator); } @Override public List getRequiredEmotes() { - return Arrays.asList("readReaction"); + return List.of("readReaction"); } @Override public List getAvailableModes() { - return Arrays.asList(ModMailMode.LOGGING, ModMailMode.SEPARATE_MESSAGE, ModMailMode.THREAD_CONTAINER); + return List.of(ModMailMode.LOGGING, + ModMailMode.SEPARATE_MESSAGE, + ModMailMode.THREAD_CONTAINER, + ModMailMode.MOD_MAIL_APPEALS + ); } @Override public List getRequiredSystemConfigKeys() { - return Arrays.asList(MOD_MAIL_CLOSING_TEXT_SYSTEM_CONFIG_KEY); + return List.of(MOD_MAIL_CLOSING_TEXT_SYSTEM_CONFIG_KEY, MOD_MAIL_APPEAL_SERVER); } @Override diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailMode.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailMode.java index 048bf10fc..4d70cdea0 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailMode.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/config/ModMailMode.java @@ -9,7 +9,10 @@ import lombok.Getter; */ @Getter public enum ModMailMode implements FeatureMode { - LOGGING("log"), SEPARATE_MESSAGE("threadMessage"), THREAD_CONTAINER("threadContainer"); + LOGGING("log"), + SEPARATE_MESSAGE("threadMessage"), + THREAD_CONTAINER("threadContainer"), + MOD_MAIL_APPEALS("modMailAppeals"); private final String key; diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/ClosingContext.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/ClosingContext.java index 2f49f775e..96111aa86 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/ClosingContext.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/ClosingContext.java @@ -13,6 +13,5 @@ public class ClosingContext { private Boolean notifyUser; private Boolean log; private Member closingMember; - private Channel channel; private String note; } diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ContactNotificationModel.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ContactNotificationModel.java index 6d1ce22cb..51b228269 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ContactNotificationModel.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ContactNotificationModel.java @@ -1,15 +1,15 @@ package dev.sheldan.abstracto.modmail.model.template; +import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import lombok.Builder; import lombok.Getter; import lombok.Setter; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; @Getter @Setter @Builder public class ContactNotificationModel { - private Member targetMember; + private UserDisplay userDisplay; private MessageChannel createdChannel; } diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailModeratorReplyModel.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailModeratorReplyModel.java index 8a2e5763c..7205e0659 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailModeratorReplyModel.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailModeratorReplyModel.java @@ -1,6 +1,6 @@ package dev.sheldan.abstracto.modmail.model.template; -import dev.sheldan.abstracto.core.models.FullUserInServer; +import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import dev.sheldan.abstracto.modmail.model.database.ModMailThread; import lombok.Builder; import lombok.Getter; @@ -18,10 +18,7 @@ import java.util.Map; @Setter @Builder public class ModMailModeratorReplyModel { - /** - * A {@link FullUserInServer} reference representing the user the thread is about. The member attribute is null, if the user left the guild - */ - private FullUserInServer threadUser; + private UserDisplay userDisplay; /** * The staff {@link Member} which replied to the thread, be it anonymously or normal. */ diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailNotificationModel.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailNotificationModel.java index 2948ca085..7e595efb1 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailNotificationModel.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailNotificationModel.java @@ -1,13 +1,12 @@ package dev.sheldan.abstracto.modmail.model.template; -import dev.sheldan.abstracto.core.models.FullUserInServer; import dev.sheldan.abstracto.core.models.context.ServerContext; +import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import dev.sheldan.abstracto.modmail.model.database.ModMailRole; import dev.sheldan.abstracto.modmail.model.database.ModMailThread; import lombok.Getter; import lombok.Setter; import lombok.experimental.SuperBuilder; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import java.util.List; @@ -24,10 +23,7 @@ public class ModMailNotificationModel extends ServerContext { * The created {@link ModMailThread} which was just created */ private ModMailThread modMailThread; - /** - * The {@link FullUserInServer} for which this thread is about - */ - private Member member; + private UserDisplay userDisplay; /** * A list of roles which will be notified upon creation of the mod mail thread. */ diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailThreaderHeader.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailThreaderHeader.java index 4728987e2..605f2bade 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailThreaderHeader.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailThreaderHeader.java @@ -1,12 +1,10 @@ package dev.sheldan.abstracto.modmail.model.template; +import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import dev.sheldan.abstracto.modmail.model.database.ModMailThread; import lombok.Builder; import lombok.Getter; import lombok.Setter; -import net.dv8tion.jda.api.entities.Member; - -import java.time.Instant; /** * This is the model used when a new mod mail thread is opened and a message containing some information about the user @@ -16,16 +14,12 @@ import java.time.Instant; @Setter @Builder public class ModMailThreaderHeader { - /** - * A {@link Member} instance to retrieve information from - */ - private Member member; + private UserDisplay userDisplay; /** * The latest {@link ModMailThread}, before the current opened one. This is null if there is no closed mod mail thread * for the user */ private ModMailThread latestModMailThread; - private Instant memberJoinDate; /** * The amount of previous mod mail thread the user has. */ diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailUserReplyModel.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailUserReplyModel.java index 95aa67559..169db4b4e 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailUserReplyModel.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/template/ModMailUserReplyModel.java @@ -1,5 +1,6 @@ package dev.sheldan.abstracto.modmail.model.template; +import dev.sheldan.abstracto.core.models.template.display.UserDisplay; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -16,10 +17,7 @@ import java.util.Map; @Setter @Builder public class ModMailUserReplyModel { - /** - * The {@link Member} from which the message is and whose mod mail thread it is - */ - private Member member; + private UserDisplay userDisplay; /** * The {@link Message} which was posted, which contains all the possible information */ diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadService.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadService.java index 4fa881337..a684d8156 100644 --- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadService.java +++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/service/ModMailThreadService.java @@ -22,16 +22,16 @@ public interface ModMailThreadService { * Creates a new mod mail thread for the given user. including: the {@link net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel} * in the appropriate {@link net.dv8tion.jda.api.entities.channel.concrete.Category} and calls the methods responsible for storing * the necessary data in the database, notifying the users and sending messages related to the creation of the {@link ModMailThread} - * @param member The {@link AUserInAServer} to create the mod mail thread for + * @param user The {@link User} to create the mod mail thread for * @param initialMessage The initial message sparking this mod mail thread, null in case it was created by a command * @param userInitiated Whether or not the mod mail thread was initiated by a user * @param undoActions A list of {@link dev.sheldan.abstracto.core.models.UndoAction actions} to be undone in case the operation fails. This list will be filled in the method. * @return A {@link CompletableFuture future} which completes when the modmail thread is set up */ - CompletableFuture createModMailThreadForUser(Member member, Message initialMessage, boolean userInitiated, List undoActions); + CompletableFuture createModMailThreadForUser(User user, Guild guild, Message initialMessage, boolean userInitiated, List undoActions); - CompletableFuture sendContactNotification(Member member, MessageChannel createdMessageChannel, MessageChannel feedBackChannel); - CompletableFuture sendContactNotification(Member member, MessageChannel createdMessageChannel, InteractionHook interactionHook); + CompletableFuture sendContactNotification(User user, MessageChannel createdMessageChannel, MessageChannel feedBackChannel); + CompletableFuture sendContactNotification(User user, MessageChannel createdMessageChannel, InteractionHook interactionHook); /** * Changes the configuration value of the category used to create mod mail threads to the given ID. @@ -68,10 +68,11 @@ public interface ModMailThreadService { * @param text The parsed text of the reply * @param message The pure {@link Message} containing the command which caused the reply * @param anonymous Whether or nor the message should be send anonymous - * @param targetMember The {@link Member} the {@link ModMailThread} is about. + * @param targetUser The {@link User} the {@link ModMailThread} is about. + * @param guild The guild the reply is created in * @return A {@link CompletableFuture future} which completes when the message has been relayed to the DM */ - CompletableFuture loadExecutingMemberAndRelay(Long threadId, String text, Message message, boolean anonymous, Member targetMember); + CompletableFuture loadExecutingMemberAndRelay(Long threadId, String text, Message message, boolean anonymous, User targetUser, Guild guild); /** * Closes the mod mail thread which means: deletes the {@link net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel} associated with the mod mail thread, @@ -98,4 +99,6 @@ public interface ModMailThreadService { boolean isModMailThread(AChannel channel); boolean isModMailThread(Long channelId); + + CompletableFuture rejectAppeal(ModMailThread modMailThread, String reason, Member memberPerforming); } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/UserDisplay.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/UserDisplay.java index 861848d6c..85c374981 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/UserDisplay.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/UserDisplay.java @@ -15,6 +15,8 @@ public class UserDisplay { private String userMention; private String discriminator; private String name; + private String avatarUrl; + // TODO add avatar, only available from user public static UserDisplay fromUser(User user) { return UserDisplay @@ -23,6 +25,7 @@ public class UserDisplay { .name(user.getEffectiveName()) .discriminator(user.getDiscriminator()) .id(user.getIdLong()) + .avatarUrl(user.getEffectiveAvatarUrl()) .build(); }