Skip to content

Commit c2e206c

Browse files
committed
Make invite spam auto-ban limits configurable and add account age filter
- Extract hardcoded thresholds into InviteSpamConfig (maxViolations, maxChannels, violationWindowMs) - Add accountAgeDays setting to restrict auto-ban to recently joined members (0 = everyone)
1 parent cfe2839 commit c2e206c

1 file changed

Lines changed: 37 additions & 2 deletions

File tree

src/modules/moderation/discordInvitesMonitor.listener.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,49 @@ const invitePatterns = [
1818

1919
const whitelistDomains: string[] = []; // For any .gg domains that are not discord.gg
2020

21+
interface InviteSpamConfig {
22+
/** Max violations before auto-ban (default: 4) */
23+
maxViolations: number;
24+
/** Max unique channels before auto-ban (default: 3) */
25+
maxChannels: number;
26+
/** Time window in ms for tracking violations (default: 30s) */
27+
violationWindowMs: number;
28+
/** Only auto-ban accounts that joined the server less than X days ago. 0 = disabled (everyone is considered) */
29+
accountAgeDays: number;
30+
}
31+
32+
const inviteSpamConfig: InviteSpamConfig = {
33+
maxViolations: 4,
34+
maxChannels: 3,
35+
violationWindowMs: 30_000,
36+
accountAgeDays: 0,
37+
};
38+
2139
interface InviteViolation {
2240
count: number;
2341
channels: Set<string>;
2442
}
2543

26-
const inviteViolationCache = new ExpiryMap<string, InviteViolation>(30_000);
44+
const inviteViolationCache = new ExpiryMap<string, InviteViolation>(
45+
inviteSpamConfig.violationWindowMs,
46+
);
2747

2848
const isAllowedToSendDiscordInvites = async (member: GuildMember) => {
2949
const ddUser = await getOrCreateUserById(BigInt(member.id));
3050
return getTierByLevel(ddUser.level) >= 2;
3151
};
3252

53+
const isSubjectToAutoban = (member: GuildMember): boolean => {
54+
if (inviteSpamConfig.accountAgeDays === 0) return true;
55+
56+
const joinedAt = member.joinedAt;
57+
if (!joinedAt) return true;
58+
59+
const daysSinceJoin =
60+
(Date.now() - joinedAt.getTime()) / (1000 * 60 * 60 * 24);
61+
return daysSinceJoin < inviteSpamConfig.accountAgeDays;
62+
};
63+
3364
export function parseInvites(message: Message<true>) {
3465
// Check if message contains any Discord invite
3566
const matches = invitePatterns
@@ -128,7 +159,11 @@ async function handleInvite(
128159
violation.channels.add(message.channelId);
129160
inviteViolationCache.set(member.id, violation);
130161

131-
if (violation.count >= 4 || violation.channels.size >= 3) {
162+
const shouldBan =
163+
violation.count >= inviteSpamConfig.maxViolations ||
164+
violation.channels.size >= inviteSpamConfig.maxChannels;
165+
166+
if (shouldBan && isSubjectToAutoban(member)) {
132167
await banForInviteSpam(message, member, violation);
133168
}
134169
} catch (error) {

0 commit comments

Comments
 (0)