268 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const { extractUserId } = require('../utils/userUtils');
 | |
| const { sendCommandResponse } = require('../utils/messageUtils');
 | |
| 
 | |
| let activeKicks = new Map();
 | |
| let cooldowns = new Map();
 | |
| let voiceStateUpdateHandlers = new Map();
 | |
| let checkIntervals = new Map();
 | |
| 
 | |
| const getKickDelay = () => {
 | |
|     const baseDelay = Math.floor(Math.random() * 400) + 600;
 | |
|     const jitter = Math.floor(Math.random() * 200) - 100;
 | |
|     return Math.max(400, baseDelay + jitter);
 | |
| };
 | |
| 
 | |
| const calculateCooldown = (kickCount) => {
 | |
|     if (kickCount <= 2) return 0;
 | |
|     if (kickCount <= 5) return Math.min((kickCount - 2) * 300, 900);
 | |
|     if (kickCount <= 10) return Math.min(900 + (kickCount - 5) * 400, 2900);
 | |
|     return Math.min(2900 + (kickCount - 10) * 500, 5000);
 | |
| };
 | |
| 
 | |
| const getSafetyPause = () => {
 | |
|     if (Math.random() < 0.25) {
 | |
|         return Math.floor(Math.random() * 4000) + 3000;
 | |
|     }
 | |
|     return 0;
 | |
| };
 | |
| 
 | |
| const performUserKick = async (userId, guild, voiceChannel, kickData) => {
 | |
|     if (!activeKicks.has(userId)) return false;
 | |
|     
 | |
|     try {
 | |
|         const member = guild.members.cache.get(userId);
 | |
|         if (!member || !member.voice.channelId) return false;
 | |
|         
 | |
|         console.log(`[KICKVC] Found user ${userId} in VC: ${voiceChannel.name}`);
 | |
|         
 | |
|         const currentTime = Date.now();
 | |
|         const lastKickTime = kickData.lastKick;
 | |
|         const timeSinceLastKick = currentTime - lastKickTime;
 | |
|         
 | |
|         let cooldownTime = cooldowns.get(userId) || 0;
 | |
|         
 | |
|         if (timeSinceLastKick < 3000) {
 | |
|             cooldownTime = calculateCooldown(kickData.count);
 | |
|             cooldowns.set(userId, cooldownTime);
 | |
|         }
 | |
|         
 | |
|         if (cooldownTime > 0) {
 | |
|             console.log(`[KICKVC] Cooldown active for ${userId}: ${cooldownTime}ms`);
 | |
|             await new Promise(resolve => setTimeout(resolve, cooldownTime));
 | |
|         }
 | |
|         
 | |
|         const safetyPause = getSafetyPause();
 | |
|         if (safetyPause > 0) {
 | |
|             console.log(`[KICKVC] Adding safety pause of ${safetyPause}ms before kicking ${userId}`);
 | |
|             await new Promise(resolve => setTimeout(resolve, safetyPause));
 | |
|         }
 | |
|         
 | |
|         const delay = getKickDelay();
 | |
|         console.log(`[KICKVC] Will kick ${userId} after ${delay}ms delay`);
 | |
|         
 | |
|         await new Promise(resolve => setTimeout(resolve, delay));
 | |
|         
 | |
|         if (!activeKicks.has(userId)) {
 | |
|             console.log(`[KICKVC] Kick for ${userId} was stopped during delay`);
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         if (member && member.voice.channelId) {
 | |
|             await member.voice.disconnect();
 | |
|             
 | |
|             kickData.count++;
 | |
|             kickData.lastKick = Date.now();
 | |
|             activeKicks.set(userId, kickData);
 | |
|             
 | |
|             console.log(`[KICKVC] Successfully kicked ${userId} (${kickData.count} kicks so far)`);
 | |
|             
 | |
|             if (kickData.count % 5 === 0) {
 | |
|                 cooldowns.set(userId, calculateCooldown(kickData.count) + 2000);
 | |
|                 console.log(`[KICKVC] Increased cooldown after ${kickData.count} kicks`);
 | |
|             }
 | |
|             
 | |
|             return true;
 | |
|         }
 | |
|         
 | |
|         return false;
 | |
|     } catch (error) {
 | |
|         console.log(`[KICKVC] Failed to kick ${userId}:`, error);
 | |
|         
 | |
|         if (Math.random() < 0.3 && kickData.count > 0) {
 | |
|             setTimeout(() => {
 | |
|                 try {
 | |
|                     const member = guild.members.cache.get(userId);
 | |
|                     if (member && member.voice.channelId) {
 | |
|                         member.voice.disconnect().catch(e => 
 | |
|                             console.log(`[KICKVC] Retry failed for ${userId}:`, e)
 | |
|                         );
 | |
|                     }
 | |
|                 } catch (retryError) {
 | |
|                     console.log(`[KICKVC] Retry setup failed for ${userId}:`, retryError);
 | |
|                 }
 | |
|             }, 2000 + Math.random() * 1000);
 | |
|         }
 | |
|         
 | |
|         return false;
 | |
|     }
 | |
| };
 | |
| 
 | |
| module.exports = {
 | |
|     name: 'kickvc',
 | |
|     description: 'Automatically kicks specified users from voice channels.',
 | |
|     async execute(message, args, deleteTimeout) {
 | |
|         if (args.length === 0) {
 | |
|             await sendCommandResponse(message, 'Please provide a command: `start <userId(s)>` or `stop <userId or "all">`', deleteTimeout, true);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         const command = args[0].toLowerCase();
 | |
| 
 | |
|         if (command === 'stop') {
 | |
|             if (args.length < 2) {
 | |
|                 await sendCommandResponse(message, 'Please specify a user ID or "all" to stop kicking.', deleteTimeout, true);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const target = args[1].toLowerCase();
 | |
|             
 | |
|             if (target === 'all') {
 | |
|                 for (const [userId, handler] of voiceStateUpdateHandlers.entries()) {
 | |
|                     message.client.off('voiceStateUpdate', handler);
 | |
|                     activeKicks.delete(userId);
 | |
|                     cooldowns.delete(userId);
 | |
|                     clearInterval(checkIntervals.get(userId));
 | |
|                     checkIntervals.delete(userId);
 | |
|                     console.log(`[KICKVC] Stopped kicking user: ${userId}`);
 | |
|                 }
 | |
|                 voiceStateUpdateHandlers.clear();
 | |
|                 
 | |
|                 await sendCommandResponse(message, 'Stopped all active VC kicks.', deleteTimeout, true);
 | |
|                 return;
 | |
|             } else {
 | |
|                 const userId = extractUserId(target);
 | |
|                 
 | |
|                 if (!userId) {
 | |
|                     await sendCommandResponse(message, 'Invalid user ID.', deleteTimeout, true);
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 if (voiceStateUpdateHandlers.has(userId)) {
 | |
|                     message.client.off('voiceStateUpdate', voiceStateUpdateHandlers.get(userId));
 | |
|                     activeKicks.delete(userId);
 | |
|                     cooldowns.delete(userId);
 | |
|                     clearInterval(checkIntervals.get(userId));
 | |
|                     checkIntervals.delete(userId);
 | |
|                     console.log(`[KICKVC] Stopped kicking user: ${userId}`);
 | |
|                     
 | |
|                     await sendCommandResponse(message, `Stopped kicking user: ${userId}`, deleteTimeout, true);
 | |
|                 } else {
 | |
|                     await sendCommandResponse(message, `No active kick for user: ${userId}`, deleteTimeout, true);
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (command === 'start') {
 | |
|             if (args.length < 2) {
 | |
|                 await sendCommandResponse(message, 'Please provide at least one user ID to kick.', deleteTimeout, true);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const userIds = args.slice(1)
 | |
|                 .map(arg => extractUserId(arg))
 | |
|                 .filter(id => id !== null);
 | |
| 
 | |
|             if (userIds.length === 0) {
 | |
|                 await sendCommandResponse(message, 'No valid user IDs provided.', deleteTimeout, true);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const startedKicking = [];
 | |
|             const alreadyKicking = [];
 | |
| 
 | |
|             for (const userId of userIds) {
 | |
|                 if (activeKicks.has(userId)) {
 | |
|                     alreadyKicking.push(userId);
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 activeKicks.set(userId, { count: 0, lastKick: 0 });
 | |
|                 cooldowns.set(userId, 0);
 | |
| 
 | |
|                 for (const guild of message.client.guilds.cache.values()) {
 | |
|                     try {
 | |
|                         const member = await guild.members.fetch(userId).catch(() => null);
 | |
|                         if (member && member.voice.channelId) {
 | |
|                             const kickData = activeKicks.get(userId);
 | |
|                             console.log(`[KICKVC] Found target ${userId} already in voice in ${guild.name}`);
 | |
|                             performUserKick(userId, guild, member.voice.channel, kickData);
 | |
|                             break;
 | |
|                         }
 | |
|                     } catch (error) {
 | |
|                         console.log(`[KICKVC] Error checking guild ${guild.name} for user ${userId}:`, error);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 const checkInterval = setInterval(async () => {
 | |
|                     if (!activeKicks.has(userId)) {
 | |
|                         clearInterval(checkInterval);
 | |
|                         return;
 | |
|                     }
 | |
|                     
 | |
|                     const kickData = activeKicks.get(userId);
 | |
|                     
 | |
|                     for (const guild of message.client.guilds.cache.values()) {
 | |
|                         try {
 | |
|                             const member = await guild.members.fetch(userId).catch(() => null);
 | |
|                             if (member && member.voice.channelId) {
 | |
|                                 performUserKick(userId, guild, member.voice.channel, kickData);
 | |
|                                 return;
 | |
|                             }
 | |
|                         } catch (error) {}
 | |
|                     }
 | |
|                 }, 4000 + Math.floor(Math.random() * 2000));
 | |
|                 
 | |
|                 checkIntervals.set(userId, checkInterval);
 | |
| 
 | |
|                 const handleVoiceStateUpdate = async (oldState, newState) => {
 | |
|                     if (!activeKicks.has(userId)) return;
 | |
|                     
 | |
|                     const member = newState.member || oldState.member;
 | |
|                     if (!member || member.user.id !== userId) return;
 | |
|                     
 | |
|                     const kickData = activeKicks.get(userId);
 | |
|                     
 | |
|                     if ((!oldState.channelId && newState.channelId) || 
 | |
|                         (oldState.channelId !== newState.channelId && newState.channelId)) {
 | |
|                         
 | |
|                         const guild = newState.guild;
 | |
|                         const voiceChannel = newState.channel;
 | |
|                         
 | |
|                         console.log(`[KICKVC] Target user ${userId} joined/moved to VC: ${voiceChannel.name}`);
 | |
|                         performUserKick(userId, guild, voiceChannel, kickData);
 | |
|                     }
 | |
|                 };
 | |
| 
 | |
|                 voiceStateUpdateHandlers.set(userId, handleVoiceStateUpdate);
 | |
|                 message.client.on('voiceStateUpdate', handleVoiceStateUpdate);
 | |
|                 startedKicking.push(userId);
 | |
|                 console.log(`[KICKVC] Started kicking user: ${userId}`);
 | |
|             }
 | |
| 
 | |
|             if (startedKicking.length > 0) {
 | |
|                 await sendCommandResponse(
 | |
|                     message,
 | |
|                     `Started kicking: ${startedKicking.join(', ')}${alreadyKicking.length > 0 ? `\nAlready kicking: ${alreadyKicking.join(', ')}` : ''}`,
 | |
|                     deleteTimeout,
 | |
|                     true
 | |
|                 );
 | |
|             } else if (alreadyKicking.length > 0) {
 | |
|                 await sendCommandResponse(message, `Already kicking: ${alreadyKicking.join(', ')}`, deleteTimeout, true);
 | |
|             }
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         await sendCommandResponse(message, 'Unknown command. Use `start <userId(s)>` or `stop <userId or "all">`.', deleteTimeout, true);
 | |
|     }
 | |
| }; |