2025-02-06 07:40:53 -05:00
let targetUserIds = new Set ( ) ;
let isActive = false ;
let channelToWatch = null ;
let lastAddTimes = new Map ( ) ;
2025-04-10 15:34:40 -04:00
let failedAttempts = new Map ( ) ;
let recentAdds = new Map ( ) ;
2025-02-06 07:40:53 -05:00
2025-04-10 15:34:40 -04:00
const getBackoffDelay = ( userId ) => {
const attempts = failedAttempts . get ( userId ) || 0 ;
if ( attempts <= 1 ) return 2000 ;
if ( attempts <= 3 ) return 4000 ;
if ( attempts <= 5 ) return 7000 ;
if ( attempts <= 10 ) return 15000 ;
return 30000 ;
} ;
const getAddDelay = ( ) => {
const baseDelay = Math . floor ( Math . random ( ) * 700 ) + 800 ;
const jitter = Math . floor ( Math . random ( ) * 300 ) - 150 ;
return Math . max ( 500 , baseDelay + jitter ) ;
2025-02-06 07:40:53 -05:00
} ;
module . exports = {
name : 'groupadd' ,
2025-04-10 15:34:40 -04:00
description : 'Automatically re-adds users to group when they leave.' ,
2025-02-06 07:40:53 -05:00
async execute ( message , args , deleteTimeout ) {
2025-04-10 14:52:22 -04:00
const { extractUserId } = require ( '../utils/userUtils' ) ;
2025-02-06 07:40:53 -05:00
if ( message . channel . type !== 'GROUP_DM' ) {
message . channel . send ( 'This command only works in group DMs.' )
. then ( msg => setTimeout ( ( ) => msg . delete ( ) . catch ( ( ) => { } ) , deleteTimeout ) ) ;
return ;
}
if ( args [ 0 ] ? . toLowerCase ( ) === 'stop' ) {
isActive = false ;
targetUserIds . clear ( ) ;
lastAddTimes . clear ( ) ;
2025-04-10 15:34:40 -04:00
failedAttempts . clear ( ) ;
recentAdds . clear ( ) ;
2025-02-06 07:40:53 -05:00
channelToWatch = null ;
console . log ( '[GROUPADD] System deactivated' ) ;
message . channel . send ( 'Group auto-add deactivated.' )
. then ( msg => setTimeout ( ( ) => msg . delete ( ) . catch ( ( ) => { } ) , deleteTimeout ) ) ;
return ;
}
2025-04-10 14:52:22 -04:00
const validIds = args
. map ( arg => extractUserId ( arg ) )
. filter ( id => id !== null ) ;
2025-02-06 07:40:53 -05:00
if ( validIds . length === 0 ) {
2025-04-10 14:52:22 -04:00
message . channel . send ( 'Please provide at least one valid user ID or @mention.' )
2025-02-06 07:40:53 -05:00
. then ( msg => setTimeout ( ( ) => msg . delete ( ) . catch ( ( ) => { } ) , deleteTimeout ) ) ;
return ;
}
channelToWatch = message . channel ;
targetUserIds = new Set ( validIds ) ;
isActive = true ;
2025-04-10 15:34:40 -04:00
failedAttempts . clear ( ) ;
recentAdds . clear ( ) ;
2025-02-06 07:40:53 -05:00
console . log ( ` [GROUPADD] System activated - Targeting users: ${ Array . from ( targetUserIds ) . join ( ', ' ) } ` ) ;
for ( const userId of targetUserIds ) {
try {
if ( ! channelToWatch . recipients . has ( userId ) ) {
console . log ( ` [GROUPADD] Target ${ userId } not in group, attempting initial add ` ) ;
2025-04-10 15:34:40 -04:00
const initialDelay = Math . floor ( Math . random ( ) * 500 ) + 300 ;
await new Promise ( resolve => setTimeout ( resolve , initialDelay ) ) ;
2025-02-06 07:40:53 -05:00
await channelToWatch . addUser ( userId ) ;
2025-04-10 15:34:40 -04:00
lastAddTimes . set ( userId , Date . now ( ) ) ;
recentAdds . set ( userId , true ) ;
setTimeout ( ( ) => {
recentAdds . delete ( userId ) ;
} , 10000 ) ;
2025-02-06 07:40:53 -05:00
console . log ( ` [GROUPADD] Initial add successful for ${ userId } ` ) ;
}
} catch ( error ) {
console . log ( ` [GROUPADD] Initial add failed for ${ userId } : ` , error ) ;
2025-04-10 15:34:40 -04:00
failedAttempts . set ( userId , ( failedAttempts . get ( userId ) || 0 ) + 1 ) ;
2025-02-06 07:40:53 -05:00
}
}
2025-04-10 15:34:40 -04:00
const handleRecipientRemove = async ( channel , user ) => {
2025-02-06 07:40:53 -05:00
if ( ! isActive || channel . id !== channelToWatch . id || ! targetUserIds . has ( user . id ) ) return ;
const currentTime = Date . now ( ) ;
const lastAddTime = lastAddTimes . get ( user . id ) || 0 ;
const timeSinceLastAdd = currentTime - lastAddTime ;
2025-04-10 15:34:40 -04:00
const isRecentlyAdded = recentAdds . has ( user . id ) ;
const failCount = failedAttempts . get ( user . id ) || 0 ;
2025-02-06 07:40:53 -05:00
2025-04-10 15:34:40 -04:00
console . log ( ` [GROUPADD] User ${ user . id } left. Time since last add: ${ timeSinceLastAdd } ms, Recent add: ${ isRecentlyAdded } , Failed attempts: ${ failCount } ` ) ;
if ( isRecentlyAdded ) {
console . log ( ` [GROUPADD] User ${ user . id } was recently added and left immediately. Waiting longer. ` ) ;
await new Promise ( resolve => setTimeout ( resolve , 5000 + Math . random ( ) * 5000 ) ) ;
}
if ( timeSinceLastAdd < 2000 ) {
const backoffTime = getBackoffDelay ( user . id ) ;
console . log ( ` [GROUPADD] Rate limiting for ${ user . id } , waiting ${ backoffTime } ms... ` ) ;
await new Promise ( resolve => setTimeout ( resolve , backoffTime ) ) ;
2025-02-06 07:40:53 -05:00
}
2025-04-10 15:34:40 -04:00
const addDelay = getAddDelay ( ) ;
console . log ( ` [GROUPADD] Will readd user ${ user . id } after ${ addDelay } ms ` ) ;
await new Promise ( resolve => setTimeout ( resolve , addDelay ) ) ;
if ( ! isActive ) {
console . log ( ` [GROUPADD] Command was deactivated during delay, cancelling re-add for ${ user . id } ` ) ;
return ;
}
2025-02-06 07:40:53 -05:00
try {
await channel . addUser ( user . id ) ;
lastAddTimes . set ( user . id , Date . now ( ) ) ;
2025-04-10 15:34:40 -04:00
recentAdds . set ( user . id , true ) ;
setTimeout ( ( ) => {
recentAdds . delete ( user . id ) ;
} , 10000 ) ;
2025-02-06 07:40:53 -05:00
console . log ( ` [GROUPADD] Successfully re-added user ${ user . id } ` ) ;
2025-04-10 15:34:40 -04:00
if ( failedAttempts . get ( user . id ) > 0 ) {
failedAttempts . set ( user . id , Math . max ( 0 , failedAttempts . get ( user . id ) - 1 ) ) ;
}
2025-02-06 07:40:53 -05:00
} catch ( error ) {
console . log ( ` [GROUPADD] Failed to re-add user ${ user . id } : ` , error ) ;
2025-04-10 15:34:40 -04:00
failedAttempts . set ( user . id , ( failedAttempts . get ( user . id ) || 0 ) + 1 ) ;
if ( Math . random ( ) < 0.4 && timeSinceLastAdd > 5000 ) {
console . log ( ` [GROUPADD] Will try again after a pause ` ) ;
setTimeout ( ( ) => {
if ( isActive && ! channel . recipients . has ( user . id ) ) {
channel . addUser ( user . id ) . catch ( e =>
console . log ( ` [GROUPADD] Retry failed for ${ user . id } : ` , e )
) ;
}
} , 3000 + Math . random ( ) * 2000 ) ;
}
2025-02-06 07:40:53 -05:00
}
2025-04-10 15:34:40 -04:00
} ;
message . client . on ( 'channelRecipientRemove' , handleRecipientRemove ) ;
2025-02-06 07:40:53 -05:00
const targetCount = targetUserIds . size ;
message . channel . send ( ` Now watching for ${ targetCount } user ${ targetCount > 1 ? 's' : '' } to leave the group. ` )
. then ( msg => setTimeout ( ( ) => msg . delete ( ) . catch ( ( ) => { } ) , deleteTimeout ) ) ;
} ,
} ;