Skip to content

Commit

Permalink
[FEAT] Implement basic message spam detector
Browse files Browse the repository at this point in the history
  • Loading branch information
hypherionmc committed Jan 12, 2025
1 parent 3ae6cc7 commit b96963a
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ public void sendMessage() {
if (message.isEmpty())
return;

BotController.INSTANCE.getSpamManager().receiveMessage(message);

if (BotController.INSTANCE.getSpamManager().isBlocked(message)) {
if (SDLinkConfig.INSTANCE.generalConfig.debugging)
BotController.INSTANCE.getLogger().warn("Blocked message {} due to spam", message);

return;
}

try {
if (messageType == MessageType.CONSOLE) {
sendConsoleMessage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import com.hypherionmc.sdlink.core.discord.commands.CommandManager;
import com.hypherionmc.sdlink.core.discord.events.DiscordEventHandler;
import com.hypherionmc.sdlink.core.editor.ConfigEditorClient;
import com.hypherionmc.sdlink.core.managers.DatabaseManager;
import com.hypherionmc.sdlink.core.managers.EmbedManager;
import com.hypherionmc.sdlink.core.managers.HiddenPlayersManager;
import com.hypherionmc.sdlink.core.managers.WebhookManager;
import com.hypherionmc.sdlink.core.managers.*;
import com.hypherionmc.sdlink.util.EncryptionUtil;
import com.hypherionmc.sdlink.util.ThreadedEventManager;
import com.jagrosh.jdautilities.command.CommandClient;
Expand All @@ -31,6 +28,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* @author HypherionSA
Expand All @@ -40,7 +38,7 @@ public final class BotController {

// Thread Execution Manager
public final ExecutorService taskManager = Executors.newCachedThreadPool();
public final ScheduledExecutorService updatesManager = Executors.newScheduledThreadPool(2);
public final ScheduledExecutorService updatesManager = Executors.newScheduledThreadPool(4);

// Public instance of this class that can be called anywhere
public static BotController INSTANCE;
Expand All @@ -50,6 +48,9 @@ public final class BotController {
@Getter
private final Logger logger;

@Getter
private final SpamManager spamManager;

// Required Variables
private JDA _jda;
private boolean shutdownCalled = false;
Expand Down Expand Up @@ -81,6 +82,9 @@ private BotController(Logger logger, boolean wasReload) {

// Initialize Hidden players
HiddenPlayersManager.INSTANCE.loadHiddenPlayers();

// Initialize spam detector with the following limits: 5 Messages in 2 seconds, with an expiration of 2 minutes
spamManager = new SpamManager(5, 2000, 120000, updatesManager);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.hypherionmc.sdlink.core.managers;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* @author HypherionSA
* Basic Message Spam Detector
*/
public class SpamManager {

private final ConcurrentHashMap<String, List<Long>> messageTimestamps = new ConcurrentHashMap<>();
private final Set<String> blockedMessages = new HashSet<>();

private final int threshold;
private final int timeWindowMillis;
private final int blockMillis;
private final ScheduledExecutorService executor;

public SpamManager(int threshold, int timeWindowMillis, int blockMillis, ScheduledExecutorService executor) {
this.threshold = threshold;
this.timeWindowMillis = timeWindowMillis;
this.blockMillis = blockMillis;
this.executor = executor;
startSpamChecker();
}

public void receiveMessage(String message) {
long currentTime = System.currentTimeMillis();

messageTimestamps.compute(message, (msg, timestamps) -> {
if (timestamps == null)
timestamps = new ArrayList<>();

timestamps.add(currentTime);
return timestamps.stream()
.filter(timestamp -> currentTime - timestamp <= timeWindowMillis)
.toList();
});

if (messageTimestamps.get(message).size() >= threshold)
blockedMessages.add(message);
}

public boolean isBlocked(String message) {
return blockedMessages.contains(message);
}

private void startSpamChecker() {
executor.scheduleAtFixedRate(() -> {
long currentTime = System.currentTimeMillis();
blockedMessages.removeIf(message -> {
List<Long> timestamps = messageTimestamps.getOrDefault(message, new ArrayList<>());
timestamps = timestamps.stream()
.filter(timestamp -> currentTime - timestamp <= timeWindowMillis)
.toList();
messageTimestamps.put(message, timestamps);
return timestamps.size() < threshold;
});
}, blockMillis, blockMillis, TimeUnit.MILLISECONDS);
}

}

0 comments on commit b96963a

Please sign in to comment.