mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-04 08:42:45 +00:00
[AB-261] fixing invite filter only acting in the message received event
This commit is contained in:
@@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<CompletableFuture<Message>> 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<DeletedInvite> groupInvites(List<String> 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<String> foundInvites = new ArrayList<>();
|
||||
Matcher matcher = Message.INVITE_PATTERN.matcher(message.getContentRaw());
|
||||
while(matcher.find()) {
|
||||
foundInvites.add(matcher.group("code"));
|
||||
}
|
||||
List<String> 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<CompletableFuture<Invite>> inviteList = new ArrayList<>();
|
||||
JDA jda = model.getMessage().getJDA();
|
||||
foundInvites.forEach(s -> inviteList.add(inviteLinkFilterService.resolveInvite(jda, s)));
|
||||
|
||||
CompletableFutureList<Invite> list = new CompletableFutureList<>(inviteList);
|
||||
list.getMainFuture().whenComplete((unused, throwable) -> {
|
||||
List<Invite> 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<Long, String> targetServers = new HashMap<>();
|
||||
List<String> 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<String> 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;
|
||||
}
|
||||
|
||||
@@ -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("(?<code>[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<Invite> resolveInvite(JDA jda, String code) {
|
||||
return Invite.resolve(jda, extractCode(code)).submit();
|
||||
}
|
||||
|
||||
private void sendDeletionNotification(List<String> 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<CompletableFuture<Message>> 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<DeletedInvite> groupInvites(List<String> 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<String> findInvitesInMessage(Message message) {
|
||||
List<String> 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<String> foundInvites) {
|
||||
List<CompletableFuture<Invite>> inviteList = new ArrayList<>();
|
||||
JDA jda = message.getJDA();
|
||||
foundInvites.forEach(s -> inviteList.add(resolveInvite(jda, s)));
|
||||
|
||||
CompletableFutureList<Invite> list = new CompletableFutureList<>(inviteList);
|
||||
list.getMainFuture().whenComplete((unused, throwable) -> {
|
||||
List<Invite> 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<Long, String> targetServers = new HashMap<>();
|
||||
List<String> 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<String> 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<FilteredInviteLink> getTopFilteredInviteLinks(Long serverId, Integer count);
|
||||
List<FilteredInviteLink> getTopFilteredInviteLinks(Long serverId);
|
||||
CompletableFuture<Invite> resolveInvite(JDA jda, String code);
|
||||
boolean isInviteFilterActiveInChannel(MessageChannel channel);
|
||||
boolean isMemberImmuneAgainstInviteFilter(Member member);
|
||||
List<String> findInvitesInMessage(Message message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user