diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterEditedListener.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterEditedListener.java new file mode 100644 index 000000000..4b1c62d71 --- /dev/null +++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterEditedListener.java @@ -0,0 +1,61 @@ +package dev.sheldan.abstracto.invitefilter.listener; + +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.listener.DefaultListenerResult; +import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageUpdatedListener; +import dev.sheldan.abstracto.core.models.listener.MessageUpdatedModel; +import dev.sheldan.abstracto.invitefilter.config.InviteFilterFeatureDefinition; +import dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService; +import dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterServiceBean; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@Slf4j +public class InviteLinkFilterEditedListener implements AsyncMessageUpdatedListener { + + @Autowired + private InviteLinkFilterService inviteLinkFilterService; + + @Autowired + private InviteLinkFilterServiceBean filterServiceBean; + + @Override + public FeatureDefinition getFeature() { + return InviteFilterFeatureDefinition.INVITE_FILTER; + } + + @Override + public DefaultListenerResult execute(MessageUpdatedModel model) { + Message message = model.getAfter(); + + if(!message.isFromGuild() || message.isWebhookMessage() || message.getType().isSystem()) { + return DefaultListenerResult.IGNORED; + } + + List foundInvites = inviteLinkFilterService.findInvitesInMessage(message); + + if(foundInvites.isEmpty()){ + return DefaultListenerResult.IGNORED; + } + + if(!inviteLinkFilterService.isInviteFilterActiveInChannel(message.getChannel())) { + return DefaultListenerResult.IGNORED; + } + + if(inviteLinkFilterService.isMemberImmuneAgainstInviteFilter(message.getMember())) { + log.info("Not checking for invites in message, because author {} in channel {} in guild {} is immune against invite filter.", + message.getMember().getIdLong(), message.getGuild().getIdLong(), message.getChannel().getIdLong()); + return DefaultListenerResult.IGNORED; + } + + // only to reduce code duplication, the interface is too concrete + filterServiceBean.resolveAndCheckInvites(message, foundInvites); + + return DefaultListenerResult.PROCESSED; + } +} diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListener.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListener.java index a77c21bfc..2130f370f 100644 --- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListener.java +++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListener.java @@ -3,39 +3,16 @@ package dev.sheldan.abstracto.invitefilter.listener; import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.listener.DefaultListenerResult; import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener; -import dev.sheldan.abstracto.core.metric.service.CounterMetric; -import dev.sheldan.abstracto.core.metric.service.MetricService; -import dev.sheldan.abstracto.core.metric.service.MetricTag; -import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel; -import dev.sheldan.abstracto.core.service.*; -import dev.sheldan.abstracto.core.templating.model.MessageToSend; -import dev.sheldan.abstracto.core.templating.service.TemplateService; -import dev.sheldan.abstracto.core.utils.CompletableFutureList; -import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.invitefilter.config.InviteFilterFeatureDefinition; -import dev.sheldan.abstracto.invitefilter.config.InviteFilterMode; -import dev.sheldan.abstracto.invitefilter.config.InviteFilterPostTarget; -import dev.sheldan.abstracto.invitefilter.model.template.listener.DeletedInvite; -import dev.sheldan.abstracto.invitefilter.model.template.listener.DeletedInvitesNotificationModel; import dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService; +import dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterServiceBean; import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.Invite; import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageChannel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.stream.Collectors; - -import static dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService.INVITE_FILTER_CHANNEL_GROUP_TYPE; -import static dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService.INVITE_FILTER_EFFECT_KEY; +import java.util.List; @Component @Slf4j @@ -45,88 +22,13 @@ public class InviteLinkFilterListener implements AsyncMessageReceivedListener { private InviteLinkFilterService inviteLinkFilterService; @Autowired - private FeatureModeService featureModeService; - - @Autowired - private PostTargetService postTargetService; - - @Autowired - private TemplateService templateService; - - @Autowired - private MetricService metricService; - - @Autowired - private MessageService messageService; - - @Autowired - private ChannelGroupService channelGroupService; - - @Autowired - private RoleImmunityService roleImmunityService; - - public static final String INVITE_FILTER_METRIC = "invite.filter"; - public static final String CONSEQUENCE = "consequence"; - - private static final CounterMetric MESSAGE_INVITE_FILTERED = - CounterMetric - .builder() - .tagList(Arrays.asList(MetricTag.getTag(CONSEQUENCE, "filtered"))) - .name(INVITE_FILTER_METRIC) - .build(); - - public static final String INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY = "invite_link_deleted_notification"; - - private void sendDeletionNotification(List codes, Message message) { - Long serverId = message.getGuild().getIdLong(); - if(!postTargetService.postTargetDefinedInServer(InviteFilterPostTarget.INVITE_DELETE_LOG, serverId)) { - log.info("Post target {} not defined for server {} - not sending invite link deletion notification.", InviteFilterPostTarget.INVITE_DELETE_LOG.getKey(), serverId); - return; - } - DeletedInvitesNotificationModel model = DeletedInvitesNotificationModel - .builder() - .author(message.getMember()) - .guild(message.getGuild()) - .message(message) - .channel(message.getChannel()) - .invites(groupInvites(codes)) - .build(); - log.info("Sending notification about {} deleted invite links in guild {} from user {} in channel {} in message {}.", - codes.size(), serverId, message.getAuthor().getIdLong(), message.getChannel().getIdLong(), message.getIdLong()); - MessageToSend messageToSend = templateService.renderEmbedTemplate(INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY, model, message.getGuild().getIdLong()); - List> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, InviteFilterPostTarget.INVITE_DELETE_LOG, serverId); - FutureUtils.toSingleFutureGeneric(messageFutures).thenAccept(unused -> - log.debug("Successfully send notification about deleted invite link in message {}.", message.getIdLong()) - ).exceptionally(throwable -> { - log.error("Failed to send notification about deleted invite link in message {}.", message.getIdLong()); - return null; - }); - } - - private List groupInvites(List codes) { - return codes - .stream() - .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) - .entrySet() - .stream() - .map(functionLongEntry -> new DeletedInvite(functionLongEntry.getKey(), functionLongEntry.getValue())) - .collect(Collectors.toList()); - } + private InviteLinkFilterServiceBean filterServiceBean; @Override public FeatureDefinition getFeature() { return InviteFilterFeatureDefinition.INVITE_FILTER; } - @PostConstruct - public void postConstruct() { - metricService.registerCounter(MESSAGE_INVITE_FILTERED, "Amount of messages containing an invite filtered"); - } - - private boolean isInviteFilterActiveInChannel(MessageChannel channel) { - return channelGroupService.isChannelInEnabledChannelGroupOfType(INVITE_FILTER_CHANNEL_GROUP_TYPE, channel.getIdLong()); - } - @Override public DefaultListenerResult execute(MessageReceivedModel model) { Message message = model.getMessage(); @@ -135,72 +37,24 @@ public class InviteLinkFilterListener implements AsyncMessageReceivedListener { return DefaultListenerResult.IGNORED; } - List foundInvites = new ArrayList<>(); - Matcher matcher = Message.INVITE_PATTERN.matcher(message.getContentRaw()); - while(matcher.find()) { - foundInvites.add(matcher.group("code")); - } + List foundInvites = inviteLinkFilterService.findInvitesInMessage(message); if(foundInvites.isEmpty()){ return DefaultListenerResult.IGNORED; } - if(!isInviteFilterActiveInChannel(model.getMessage().getChannel())) { + if(!inviteLinkFilterService.isInviteFilterActiveInChannel(message.getChannel())) { return DefaultListenerResult.IGNORED; } - if(roleImmunityService.isImmune(message.getMember(), INVITE_FILTER_EFFECT_KEY)) { + if(inviteLinkFilterService.isMemberImmuneAgainstInviteFilter(message.getMember())) { log.info("Not checking for invites in message, because author {} in channel {} in guild {} is immune against invite filter.", message.getMember().getIdLong(), message.getGuild().getIdLong(), message.getChannel().getIdLong()); return DefaultListenerResult.IGNORED; } - List> inviteList = new ArrayList<>(); - JDA jda = model.getMessage().getJDA(); - foundInvites.forEach(s -> inviteList.add(inviteLinkFilterService.resolveInvite(jda, s))); - - CompletableFutureList list = new CompletableFutureList<>(inviteList); - list.getMainFuture().whenComplete((unused, throwable) -> { - List invites = list.getObjects(); - Long serverId = message.getGuild().getIdLong(); - ServerUser author = ServerUser.builder().userId(message.getAuthor().getIdLong()).serverId(message.getGuild().getIdLong()).build(); - boolean toDelete = false; - Map targetServers = new HashMap<>(); - List deletedInvites = new ArrayList<>(); - for (Invite invite : invites) { - if (invite.getType().equals(Invite.InviteType.GUILD) - && inviteLinkFilterService.isCodeFiltered(invite.getGuild().getIdLong(), author)) { - toDelete = true; - deletedInvites.add(invite.getCode()); - targetServers.put(invite.getGuild().getIdLong(), invite.getGuild().getName()); - } - } - List unResolvedInvites = new ArrayList<>(); - foundInvites.forEach(possibleUnresolvedInvite -> { - if(invites.stream().noneMatch(invite -> invite.getCode().equals(possibleUnresolvedInvite))) { - unResolvedInvites.add(possibleUnresolvedInvite); - } - }); - - for(String unresolvedInvite : unResolvedInvites) { - if(inviteLinkFilterService.isCodeFiltered(unresolvedInvite, author)) { - toDelete = true; - deletedInvites.add(unresolvedInvite); - } - } - if(toDelete) { - metricService.incrementCounter(MESSAGE_INVITE_FILTERED); - messageService.deleteMessage(message); - boolean trackUsages = featureModeService.featureModeActive(InviteFilterFeatureDefinition.INVITE_FILTER, serverId, InviteFilterMode.TRACK_USES); - if(trackUsages) { - targetServers.forEach((targetServerId, serverName) -> inviteLinkFilterService.storeFilteredInviteLinkUsage(targetServerId, serverName, author)); - } - boolean sendNotification = featureModeService.featureModeActive(InviteFilterFeatureDefinition.INVITE_FILTER, serverId, InviteFilterMode.FILTER_NOTIFICATIONS); - if(sendNotification) { - sendDeletionNotification(deletedInvites, message); - } - } - }); + // only to reduce code duplication, the interface is too concrete + filterServiceBean.resolveAndCheckInvites(message, foundInvites); return DefaultListenerResult.PROCESSED; } diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterServiceBean.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterServiceBean.java index 9281b5afa..1002a9e2a 100644 --- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterServiceBean.java +++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterServiceBean.java @@ -1,26 +1,43 @@ package dev.sheldan.abstracto.invitefilter.service; import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException; +import dev.sheldan.abstracto.core.metric.service.CounterMetric; +import dev.sheldan.abstracto.core.metric.service.MetricService; +import dev.sheldan.abstracto.core.metric.service.MetricTag; import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.service.*; import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import dev.sheldan.abstracto.core.templating.service.TemplateService; +import dev.sheldan.abstracto.core.utils.CompletableFutureList; +import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.invitefilter.config.InviteFilterFeatureDefinition; +import dev.sheldan.abstracto.invitefilter.config.InviteFilterMode; +import dev.sheldan.abstracto.invitefilter.config.InviteFilterPostTarget; import dev.sheldan.abstracto.invitefilter.exception.InvalidInviteException; import dev.sheldan.abstracto.invitefilter.model.database.FilteredInviteLink; +import dev.sheldan.abstracto.invitefilter.model.template.listener.DeletedInvite; +import dev.sheldan.abstracto.invitefilter.model.template.listener.DeletedInvitesNotificationModel; import dev.sheldan.abstracto.invitefilter.service.management.AllowedInviteLinkManagement; import dev.sheldan.abstracto.invitefilter.service.management.FilteredInviteLinkManagement; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Invite; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Optional; +import javax.annotation.PostConstruct; +import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Component @Slf4j @@ -38,8 +55,42 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService { @Autowired private InviteLinkFilterServiceBean self; + @Autowired + private FeatureModeService featureModeService; + + @Autowired + private PostTargetService postTargetService; + + @Autowired + private TemplateService templateService; + + @Autowired + private MetricService metricService; + + @Autowired + private MessageService messageService; + + @Autowired + private ChannelGroupService channelGroupService; + + @Autowired + private RoleImmunityService roleImmunityService; + private static final Pattern INVITE_CODE_PATTERN = Pattern.compile("(?[a-z0-9-]+)", Pattern.CASE_INSENSITIVE); + public static final String INVITE_FILTER_METRIC = "invite.filter"; + public static final String CONSEQUENCE = "consequence"; + + private static final CounterMetric MESSAGE_INVITE_FILTERED = + CounterMetric + .builder() + .tagList(Arrays.asList(MetricTag.getTag(CONSEQUENCE, "filtered"))) + .name(INVITE_FILTER_METRIC) + .build(); + + public static final String INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY = "invite_link_deleted_notification"; + + @Override public boolean isCodeFiltered(Long targetServerId, ServerUser serverUser) { return !isCodeAllowed(targetServerId, serverUser); @@ -170,4 +221,116 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService { public CompletableFuture resolveInvite(JDA jda, String code) { return Invite.resolve(jda, extractCode(code)).submit(); } + + private void sendDeletionNotification(List codes, Message message) { + Long serverId = message.getGuild().getIdLong(); + if(!postTargetService.postTargetDefinedInServer(InviteFilterPostTarget.INVITE_DELETE_LOG, serverId)) { + log.info("Post target {} not defined for server {} - not sending invite link deletion notification.", InviteFilterPostTarget.INVITE_DELETE_LOG.getKey(), serverId); + return; + } + DeletedInvitesNotificationModel model = DeletedInvitesNotificationModel + .builder() + .author(message.getMember()) + .guild(message.getGuild()) + .message(message) + .channel(message.getChannel()) + .invites(groupInvites(codes)) + .build(); + log.info("Sending notification about {} deleted invite links in guild {} from user {} in channel {} in message {}.", + codes.size(), serverId, message.getAuthor().getIdLong(), message.getChannel().getIdLong(), message.getIdLong()); + MessageToSend messageToSend = templateService.renderEmbedTemplate(INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY, model, message.getGuild().getIdLong()); + List> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, InviteFilterPostTarget.INVITE_DELETE_LOG, serverId); + FutureUtils.toSingleFutureGeneric(messageFutures).thenAccept(unused -> + log.debug("Successfully send notification about deleted invite link in message {}.", message.getIdLong()) + ).exceptionally(throwable -> { + log.error("Failed to send notification about deleted invite link in message {}.", message.getIdLong()); + return null; + }); + } + + private List groupInvites(List codes) { + return codes + .stream() + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) + .entrySet() + .stream() + .map(functionLongEntry -> new DeletedInvite(functionLongEntry.getKey(), functionLongEntry.getValue())) + .collect(Collectors.toList()); + } + + @Override + public boolean isInviteFilterActiveInChannel(MessageChannel channel) { + return channelGroupService.isChannelInEnabledChannelGroupOfType(INVITE_FILTER_CHANNEL_GROUP_TYPE, channel.getIdLong()); + } + + @Override + public boolean isMemberImmuneAgainstInviteFilter(Member member) { + return roleImmunityService.isImmune(member, INVITE_FILTER_EFFECT_KEY); + } + + @Override + public List findInvitesInMessage(Message message) { + List foundInvites; + foundInvites = new ArrayList<>(); + Matcher matcher = Message.INVITE_PATTERN.matcher(message.getContentRaw()); + while(matcher.find()) { + foundInvites.add(matcher.group("code")); + } + return foundInvites; + } + + public void resolveAndCheckInvites(Message message, List foundInvites) { + List> inviteList = new ArrayList<>(); + JDA jda = message.getJDA(); + foundInvites.forEach(s -> inviteList.add(resolveInvite(jda, s))); + + CompletableFutureList list = new CompletableFutureList<>(inviteList); + list.getMainFuture().whenComplete((unused, throwable) -> { + List invites = list.getObjects(); + Long serverId = message.getGuild().getIdLong(); + ServerUser author = ServerUser.builder().userId(message.getAuthor().getIdLong()).serverId(message.getGuild().getIdLong()).build(); + boolean toDelete = false; + Map targetServers = new HashMap<>(); + List deletedInvites = new ArrayList<>(); + for (Invite invite : invites) { + if (invite.getType().equals(Invite.InviteType.GUILD) + && isCodeFiltered(invite.getGuild().getIdLong(), author)) { + toDelete = true; + deletedInvites.add(invite.getCode()); + targetServers.put(invite.getGuild().getIdLong(), invite.getGuild().getName()); + } + } + List unResolvedInvites = new ArrayList<>(); + foundInvites.forEach(possibleUnresolvedInvite -> { + if(invites.stream().noneMatch(invite -> invite.getCode().equals(possibleUnresolvedInvite))) { + unResolvedInvites.add(possibleUnresolvedInvite); + } + }); + + for(String unresolvedInvite : unResolvedInvites) { + if(isCodeFiltered(unresolvedInvite, author)) { + toDelete = true; + deletedInvites.add(unresolvedInvite); + } + } + if(toDelete) { + metricService.incrementCounter(MESSAGE_INVITE_FILTERED); + messageService.deleteMessage(message); + boolean trackUsages = featureModeService.featureModeActive(InviteFilterFeatureDefinition.INVITE_FILTER, serverId, InviteFilterMode.TRACK_USES); + if(trackUsages) { + targetServers.forEach((targetServerId, serverName) -> storeFilteredInviteLinkUsage(targetServerId, serverName, author)); + } + boolean sendNotification = featureModeService.featureModeActive(InviteFilterFeatureDefinition.INVITE_FILTER, serverId, InviteFilterMode.FILTER_NOTIFICATIONS); + if(sendNotification) { + sendDeletionNotification(deletedInvites, message); + } + } + }); + } + + @PostConstruct + public void postConstruct() { + metricService.registerCounter(MESSAGE_INVITE_FILTERED, "Amount of messages containing an invite filtered"); + } + } diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/test/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListenerTest.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/test/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListenerTest.java index 5ac34641e..ab0a4b57c 100644 --- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/test/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListenerTest.java +++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-impl/src/test/java/dev/sheldan/abstracto/invitefilter/listener/InviteLinkFilterListenerTest.java @@ -2,23 +2,24 @@ package dev.sheldan.abstracto.invitefilter.listener; import dev.sheldan.abstracto.core.listener.DefaultListenerResult; import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel; -import dev.sheldan.abstracto.core.service.ChannelGroupService; -import dev.sheldan.abstracto.core.service.RoleImmunityService; import dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService; -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.*; +import dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterServiceBean; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageType; import org.junit.Assert; 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.concurrent.CompletableFuture; +import java.util.ArrayList; +import java.util.Arrays; -import static dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService.INVITE_FILTER_CHANNEL_GROUP_TYPE; -import static dev.sheldan.abstracto.invitefilter.service.InviteLinkFilterService.INVITE_FILTER_EFFECT_KEY; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class InviteLinkFilterListenerTest { @@ -32,9 +33,6 @@ public class InviteLinkFilterListenerTest { @Mock private Message message; - @Mock - private User author; - @Mock private Member member; @@ -42,66 +40,40 @@ public class InviteLinkFilterListenerTest { private MessageChannel messageChannel; @Mock - private Guild guild; + private InviteLinkFilterServiceBean filterServiceBean; @Mock private MessageReceivedModel model; - @Mock - private ChannelGroupService channelGroupService; - - @Mock - private RoleImmunityService roleImmunityService; - - @Mock - private JDA jda; - - @Mock - private Invite invite; - - private static final Long SERVER_ID = 1L; - private static final Long CHANNEL_ID = 2L; - private static final Long USER_ID = 3L; private static final String INVITE_CODE = "code"; - private static final String INVITE_LINK = "discord.gg/" + INVITE_CODE; @Test public void testExecutionWithNoInvite() { - when(message.getContentRaw()).thenReturn("text"); + when(inviteLinkFilterService.findInvitesInMessage(message)).thenReturn(new ArrayList<>()); setupBasicMessage(); - setupFiltering(); when(model.getMessage()).thenReturn(message); DefaultListenerResult result = testUnit.execute(model); Assert.assertEquals(DefaultListenerResult.IGNORED, result); + verify(filterServiceBean, times(0)).resolveAndCheckInvites(eq(message), any()); } @Test public void testExecutionWithOneInvite() { - setupFiltering(); - when(message.getContentRaw()).thenReturn(INVITE_LINK); + when(inviteLinkFilterService.findInvitesInMessage(message)).thenReturn(Arrays.asList(INVITE_CODE)); setupBasicMessage(); - when(inviteLinkFilterService.resolveInvite(jda, INVITE_CODE)).thenReturn(CompletableFuture.completedFuture(invite)); when(model.getMessage()).thenReturn(message); DefaultListenerResult result = testUnit.execute(model); Assert.assertEquals(DefaultListenerResult.PROCESSED, result); - } - - private void setupFiltering() { - when(channelGroupService.isChannelInEnabledChannelGroupOfType(INVITE_FILTER_CHANNEL_GROUP_TYPE, CHANNEL_ID)).thenReturn(true); - when(roleImmunityService.isImmune(member, INVITE_FILTER_EFFECT_KEY)).thenReturn(false); + verify(filterServiceBean, times(1)).resolveAndCheckInvites(eq(message), any()); } private void setupBasicMessage() { - when(messageChannel.getIdLong()).thenReturn(CHANNEL_ID); when(message.getChannel()).thenReturn(messageChannel); - when(message.getAuthor()).thenReturn(author); when(message.getMember()).thenReturn(member); - when(author.getIdLong()).thenReturn(USER_ID); - when(message.getGuild()).thenReturn(guild); - when(guild.getIdLong()).thenReturn(SERVER_ID); when(message.isFromGuild()).thenReturn(true); when(message.isWebhookMessage()).thenReturn(false); - when(message.getJDA()).thenReturn(jda); + when(inviteLinkFilterService.isInviteFilterActiveInChannel(messageChannel)).thenReturn(true); + when(inviteLinkFilterService.isMemberImmuneAgainstInviteFilter(member)).thenReturn(false); MessageType type = MessageType.DEFAULT; when(message.getType()).thenReturn(type); } diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterService.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterService.java index acaa13f2b..dc19a476d 100644 --- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterService.java +++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/service/InviteLinkFilterService.java @@ -4,6 +4,9 @@ import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.invitefilter.model.database.FilteredInviteLink; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Invite; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -25,4 +28,7 @@ public interface InviteLinkFilterService { List getTopFilteredInviteLinks(Long serverId, Integer count); List getTopFilteredInviteLinks(Long serverId); CompletableFuture resolveInvite(JDA jda, String code); + boolean isInviteFilterActiveInChannel(MessageChannel channel); + boolean isMemberImmuneAgainstInviteFilter(Member member); + List findInvitesInMessage(Message message); }