Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ public class ModerationConfig extends GuildConfigItem {
private long adminRoleId = 0;
private long expertRoleId = 0;

/**
* The time window, in seconds, that the cross-channel spam automod looks back over when
* counting a user's recent messages. If this is {@code 0}, the cross-channel spam automod
* is disabled.
*/
private int crossChannelSpamWindowSeconds = 0;

/**
* The number of distinct channels a user must post in within
* {@link #crossChannelSpamWindowSeconds} seconds before the cross-channel spam automod acts.
*/
private int crossChannelSpamMinChannels = 3;
/**
* ID of the share-knowledge channel.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public void onMessageUpdate(@NotNull MessageUpdateEvent event) {
CachedMessage before;
if (optional.isPresent()) {
CachedMessage inCache= optional.get();
before = new CachedMessage(inCache.getMessageId(), inCache.getAuthorId(), inCache.getMessageContent(), inCache.getAttachments());
before = new CachedMessage(inCache.getMessageId(), inCache.getAuthorId(), inCache.getChannelId(),inCache.getMessageContent(), inCache.getAttachments());
inCache.init(event.getMessage());
} else {
before = new CachedMessage(event.getMessageIdLong(), event.getAuthor().getIdLong(), "[unknown content]", List.of());
before = new CachedMessage(event.getMessageIdLong(), event.getAuthor().getIdLong(), event.getChannel().getIdLong(),"[unknown content]", List.of());
messageCache.cache(event.getMessage());
}
messageCache.sendUpdatedMessageToLog(event.getMessage(), before);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public List<CachedMessage> getAll() throws DataAccessException {
messages.merge(msg.getMessageId(), msg, (oldValue, value) -> {
ArrayList<String> attachments = new ArrayList<>(oldValue.getAttachments());
attachments.addAll(value.getAttachments());
return new CachedMessage(oldValue.getMessageId(), oldValue.getAuthorId(), oldValue.getMessageContent(), attachments);
return new CachedMessage(oldValue.getMessageId(), oldValue.getAuthorId(), oldValue.getChannelId(),oldValue.getMessageContent(), attachments);
});
}
return new ArrayList<>(messages.values());
Expand Down Expand Up @@ -119,6 +119,7 @@ private CachedMessage read(ResultSet rs) throws SQLException {
return new CachedMessage(
rs.getLong("message_cache.message_id"),
rs.getLong("author_id"),
rs.getLong("channel_id"),
rs.getString("message_content"),
attachments);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@
public class CachedMessage {
private final long messageId;
private final long authorId;
private final long channelId;
private String messageContent;
private List<String> attachments=new ArrayList<>();

private CachedMessage(long messageId, long authorId) {
private CachedMessage(long messageId, long authorId,long channelId) {
this.messageId = messageId;
this.authorId = authorId;
this.channelId = channelId;
}

/**
* Creates a {@link CachedMessage} with the given information.
* @param messageId The Discord ID of the message
* @param authorId The Discord ID of the message author
* @param channelId The Discord ID of the message channel
* @param messageContent the textual content of the message
* @param attachments The attachment URLs
*/
public CachedMessage(long messageId, long authorId, String messageContent, List<String> attachments) {
public CachedMessage(long messageId, long authorId, long channelId, String messageContent, List<String> attachments) {
super();
this.messageId = messageId;
this.authorId = authorId;
this.channelId = channelId;
this.messageContent = messageContent;
this.attachments = List.copyOf(attachments);
}
Expand All @@ -49,7 +53,7 @@ public CachedMessage(long messageId, long authorId, String messageContent, List<
* @return The built {@link CachedMessage}.
*/
public static CachedMessage of(Message message) {
CachedMessage cachedMessage = new CachedMessage(message.getIdLong(), message.getAuthor().getIdLong());
CachedMessage cachedMessage = new CachedMessage(message.getIdLong(), message.getAuthor().getIdLong(),message.getChannelIdLong());
cachedMessage.init(message);
return cachedMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import lombok.extern.slf4j.Slf4j;
import net.discordjug.javabot.data.config.BotConfig;
import net.discordjug.javabot.data.h2db.message_cache.MessageCache;
import net.discordjug.javabot.data.h2db.message_cache.model.CachedMessage;
import net.discordjug.javabot.systems.moderation.warn.model.WarnSeverity;
import net.discordjug.javabot.systems.notification.NotificationService;
import net.discordjug.javabot.util.ExceptionLogger;
import net.discordjug.javabot.util.MessageUtils;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
Expand All @@ -24,6 +26,7 @@
import java.net.URL;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
Expand All @@ -50,7 +53,7 @@ public class AutoMod extends ListenerAdapter {
private final MessageCache messageCache;

/**
* Constructor of the class, that creates a list of strings with potential spam/scam urls.
* Constructor of the class, that creates a list of strings with potential spam/scam URLs.
* @param notificationService The {@link QOTWPointsService}
* @param botConfig The main configuration of the bot
* @param moderationService Service object for moderating members
Expand Down Expand Up @@ -108,18 +111,33 @@ private void checkNewMessageAutomod(@Nonnull Message message) {
.stream()
.filter(cached -> cached.getMessageId() != message.getIdLong()) // exclude new/current message
.filter(cached -> cached.getAuthorId() == message.getAuthor().getIdLong())
.filter(cached ->
.filter(cached ->
// only java files -> not spam
cached.getAttachments().isEmpty() ||
cached.getAttachments().isEmpty() ||
cached.getAttachments().stream()
.anyMatch(attachment -> !attachment.contains(".java?")))
.count() + 1; // include new message

if (spamCount >= 5) {
handleSpam(message, message.getMember());
}

checkContentAutomod(message);
checkCrossChannelSpam(message);
}

private void checkCrossChannelSpam(@Nonnull Message message) {
List<CachedMessage> spamMessages = new ArrayList<>();
messageCache.getMessagesAfter(message.getTimeCreated().minusSeconds((botConfig.get(message.getGuild()).getModerationConfig()).getCrossChannelSpamWindowSeconds())).stream()
.filter(cachedMessage -> cachedMessage.getAuthorId() == message.getAuthor().getIdLong())
.filter(cachedMessage -> spamMessages.stream().noneMatch(
spamMessage -> spamMessage.getChannelId() == cachedMessage.getChannelId()))
.forEach(spamMessages::add);

if(spamMessages.size() > (botConfig.get(message.getGuild()).getModerationConfig()).getCrossChannelSpamMinChannels()){
handleSpam(spamMessages,message);
}

}

/**
Expand Down Expand Up @@ -174,6 +192,22 @@ private void handleSpam(@Nonnull Message msg, Member member) {
msg.delete().queue();
}

private void handleSpam(@Nonnull List<CachedMessage> cachedMessages, Message message) {
Guild guild = message.getGuild();
moderationService
.timeout(
message.getAuthor(),
"Automod: Spam",
message.getGuild().getSelfMember(),
Duration.of(6, ChronoUnit.HOURS),
message.getChannel(),
false
);

cachedMessages.forEach(cachedMessage -> guild.getTextChannelById(cachedMessage.getChannelId())
.deleteMessageById(cachedMessage.getMessageId()).queue());
}

/**
* returns the original String cleaned up of unused code points and spaces.
*
Expand Down Expand Up @@ -218,7 +252,7 @@ public boolean hasSuspiciousLink(@NotNull Message message) {
* Checks whether the given message contains a discord invite link.
*
* @param message The Message to check.
* @return True if an invite is found and False if not.
* @return True if an invitation is found and False if not.
*/
public boolean hasAdvertisingLink(@NotNull Message message) {
// Advertising
Expand All @@ -237,5 +271,5 @@ private boolean isSuggestionsChannel(@NotNull MessageChannelUnion channel) {
return channel.getType().isGuild() &&
channel.getIdLong() == botConfig.get(channel.asGuildMessageChannel().getGuild()).getModerationConfig().getSuggestionChannel().getIdLong();
}

}