[AB-xxx] adding honeypot message feature to ban members that message a certain channel

This commit is contained in:
Sheldan
2026-05-03 00:31:56 +02:00
parent dafde2d8f6
commit 6addd5939b
7 changed files with 132 additions and 8 deletions

View File

@@ -18,7 +18,7 @@ import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
import dev.sheldan.abstracto.moderation.config.feature.HoneyPotFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.listener.HoneyPotServiceBean;
import dev.sheldan.abstracto.moderation.service.HoneyPotServiceBean;
import dev.sheldan.abstracto.moderation.model.template.command.HoneyPotBanResponseModel;
import java.time.Duration;
import java.time.Instant;
@@ -98,7 +98,7 @@ public class HoneyPotBan extends AbstractConditionableCommand {
.toList();
Role honeyPotRole = guild.getRoleById(honeyPotServiceBean.getHoneyPotRoleId(guild.getIdLong()));
List<CompletableFuture<Void>> futures = currentMembersWithHoneypotRole.stream().map(member ->
honeyPotServiceBean.banForHoneyPot(member, honeyPotRole)
honeyPotServiceBean.banForHoneyPotRole(member, honeyPotRole)
).toList();
Integer memberCount = currentMembersWithHoneypotRole.size();
CompletableFutureList<Void> futureList = new CompletableFutureList<>(futures);

View File

@@ -0,0 +1,61 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.moderation.config.feature.HoneyPotFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.feature.mode.HoneypotMode;
import dev.sheldan.abstracto.moderation.service.HoneyPotServiceBean;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class HoneyPotMessageListener implements AsyncMessageReceivedListener {
@Autowired
private ConfigService configService;
@Autowired
private HoneyPotServiceBean honeyPotServiceBean;
@Autowired
private MessageService messageService;
@Override
public DefaultListenerResult execute(MessageReceivedModel model) {
Long honeyPotChannel = configService.getLongValueOrConfigDefault(HoneyPotFeatureConfig.HONEYPOT_CHANNEL, model.getServerId());
if(honeyPotChannel == 0) {
return DefaultListenerResult.IGNORED;
}
if(model.getMessage().getChannelId().equals(honeyPotChannel.toString())) {
boolean honeyPotActivated = honeyPotServiceBean.fellIntoHoneyPotIgnoringJoinDate(model.getServerId(), model.getMessage().getMember());
if(honeyPotActivated) {
log.info("Banning user {} in guild {} due to a message in channel {}.", model.getMessage().getAuthor().getIdLong(), model.getServerId(), honeyPotChannel);
honeyPotServiceBean.banForHoneyPotMessage(model.getMessage().getMember(), honeyPotChannel);
} else {
log.info("NOT banning user {} in guild {} due to a message in honeypot channel {}.", model.getMessage().getAuthor().getIdLong(), model.getServerId(), honeyPotChannel);
messageService.deleteMessage(model.getMessage());
}
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.HONEYPOT;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return List.of(HoneypotMode.MESSAGE);
}
}

View File

@@ -1,12 +1,15 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.sync.jda.RoleAddedListener;
import dev.sheldan.abstracto.core.models.listener.RoleAddedModel;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.feature.mode.HoneypotMode;
import dev.sheldan.abstracto.moderation.service.HoneyPotServiceBean;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -37,7 +40,7 @@ public class HoneyPotRoleAddedListener implements RoleAddedListener {
boolean fellIntoHoneyPot = honeyPotServiceBean.fellIntoHoneyPot(model.getServerId(), model.getTargetMember());
if (fellIntoHoneyPot) {
log.info("Banning user {} in guild {} due to role {}.", model.getTargetUser().getUserId(), model.getTargetUser().getServerId(), model.getRoleId());
honeyPotServiceBean.banForHoneyPot(model.getTargetMember(), model.getRole());
honeyPotServiceBean.banForHoneyPotRole(model.getTargetMember(), model.getRole());
} else {
log.info("User {} in server {} will not get banned by honeypot. All existing roles besides {} will be removed.",
model.getTargetUser().getUserId(), model.getTargetUser().getServerId(), honeyPotRoleId);
@@ -83,4 +86,8 @@ public class HoneyPotRoleAddedListener implements RoleAddedListener {
return ListenerPriority.MEDIUM;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return List.of(HoneypotMode.ROLE);
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.moderation.listener;
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.ServerUser;
@@ -13,7 +13,6 @@ import dev.sheldan.abstracto.core.service.management.UserInServerManagementServi
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.config.feature.HoneyPotFeatureConfig;
import dev.sheldan.abstracto.moderation.model.listener.HoneyPotReasonModel;
import dev.sheldan.abstracto.moderation.service.BanService;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -68,11 +67,33 @@ public class HoneyPotServiceBean {
return !allowed;
}
public boolean fellIntoHoneyPotIgnoringJoinDate(Long serverId, Member member) {
Integer levelToSkipBan = configService.getLongValueOrConfigDefault(HoneyPotFeatureConfig.HONEYPOT_IGNORED_LEVEL, serverId).intValue();
boolean allowed = userHasLevel(member, levelToSkipBan);
return !allowed;
}
public List<Member> getCurrentMembersWithHoneypotRole(Guild guild) {
return memberService.getMembersWithRole(guild.getIdLong(), getHoneyPotRoleId(guild.getIdLong()));
}
public CompletableFuture<Void> banForHoneyPot(Member targetMember, Role role) {
public CompletableFuture<Void> banForHoneyPotMessage(Member targetMember, Long channelId) {
HoneyPotReasonModel reasonModel = HoneyPotReasonModel
.builder()
.memberDisplay(MemberDisplay.fromMember(targetMember))
.build();
ServerUser bannedUser = ServerUser.fromMember(targetMember);
String banReason = templateService.renderTemplate(HONEYPOT_BAN_REASON_TEMPLATE, reasonModel, bannedUser.getServerId());
return banService.banUserWithNotification(bannedUser, banReason, ServerUser.fromMember(targetMember.getGuild().getSelfMember()),
targetMember.getGuild(), Duration.ofDays(7)).thenAccept(banResult -> {
log.info("Banned user {} in guild {} due to a message in channel {}.", bannedUser.getUserId(), bannedUser.getServerId(), channelId);
}).exceptionally(throwable -> {
log.error("Failed to ban user {} in guild {} due to a message in channel {}.", bannedUser.getUserId(), bannedUser.getServerId(), channelId, throwable);
return null;
});
}
public CompletableFuture<Void> banForHoneyPotRole(Member targetMember, Role role) {
HoneyPotReasonModel reasonModel = HoneyPotReasonModel
.builder()
.memberDisplay(MemberDisplay.fromMember(targetMember))
@@ -91,7 +112,7 @@ public class HoneyPotServiceBean {
}
private boolean userHasLevel(Member member, Integer level) {
log.info("Checking if member {} is ignored to click on the honeypot in server {}.", member.getIdLong(),member.getGuild().getIdLong());
log.info("Checking if member {} is ignored by the honeypot in server {}.", member.getIdLong(),member.getGuild().getIdLong());
Map<String, Object> parameters = new HashMap<>();
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
parameters.put(LEVEL_CONDITION_USER_ID_PARAMETER, userInAServer.getUserInServerId());

View File

@@ -99,3 +99,14 @@ abstracto.systemConfigs.honeypotIgnoredJoinDurationSeconds.longValue=86400
abstracto.featureFlags.honeypot.featureName=honeypot
abstracto.featureFlags.honeypot.enabled=false
abstracto.featureModes.honeypotRole.featureName=honeypot
abstracto.featureModes.honeypotRole.mode=honeypotRole
abstracto.featureModes.honeypotRole.enabled=false
abstracto.featureModes.honeypotMessage.featureName=honeypot
abstracto.featureModes.honeypotMessage.mode=honeypotMessage
abstracto.featureModes.honeypotMessage.enabled=false
abstracto.systemConfigs.honeypotChannel.name=honeypotChannel
abstracto.systemConfigs.honeypotChannel.longValue=0

View File

@@ -2,6 +2,8 @@ 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.FeatureMode;
import dev.sheldan.abstracto.moderation.config.feature.mode.HoneypotMode;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@@ -12,6 +14,7 @@ public class HoneyPotFeatureConfig implements FeatureConfig {
public static final String HONEYPOT_ROLE_ID = "honeypotRoleId";
public static final String HONEYPOT_IGNORED_LEVEL = "honeypotIgnoredLevel";
public static final String HONEYPOT_CHANNEL = "honeypotChannel";
public static final String HONEYPOT_IGNORED_JOIN_DURATION_SECONDS = "honeypotIgnoredJoinDurationSeconds";
@Override
@@ -21,6 +24,11 @@ public class HoneyPotFeatureConfig implements FeatureConfig {
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(HONEYPOT_ROLE_ID, HONEYPOT_IGNORED_LEVEL, HONEYPOT_IGNORED_JOIN_DURATION_SECONDS);
return Arrays.asList(HONEYPOT_ROLE_ID, HONEYPOT_IGNORED_LEVEL, HONEYPOT_IGNORED_JOIN_DURATION_SECONDS, HONEYPOT_CHANNEL);
}
@Override
public List<FeatureMode> getAvailableModes() {
return Arrays.asList(HoneypotMode.values());
}
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.moderation.config.feature.mode;
import dev.sheldan.abstracto.core.config.FeatureMode;
import lombok.Getter;
@Getter
public enum HoneypotMode implements FeatureMode {
ROLE("honeypotRole"),
MESSAGE("honeypotMessage");
private final String key;
HoneypotMode(String key) {
this.key = key;
}
}