Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/main/java/net/javadiscord/javabot2/Bot.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -46,6 +52,11 @@ public class Bot {
*/
public static ScheduledExecutorService asyncPool;

/**
* The message cache.
*/
public static ConcurrentHashMap<MessageAuthor, LinkedList<Message>> messageCache;
Comment thread
cbrt-x marked this conversation as resolved.
Comment thread
cbrt-x marked this conversation as resolved.

// Hide constructor.
private Bot() {}

Expand All @@ -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);

Comment thread
cbrt-x marked this conversation as resolved.
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"),
Expand All @@ -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();
Comment thread
cbrt-x marked this conversation as resolved.

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment thread
cbrt-x marked this conversation as resolved.

/**
* The amount of messages to be sent within {@link #pastMessageCountBeforeDurationInSeconds}.
*/
private int messageSpamAmount;
Comment thread
cbrt-x marked this conversation as resolved.

/**
* 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;

Comment thread
cbrt-x marked this conversation as resolved.
public Role getStaffRole() {
return this.getGuild().getRoleById(staffRoleId).orElseThrow();
}
Expand Down
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;
Comment thread
cbrt-x marked this conversation as resolved.

/**
* Creates the cache.
*/
public MessageCache() {
lastModifications = new ConcurrentHashMap<>();
cache = new ConcurrentHashMap<>();
Comment thread
cbrt-x marked this conversation as resolved.
// TODO config
Bot.asyncPool.scheduleAtFixedRate(this::clean, -1, -1, TimeUnit.MINUTES);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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()) {
Comment thread
cbrt-x marked this conversation as resolved.
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)))) {
Comment thread
cbrt-x marked this conversation as resolved.
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());
Comment thread
cbrt-x marked this conversation as resolved.
msgList.offer(msg);
Comment thread
cbrt-x marked this conversation as resolved.
lastModifications.replace(msg.getAuthor(), Instant.now());

// TODO config
if (msgList.size() >= -1) {
msgList.poll();
}
Comment thread
cbrt-x marked this conversation as resolved.

} else {
LinkedList<Message> messages = new LinkedList<>();
Comment thread
cbrt-x marked this conversation as resolved.
messages.add(msg);
cache.put(msg.getAuthor(), messages);
lastModifications.put(msg.getAuthor(), Instant.now());
}
}

public ConcurrentHashMap<MessageAuthor, LinkedList<Message>> getCache() {
return cache;
}
Comment thread
cbrt-x marked this conversation as resolved.
}

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 {
Comment thread
cbrt-x marked this conversation as resolved.

@Override
public void onMessageCreate(MessageCreateEvent event) {
if (!event.getMessageAuthor().isYourself() && Bot.messageCache.containsKey(event.getMessageAuthor())) {
Comment thread
cbrt-x marked this conversation as resolved.
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address these TODOs

}
}

}