mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-26 13:46:19 +00:00
[AB-277] adding report mechanism via reactions
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package dev.sheldan.abstracto.moderation.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.database.AEmote;
|
||||
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
|
||||
import dev.sheldan.abstracto.core.service.EmoteService;
|
||||
import dev.sheldan.abstracto.core.service.MemberService;
|
||||
import dev.sheldan.abstracto.core.service.ReactionService;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.service.ReactionReportService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ReactionReportListener implements AsyncReactionAddedListener {
|
||||
|
||||
@Autowired
|
||||
private EmoteService emoteService;
|
||||
|
||||
@Autowired
|
||||
private ReactionService reactionService;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Autowired
|
||||
private ReactionReportService reactionReportService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(ReactionAddedModel model) {
|
||||
CachedMessage cachedMessage = model.getMessage();
|
||||
if(model.getUserReacting().getUserId().equals(cachedMessage.getAuthor().getAuthorId())) {
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
|
||||
if(model.getMemberReacting().getUser().isBot()) {
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
|
||||
Long serverId = model.getServerId();
|
||||
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(ReactionReportService.REACTION_REPORT_EMOTE_KEY, serverId);
|
||||
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
|
||||
memberService.retrieveMemberInServer(model.getUserReacting())
|
||||
.thenCompose(member -> reactionService.removeReactionFromMessage(model.getReaction(), cachedMessage, member.getUser()))
|
||||
.thenAccept(unused -> log.info("Removed report reaction on message {} in server {} in channel {}.", cachedMessage.getMessageId(), serverId, cachedMessage.getChannelId()));
|
||||
log.info("User {} in server {} reacted to report a message {} from channel {}.",
|
||||
model.getUserReacting().getUserId(), model.getServerId(), cachedMessage.getMessageId(), cachedMessage.getChannelId());
|
||||
reactionReportService.createReactionReport(cachedMessage, model.getUserReacting()).exceptionally(throwable -> {
|
||||
log.error("Failed to create reaction report in server {} on message {} in channel {}.", serverId, cachedMessage.getMessageId(), cachedMessage.getChannelId(), throwable);
|
||||
return null;
|
||||
});
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
} else {
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ModerationFeatureDefinition.REPORT_REACTIONS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.moderation.repository;
|
||||
|
||||
import dev.sheldan.abstracto.moderation.model.database.ModerationUser;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ModerationUserRepository extends JpaRepository<ModerationUser, Long> {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.moderation.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ReactionReport;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface ReactionReportRepository extends JpaRepository<ReactionReport, Long> {
|
||||
|
||||
Optional<ReactionReport> findByReportedUserAndCreatedLessThan(AUserInAServer aUserInAServer, Instant maxCreated);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package dev.sheldan.abstracto.moderation.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.moderation.config.posttarget.ReactionReportPostTarget;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ModerationUser;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ReactionReport;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.ReportReactionNotificationModel;
|
||||
import dev.sheldan.abstracto.moderation.service.management.ModerationUserManagementService;
|
||||
import dev.sheldan.abstracto.moderation.service.management.ReactionReportManagementService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ReactionReportServiceBean implements ReactionReportService {
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ReactionReportManagementService reactionReportManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private ModerationUserManagementService moderationUserManagementService;
|
||||
|
||||
@Autowired
|
||||
private ReactionReportServiceBean self;
|
||||
|
||||
private static final String REACTION_REPORT_TEMPLATE_KEY = "reactionReport_notification";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> createReactionReport(CachedMessage reportedMessage, ServerUser reporter) {
|
||||
AUserInAServer reportedUser = userInServerManagementService.loadOrCreateUser(reportedMessage.getAuthorAsServerUser());
|
||||
AUserInAServer reportingUser = userInServerManagementService.loadOrCreateUser(reporter);
|
||||
Optional<ModerationUser> moderationUserOptional = moderationUserManagementService.findModerationUser(reportingUser);
|
||||
Long serverId = reporter.getServerId();
|
||||
Long cooldownSeconds = configService.getLongValueOrConfigDefault(REACTION_REPORT_COOLDOWN, serverId);
|
||||
Duration maxAge = Duration.of(cooldownSeconds, ChronoUnit.SECONDS);
|
||||
if(moderationUserOptional.isPresent()) {
|
||||
ModerationUser reporterModerationUser = moderationUserOptional.get();
|
||||
Instant minAllowedReportTime = Instant.now().minus(maxAge);
|
||||
if(reporterModerationUser.getLastReportTimeStamp() != null && reporterModerationUser.getLastReportTimeStamp().isAfter(minAllowedReportTime)) {
|
||||
log.info("User {} in server {} reported user {} within the cooldown. Ignoring.", reporter.getUserId(), serverId, reportedMessage.getAuthor().getAuthorId());
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
log.info("User {} in server {} reported user {}..", reporter.getUserId(), serverId, reportedMessage.getAuthor().getAuthorId());
|
||||
Optional<ReactionReport> recentReportOptional = reactionReportManagementService.findRecentReactionReportAboutUser(reportedUser, maxAge);
|
||||
if(!recentReportOptional.isPresent()) {
|
||||
ReportReactionNotificationModel model = ReportReactionNotificationModel
|
||||
.builder()
|
||||
.reportCount(1)
|
||||
.reportedMessage(reportedMessage)
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(REACTION_REPORT_TEMPLATE_KEY, model, serverId);
|
||||
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, ReactionReportPostTarget.REACTION_REPORTS, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(messageFutures)
|
||||
.thenAccept(unused -> self.createReactionReportInDb(reportedMessage, messageFutures.get(0).join(), reporter));
|
||||
} else {
|
||||
ReactionReport report = recentReportOptional.get();
|
||||
log.info("Report is already present in channel {} with message {}. Updating field.", report.getReportChannel().getId(), report.getReportMessageId());
|
||||
report.setReportCount(report.getReportCount() + 1);
|
||||
TextChannel reportTextChannel = channelService.getTextChannelFromServer(serverId, report.getReportChannel().getId());
|
||||
return channelService.editFieldValueInMessage(reportTextChannel, report.getReportMessageId(), 0, report.getReportCount().toString())
|
||||
.thenAccept(message -> self.updateModerationUserReportCooldown(reporter));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void createReactionReportInDb(CachedMessage cachedMessage, Message reportMessage, ServerUser reporter) {
|
||||
log.info("Creation reaction report in message {} about message {} in database.", reportMessage.getIdLong(), cachedMessage.getMessageId());
|
||||
reactionReportManagementService.createReactionReport(cachedMessage, reportMessage);
|
||||
updateModerationUserReportCooldown(reporter);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateModerationUserReportCooldown(ServerUser reporter) {
|
||||
AUserInAServer reporterAUserInServer = userInServerManagementService.loadOrCreateUser(reporter);
|
||||
Optional<ModerationUser> optionalModerationUser = moderationUserManagementService.findModerationUser(reporterAUserInServer);
|
||||
Instant reportTimeStamp = Instant.now();
|
||||
if(optionalModerationUser.isPresent()) {
|
||||
log.info("Updating last report time of user {}.", reporter.getUserId());
|
||||
optionalModerationUser.get().setLastReportTimeStamp(reportTimeStamp);
|
||||
} else {
|
||||
log.info("Creating new moderation user instance for user {} to track report cooldowns.", reporter.getUserId());
|
||||
moderationUserManagementService.createModerationUserWithReportTimeStamp(reporterAUserInServer, reportTimeStamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package dev.sheldan.abstracto.moderation.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ModerationUser;
|
||||
import dev.sheldan.abstracto.moderation.repository.ModerationUserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class ModerationUserManagementServiceBean implements ModerationUserManagementService {
|
||||
|
||||
@Autowired
|
||||
private ModerationUserRepository repository;
|
||||
|
||||
@Override
|
||||
public ModerationUser createModerationUser(AUserInAServer aUserInAServer) {
|
||||
ModerationUser moderationUser = ModerationUser
|
||||
.builder()
|
||||
.id(aUserInAServer.getUserInServerId())
|
||||
.user(aUserInAServer)
|
||||
.server(aUserInAServer.getServerReference())
|
||||
.build();
|
||||
return repository.save(moderationUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModerationUser createModerationUserWithReportTimeStamp(AUserInAServer aUserInAServer, Instant reportTime) {
|
||||
ModerationUser moderationUser = createModerationUser(aUserInAServer);
|
||||
moderationUser.setLastReportTimeStamp(reportTime);
|
||||
return moderationUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ModerationUser> findModerationUser(AUserInAServer aUserInAServer) {
|
||||
return repository.findById(aUserInAServer.getUserInServerId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package dev.sheldan.abstracto.moderation.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ReactionReport;
|
||||
import dev.sheldan.abstracto.moderation.repository.ReactionReportRepository;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class ReactionReportManagementServiceBean implements ReactionReportManagementService {
|
||||
|
||||
@Autowired
|
||||
private ReactionReportRepository repository;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private ChannelManagementService channelManagementService;
|
||||
|
||||
@Override
|
||||
public Optional<ReactionReport> findRecentReactionReportAboutUser(AUserInAServer aUserInAServer, Duration maxAge) {
|
||||
Instant maxCreation = Instant.now().minus(maxAge);
|
||||
return repository.findByReportedUserAndCreatedLessThan(aUserInAServer, maxCreation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactionReport createReactionReport(CachedMessage reportedMessage, Message reportMessage) {
|
||||
AChannel reportChannel = channelManagementService.loadChannel(reportMessage.getTextChannel());
|
||||
AChannel reportedChannel = channelManagementService.loadChannel(reportedMessage.getChannelId());
|
||||
AUserInAServer reportedUser = userInServerManagementService.loadOrCreateUser(reportedMessage.getAuthorAsServerUser());
|
||||
ReactionReport report = ReactionReport
|
||||
.builder()
|
||||
.reportChannel(reportChannel)
|
||||
.reportedChannel(reportedChannel)
|
||||
.reportCount(1)
|
||||
.reportedMessageId(reportedMessage.getMessageId())
|
||||
.reportMessageId(reportMessage.getIdLong())
|
||||
.reportedUser(reportedUser)
|
||||
.server(reportedUser.getServerReference())
|
||||
.build();
|
||||
return repository.save(report);
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,7 @@
|
||||
<column name="muted_user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="channel_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="muting_channel_id" type="BIGINT" />
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
@@ -41,7 +39,7 @@
|
||||
</column>
|
||||
</createTable>
|
||||
<addPrimaryKey columnNames="server_id, id" tableName="mute" constraintName="pk_mute" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="mute" constraintName="fk_mute_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="muting_channel_id" baseTableName="mute" constraintName="fk_mute_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="muting_user_in_server_id" baseTableName="mute" constraintName="fk_mute_muting_user" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="muted_user_in_server_id" baseTableName="mute" constraintName="fk_mute_muted_user" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="mute" constraintName="fk_mute_server_id" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
|
||||
@@ -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="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
</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="feature.xml" relativeToChangelogFile="true"/>
|
||||
<include file="default_emote.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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="reactionReport_default_emote-insert">
|
||||
<insert tableName="default_emote">
|
||||
<column name="emote_key" value="reactionReport"/>
|
||||
<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="reportReactions_feature-insertion">
|
||||
<insert tableName="feature">
|
||||
<column name="key" value="reportReactions"/>
|
||||
</insert>
|
||||
</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="moderation_user-table">
|
||||
<createTable tableName="moderation_user">
|
||||
<column name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_moderation_user"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
<column name="latest_report" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="moderation_user" constraintName="fk_moderation_user_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
|
||||
referencedTableName="server" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS moderation_user_update_trigger ON moderation_user;
|
||||
CREATE TRIGGER moderation_user_update_trigger BEFORE UPDATE ON moderation_user FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS moderation_user_insert_trigger ON moderation_user;
|
||||
CREATE TRIGGER moderation_user_insert_trigger BEFORE INSERT ON moderation_user FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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="reaction_report-table">
|
||||
<createTable tableName="reaction_report">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_reaction_report"/>
|
||||
</column>
|
||||
<column name="reported_user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="report_channel_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="reported_message_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="reported_channel_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="report_count" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="reaction_report" constraintName="fk_reaction_report_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="reported_channel_id" baseTableName="reaction_report" constraintName="fk_reaction_report_reported_channel"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="report_channel_id" baseTableName="reaction_report" constraintName="fk_reaction_report_report_channel"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="reported_user_in_server_id" baseTableName="reaction_report" constraintName="fk_reaction_report_reported_user"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS reaction_report_update_trigger ON reaction_report;
|
||||
CREATE TRIGGER reaction_report_update_trigger BEFORE UPDATE ON reaction_report FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS reaction_report_insert_trigger ON reaction_report;
|
||||
CREATE TRIGGER reaction_report_insert_trigger BEFORE INSERT ON reaction_report 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="moderation_user.xml" relativeToChangelogFile="true"/>
|
||||
<include file="reaction_report.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -8,4 +8,5 @@
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<include file="1.0-moderation/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.2.11-moderation/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.2.15/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -1,9 +1,15 @@
|
||||
abstracto.systemConfigs.decayDays.name=decayDays
|
||||
abstracto.systemConfigs.decayDays.longValue=90
|
||||
|
||||
abstracto.systemConfigs.reactionReportCooldownSeconds.name=reactionReportCooldownSeconds
|
||||
abstracto.systemConfigs.reactionReportCooldownSeconds.longValue=300
|
||||
|
||||
abstracto.featureFlags.moderation.featureName=moderation
|
||||
abstracto.featureFlags.moderation.enabled=false
|
||||
|
||||
abstracto.featureFlags.reportReactions.featureName=reportReactions
|
||||
abstracto.featureFlags.reportReactions.enabled=false
|
||||
|
||||
abstracto.featureFlags.warnings.featureName=warnings
|
||||
abstracto.featureFlags.warnings.enabled=false
|
||||
|
||||
@@ -17,12 +23,12 @@ abstracto.featureFlags.userNotes.featureName=userNotes
|
||||
abstracto.featureFlags.userNotes.enabled=false
|
||||
|
||||
abstracto.postTargets.warnLog.name=warnLog
|
||||
abstracto.postTargets.reactionReports.name=reactionReports
|
||||
abstracto.postTargets.kickLog.name=kickLog
|
||||
abstracto.postTargets.banLog.name=banLog
|
||||
abstracto.postTargets.muteLog.name=muteLog
|
||||
abstracto.postTargets.decayLog.name=decayLog
|
||||
|
||||
|
||||
abstracto.featureModes.banLogging.featureName=moderation
|
||||
abstracto.featureModes.banLogging.mode=banLogging
|
||||
abstracto.featureModes.banLogging.enabled=true
|
||||
|
||||
@@ -10,7 +10,9 @@ public enum ModerationFeatureDefinition implements FeatureDefinition {
|
||||
MUTING("muting"),
|
||||
AUTOMATIC_WARN_DECAY("warnDecay"),
|
||||
USER_NOTES("userNotes"),
|
||||
INVITE_FILTER("inviteFilter");
|
||||
INVITE_FILTER("inviteFilter"),
|
||||
REPORT_REACTIONS("reportReactions")
|
||||
;
|
||||
|
||||
private final String key;
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package dev.sheldan.abstracto.moderation.config.feature;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import dev.sheldan.abstracto.moderation.config.posttarget.ReactionReportPostTarget;
|
||||
import dev.sheldan.abstracto.moderation.service.ReactionReportService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Component
|
||||
public class ReportReactionFeatureConfig implements FeatureConfig {
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ModerationFeatureDefinition.REPORT_REACTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PostTargetEnum> getRequiredPostTargets() {
|
||||
return Arrays.asList(ReactionReportPostTarget.REACTION_REPORTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredEmotes() {
|
||||
return Arrays.asList(ReactionReportService.REACTION_REPORT_EMOTE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList(ReactionReportService.REACTION_REPORT_COOLDOWN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.moderation.config.posttarget;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ReactionReportPostTarget implements PostTargetEnum {
|
||||
REACTION_REPORTS("reactionReports");
|
||||
|
||||
private String key;
|
||||
|
||||
ReactionReportPostTarget(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package dev.sheldan.abstracto.moderation.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;
|
||||
|
||||
@Entity
|
||||
@Table(name="moderation_user")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class ModerationUser {
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
private Long id;
|
||||
|
||||
@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 = "latest_report")
|
||||
private Instant lastReportTimeStamp;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public class Mute implements Serializable {
|
||||
* The channel in which this mute was cast
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "mutingChannel")
|
||||
@JoinColumn(name = "muting_channel_id")
|
||||
private AChannel mutingChannel;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package dev.sheldan.abstracto.moderation.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.AUserInAServer;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.Instant;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "reaction_report")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class ReactionReport {
|
||||
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
private Long reportMessageId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "report_channel_id")
|
||||
private AChannel reportChannel;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "reported_user_in_server_id", nullable = false)
|
||||
private AUserInAServer reportedUser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "reported_channel_id")
|
||||
private AChannel reportedChannel;
|
||||
|
||||
@Column(name = "reported_message_id")
|
||||
private Long reportedMessageId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "server_id")
|
||||
private AServer server;
|
||||
|
||||
@Column(name = "report_count")
|
||||
private Integer reportCount;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package dev.sheldan.abstracto.moderation.model.template.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class ReportReactionNotificationModel {
|
||||
private CachedMessage reportedMessage;
|
||||
private ServerUser reporter;
|
||||
private Integer reportCount;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.moderation.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface ReactionReportService {
|
||||
String REACTION_REPORT_EMOTE_KEY = "reactionReport";
|
||||
String REACTION_REPORT_COOLDOWN = "reactionReportCooldownSeconds";
|
||||
CompletableFuture<Void> createReactionReport(CachedMessage reportedMessage, ServerUser reporter);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.abstracto.moderation.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ModerationUser;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ModerationUserManagementService {
|
||||
ModerationUser createModerationUser(AUserInAServer aUserInAServer);
|
||||
ModerationUser createModerationUserWithReportTimeStamp(AUserInAServer aUserInAServer, Instant reportTime);
|
||||
Optional<ModerationUser> findModerationUser(AUserInAServer aUserInAServer);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.moderation.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.moderation.model.database.ReactionReport;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ReactionReportManagementService {
|
||||
Optional<ReactionReport> findRecentReactionReportAboutUser(AUserInAServer aUserInAServer, Duration part);
|
||||
ReactionReport createReactionReport(CachedMessage reportedMessage, Message reportMessage);
|
||||
}
|
||||
Reference in New Issue
Block a user