@@ -18,18 +18,49 @@ const invitePatterns = [
1818
1919const 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+
2139interface 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
2848const 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+
3364export 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