overhaul automod: fix dead toggles, expose hidden settings, add new filters
Link-filter and spam-filter toggles in the dashboard wrote to config keys the backend never read, so both filters always ran regardless of the switch. Fixed by reading the same field names on both sides. Also surfaces backend features that already existed but had no UI: caps filter, a custom bad-word list, a role whitelist (members with these roles are ignored entirely), and tunable spam thresholds/window/timeout. Each filter now shows its relevant settings inline instead of hiding them in a separate generic panel. Adds two new detections: an invite-link filter separate from the general link filter, and mass-mention/raid protection with a configurable limit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,17 @@ export interface AutomodConfig {
|
||||
windowMs?: number;
|
||||
linkWhitelist?: string[];
|
||||
spamTimeoutMinutes?: number;
|
||||
/** @deprecated use linkFilter */
|
||||
deleteLinks?: boolean;
|
||||
linkFilter?: boolean;
|
||||
inviteFilter?: boolean;
|
||||
spamFilter?: boolean;
|
||||
badWordFilter?: boolean;
|
||||
capsFilter?: boolean;
|
||||
customBadwords?: string[];
|
||||
whitelistRoles?: string[];
|
||||
mentionSpamFilter?: boolean;
|
||||
maxMentions?: number;
|
||||
logChannelId?: string;
|
||||
loggingConfig?: {
|
||||
logChannelId?: string;
|
||||
@@ -31,13 +37,18 @@ export class AutoModService {
|
||||
windowMs: 7000,
|
||||
linkWhitelist: [],
|
||||
spamTimeoutMinutes: 10,
|
||||
deleteLinks: true,
|
||||
linkFilter: true,
|
||||
inviteFilter: true,
|
||||
spamFilter: true,
|
||||
badWordFilter: true,
|
||||
capsFilter: false,
|
||||
customBadwords: [],
|
||||
whitelistRoles: []
|
||||
whitelistRoles: [],
|
||||
mentionSpamFilter: true,
|
||||
maxMentions: 5
|
||||
};
|
||||
private defaultBadwords = ['badword', 'spamword'];
|
||||
private inviteRegex = /(discord\.gg\/|discord(?:app)?\.com\/invite\/)[a-z0-9-]+/i;
|
||||
|
||||
constructor(private logging?: LoggingService, private linkFilterEnabled = true, private antiSpamEnabled = true) {}
|
||||
|
||||
@@ -53,7 +64,27 @@ export class AutoModService {
|
||||
if (allowed) return;
|
||||
}
|
||||
|
||||
if (this.linkFilterEnabled && config.deleteLinks !== false && this.containsLink(message.content, config.linkWhitelist)) {
|
||||
if (config.mentionSpamFilter !== false && this.exceedsMentionLimit(message, config.maxMentions)) {
|
||||
const count = message.mentions.users.size + message.mentions.roles.size;
|
||||
const limit = config.maxMentions ?? this.defaults.maxMentions!;
|
||||
const timeoutMs = (config.spamTimeoutMinutes ?? this.defaults.spamTimeoutMinutes!) * 60 * 1000;
|
||||
message.member?.timeout(timeoutMs, 'Automod: Mass-Mention').catch(() => undefined);
|
||||
await this.deleteMessageWithReason(message, `${message.author}, bitte nicht so viele User/Rollen auf einmal erwähnen.`);
|
||||
logger.warn(`Timed out ${message.author.tag} for mass-mention`);
|
||||
await this.logAutomodAction(message, config, 'mention_spam', `${count}/${limit} Erwähnungen in einer Nachricht`);
|
||||
return true;
|
||||
}
|
||||
|
||||
const linkFilterOn = config.linkFilter ?? config.deleteLinks ?? true;
|
||||
const linkMatch = this.matchLink(message.content);
|
||||
if (linkMatch && config.inviteFilter !== false && this.inviteRegex.test(linkMatch)) {
|
||||
await this.deleteMessageWithReason(message, `${message.author}, Einladungslinks sind hier nicht erlaubt.`);
|
||||
logger.info(`Deleted invite link from ${message.author.tag}`);
|
||||
await this.logAutomodAction(message, config, 'invite_filter', 'Discord-Einladungslink erkannt', message.content);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.linkFilterEnabled && linkFilterOn && linkMatch && !this.isWhitelisted(linkMatch, config.linkWhitelist)) {
|
||||
await this.deleteMessageWithReason(message, `${message.author}, Links sind hier nicht erlaubt.`);
|
||||
const reason = `Link gefunden (nicht freigegeben)${config.linkWhitelist?.length ? ` | Whitelist: ${config.linkWhitelist.join(', ')}` : ''}`;
|
||||
logger.info(`Deleted link from ${message.author.tag}`);
|
||||
@@ -78,7 +109,8 @@ export class AutoModService {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.antiSpamEnabled) {
|
||||
const spamFilterOn = config.spamFilter ?? true;
|
||||
if (this.antiSpamEnabled && spamFilterOn) {
|
||||
const now = Date.now();
|
||||
const tracker = this.spamTracker.get(message.author.id) ?? { count: 0, lastMessage: now };
|
||||
if (now - tracker.lastMessage < (config.windowMs ?? this.windowMs)) {
|
||||
@@ -104,6 +136,12 @@ export class AutoModService {
|
||||
return false;
|
||||
}
|
||||
|
||||
private exceedsMentionLimit(message: Message, maxMentions?: number) {
|
||||
const limit = maxMentions ?? this.defaults.maxMentions!;
|
||||
const count = message.mentions.users.size + message.mentions.roles.size;
|
||||
return count >= limit;
|
||||
}
|
||||
|
||||
private containsBadword(content: string, custom: string[] = []) {
|
||||
const combined = [...this.defaultBadwords, ...(custom || [])]
|
||||
.map((w) => w?.toString().trim().toLowerCase())
|
||||
@@ -118,13 +156,15 @@ export class AutoModService {
|
||||
});
|
||||
}
|
||||
|
||||
private containsLink(content: string, whitelist: string[] = []) {
|
||||
const normalized = whitelist.map((w) => w.toLowerCase()).filter(Boolean);
|
||||
private matchLink(content: string): string | null {
|
||||
// Match common link formats, even without protocol
|
||||
const match = /(https?:\/\/[^\s]+|discord\.gg\/[^\s]+|www\.[^\s]+|[a-z0-9.-]+\.[a-z]{2,}\/?[^\s]*)/i.exec(content);
|
||||
if (!match) return false;
|
||||
const url = match[0].toLowerCase();
|
||||
return !normalized.some((w) => url.includes(w));
|
||||
return match ? match[0].toLowerCase() : null;
|
||||
}
|
||||
|
||||
private isWhitelisted(url: string, whitelist: string[] = []) {
|
||||
const normalized = whitelist.map((w) => w.toLowerCase()).filter(Boolean);
|
||||
return normalized.some((w) => url.includes(w));
|
||||
}
|
||||
|
||||
private async deleteMessageWithReason(message: Message, response: string) {
|
||||
|
||||
Reference in New Issue
Block a user