Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 0
Spam detection#14
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Spam detection #14
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -9,11 +9,17 @@ | ||
| import com.zaxxer.hikari.HikariDataSource; | ||
| import net.javadiscord.javabot2.command.SlashCommandListener; | ||
| import net.javadiscord.javabot2.config.BotConfig; | ||
| import net.javadiscord.javabot2.systems.moderation.SpamListener; | ||
| import net.javadiscord.javabot2.systems.moderation.MessageCache; | ||
| import org.javacord.api.DiscordApi; | ||
| import org.javacord.api.DiscordApiBuilder; | ||
| import org.javacord.api.entity.intent.Intent; | ||
| import org.javacord.api.entity.message.Message; | ||
| import org.javacord.api.entity.message.MessageAuthor; | ||
| import java.nio.file.Path; | ||
| import java.util.LinkedList; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.concurrent.Executors; | ||
| import java.util.concurrent.ScheduledExecutorService; | ||
| @@ -46,6 +52,11 @@ public class Bot { | ||
| */ | ||
| public static ScheduledExecutorService asyncPool; | ||
| /** | ||
| * The message cache. | ||
| */ | ||
| public static ConcurrentHashMap<MessageAuthor, LinkedList<Message>> messageCache; | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // Hide constructor. | ||
| private Bot() {} | ||
| @@ -60,8 +71,12 @@ public static void main(String[] args) { | ||
| .setToken(config.getSystems().getDiscordBotToken()) | ||
| .setAllIntentsExcept(Intent.GUILD_MESSAGE_TYPING, Intent.GUILD_PRESENCES, Intent.GUILD_VOICE_STATES) | ||
| .login().join(); | ||
| initListeners(api); | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| config.loadGuilds(api.getServers()); // Once we've logged in, load all guild config files. | ||
| config.flush(); // Flush to save any new config files that are generated for new guilds. | ||
| SlashCommandListener commandListener = new SlashCommandListener( | ||
| api, | ||
| args.length > 0 && args[0].equalsIgnoreCase("--register-commands"), | ||
| @@ -70,6 +85,18 @@ public static void main(String[] args) { | ||
| api.addSlashCommandCreateListener(commandListener); | ||
| } | ||
| /** | ||
| * Initializes and adds all listeners to the API. | ||
| * @param api the API | ||
| */ | ||
| private static void initListeners(DiscordApi api) { | ||
| MessageCache cache = new MessageCache(); | ||
| messageCache = cache.getCache(); | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| api.addMessageCreateListener(new SpamListener()); | ||
| api.addMessageCreateListener(cache); | ||
| } | ||
| /** | ||
| * Initializes all the basic data sources that are needed by the bot's other | ||
| * capabilities. This should be called <strong>before</strong> logging in | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -16,6 +16,31 @@ public class ModerationConfig extends GuildConfigItem { | ||
| */ | ||
| private long staffRoleId; | ||
| /** | ||
| * The amount of seconds to be looked into the past to determine if a user is spamming. | ||
| */ | ||
| private int pastMessageCountBeforeDurationInSeconds; | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /** | ||
| * The amount of messages to be sent within {@link #pastMessageCountBeforeDurationInSeconds}. | ||
| */ | ||
| private int messageSpamAmount; | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /** | ||
| * The amount of messages to be cached for each user. | ||
| */ | ||
| private int cachedMessagesPerUser; | ||
| /** | ||
| * The frequency of cleaning up the cached messages. Amount in minutes. | ||
| */ | ||
| private int cachedMessageCleanupFrequency; | ||
| /** | ||
| * The amount of minutes for removing this cached messages. | ||
| */ | ||
| private int amountOfMinutesForRemoval; | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| public Role getStaffRole() { | ||
| return this.getGuild().getRoleById(staffRoleId).orElseThrow(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package net.javadiscord.javabot2.systems.moderation; | ||
| import net.javadiscord.javabot2.Bot; | ||
| import org.javacord.api.entity.message.Message; | ||
| import org.javacord.api.entity.message.MessageAuthor; | ||
| import org.javacord.api.event.message.MessageCreateEvent; | ||
| import org.javacord.api.listener.message.MessageCreateListener; | ||
| import java.time.Duration; | ||
| import java.time.Instant; | ||
| import java.util.LinkedList; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.concurrent.TimeUnit; | ||
| /** | ||
| * Caches incoming messages and cleans it out when needed. | ||
| */ | ||
| public class MessageCache implements MessageCreateListener { | ||
| private final ConcurrentHashMap<MessageAuthor, Instant> lastModifications; | ||
| private final ConcurrentHashMap<MessageAuthor, LinkedList<Message>> cache; | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /** | ||
| * Creates the cache. | ||
| */ | ||
| public MessageCache() { | ||
| lastModifications = new ConcurrentHashMap<>(); | ||
| cache = new ConcurrentHashMap<>(); | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // TODO config | ||
| Bot.asyncPool.scheduleAtFixedRate(this::clean, -1, -1, TimeUnit.MINUTES); | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't do this here; I would never expect constructing an object to begin an irreversible scheduled task. | ||
| } | ||
| @Override | ||
| public void onMessageCreate(MessageCreateEvent event) { | ||
| if (!event.getMessageAuthor().isYourself()) { | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| add(event.getMessage()); | ||
| } | ||
| } | ||
| /** | ||
| * Removes all Map entries which have not been modified within the last 10 minutes. | ||
| */ | ||
| private void clean() { | ||
| lastModifications.forEach((messageAuthor, instant) -> { | ||
| // if last modification is older than n minutes | ||
| // TODO config | ||
| if (instant.isAfter(Instant.now().minus(Duration.ofMinutes(-1)))) { | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| cache.remove(messageAuthor); | ||
| lastModifications.remove(messageAuthor); | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Add the message either as a new Map entry or to the existing list per user. | ||
| * Also removes if the amount of messages per user is over a certain treshold. | ||
| * @param msg the message to be added | ||
| */ | ||
| private void add(Message msg) { | ||
| if (cache.containsKey(msg.getAuthor())) { | ||
| LinkedList<Message> msgList = cache.get(msg.getAuthor()); | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| msgList.offer(msg); | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| lastModifications.replace(msg.getAuthor(), Instant.now()); | ||
| // TODO config | ||
| if (msgList.size() >= -1) { | ||
| msgList.poll(); | ||
| } | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } else { | ||
| LinkedList<Message> messages = new LinkedList<>(); | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| messages.add(msg); | ||
| cache.put(msg.getAuthor(), messages); | ||
| lastModifications.put(msg.getAuthor(), Instant.now()); | ||
| } | ||
| } | ||
| public ConcurrentHashMap<MessageAuthor, LinkedList<Message>> getCache() { | ||
| return cache; | ||
| } | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package net.javadiscord.javabot2.systems.moderation; | ||
| import net.javadiscord.javabot2.Bot; | ||
| import org.javacord.api.event.message.MessageCreateEvent; | ||
| import org.javacord.api.listener.message.MessageCreateListener; | ||
| import java.time.Duration; | ||
| import java.time.Instant; | ||
| /** | ||
| * Listens for spam using the {@link MessageCache}. | ||
| */ | ||
| public class SpamListener implements MessageCreateListener { | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| @Override | ||
| public void onMessageCreate(MessageCreateEvent event) { | ||
| if (!event.getMessageAuthor().isYourself() && Bot.messageCache.containsKey(event.getMessageAuthor())) { | ||
cbrt-x marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| int amountOfMessages = (int) Bot.messageCache.get(event.getMessageAuthor()) | ||
| .stream() | ||
| //TODO get time from config | ||
| .filter(msg -> msg.getCreationTimestamp().isAfter(Instant.now().minus(Duration.ofSeconds(-1)))) | ||
| .count(); | ||
| //TODO get amount from config | ||
| if (amountOfMessages >= -1) { | ||
| //TODO spam detected | ||
| // placeholder for checkstyle not to complain. | ||
| // should be replaced with a warn + purge which is not implemented yet | ||
| event.deleteMessage("spam"); | ||
| } | ||
Comment on lines
+18
to
+30
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Address these TODOs | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.