Skip to content

delete blacklisted attachments from message and replace with webhook #520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 24, 2025
Merged
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 @@ -18,6 +18,7 @@
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

/**
Expand All @@ -37,6 +38,7 @@ public class GuildConfig {
private StarboardConfig starboardConfig;
private MessageCacheConfig messageCacheConfig;
private ServerLockConfig serverLockConfig;
private List<String> blacklistedMessageExtensions = List.of("jar", "exe", "zip");

/**
* Constructor that initializes all Config classes.
Expand Down
120 changes: 0 additions & 120 deletions src/main/java/net/discordjug/javabot/listener/HugListener.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.discordjug.javabot.listener.filter;

import lombok.RequiredArgsConstructor;
import net.discordjug.javabot.data.config.BotConfig;
import net.discordjug.javabot.data.config.GuildConfig;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* This {@link MessageFilter} blocks attachments blacklisted using {@link GuildConfig}.
*/
@Component
@RequiredArgsConstructor
public class BlacklistedMessageAttachmentFilter implements MessageFilter {

private final BotConfig botConfig;

@Override
public MessageModificationStatus processMessage(MessageContent content) {
MessageReceivedEvent event = content.event();
List<Message.Attachment> attachments = content.attachments();
List<MessageEmbed> embeds = content.embeds();
GuildConfig guildConfig = botConfig.get(event.getGuild());
List<String> blacklistedMessageExtensions = guildConfig.getBlacklistedMessageExtensions();
boolean removed = attachments.removeIf(attachment -> blacklistedMessageExtensions.contains(attachment.getFileExtension()));
if (removed) {
MessageEmbed attachmentRemovedInfo = new EmbedBuilder()
.setDescription("Disallowed attachments have been removed from this message.")
.build();
embeds.add(attachmentRemovedInfo);
return MessageModificationStatus.MODIFIED;
} else {
return MessageModificationStatus.NOT_MODIFIED;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package net.discordjug.javabot.listener.filter;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.regex.Pattern;

/**
* This {@link MessageFilter} replaces all occurrences of 'fuck' in incoming messages with 'hug'.
*/
@Component
@RequiredArgsConstructor
public class HugFilter implements MessageFilter {

private static final Pattern FUCKER = Pattern.compile(
"(fuck)(ing|er|ed|k+)?",
Pattern.CASE_INSENSITIVE
);

@Override
public MessageModificationStatus processMessage(MessageContent content) {
if (!content.event().getMessage().getMentions().getUsers().isEmpty()) {
return MessageModificationStatus.NOT_MODIFIED;
}
String before = content.messageText().toString();
String processed = replaceFucks(content.messageText().toString());
if (!before.equals(processed)) {
content.messageText().setLength(0);
content.messageText().append(processed);
return MessageModificationStatus.MODIFIED;
} else {
return MessageModificationStatus.NOT_MODIFIED;
}
}

private static String processHug(String originalText) {
// FucK -> HuG, FuCk -> Hug
return String.valueOf(copyCase(originalText, 0, 'h')) + copyCase(originalText, 1, 'u') +
copyCase(originalText, 3, 'g');
}

private static String replaceFucks(String str) {
return FUCKER.matcher(str).replaceAll(matchResult -> {
String theFuck = matchResult.group(1);
String suffix = Objects.requireNonNullElse(matchResult.group(2), "");
String processedSuffix = switch (suffix.toLowerCase()) {
case "er", "ed", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker, fucked
case "" -> ""; // just fuck
default -> copyCase(suffix, "g".repeat(suffix.length())); // fuckkkkk...
};
return processHug(theFuck) + processedSuffix;
});
}

private static String copyCase(String source, String toChange) {
if (source.length() != toChange.length()) {
throw new IllegalArgumentException("lengths differ");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < source.length(); i++) {
sb.append(copyCase(source, i, toChange.charAt(i)));
}
return sb.toString();
}

private static char copyCase(String original, int index, char newChar) {
if (Character.isUpperCase(original.charAt(index))) {
return Character.toUpperCase(newChar);
} else {
return newChar;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.discordjug.javabot.listener.filter;

import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;

import java.util.List;

/**
* A class containing modifiable content of a message that has been received.
* @param event The event associated with receiving the message
* @param messageText The text associated with the message
* @param attachments The attachments associated with the message
* @param embeds The embeds associated with the message
*/
public record MessageContent(MessageReceivedEvent event,
StringBuilder messageText,
List<Message.Attachment> attachments,
List<MessageEmbed> embeds) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.discordjug.javabot.listener.filter;

/**
* This interface is implemented by all message filters.
*
* The {@link MessageContent} is processed by every class implementing {@link MessageFilter}
* unless one of the filters returns {@link MessageModificationStatus#STOP_PROCESSING} which stops further filters from executing.
*/
public interface MessageFilter {

/**
* When a message is received, it is processed by the registered filters.
*
* @param content The content of the new message that will be reposted instead of the received message
* if at least one filter returns {@link MessageModificationStatus#MODIFIED}
* and no filter returns {@link MessageModificationStatus#STOP_PROCESSING}.
* This {@link MessageContent} is built up incrementally by the filters.
* @return the appropriate {@link MessageModificationStatus} based on the filter's processing.
* @see MessageFilterHandler
*/
MessageModificationStatus processMessage(MessageContent content);

}
Loading