diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/pom.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/pom.xml
new file mode 100644
index 000000000..cf006eba1
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/pom.xml
@@ -0,0 +1,56 @@
+
+
+
+ anti-raid
+ dev.sheldan.abstracto.modules
+ 1.3.3-SNAPSHOT
+
+ 4.0.0
+
+ anti-raid-impl
+ jar
+
+
+ 8
+ 8
+
+
+
+
+ dev.sheldan.abstracto.core
+ core-int
+ ${project.version}
+ compile
+
+
+ dev.sheldan.abstracto.modules
+ anti-raid-int
+ ${project.version}
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ src/main/assembly/liquibase.xml
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/assembly/liquibase.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/assembly/liquibase.xml
new file mode 100644
index 000000000..8b4774fa0
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/assembly/liquibase.xml
@@ -0,0 +1,18 @@
+
+ liquibase
+
+ zip
+
+ false
+
+
+ .
+ ${project.basedir}/src/main/resources/migrations
+
+ **/*
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidProperties.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidProperties.java
new file mode 100644
index 000000000..ec63b1f1c
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidProperties.java
@@ -0,0 +1,10 @@
+package dev.sheldan.abstracto.antiraid.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+@PropertySource("classpath:antiRaid-config.properties")
+public class AntiRaidProperties {
+
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/listener/MassPingMessageListener.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/listener/MassPingMessageListener.java
new file mode 100644
index 000000000..4f9d2e4c0
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/listener/MassPingMessageListener.java
@@ -0,0 +1,36 @@
+package dev.sheldan.abstracto.antiraid.listener;
+
+import dev.sheldan.abstracto.antiraid.config.AntiRaidFeatureDefinition;
+import dev.sheldan.abstracto.antiraid.service.MassPingService;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
+import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
+import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
+import lombok.extern.slf4j.Slf4j;
+import net.dv8tion.jda.api.entities.ChannelType;
+import net.dv8tion.jda.api.entities.Message;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class MassPingMessageListener implements AsyncMessageReceivedListener {
+
+ @Autowired
+ private MassPingService massPingService;
+
+ @Override
+ public DefaultListenerResult execute(MessageReceivedModel model) {
+ Message message = model.getMessage();
+ if(message.getAuthor().isBot() || message.isWebhookMessage() || !message.isFromGuild() || !message.isFromType(ChannelType.TEXT)) {
+ return DefaultListenerResult.IGNORED;
+ }
+ massPingService.processMessage(message);
+ return DefaultListenerResult.PROCESSED;
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return AntiRaidFeatureDefinition.ANTI_RAID;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/service/MassPingServiceBean.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/service/MassPingServiceBean.java
new file mode 100644
index 000000000..b95024284
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/java/dev/sheldan/abstracto/antiraid/service/MassPingServiceBean.java
@@ -0,0 +1,109 @@
+package dev.sheldan.abstracto.antiraid.service;
+
+import dev.sheldan.abstracto.antiraid.config.AntiRaidPostTarget;
+import dev.sheldan.abstracto.antiraid.model.MassPingNotificationModel;
+import dev.sheldan.abstracto.core.models.ConditionContextInstance;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
+import dev.sheldan.abstracto.core.service.ConditionService;
+import dev.sheldan.abstracto.core.service.ConfigService;
+import dev.sheldan.abstracto.core.service.PostTargetService;
+import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
+import dev.sheldan.abstracto.core.templating.model.MessageToSend;
+import dev.sheldan.abstracto.core.templating.service.TemplateService;
+import dev.sheldan.abstracto.core.utils.FutureUtils;
+import dev.sheldan.abstracto.moderation.service.MuteService;
+import lombok.extern.slf4j.Slf4j;
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.Message;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+@Component
+@Slf4j
+public class MassPingServiceBean implements MassPingService {
+
+ private static final String MASS_PING_MUTE_NOTIFICATION_TEMPLATE_KEY = "massPing_mute_notification";
+
+ private static final String LEVEL_CONDITION_NAME = "HAS_LEVEL";
+ private static final String LEVEL_CONDITION_USER_ID_PARAMETER = "userId";
+ private static final String LEVEL_CONDITION_LEVEL_PARAMETER = "level";
+
+ @Autowired
+ private ConfigService configService;
+
+ @Autowired
+ private MuteService muteService;
+
+ @Autowired
+ private TemplateService templateService;
+
+ @Autowired
+ private MassPingServiceBean self;
+
+ @Autowired
+ private PostTargetService postTargetService;
+
+ @Autowired
+ private ConditionService conditionService;
+
+ @Value("${abstracto.massPing.maxAllowedMentions}")
+ private Integer maxAllowedMentions;
+
+ @Autowired
+ private UserInServerManagementService userInServerManagementService;
+
+ @Override
+ public CompletableFuture processMessage(Message message) {
+ if(message.getMentionedMembers().size() > maxAllowedMentions) {
+ Integer level = configService.getLongValueOrConfigDefault(MassPingService.MAX_AFFECTED_LEVEL_KEY, message.getGuild().getIdLong()).intValue();
+ boolean allowed = allowedToMassMention(message, level);
+ if(!allowed) {
+ return muteService.muteMemberWithoutContext(message.getMember())
+ .thenAccept(unused -> self.sendMassPingMuteNotification(message))
+ .thenAccept(unused -> log.info("Muted member {} in server {} because of too many member mentions. (> {}).",
+ message.getMember().getIdLong(), message.getGuild().getIdLong(), maxAllowedMentions));
+ } else {
+ log.info("User {} in server {} is allowed to mass mention, because of level (or lack of level configuration).",
+ message.getMember().getIdLong(), message.getGuild().getIdLong());
+ return CompletableFuture.completedFuture(null);
+ }
+ } else {
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ private boolean allowedToMassMention(Message message, Integer level) {
+ log.info("Checking if member {} is allowed to mention a lot of users in server {}.", message.getAuthor().getIdLong(), message.getGuild().getIdLong());
+ Map parameters = new HashMap<>();
+ AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(message.getMember());
+ parameters.put(LEVEL_CONDITION_USER_ID_PARAMETER, userInAServer.getUserInServerId());
+ parameters.put(LEVEL_CONDITION_LEVEL_PARAMETER, level);
+ ConditionContextInstance contextInstance = ConditionContextInstance
+ .builder()
+ .conditionName(LEVEL_CONDITION_NAME)
+ .parameters(parameters)
+ .build();
+ return conditionService.checkConditions(contextInstance);
+ }
+
+ @Transactional
+ public CompletableFuture sendMassPingMuteNotification(Message message) {
+ Member member = message.getMember();
+ MassPingNotificationModel model = MassPingNotificationModel
+ .builder()
+ .messageLink(message.getJumpUrl())
+ .mentionCount(message.getMentionedMembers().size())
+ .messageContent(message.getContentRaw())
+ .memberDisplay(MemberDisplay.fromMember(member))
+ .build();
+ MessageToSend messageToSend = templateService.renderEmbedTemplate(MASS_PING_MUTE_NOTIFICATION_TEMPLATE_KEY, model, member.getGuild().getIdLong());
+ return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, AntiRaidPostTarget.MASS_PING_LOG, member.getGuild().getIdLong()));
+ }
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/antiRaid-config.properties b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/antiRaid-config.properties
new file mode 100644
index 000000000..e9b64bfb4
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/antiRaid-config.properties
@@ -0,0 +1,9 @@
+abstracto.featureFlags.antiRaid.featureName=antiRaid
+abstracto.featureFlags.antiRaid.enabled=false
+
+abstracto.postTargets.massPingLog.name=massPingLog
+
+abstracto.massPing.maxAllowedMentions=5
+
+abstracto.systemConfigs.massPingMinLevel.name=massPingMinLevel
+abstracto.systemConfigs.massPingMinLevel.longValue=15
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/collection.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/collection.xml
new file mode 100644
index 000000000..4cd75ecab
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/collection.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/seedData/data.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/seedData/data.xml
new file mode 100644
index 000000000..c3e39a033
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/seedData/data.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/seedData/feature.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/seedData/feature.xml
new file mode 100644
index 000000000..012a2083e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/seedData/feature.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/tables/tables.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/tables/tables.xml
new file mode 100644
index 000000000..91d7a84f1
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/1.3.3/tables/tables.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/antiRaid-changeLog.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/antiRaid-changeLog.xml
new file mode 100644
index 000000000..2abfeb43e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/antiRaid-changeLog.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/dbchangelog.xsd b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/dbchangelog.xsd
new file mode 100644
index 000000000..83483a5ec
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-impl/src/main/resources/migrations/dbchangelog.xsd
@@ -0,0 +1,1386 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Extension to standard XSD boolean type to allow ${} parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Extension to standard XSD integer type to allow ${} parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onChangeLogPreconditionOnSqlOutput determines what should
+ happen when evaluating this precondition in updateSQL mode. TEST: Run
+ precondition, FAIL: Fail precondition, IGNORE: Skip precondition check
+ [DEFAULT]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Used with valueClobFile to specify file encoding explicitly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true for a cycling sequence, false for a non-cycling sequence.
+ Default is false.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/pom.xml b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/pom.xml
new file mode 100644
index 000000000..5a9ecbb27
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ anti-raid
+ dev.sheldan.abstracto.modules
+ 1.3.3-SNAPSHOT
+
+ 4.0.0
+
+ anti-raid-int
+ jar
+
+
+ 8
+ 8
+
+
+
+
+ dev.sheldan.abstracto.core
+ core-int
+ ${project.version}
+ compile
+
+
+ dev.sheldan.abstracto.modules
+ moderation-int
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidFeatureConfig.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidFeatureConfig.java
new file mode 100644
index 000000000..abed8b9e8
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidFeatureConfig.java
@@ -0,0 +1,39 @@
+package dev.sheldan.abstracto.antiraid.config;
+
+import dev.sheldan.abstracto.antiraid.service.MassPingService;
+import dev.sheldan.abstracto.core.config.FeatureConfig;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.config.PostTargetEnum;
+import dev.sheldan.abstracto.moderation.config.feature.MutingFeatureConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Component
+public class AntiRaidFeatureConfig implements FeatureConfig {
+
+ @Autowired
+ private MutingFeatureConfig mutingFeatureConfig;
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return AntiRaidFeatureDefinition.ANTI_RAID;
+ }
+
+ @Override
+ public List getRequiredPostTargets() {
+ return Arrays.asList(AntiRaidPostTarget.MASS_PING_LOG);
+ }
+
+ @Override
+ public List getRequiredSystemConfigKeys() {
+ return Arrays.asList(MassPingService.MAX_AFFECTED_LEVEL_KEY);
+ }
+
+ @Override
+ public List getRequiredFeatures() {
+ return Arrays.asList(mutingFeatureConfig);
+ }
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidFeatureDefinition.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidFeatureDefinition.java
new file mode 100644
index 000000000..a09b430a2
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidFeatureDefinition.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.antiraid.config;
+
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import lombok.Getter;
+
+@Getter
+public enum AntiRaidFeatureDefinition implements FeatureDefinition {
+ ANTI_RAID("antiRaid");
+
+ private String key;
+
+ AntiRaidFeatureDefinition(String key) {
+ this.key = key;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidPostTarget.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidPostTarget.java
new file mode 100644
index 000000000..a994158c3
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/config/AntiRaidPostTarget.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.antiraid.config;
+
+import dev.sheldan.abstracto.core.config.PostTargetEnum;
+import lombok.Getter;
+
+@Getter
+public enum AntiRaidPostTarget implements PostTargetEnum {
+ MASS_PING_LOG("massPingLog");
+
+ private String key;
+
+ AntiRaidPostTarget(String key) {
+ this.key = key;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/model/MassPingNotificationModel.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/model/MassPingNotificationModel.java
new file mode 100644
index 000000000..d8e6017c3
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/model/MassPingNotificationModel.java
@@ -0,0 +1,16 @@
+package dev.sheldan.abstracto.antiraid.model;
+
+import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class MassPingNotificationModel {
+ private MemberDisplay memberDisplay;
+ private String messageContent;
+ private String messageLink;
+ private Integer mentionCount;
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/service/MassPingService.java b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/service/MassPingService.java
new file mode 100644
index 000000000..44190c516
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/anti-raid-int/src/main/java/dev/sheldan/abstracto/antiraid/service/MassPingService.java
@@ -0,0 +1,10 @@
+package dev.sheldan.abstracto.antiraid.service;
+
+import net.dv8tion.jda.api.entities.Message;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface MassPingService {
+ String MAX_AFFECTED_LEVEL_KEY = "massPingMinLevel";
+ CompletableFuture processMessage(Message message);
+}
diff --git a/abstracto-application/abstracto-modules/anti-raid/pom.xml b/abstracto-application/abstracto-modules/anti-raid/pom.xml
new file mode 100644
index 000000000..30b849928
--- /dev/null
+++ b/abstracto-application/abstracto-modules/anti-raid/pom.xml
@@ -0,0 +1,19 @@
+
+
+
+ abstracto-modules
+ dev.sheldan.abstracto.modules
+ 1.3.3-SNAPSHOT
+
+ 4.0.0
+
+ anti-raid
+ pom
+
+ anti-raid-int
+ anti-raid-impl
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/condition/HasLevelCondition.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/condition/HasLevelCondition.java
index 9511aba20..aae1f9593 100644
--- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/condition/HasLevelCondition.java
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/condition/HasLevelCondition.java
@@ -46,7 +46,9 @@ public class HasLevelCondition implements SystemCondition {
log.info("Evaluating has level condition for user {} in server {} with level {}.",
userInServer.getUserReference().getId(), userInServer.getServerReference().getId(), level);
AUserExperience user = userExperienceManagementService.findUserInServer(userInServer);
- return user.getCurrentLevel() != null && user.getCurrentLevel().getLevel() >= level;
+ boolean conditionResult = user.getCurrentLevel() != null && user.getCurrentLevel().getLevel() >= level;
+ log.info("Condition evaluated to {}", conditionResult);
+ return conditionResult;
}
log.info("No user in server object was found. Evaluating to false.");
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java
index 2b543d6dd..2860cf69a 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java
@@ -378,4 +378,10 @@ public class MuteServiceBean implements MuteService {
completelyUnMuteUser(userInServerManagementService.loadOrCreateUser(member));
}
+ @Override
+ public CompletableFuture muteMemberWithoutContext(Member member) {
+ AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
+ return applyMuteRole(aUserInAServer);
+ }
+
}
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/MuteService.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/MuteService.java
index b18ba75e7..ca99ad07c 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/MuteService.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/service/MuteService.java
@@ -23,4 +23,5 @@ public interface MuteService {
CompletableFuture endMute(Long muteId, Long serverId);
void completelyUnMuteUser(AUserInAServer aUserInAServer);
void completelyUnMuteMember(Member member);
+ CompletableFuture muteMemberWithoutContext(Member member);
}
diff --git a/abstracto-application/abstracto-modules/pom.xml b/abstracto-application/abstracto-modules/pom.xml
index f6d23a4e1..f0d6c78ad 100644
--- a/abstracto-application/abstracto-modules/pom.xml
+++ b/abstracto-application/abstracto-modules/pom.xml
@@ -29,6 +29,7 @@
profanity-filter
voice-channel-context
dynamic-activity
+ anti-raid
\ No newline at end of file
diff --git a/abstracto-application/bundle/pom.xml b/abstracto-application/bundle/pom.xml
index ce212a8ed..a4e37a05b 100644
--- a/abstracto-application/bundle/pom.xml
+++ b/abstracto-application/bundle/pom.xml
@@ -72,6 +72,18 @@
${project.version}
+
+ dev.sheldan.abstracto.modules
+ anti-raid-impl
+ ${project.version}
+
+
+
+ dev.sheldan.abstracto.modules
+ anti-raid-int
+ ${project.version}
+
+
dev.sheldan.abstracto.modules
voice-channel-context-int
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/MemberDisplay.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/MemberDisplay.java
new file mode 100644
index 000000000..4c0316cbd
--- /dev/null
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/display/MemberDisplay.java
@@ -0,0 +1,24 @@
+package dev.sheldan.abstracto.core.models.template.display;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import net.dv8tion.jda.api.entities.Member;
+
+@Getter
+@Setter
+@Builder
+public class MemberDisplay {
+ private String memberMention;
+ private Long userId;
+ private Long serverId;
+
+ public static MemberDisplay fromMember(Member member) {
+ return MemberDisplay
+ .builder()
+ .memberMention(member.getAsMention())
+ .serverId(member.getGuild().getIdLong())
+ .userId(member.getIdLong())
+ .build();
+ }
+}
diff --git a/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc b/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc
index 2440a4d32..f8a27efd6 100644
--- a/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc
+++ b/abstracto-application/documentation/src/main/docs/asciidoc/modules/moderation.adoc
@@ -235,4 +235,18 @@ notifying the moderation of the server. Additional reports of the same user, wit
`reactionReports`:: target for report notification messages
==== Emotes
-* `reactionReport` reaction emote to report a message
\ No newline at end of file
+* `reactionReport` reaction emote to report a message
+
+
+=== Mass mention automatic mute
+
+Feature key `massPingLog`
+
+This functionality requires the feature `mutes` to be enabled and optionally has configuration for integration for `experience` feature.
+This functionality will automatically mute a member who mentions more than a configured amount of users.
+
+==== Post targets
+`massPingLog`:: target for notifications of automatic mutes
+
+==== Relevant system configuration
+`massPingMinLevel`:: The level at which members are allowed to mass ping and not get muted.
\ No newline at end of file