mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-03-27 14:23:56 +00:00
[AB-84] adding profanity filter with different feature modes
react command: adding additional mapping for character r
This commit is contained in:
@@ -119,7 +119,8 @@
|
|||||||
"♌"
|
"♌"
|
||||||
],
|
],
|
||||||
"r": [
|
"r": [
|
||||||
"🇷"
|
"🇷",
|
||||||
|
"🌱"
|
||||||
],
|
],
|
||||||
"s": [
|
"s": [
|
||||||
"🇸",
|
"🇸",
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ public class ExperienceLevelServiceBean implements ExperienceLevelService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long calculateExperienceToNextLevel(Integer level, Long currentExperience) {
|
public Long calculateExperienceToNextLevel(Integer level, Long currentExperience) {
|
||||||
AExperienceLevel nextLevel = experienceLevelManagementService.getLevel(level + 1).orElseThrow(() -> new AbstractoRunTimeException(String.format("Could not find level %s", level)));
|
AExperienceLevel nextLevel = experienceLevelManagementService.getLevel(level + 1)
|
||||||
|
.orElseThrow(() -> new AbstractoRunTimeException(String.format("Could not find level %s", level)));
|
||||||
return nextLevel.getExperienceNeeded() - currentExperience;
|
return nextLevel.getExperienceNeeded() - currentExperience;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import dev.sheldan.abstracto.core.metric.service.MetricTag;
|
|||||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||||
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
|
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
|
||||||
import dev.sheldan.abstracto.core.service.*;
|
import dev.sheldan.abstracto.core.service.*;
|
||||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
|
||||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||||
@@ -25,7 +24,6 @@ import net.dv8tion.jda.api.JDA;
|
|||||||
import net.dv8tion.jda.api.entities.Invite;
|
import net.dv8tion.jda.api.entities.Invite;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
import net.dv8tion.jda.api.entities.MessageChannel;
|
||||||
import net.dv8tion.jda.api.entities.MessageType;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -67,14 +65,14 @@ public class InviteLinkFilterListener implements AsyncMessageReceivedListener {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RoleImmunityService roleImmunityService;
|
private RoleImmunityService roleImmunityService;
|
||||||
|
|
||||||
public static final String MODERATION_PURGE_METRIC = "invite.filter";
|
public static final String INVITE_FILTER_METRIC = "invite.filter";
|
||||||
public static final String CONSEQUENCE = "consequence";
|
public static final String CONSEQUENCE = "consequence";
|
||||||
|
|
||||||
private static final CounterMetric MESSAGE_INVITE_FILTERED =
|
private static final CounterMetric MESSAGE_INVITE_FILTERED =
|
||||||
CounterMetric
|
CounterMetric
|
||||||
.builder()
|
.builder()
|
||||||
.tagList(Arrays.asList(MetricTag.getTag(CONSEQUENCE, "filtered")))
|
.tagList(Arrays.asList(MetricTag.getTag(CONSEQUENCE, "filtered")))
|
||||||
.name(MODERATION_PURGE_METRIC)
|
.name(INVITE_FILTER_METRIC)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static final String INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY = "invite_link_deleted_notification";
|
public static final String INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY = "invite_link_deleted_notification";
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<module>webservices</module>
|
<module>webservices</module>
|
||||||
<module>logging</module>
|
<module>logging</module>
|
||||||
<module>invite-filter</module>
|
<module>invite-filter</module>
|
||||||
|
<module>profanity-filter</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>abstracto-modules</artifactId>
|
||||||
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
|
<version>1.2.12-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>profanity-filter</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<modules>
|
||||||
|
<module>profanity-filter-int</module>
|
||||||
|
<module>profanity-filter-impl</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>profanity-filter</artifactId>
|
||||||
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
|
<version>1.2.12-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>profanity-filter-impl</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>src/main/assembly/liquibase.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
|
<artifactId>profanity-filter-int</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.sheldan.abstracto.core</groupId>
|
||||||
|
<artifactId>metrics-int</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||||
|
<id>liquibase</id>
|
||||||
|
<formats>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<outputDirectory>.</outputDirectory>
|
||||||
|
<directory>${project.basedir}/src/main/resources/migrations</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.command;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||||
|
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.CommandContext;
|
||||||
|
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.models.ServerChannelMessage;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||||
|
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||||
|
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterFeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterMode;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterModerationModuleDefinition;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.template.ProfanitiesModel;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.ProfanityFilterService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.management.ProfanityUserInServerManagementService;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class Profanities extends AbstractConditionableCommand {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityFilterService profanityFilterService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserInServerManagementService userInServerManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityUserInServerManagementService profanityUserInServerManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChannelService channelService;
|
||||||
|
|
||||||
|
private static final String PROFANITIES_TEMPLATE_KEY = "profanities_response";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||||
|
Member member = (Member) commandContext.getParameters().getParameters().get(0);
|
||||||
|
AUserInAServer userInServer = userInServerManagementService.loadOrCreateUser(member);
|
||||||
|
ProfanityUserInAServer profanityUser = profanityUserInServerManagementService.getProfanityUser(userInServer);
|
||||||
|
Long positiveReports = profanityFilterService.getPositiveReportCountForUser(profanityUser);
|
||||||
|
Long falsePositives = profanityFilterService.getFalseProfanityReportCountForUser(profanityUser);
|
||||||
|
List<ServerChannelMessage> reports = profanityFilterService.getRecentPositiveReports(profanityUser, 3);
|
||||||
|
ProfanitiesModel model = ProfanitiesModel
|
||||||
|
.builder()
|
||||||
|
.member(member)
|
||||||
|
.recentPositiveReports(reports)
|
||||||
|
.falsePositives(falsePositives)
|
||||||
|
.truePositives(positiveReports)
|
||||||
|
.build();
|
||||||
|
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(PROFANITIES_TEMPLATE_KEY, model, commandContext.getChannel()))
|
||||||
|
.thenApply(unused -> CommandResult.fromSuccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandConfiguration getConfiguration() {
|
||||||
|
Parameter memberParameter = Parameter.builder().templated(true).name("member").type(Member.class).optional(true).build();
|
||||||
|
List<Parameter> parameters = Collections.singletonList(memberParameter);
|
||||||
|
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
|
||||||
|
return CommandConfiguration.builder()
|
||||||
|
.name("profanities")
|
||||||
|
.module(ProfanityFilterModerationModuleDefinition.MODERATION)
|
||||||
|
.templated(true)
|
||||||
|
.async(true)
|
||||||
|
.causesReaction(false)
|
||||||
|
.parameters(parameters)
|
||||||
|
.help(helpInfo)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureDefinition getFeature() {
|
||||||
|
return ProfanityFilterFeatureDefinition.PROFANITY_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FeatureMode> getFeatureModeLimitations() {
|
||||||
|
return Arrays.asList(ProfanityFilterMode.TRACK_PROFANITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@PropertySource("classpath:profanityFilter-config.properties")
|
||||||
|
public class ProfanityFilterProperties {
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.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.database.ProfanityRegex;
|
||||||
|
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
|
||||||
|
import dev.sheldan.abstracto.core.service.FeatureModeService;
|
||||||
|
import dev.sheldan.abstracto.core.service.MessageService;
|
||||||
|
import dev.sheldan.abstracto.core.service.ProfanityService;
|
||||||
|
import dev.sheldan.abstracto.core.service.RoleImmunityService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterFeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterMode;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.ProfanityFilterService;
|
||||||
|
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 javax.annotation.PostConstruct;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static dev.sheldan.abstracto.profanityfilter.service.ProfanityFilterService.PROFANITY_FILTER_EFFECT_KEY;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class ProfanityDetectionListener implements AsyncMessageReceivedListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityService profanityService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityFilterService profanityFilterService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeatureModeService featureModeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageService messageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetricService metricService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RoleImmunityService roleImmunityService;
|
||||||
|
|
||||||
|
public static final String MODERATION_PURGE_METRIC = "profanity.filter";
|
||||||
|
public static final String STEP = "step";
|
||||||
|
|
||||||
|
private static final CounterMetric PROFANITIES_DETECTED_METRIC =
|
||||||
|
CounterMetric
|
||||||
|
.builder()
|
||||||
|
.tagList(Arrays.asList(MetricTag.getTag(STEP, "detection")))
|
||||||
|
.name(MODERATION_PURGE_METRIC)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultListenerResult execute(MessageReceivedModel model) {
|
||||||
|
Message message = model.getMessage();
|
||||||
|
if(message.isWebhookMessage() || message.getType().isSystem() || !message.isFromGuild()) {
|
||||||
|
return DefaultListenerResult.IGNORED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(roleImmunityService.isImmune(message.getMember(), PROFANITY_FILTER_EFFECT_KEY)) {
|
||||||
|
log.info("Not checking for profanities in message, because author {} in channel {} in guild {} is immune against profanity filter.",
|
||||||
|
message.getMember().getIdLong(), message.getGuild().getIdLong(), message.getChannel().getIdLong());
|
||||||
|
return DefaultListenerResult.IGNORED;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long serverId = model.getServerId();
|
||||||
|
Optional<ProfanityRegex> potentialProfanityGroup = profanityService.getProfanityRegex(message.getContentRaw(), serverId);
|
||||||
|
if(potentialProfanityGroup.isPresent()) {
|
||||||
|
metricService.incrementCounter(PROFANITIES_DETECTED_METRIC);
|
||||||
|
if(featureModeService.featureModeActive(ProfanityFilterFeatureDefinition.PROFANITY_FILTER, serverId, ProfanityFilterMode.PROFANITY_REPORT)) {
|
||||||
|
ProfanityRegex foundProfanityGroup = potentialProfanityGroup.get();
|
||||||
|
profanityFilterService.createProfanityReport(message, foundProfanityGroup).exceptionally(throwable -> {
|
||||||
|
log.error("Failed to report or persist profanities in server {} for message {} in channel {}.",
|
||||||
|
serverId, message.getChannel().getIdLong(), message.getIdLong(), throwable);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(featureModeService.featureModeActive(ProfanityFilterFeatureDefinition.PROFANITY_FILTER, serverId, ProfanityFilterMode.AUTO_DELETE_PROFANITIES)) {
|
||||||
|
messageService.deleteMessage(message).exceptionally(throwable -> {
|
||||||
|
log.error("Failed to delete profanity message with id {} in channel {} in server {}.",
|
||||||
|
message.getIdLong(), message.getChannel().getIdLong(), message.getGuild().getIdLong(), throwable);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return DefaultListenerResult.PROCESSED;
|
||||||
|
}
|
||||||
|
return DefaultListenerResult.IGNORED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureDefinition getFeature() {
|
||||||
|
return ProfanityFilterFeatureDefinition.PROFANITY_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
metricService.registerCounter(PROFANITIES_DETECTED_METRIC, "Amount of profanities detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.listener;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||||
|
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
|
||||||
|
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||||
|
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AEmote;
|
||||||
|
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
|
||||||
|
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||||
|
import dev.sheldan.abstracto.core.service.EmoteService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterFeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUse;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.ProfanityFilterService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.management.ProfanityUseManagementService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static dev.sheldan.abstracto.profanityfilter.service.ProfanityFilterService.PROFANITY_VOTES_CONFIG_KEY;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfanityReportVoteListener implements AsyncReactionAddedListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EmoteService emoteService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityUseManagementService profanityUseManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigService configService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityFilterService profanityFilterService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultListenerResult execute(ReactionAddedModel model) {
|
||||||
|
Optional<ProfanityUse> profanityUseOptional = profanityUseManagementService.getProfanityUseViaReportMessageId(model.getMessage().getMessageId());
|
||||||
|
if(profanityUseOptional.isPresent()) {
|
||||||
|
ProfanityUse use = profanityUseOptional.get();
|
||||||
|
if(use.getVerified()) {
|
||||||
|
return DefaultListenerResult.PROCESSED;
|
||||||
|
}
|
||||||
|
AEmote addedEmote = emoteService.buildAEmoteFromReaction(model.getReaction().getReactionEmote());
|
||||||
|
AEmote agreeEmote = emoteService.getEmoteOrDefaultEmote(ProfanityFilterService.REPORT_AGREE_EMOTE, model.getServerId());
|
||||||
|
boolean isAgreement = emoteService.compareAEmote(addedEmote, agreeEmote);
|
||||||
|
boolean reactionWasVote;
|
||||||
|
AEmote disApproveEmote = emoteService.getEmoteOrDefaultEmote(ProfanityFilterService.REPORT_DISAGREE_EMOTE, model.getServerId());
|
||||||
|
if(!isAgreement) {
|
||||||
|
reactionWasVote = emoteService.compareAEmote(addedEmote, disApproveEmote);
|
||||||
|
} else {
|
||||||
|
reactionWasVote = true;
|
||||||
|
}
|
||||||
|
if(reactionWasVote) {
|
||||||
|
ProfanityFilterService.VoteResult voteResult = getVoteResultOnMessage(model.getMessage(), agreeEmote, disApproveEmote);
|
||||||
|
if(ProfanityFilterService.VoteResult.isFinal(voteResult)) {
|
||||||
|
profanityFilterService.verifyProfanityUse(use, voteResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DefaultListenerResult.IGNORED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfanityFilterService.VoteResult getVoteResultOnMessage(CachedMessage cachedMessage, AEmote agreementEmote, AEmote disagreementEmote) {
|
||||||
|
Long voteThreshold = configService.getLongValueOrConfigDefault(PROFANITY_VOTES_CONFIG_KEY, cachedMessage.getServerId());
|
||||||
|
Optional<CachedReactions> agreementReactionsOptional = emoteService.getReactionFromMessageByEmote(cachedMessage, agreementEmote);
|
||||||
|
Optional<CachedReactions> disAgreementReactionsOptional = emoteService.getReactionFromMessageByEmote(cachedMessage, disagreementEmote);
|
||||||
|
int agreementVotes = 0;
|
||||||
|
int disagreementVotes = 0;
|
||||||
|
if(agreementReactionsOptional.isPresent()) {
|
||||||
|
agreementVotes = getUserCount(agreementReactionsOptional.get());
|
||||||
|
}
|
||||||
|
if(disAgreementReactionsOptional.isPresent()) {
|
||||||
|
disagreementVotes = getUserCount(disAgreementReactionsOptional.get());
|
||||||
|
}
|
||||||
|
if(agreementVotes >= voteThreshold) {
|
||||||
|
return ProfanityFilterService.VoteResult.AGREEMENT;
|
||||||
|
} else if(disagreementVotes >= voteThreshold) {
|
||||||
|
return ProfanityFilterService.VoteResult.DISAGREEMENT;
|
||||||
|
} else {
|
||||||
|
return ProfanityFilterService.VoteResult.BELOW_THRESHOLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getUserCount(CachedReactions agreementReactionsOptional) {
|
||||||
|
int reactionCount = agreementReactionsOptional.getUsers().size();
|
||||||
|
if(agreementReactionsOptional.getSelf()) {
|
||||||
|
reactionCount--;
|
||||||
|
}
|
||||||
|
return reactionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureDefinition getFeature() {
|
||||||
|
return ProfanityFilterFeatureDefinition.PROFANITY_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.repository;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUse;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ProfanityUseRepository extends JpaRepository<ProfanityUse, Long> {
|
||||||
|
Long countByProfanityUserAndVerifiedTrueAndConfirmedTrue(ProfanityUserInAServer profanityUserInAServer);
|
||||||
|
Long countByProfanityUserAndVerifiedTrueAndConfirmedFalse(ProfanityUserInAServer profanityUserInAServer);
|
||||||
|
List<ProfanityUse> findAllByProfanityUserAndConfirmedTrueOrderByCreatedDesc(ProfanityUserInAServer profanityUserInAServer, Pageable pageable);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.repository;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ProfanityUserInServerRepository extends JpaRepository<ProfanityUserInAServer, Long> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.service;
|
||||||
|
|
||||||
|
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.ServerChannelMessage;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.ProfanityRegex;
|
||||||
|
import dev.sheldan.abstracto.core.service.FeatureModeService;
|
||||||
|
import dev.sheldan.abstracto.core.service.MessageService;
|
||||||
|
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||||
|
import dev.sheldan.abstracto.core.service.ReactionService;
|
||||||
|
import dev.sheldan.abstracto.core.service.management.ProfanityRegexManagementService;
|
||||||
|
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.profanityfilter.config.ProfanityFilterFeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterMode;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.config.ProfanityFilterPostTarget;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.listener.ProfanityDetectionListener;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUse;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.template.ProfanityReportModel;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.management.ProfanityUseManagementService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.management.ProfanityUserInServerManagementService;
|
||||||
|
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 org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class ProfanityFilterServiceBean implements ProfanityFilterService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PostTargetService postTargetService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TemplateService templateService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReactionService reactionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityUserInServerManagementService profanityUserInServerManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityUseManagementService profanityUseManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityRegexManagementService profanityRegexManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserInServerManagementService userInServerManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeatureModeService featureModeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageService messageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetricService metricService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityFilterServiceBean self;
|
||||||
|
|
||||||
|
private static final String PROFANITY_REPORT_TEMPLATE_KEY = "profanityDetection_listener_report";
|
||||||
|
|
||||||
|
private static final CounterMetric PROFANITIES_AGREEMENT =
|
||||||
|
CounterMetric
|
||||||
|
.builder()
|
||||||
|
.tagList(Arrays.asList(MetricTag.getTag(ProfanityDetectionListener.STEP, "agreement")))
|
||||||
|
.name(ProfanityDetectionListener.MODERATION_PURGE_METRIC)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final CounterMetric PROFANITIES_DISAGREEMENT =
|
||||||
|
CounterMetric
|
||||||
|
.builder()
|
||||||
|
.tagList(Arrays.asList(MetricTag.getTag(ProfanityDetectionListener.STEP, "disagreement")))
|
||||||
|
.name(ProfanityDetectionListener.MODERATION_PURGE_METRIC)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> createProfanityReport(Message message, ProfanityRegex foundProfanityRegex) {
|
||||||
|
ProfanityReportModel reportModel = ProfanityReportModel
|
||||||
|
.builder()
|
||||||
|
.profaneMessage(message)
|
||||||
|
.profanityGroupKey(foundProfanityRegex.getGroup().getGroupName())
|
||||||
|
.profanityRegexName(foundProfanityRegex.getRegexName())
|
||||||
|
.build();
|
||||||
|
Long serverId = message.getGuild().getIdLong();
|
||||||
|
MessageToSend messageToSend = templateService.renderEmbedTemplate(PROFANITY_REPORT_TEMPLATE_KEY, reportModel, serverId);
|
||||||
|
List<CompletableFuture<Message>> messageFutures = postTargetService
|
||||||
|
.sendEmbedInPostTarget(messageToSend, ProfanityFilterPostTarget.PROFANITY_FILTER_QUEUE, serverId);
|
||||||
|
Long profanityRegexId = foundProfanityRegex.getId();
|
||||||
|
return FutureUtils.toSingleFutureGeneric(messageFutures).thenCompose(aVoid -> {
|
||||||
|
Message createdMessage = messageFutures.get(0).join();
|
||||||
|
return self.afterReportCreation(message, serverId, profanityRegexId, createdMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public CompletableFuture<Void> afterReportCreation(Message message, Long serverId, Long profanityRegexId, Message createdMessage) {
|
||||||
|
if(featureModeService.featureModeActive(ProfanityFilterFeatureDefinition.PROFANITY_FILTER, serverId, ProfanityFilterMode.PROFANITY_VOTE)) {
|
||||||
|
CompletableFuture<Void> firstReaction = reactionService.addReactionToMessageAsync(ProfanityFilterService.REPORT_AGREE_EMOTE, serverId, createdMessage);
|
||||||
|
CompletableFuture<Void> secondReaction = reactionService.addReactionToMessageAsync(ProfanityFilterService.REPORT_DISAGREE_EMOTE, serverId, createdMessage);
|
||||||
|
return CompletableFuture.allOf(firstReaction, secondReaction).thenAccept(aVoid1 -> {
|
||||||
|
log.debug("Reaction added to message {} for a profanity report.", message.getId());
|
||||||
|
self.persistProfanityReport(message, createdMessage, profanityRegexId);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMessageProfanityReport(Long messageId) {
|
||||||
|
return profanityUseManagementService.getProfanityUseViaReportMessageId(messageId).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verifyProfanityUse(ProfanityUse profanityUse, VoteResult result) {
|
||||||
|
switch(result) {
|
||||||
|
case DISAGREEMENT:
|
||||||
|
profanityUse.setConfirmed(false);
|
||||||
|
metricService.incrementCounter(PROFANITIES_DISAGREEMENT);
|
||||||
|
break;
|
||||||
|
case AGREEMENT:
|
||||||
|
profanityUse.setConfirmed(true);
|
||||||
|
metricService.incrementCounter(PROFANITIES_AGREEMENT);
|
||||||
|
deleteProfaneMessage(profanityUse);
|
||||||
|
break;
|
||||||
|
default: throw new IllegalArgumentException("Final vote result given. No mapping to action found.");
|
||||||
|
}
|
||||||
|
profanityUse.setVerified(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getPositiveReportCountForUser(AUserInAServer aUserInAServer) {
|
||||||
|
ProfanityUserInAServer profanityUserInAServer = profanityUserInServerManagementService.getProfanityUser(aUserInAServer);
|
||||||
|
return getPositiveReportCountForUser(profanityUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getPositiveReportCountForUser(ProfanityUserInAServer aUserInAServer) {
|
||||||
|
return profanityUseManagementService.getPositiveReports(aUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getFalseProfanityReportCountForUser(AUserInAServer aUserInAServer) {
|
||||||
|
ProfanityUserInAServer profanityUserInAServer = profanityUserInServerManagementService.getProfanityUser(aUserInAServer);
|
||||||
|
return getFalseProfanityReportCountForUser(profanityUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getFalseProfanityReportCountForUser(ProfanityUserInAServer aUserInAServer) {
|
||||||
|
return profanityUseManagementService.getFalsePositiveReports(aUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ServerChannelMessage> getRecentPositiveReports(AUserInAServer aUserInAServer, int count) {
|
||||||
|
ProfanityUserInAServer profanityUserInAServer = profanityUserInServerManagementService.getProfanityUser(aUserInAServer);
|
||||||
|
return getRecentPositiveReports(profanityUserInAServer, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteProfaneMessage(ProfanityUse profanityUse) {
|
||||||
|
messageService.deleteMessageInChannelInServer(profanityUse.getServer().getId(), profanityUse.getProfaneChannel().getId(), profanityUse.getProfaneMessageId())
|
||||||
|
.exceptionally(throwable -> {
|
||||||
|
log.info("Failed to delete profane message ");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ServerChannelMessage> getRecentPositiveReports(ProfanityUserInAServer aUserInAServer, int count) {
|
||||||
|
return profanityUseManagementService.getMostRecentProfanityReports(aUserInAServer, count)
|
||||||
|
.stream()
|
||||||
|
.map(profanityUse -> ServerChannelMessage
|
||||||
|
.builder()
|
||||||
|
.messageId(profanityUse.getReportMessageId())
|
||||||
|
.channelId(profanityUse.getReportChannel().getId())
|
||||||
|
.serverId(profanityUse.getServer().getId()).build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void persistProfanityReport(Message profaneMessage, Message reportMessage, Long profanityRegexId) {
|
||||||
|
ServerChannelMessage profaneMessageObj = ServerChannelMessage.fromMessage(profaneMessage);
|
||||||
|
ServerChannelMessage reportMessageObj = ServerChannelMessage.fromMessage(reportMessage);
|
||||||
|
ProfanityRegex profanityRegex = profanityRegexManagementService.getProfanityRegexViaId(profanityRegexId);
|
||||||
|
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(profaneMessage.getMember());
|
||||||
|
ProfanityUserInAServer profaneUser = profanityUserInServerManagementService.getOrCreateProfanityUser(aUserInAServer);
|
||||||
|
profanityUseManagementService.createProfanityUse(profaneMessageObj, reportMessageObj, profaneUser, profanityRegex.getGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
metricService.registerCounter(PROFANITIES_AGREEMENT, "Amount of profanity votes resulting in agreement");
|
||||||
|
metricService.registerCounter(PROFANITIES_DISAGREEMENT, "Amount of profanity votes resulting in disagreement");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
||||||
|
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUse;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.repository.ProfanityUseRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfanityUseManagementServiceBean implements ProfanityUseManagementService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityUseRepository profanityUseRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChannelManagementService channelManagementService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProfanityUse createProfanityUse(ServerChannelMessage profaneMessage, ServerChannelMessage reportMessage, ProfanityUserInAServer reportedUser, ProfanityGroup usedProfanityGroup) {
|
||||||
|
AChannel profaneChannel = channelManagementService.loadChannel(profaneMessage.getChannelId());
|
||||||
|
AChannel reportChannel = channelManagementService.loadChannel(reportMessage.getChannelId());
|
||||||
|
ProfanityUse profanityUse = ProfanityUse
|
||||||
|
.builder()
|
||||||
|
.profanityUser(reportedUser)
|
||||||
|
.profanityGroup(usedProfanityGroup)
|
||||||
|
.profaneMessageId(profaneMessage.getMessageId())
|
||||||
|
.profaneChannel(profaneChannel)
|
||||||
|
.reportMessageId(reportMessage.getMessageId())
|
||||||
|
.reportChannel(reportChannel)
|
||||||
|
.verified(false)
|
||||||
|
.server(reportedUser.getServer())
|
||||||
|
.build();
|
||||||
|
return profanityUseRepository.save(profanityUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ProfanityUse> getProfanityUseViaReportMessageId(Long messageId) {
|
||||||
|
return profanityUseRepository.findById(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getPositiveReports(ProfanityUserInAServer profanityUserInAServer) {
|
||||||
|
return profanityUseRepository.countByProfanityUserAndVerifiedTrueAndConfirmedTrue(profanityUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getFalsePositiveReports(ProfanityUserInAServer profanityUserInAServer) {
|
||||||
|
return profanityUseRepository.countByProfanityUserAndVerifiedTrueAndConfirmedFalse(profanityUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProfanityUse> getMostRecentProfanityReports(ProfanityUserInAServer profanityUserInAServer, int count) {
|
||||||
|
return profanityUseRepository.findAllByProfanityUserAndConfirmedTrueOrderByCreatedDesc(profanityUserInAServer, PageRequest.of(0, count));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.repository.ProfanityUserInServerRepository;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfanityUserInServerManagementServiceBean implements ProfanityUserInServerManagementService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfanityUserInServerRepository repository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserInServerManagementService userInServerManagementService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ProfanityUserInAServer> getProfanityUserOptional(Member member) {
|
||||||
|
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||||
|
return getProfanityUserOptional(userInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ProfanityUserInAServer> getProfanityUserOptional(AUserInAServer aUserInAServer) {
|
||||||
|
return repository.findById(aUserInAServer.getUserInServerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProfanityUserInAServer getProfanityUser(AUserInAServer aUserInAServer) {
|
||||||
|
return getProfanityUserOptional(aUserInAServer).orElseThrow(() -> new AbstractoRunTimeException("Profanity user in server not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProfanityUserInAServer createProfanityUser(AUserInAServer aUserInAServer) {
|
||||||
|
ProfanityUserInAServer profanityUserInAServer = ProfanityUserInAServer
|
||||||
|
.builder()
|
||||||
|
.user(aUserInAServer)
|
||||||
|
.id(aUserInAServer.getUserInServerId())
|
||||||
|
.server(aUserInAServer.getServerReference())
|
||||||
|
.build();
|
||||||
|
return repository.save(profanityUserInAServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProfanityUserInAServer getOrCreateProfanityUser(AUserInAServer aUserInAServer) {
|
||||||
|
Optional<ProfanityUserInAServer> profanityUserOptional = getProfanityUserOptional(aUserInAServer);
|
||||||
|
return profanityUserOptional.orElseGet(() -> createProfanityUser(aUserInAServer));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<property name="profanityFilterFeature" value="(SELECT id FROM feature WHERE key = 'profanityFilter')"/>
|
||||||
|
<property name="moderationModule" value="(SELECT id FROM module WHERE name = 'moderation')"/>
|
||||||
|
<changeSet author="Sheldan" id="profanityFilter_command-commands">
|
||||||
|
<insert tableName="command">
|
||||||
|
<column name="name" value="profanities"/>
|
||||||
|
<column name="module_id" valueComputed="${moderationModule}"/>
|
||||||
|
<column name="feature_id" valueComputed="${profanityFilterFeature}"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<include file="feature.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="module.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="command.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="effect_types.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="default_emote.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd">
|
||||||
|
<changeSet author="Sheldan" id="profanityFilter_emote-insert">
|
||||||
|
<insert tableName="default_emote">
|
||||||
|
<column name="emote_key" value="profanityFilterAgreeEmote"/>
|
||||||
|
<column name="name" value="⏫"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="default_emote">
|
||||||
|
<column name="emote_key" value="profanityFilterDisagreeEmote"/>
|
||||||
|
<column name="name" value="⏬"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<changeSet author="Sheldan" id="profanityFilter_effect_type-insertion">
|
||||||
|
<insert tableName="effect_type">
|
||||||
|
<column name="effect_type_key" value="profanityFilter"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<changeSet author="Sheldan" id="profanityFilter_feature-insertion">
|
||||||
|
<insert tableName="feature">
|
||||||
|
<column name="key" value="profanityFilter"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<changeSet author="Sheldan" id="profanityFilter-moderation-module-insertion">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">
|
||||||
|
SELECT COUNT(*) FROM module WHERE name='moderation';
|
||||||
|
</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="module">
|
||||||
|
<column name="name" value="moderation"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
|
||||||
|
<changeSet author="Sheldan" id="profanity_use-table">
|
||||||
|
<createTable tableName="profanity_use">
|
||||||
|
<column name="report_message_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_profanity_use"/>
|
||||||
|
</column>
|
||||||
|
<column name="profanity_group_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="profanity_user_in_server_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="server_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="report_channel_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="profane_message_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="profane_channel_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||||
|
<column name="confirmed" type="BOOLEAN">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="verified" type="BOOLEAN">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<createIndex indexName="idx_profanity_use_user_in_server" tableName="profanity_use">
|
||||||
|
<column name="profanity_user_in_server_id"/>
|
||||||
|
</createIndex>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="profanity_use" constraintName="fk_profanity_use_server"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||||
|
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="report_channel_id" baseTableName="profanity_use" constraintName="fk_profanity_use_report_channel"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
|
||||||
|
referencedTableName="channel" validate="true"/>
|
||||||
|
|
||||||
|
<addForeignKeyConstraint baseColumnNames="profane_channel_id" baseTableName="profanity_use" constraintName="fk_profanity_use_profane_channel"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
|
||||||
|
referencedTableName="channel" validate="true"/>
|
||||||
|
|
||||||
|
<addForeignKeyConstraint baseColumnNames="profanity_group_id" baseTableName="profanity_use" constraintName="fk_profanity_use_profanity_group"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
|
||||||
|
referencedTableName="profanity_group" validate="true"/>
|
||||||
|
|
||||||
|
<addForeignKeyConstraint baseColumnNames="profanity_user_in_server_id" baseTableName="profanity_use" constraintName="fk_profanity_use_profanity_user"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
|
||||||
|
referencedTableName="profanity_user_in_server" validate="true"/>
|
||||||
|
|
||||||
|
<sql>
|
||||||
|
DROP TRIGGER IF EXISTS profanity_use_update_trigger ON profanity_use;
|
||||||
|
CREATE TRIGGER profanity_use_update_trigger BEFORE UPDATE ON profanity_use FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||||
|
</sql>
|
||||||
|
<sql>
|
||||||
|
DROP TRIGGER IF EXISTS profanity_use_insert_trigger ON profanity_use;
|
||||||
|
CREATE TRIGGER profanity_use_insert_trigger BEFORE INSERT ON profanity_use FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
|
||||||
|
<changeSet author="Sheldan" id="profanity_user_in_server-table">
|
||||||
|
<createTable tableName="profanity_user_in_server">
|
||||||
|
<column name="id" type="BIGINT">
|
||||||
|
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_profanity_user_in_server"/>
|
||||||
|
</column>
|
||||||
|
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||||
|
<column name="server_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="profanity_user_in_server" constraintName="fk_profanity_user_in_server_server"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||||
|
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||||
|
<sql>
|
||||||
|
DROP TRIGGER IF EXISTS profanity_user_in_server_update_trigger ON profanity_user_in_server;
|
||||||
|
CREATE TRIGGER profanity_user_in_server_update_trigger BEFORE UPDATE ON profanity_user_in_server FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||||
|
</sql>
|
||||||
|
<sql>
|
||||||
|
DROP TRIGGER IF EXISTS profanity_user_in_server_insert_trigger ON profanity_user_in_server;
|
||||||
|
CREATE TRIGGER profanity_user_in_server_insert_trigger BEFORE INSERT ON profanity_user_in_server FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<include file="profanity_user_in_server.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="profanity_use.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<include file="1.2.12/collection.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
abstracto.featureFlags.profanityFilter.featureName=profanityFilter
|
||||||
|
abstracto.featureFlags.profanityFilter.enabled=false
|
||||||
|
|
||||||
|
abstracto.postTargets.profanityQueue.name=profanityQueue
|
||||||
|
|
||||||
|
abstracto.featureModes.profanityVote.featureName=profanityFilter
|
||||||
|
abstracto.featureModes.profanityVote.mode=profanityVote
|
||||||
|
abstracto.featureModes.profanityVote.enabled=true
|
||||||
|
|
||||||
|
abstracto.featureModes.autoDeleteProfanities.featureName=profanityFilter
|
||||||
|
abstracto.featureModes.autoDeleteProfanities.mode=autoDeleteProfanities
|
||||||
|
abstracto.featureModes.autoDeleteProfanities.enabled=false
|
||||||
|
|
||||||
|
abstracto.featureModes.profanityReport.featureName=profanityFilter
|
||||||
|
abstracto.featureModes.profanityReport.mode=profanityReport
|
||||||
|
abstracto.featureModes.profanityReport.enabled=true
|
||||||
|
|
||||||
|
abstracto.featureModes.trackProfanities.featureName=profanityFilter
|
||||||
|
abstracto.featureModes.trackProfanities.mode=trackProfanities
|
||||||
|
abstracto.featureModes.trackProfanities.enabled=true
|
||||||
|
|
||||||
|
abstracto.featureModes.autoDeleteAfterVote.featureName=profanityFilter
|
||||||
|
abstracto.featureModes.autoDeleteAfterVote.mode=autoDeleteAfterVote
|
||||||
|
abstracto.featureModes.autoDeleteAfterVote.enabled=true
|
||||||
|
|
||||||
|
abstracto.systemConfigs.profanityVotes.name=profanityVotes
|
||||||
|
abstracto.systemConfigs.profanityVotes.longValue=5
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
|
<artifactId>profanity-filter</artifactId>
|
||||||
|
<version>1.2.12-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>profanity-filter-int</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.sheldan.abstracto.core</groupId>
|
||||||
|
<artifactId>core-int</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.config;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureMode;
|
||||||
|
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.service.ProfanityFilterService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfanityFilterFeatureConfig implements FeatureConfig {
|
||||||
|
@Override
|
||||||
|
public FeatureDefinition getFeature() {
|
||||||
|
return ProfanityFilterFeatureDefinition.PROFANITY_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PostTargetEnum> getRequiredPostTargets() {
|
||||||
|
return Arrays.asList(ProfanityFilterPostTarget.PROFANITY_FILTER_QUEUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FeatureMode> getAvailableModes() {
|
||||||
|
return Arrays.asList(
|
||||||
|
ProfanityFilterMode.PROFANITY_VOTE,
|
||||||
|
ProfanityFilterMode.AUTO_DELETE_PROFANITIES,
|
||||||
|
ProfanityFilterMode.TRACK_PROFANITIES,
|
||||||
|
ProfanityFilterMode.AUTO_DELETE_AFTER_VOTE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getRequiredSystemConfigKeys() {
|
||||||
|
return Arrays.asList(ProfanityFilterService.PROFANITY_VOTES_CONFIG_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getRequiredEmotes() {
|
||||||
|
return Arrays.asList(ProfanityFilterService.REPORT_AGREE_EMOTE, ProfanityFilterService.REPORT_DISAGREE_EMOTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.config;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ProfanityFilterFeatureDefinition implements FeatureDefinition {
|
||||||
|
PROFANITY_FILTER("profanityFilter");
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
ProfanityFilterFeatureDefinition(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.config;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureMode;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ProfanityFilterMode implements FeatureMode {
|
||||||
|
AUTO_DELETE_PROFANITIES("autoDeleteProfanities"),
|
||||||
|
PROFANITY_VOTE("profanityVote"),
|
||||||
|
PROFANITY_REPORT("profanityReport"),
|
||||||
|
AUTO_DELETE_AFTER_VOTE("autoDeleteAfterVote"),
|
||||||
|
TRACK_PROFANITIES("trackProfanities");
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
ProfanityFilterMode(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.config;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.command.config.ModuleDefinition;
|
||||||
|
import dev.sheldan.abstracto.core.command.config.ModuleInfo;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfanityFilterModerationModuleDefinition implements ModuleDefinition {
|
||||||
|
|
||||||
|
public static final String MODERATION = "moderation";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModuleInfo getInfo() {
|
||||||
|
return ModuleInfo.builder().name(MODERATION).templated(true).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentModule() {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.config;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ProfanityFilterPostTarget implements PostTargetEnum {
|
||||||
|
PROFANITY_FILTER_QUEUE("profanityQueue");
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
ProfanityFilterPostTarget(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.model.database;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "profanity_use")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class ProfanityUse {
|
||||||
|
|
||||||
|
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||||
|
@JoinColumn(name = "profanity_group_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private ProfanityGroup profanityGroup;
|
||||||
|
|
||||||
|
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||||
|
@JoinColumn(name = "profanity_user_in_server_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private ProfanityUserInAServer profanityUser;
|
||||||
|
|
||||||
|
@Column(name = "report_message_id")
|
||||||
|
@Id
|
||||||
|
private Long reportMessageId;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "report_channel_id", nullable = false)
|
||||||
|
private AChannel reportChannel;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "server_id", nullable = false)
|
||||||
|
private AServer server;
|
||||||
|
|
||||||
|
@Column(name = "profane_message_id")
|
||||||
|
private Long profaneMessageId;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "profane_channel_id", nullable = false)
|
||||||
|
private AChannel profaneChannel;
|
||||||
|
|
||||||
|
@Column(name = "confirmed")
|
||||||
|
private Boolean confirmed;
|
||||||
|
|
||||||
|
@Column(name = "verified")
|
||||||
|
private Boolean verified;
|
||||||
|
|
||||||
|
@Column(name = "created")
|
||||||
|
private Instant created;
|
||||||
|
|
||||||
|
@Column(name = "updated")
|
||||||
|
private Instant updated;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.model.database;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "profanity_user_in_server")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class ProfanityUserInAServer {
|
||||||
|
@Id
|
||||||
|
@Column(name = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link AUserInAServer user} which is represented by this object
|
||||||
|
*/
|
||||||
|
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||||
|
@PrimaryKeyJoinColumn
|
||||||
|
private AUserInAServer user;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "server_id", nullable = false)
|
||||||
|
private AServer server;
|
||||||
|
|
||||||
|
@Column(name = "created")
|
||||||
|
private Instant created;
|
||||||
|
|
||||||
|
@Column(name = "updated")
|
||||||
|
private Instant updated;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "profanityUser", fetch = FetchType.LAZY)
|
||||||
|
private List<ProfanityUse> usedProfanities;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.model.template;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
public class ProfanitiesModel {
|
||||||
|
private Member member;
|
||||||
|
private Long falsePositives;
|
||||||
|
private Long truePositives;
|
||||||
|
private List<ServerChannelMessage> recentPositiveReports;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.model.template;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
public class ProfanityReportModel {
|
||||||
|
private String profanityGroupKey;
|
||||||
|
private String profanityRegexName;
|
||||||
|
private Message profaneMessage;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.service;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.ProfanityRegex;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUse;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public interface ProfanityFilterService {
|
||||||
|
String REPORT_AGREE_EMOTE = "profanityFilterAgreeEmote";
|
||||||
|
String PROFANITY_VOTES_CONFIG_KEY = "profanityVotes";
|
||||||
|
String REPORT_DISAGREE_EMOTE = "profanityFilterDisagreeEmote";
|
||||||
|
String PROFANITY_FILTER_EFFECT_KEY = "profanityFilter";
|
||||||
|
|
||||||
|
enum VoteResult {
|
||||||
|
AGREEMENT, DISAGREEMENT, BELOW_THRESHOLD;
|
||||||
|
public static boolean isFinal(VoteResult result) {
|
||||||
|
return result.equals(AGREEMENT) || result.equals(DISAGREEMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompletableFuture<Void> createProfanityReport(Message message, ProfanityRegex profanityRegex);
|
||||||
|
boolean isMessageProfanityReport(Long messageId);
|
||||||
|
void verifyProfanityUse(ProfanityUse profanityUse, VoteResult result);
|
||||||
|
Long getPositiveReportCountForUser(AUserInAServer aUserInAServer);
|
||||||
|
Long getPositiveReportCountForUser(ProfanityUserInAServer aUserInAServer);
|
||||||
|
Long getFalseProfanityReportCountForUser(AUserInAServer aUserInAServer);
|
||||||
|
Long getFalseProfanityReportCountForUser(ProfanityUserInAServer aUserInAServer);
|
||||||
|
List<ServerChannelMessage> getRecentPositiveReports(AUserInAServer aUserInAServer, int count);
|
||||||
|
List<ServerChannelMessage> getRecentPositiveReports(ProfanityUserInAServer aUserInAServer, int count);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
|
||||||
|
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUse;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface ProfanityUseManagementService {
|
||||||
|
ProfanityUse createProfanityUse(ServerChannelMessage profaneMessage, ServerChannelMessage reportMessage, ProfanityUserInAServer reportedUser, ProfanityGroup usedProfanityGroup);
|
||||||
|
Optional<ProfanityUse> getProfanityUseViaReportMessageId(Long messageId);
|
||||||
|
Long getPositiveReports(ProfanityUserInAServer profanityUserInAServer);
|
||||||
|
Long getFalsePositiveReports(ProfanityUserInAServer profanityUserInAServer);
|
||||||
|
List<ProfanityUse> getMostRecentProfanityReports(ProfanityUserInAServer profanityUserInAServer, int count);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package dev.sheldan.abstracto.profanityfilter.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.profanityfilter.model.database.ProfanityUserInAServer;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface ProfanityUserInServerManagementService {
|
||||||
|
Optional<ProfanityUserInAServer> getProfanityUserOptional(Member member);
|
||||||
|
Optional<ProfanityUserInAServer> getProfanityUserOptional(AUserInAServer aUserInAServer);
|
||||||
|
ProfanityUserInAServer getProfanityUser(AUserInAServer aUserInAServer);
|
||||||
|
ProfanityUserInAServer createProfanityUser(AUserInAServer aUserInAServer);
|
||||||
|
ProfanityUserInAServer getOrCreateProfanityUser(AUserInAServer aUserInAServer);
|
||||||
|
}
|
||||||
@@ -55,7 +55,6 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.sheldan.abstracto.core</groupId>
|
<groupId>dev.sheldan.abstracto.core</groupId>
|
||||||
<artifactId>core-int</artifactId>
|
<artifactId>core-int</artifactId>
|
||||||
@@ -63,8 +62,6 @@
|
|||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
<artifactId>loggingg-int</artifactId>
|
<artifactId>logging-int</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@@ -216,6 +216,18 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
|
<artifactId>profanity-filter-int</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
|
<artifactId>profanity-filter-impl</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||||
<artifactId>webservices-int</artifactId>
|
<artifactId>webservices-int</artifactId>
|
||||||
|
|||||||
@@ -68,17 +68,22 @@ public class ProfanityServiceBean implements ProfanityService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsProfanity(String input, Long serverId) {
|
public boolean containsProfanity(String input, Long serverId) {
|
||||||
|
return getProfanityRegex(input, serverId).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ProfanityRegex> getProfanityRegex(String input, Long serverId) {
|
||||||
if(regex.containsKey(serverId)) {
|
if(regex.containsKey(serverId)) {
|
||||||
List<PatternReplacement> regexes = regex.get(serverId);
|
List<PatternReplacement> regexes = regex.get(serverId);
|
||||||
log.debug("Checking existence of {} regexes for server {}.", regexes.size(), serverId);
|
log.debug("Checking existence of {} regexes for server {}.", regexes.size(), serverId);
|
||||||
for (PatternReplacement pattern: regexes) {
|
for (PatternReplacement pattern: regexes) {
|
||||||
Matcher matcher = pattern.getPattern().matcher(input);
|
Matcher matcher = pattern.getPattern().matcher(input);
|
||||||
if(matcher.matches()) {
|
if(matcher.find()) {
|
||||||
return true;
|
return profanityRegexManagementService.getProfanityRegexViaIdOptional(pattern.profanityRegexId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,18 +137,28 @@ public class ProfanityServiceBean implements ProfanityService {
|
|||||||
regex = new HashMap<>();
|
regex = new HashMap<>();
|
||||||
List<ProfanityGroup> allGroups = profanityGroupManagementService.getAllGroups();
|
List<ProfanityGroup> allGroups = profanityGroupManagementService.getAllGroups();
|
||||||
allGroups.forEach(profanityGroup -> profanityGroup.getProfanities().forEach(profanityRegex -> {
|
allGroups.forEach(profanityGroup -> profanityGroup.getProfanities().forEach(profanityRegex -> {
|
||||||
Pattern pattern = Pattern.compile(profanityRegex.getRegex());
|
|
||||||
List<PatternReplacement> newPatterns = new ArrayList<>();
|
|
||||||
Long serverId = profanityGroup.getServer().getId();
|
Long serverId = profanityGroup.getServer().getId();
|
||||||
if(regex.containsKey(serverId)) {
|
loadProfanityRegex(profanityRegex, serverId);
|
||||||
regex.get(serverId).add(PatternReplacement.builder().pattern(pattern).replacement(profanityRegex.getReplacement()).build());
|
|
||||||
} else {
|
|
||||||
newPatterns.add(PatternReplacement.builder().pattern(pattern).replacement(profanityRegex.getReplacement()).build());
|
|
||||||
regex.put(serverId, newPatterns);
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadProfanityRegex(ProfanityRegex profanityRegex, Long serverId) {
|
||||||
|
Pattern pattern = Pattern.compile(profanityRegex.getRegex());
|
||||||
|
List<PatternReplacement> newPatterns = new ArrayList<>();
|
||||||
|
PatternReplacement patternReplacement = PatternReplacement
|
||||||
|
.builder()
|
||||||
|
.pattern(pattern)
|
||||||
|
.replacement(profanityRegex.getReplacement())
|
||||||
|
.profanityRegexId(profanityRegex.getId())
|
||||||
|
.build();
|
||||||
|
if (regex.containsKey(serverId)) {
|
||||||
|
regex.get(serverId).add(patternReplacement);
|
||||||
|
} else {
|
||||||
|
newPatterns.add(patternReplacement);
|
||||||
|
regex.put(serverId, newPatterns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reloadRegex(Long serverId) {
|
public void reloadRegex(Long serverId) {
|
||||||
log.info("Reloading regex for server {}.", serverId);
|
log.info("Reloading regex for server {}.", serverId);
|
||||||
@@ -152,22 +167,16 @@ public class ProfanityServiceBean implements ProfanityService {
|
|||||||
}
|
}
|
||||||
regex.remove(serverId);
|
regex.remove(serverId);
|
||||||
List<ProfanityGroup> allGroups = profanityGroupManagementService.getAllForServer(serverId);
|
List<ProfanityGroup> allGroups = profanityGroupManagementService.getAllForServer(serverId);
|
||||||
allGroups.forEach(profanityGroup -> profanityGroup.getProfanities().forEach(profanityRegex -> {
|
allGroups
|
||||||
Pattern pattern = Pattern.compile(profanityRegex.getRegex());
|
.forEach(profanityGroup -> profanityGroup.getProfanities()
|
||||||
List<PatternReplacement> newPatterns = new ArrayList<>();
|
.forEach(profanityRegex -> loadProfanityRegex(profanityRegex, serverId)));
|
||||||
if(regex.containsKey(serverId)) {
|
|
||||||
regex.get(serverId).add(PatternReplacement.builder().pattern(pattern).replacement(profanityRegex.getReplacement()).build());
|
|
||||||
} else {
|
|
||||||
newPatterns.add(PatternReplacement.builder().pattern(pattern).replacement(profanityRegex.getReplacement()).build());
|
|
||||||
regex.put(serverId, newPatterns);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
private static class PatternReplacement {
|
private static class PatternReplacement {
|
||||||
|
private final Long profanityRegexId;
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
private final String replacement;
|
private final String replacement;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,16 @@ public class ProfanityGroupManagementServiceBean implements ProfanityGroupManage
|
|||||||
return getProfanityGroupOptional(server, name).isPresent();
|
return getProfanityGroupOptional(server, name).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ProfanityGroup> getProfanityGroupByIdOptional(Long profanityGroupId) {
|
||||||
|
return repository.findById(profanityGroupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProfanityGroup getProfanityGroupById(Long profanityGroupId) {
|
||||||
|
return getProfanityGroupByIdOptional(profanityGroupId).orElseThrow(ProfanityGroupNotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<ProfanityGroup> getProfanityGroupOptional(AServer server, String name) {
|
public Optional<ProfanityGroup> getProfanityGroupOptional(AServer server, String name) {
|
||||||
return repository.findByServerAndGroupNameIgnoreCase(server, name);
|
return repository.findByServerAndGroupNameIgnoreCase(server, name);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.sheldan.abstracto.core.service.management;
|
package dev.sheldan.abstracto.core.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.exception.ProfanityRegexNotFoundException;
|
||||||
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
||||||
import dev.sheldan.abstracto.core.models.database.ProfanityRegex;
|
import dev.sheldan.abstracto.core.models.database.ProfanityRegex;
|
||||||
import dev.sheldan.abstracto.core.repository.ProfanityRegexRepository;
|
import dev.sheldan.abstracto.core.repository.ProfanityRegexRepository;
|
||||||
@@ -54,4 +55,14 @@ public class ProfanityRegexManagementServiceBean implements ProfanityRegexManag
|
|||||||
public Optional<ProfanityRegex> getProfanityRegexOptional(ProfanityGroup profanityGroup, String name) {
|
public Optional<ProfanityRegex> getProfanityRegexOptional(ProfanityGroup profanityGroup, String name) {
|
||||||
return repository.findByGroupAndRegexNameIgnoreCase(profanityGroup, name);
|
return repository.findByGroupAndRegexNameIgnoreCase(profanityGroup, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ProfanityRegex> getProfanityRegexViaIdOptional(Long profanityRegexId) {
|
||||||
|
return repository.findById(profanityRegexId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProfanityRegex getProfanityRegexViaId(Long profanityRegexId) {
|
||||||
|
return getProfanityRegexViaIdOptional(profanityRegexId).orElseThrow(ProfanityRegexNotFoundException::new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.utils.MessageUtils;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@@ -16,4 +17,13 @@ public class ServerChannelMessage {
|
|||||||
public String getJumpUrl() {
|
public String getJumpUrl() {
|
||||||
return MessageUtils.buildMessageUrl(serverId, channelId, messageId);
|
return MessageUtils.buildMessageUrl(serverId, channelId, messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ServerChannelMessage fromMessage(Message message) {
|
||||||
|
return ServerChannelMessage
|
||||||
|
.builder()
|
||||||
|
.serverId(message.getGuild().getIdLong())
|
||||||
|
.channelId(message.getChannel().getIdLong())
|
||||||
|
.messageId(message.getIdLong())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ package dev.sheldan.abstracto.core.service;
|
|||||||
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
|
||||||
import dev.sheldan.abstracto.core.models.database.ProfanityRegex;
|
import dev.sheldan.abstracto.core.models.database.ProfanityRegex;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ProfanityService {
|
public interface ProfanityService {
|
||||||
String replaceProfanities(String input, Long serverId);
|
String replaceProfanities(String input, Long serverId);
|
||||||
String replaceProfanities(String input, Long serverId, String replacement);
|
String replaceProfanities(String input, Long serverId, String replacement);
|
||||||
String replaceProfanitiesWithDefault(String input, Long serverId, String replacement);
|
String replaceProfanitiesWithDefault(String input, Long serverId, String replacement);
|
||||||
boolean containsProfanity(String input, Long serverId);
|
boolean containsProfanity(String input, Long serverId);
|
||||||
|
Optional<ProfanityRegex> getProfanityRegex(String input, Long serverId);
|
||||||
ProfanityGroup createProfanityGroup(Long serverId, String profanityGroupName);
|
ProfanityGroup createProfanityGroup(Long serverId, String profanityGroupName);
|
||||||
void deleteProfanityGroup(Long serverId, String profanityGroupName);
|
void deleteProfanityGroup(Long serverId, String profanityGroupName);
|
||||||
void deleteProfanityRegex(Long serverId, String profanityGroupName, String profanityRegexName);
|
void deleteProfanityRegex(Long serverId, String profanityGroupName, String profanityRegexName);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public interface ProfanityGroupManagementService {
|
|||||||
List<ProfanityGroup> getAllForServer(Long serverId);
|
List<ProfanityGroup> getAllForServer(Long serverId);
|
||||||
ProfanityGroup createProfanityGroup(AServer server, String name);
|
ProfanityGroup createProfanityGroup(AServer server, String name);
|
||||||
boolean doesProfanityGroupExist(AServer server, String name);
|
boolean doesProfanityGroupExist(AServer server, String name);
|
||||||
|
Optional<ProfanityGroup> getProfanityGroupByIdOptional(Long profanityGroupId);
|
||||||
|
ProfanityGroup getProfanityGroupById(Long profanityGroupId);
|
||||||
Optional<ProfanityGroup> getProfanityGroupOptional(AServer server, String name);
|
Optional<ProfanityGroup> getProfanityGroupOptional(AServer server, String name);
|
||||||
ProfanityGroup getProfanityGroup(AServer server, String name);
|
ProfanityGroup getProfanityGroup(AServer server, String name);
|
||||||
void deleteProfanityGroup(ProfanityGroup profanityGroup);
|
void deleteProfanityGroup(ProfanityGroup profanityGroup);
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ public interface ProfanityRegexManagementService {
|
|||||||
void deleteProfanityRegex(ProfanityGroup group, String profanityName);
|
void deleteProfanityRegex(ProfanityGroup group, String profanityName);
|
||||||
boolean doesProfanityRegexExist(ProfanityGroup profanityGroup, String name);
|
boolean doesProfanityRegexExist(ProfanityGroup profanityGroup, String name);
|
||||||
Optional<ProfanityRegex> getProfanityRegexOptional(ProfanityGroup profanityGroup, String name);
|
Optional<ProfanityRegex> getProfanityRegexOptional(ProfanityGroup profanityGroup, String name);
|
||||||
|
Optional<ProfanityRegex> getProfanityRegexViaIdOptional(Long profanityRegexId);
|
||||||
|
ProfanityRegex getProfanityRegexViaId(Long profanityRegexId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Help::
|
|||||||
This information includes a description and the available commands of this module. If the provided parameter matches a command name, information about this command is displayed.
|
This information includes a description and the available commands of this module. If the provided parameter matches a command name, information about this command is displayed.
|
||||||
The module matching takes precedence over command matching.
|
The module matching takes precedence over command matching.
|
||||||
This information includes the a short description, a more detailed description, aliases (if any), parameters (if any), which roles are allowed to execute the command,
|
This information includes the a short description, a more detailed description, aliases (if any), parameters (if any), which roles are allowed to execute the command,
|
||||||
or if it is not restricted and which roles are immune against the command.
|
or if it is not restricted and which effects a command has.
|
||||||
Changing the system configuration::
|
Changing the system configuration::
|
||||||
* Usage `setConfig <key> <value>`
|
* Usage `setConfig <key> <value>`
|
||||||
* Description: Changes the value of this configuration identified by `key` to `value`. Some of these configurations have separate commands, but this works in general.
|
* Description: Changes the value of this configuration identified by `key` to `value`. Some of these configurations have separate commands, but this works in general.
|
||||||
|
|||||||
@@ -198,4 +198,30 @@ Showing the tracked filtered invites::
|
|||||||
Remove all or individual invites from the tracked filtered invites::
|
Remove all or individual invites from the tracked filtered invites::
|
||||||
* Usage: `removeTrackedInviteLinks [invite]`
|
* Usage: `removeTrackedInviteLinks [invite]`
|
||||||
* Description: Removes the stored statistic for the given `invite`. In case `invite` is not given, it will delete all tracked filtered invites from the server.
|
* Description: Removes the stored statistic for the given `invite`. In case `invite` is not given, it will delete all tracked filtered invites from the server.
|
||||||
* Mode Restriction: This command is only available when the feature mode `trackUses` is enabled.
|
* Mode Restriction: This command is only available when the feature mode `trackUses` is enabled.
|
||||||
|
|
||||||
|
=== Profanity filter
|
||||||
|
|
||||||
|
Feature key `profanityFilter`
|
||||||
|
|
||||||
|
This functionality provides the ability to automatically delete any detected profanities. These profanities are configured via the profanity groups and profanity regexes. Every group in these groups are active and every profanity regex will be evaluated and (depending on the feature mode) reported to be voted on.
|
||||||
|
The uses of profanities can be tracked and a command is available to show the profanities for a user.
|
||||||
|
|
||||||
|
==== Post targets
|
||||||
|
`profanityQueue`:: target for reports to be voted on - if the feature mode `filterNotifications` is enabled.
|
||||||
|
|
||||||
|
==== Feature modes
|
||||||
|
`autoDeleteProfanities`:: if enabled, each detected profanity will be deleted immediately. Disabled by default.
|
||||||
|
`profanityReport`:: if enabled, sends a notification to the `profanityQueue` post target to notify about a detected profanity. Enabled by default.
|
||||||
|
`profanityVote`:: if enabled, sends a notification to the `profanityQueue` post target to notify about a detected profanity to be voted on. Requires feature mode `profanityReport` to be enabled. Enabled by default.
|
||||||
|
`autoDeleteAfterVote`:: if enabled, after a profanity vote has reached the threshold (system config key `profanityVotes`), depending on the outcome, it will be deleted. Requires feature mode `profanityVote` to be enabled. Enabled by default.
|
||||||
|
`trackProfanities`:: if enabled, the command `profanities` is available to show the profanities of a member. Requires feature mode `profanityVote` to be enabled. Enabled by default.
|
||||||
|
|
||||||
|
==== Emotes
|
||||||
|
* `profanityFilterAgreeEmote` reaction emote to indicate agreement about a reported profanity
|
||||||
|
* `profanityFilterDisagreeEmote` reaction emote to indicate disagreement about a reported profanity
|
||||||
|
|
||||||
|
==== Commands
|
||||||
|
Show the profanities of a member::
|
||||||
|
* Usage `profanities <member>`
|
||||||
|
* Description: Shows the true and false positive profanities of the given member. Also, if there any, shows the recent true positive reports.
|
||||||
Reference in New Issue
Block a user