Refactor groupadd and delete

This commit is contained in:
2025-04-10 15:34:40 -04:00
parent 1f8dced90e
commit 235398d200
4 changed files with 344 additions and 337 deletions

View File

@@ -1,176 +1,278 @@
let targetUserIds = [];
let isKickActive = false;
let voiceStateHandler = null;
let lastKickTime = 0;
let consecutiveKicks = 0;
let cooldownTime = 0;
let checkInterval = null;
const { extractUserId } = require('../utils/userUtils');
const getRandomDelay = () => {
const delay = Math.floor(Math.random() * 250) + 100;
console.log(`[KICKVC] Generated event delay: ${delay}ms`);
return delay;
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 getRandomCheckDelay = () => {
const delay = Math.floor(Math.random() * 250) + 200;
console.log(`[KICKVC] Generated interval check delay: ${delay}ms`);
return delay;
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 getCooldown = (kicks) => {
let cooldown;
if (kicks <= 3) cooldown = 200;
else if (kicks <= 5) cooldown = 500;
else if (kicks <= 10) cooldown = 1000;
else cooldown = 2500;
console.log(`[KICKVC] New cooldown calculated for ${kicks} kicks: ${cooldown}ms`);
return cooldown;
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[0]?.toLowerCase() === 'stop') {
if (voiceStateHandler) {
message.client.removeListener('voiceStateUpdate', voiceStateHandler);
voiceStateHandler = null;
}
if (checkInterval) {
clearInterval(checkInterval);
checkInterval = null;
}
isKickActive = false;
targetUserIds = [];
lastKickTime = 0;
consecutiveKicks = 0;
cooldownTime = 0;
console.log('[KICKVC] System deactivated - all variables reset');
message.channel.send('Voice kick has been deactivated.')
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
if (args.length === 0) {
message.channel.send('Please provide a command: `start <userId(s)>` or `stop <userId or "all">`')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
return;
}
const userIds = args.filter(arg => /^\d{17,19}$/.test(arg));
if (!userIds.length) {
console.log('[KICKVC] Invalid user IDs provided');
message.channel.send('Please provide at least one valid user ID.')
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
return;
}
targetUserIds = userIds;
isKickActive = true;
console.log(`[KICKVC] System activated - Targeting user IDs: ${targetUserIds.join(', ')}`);
if (voiceStateHandler) {
message.client.removeListener('voiceStateUpdate', voiceStateHandler);
console.log('[KICKVC] Removed old voice state handler');
}
if (checkInterval) {
clearInterval(checkInterval);
console.log('[KICKVC] Cleared old check interval');
}
const kickUser = async (member, guild, fromEvent = false) => {
if (!isKickActive) return;
const currentTime = Date.now();
const timeSinceLastKick = currentTime - lastKickTime;
if (timeSinceLastKick < cooldownTime) {
console.log(`[KICKVC] On cooldown - ${cooldownTime - timeSinceLastKick}ms remaining`);
const command = args[0].toLowerCase();
if (command === 'stop') {
if (args.length < 2) {
message.channel.send('Please specify a user ID or "all" to stop kicking.')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
return;
}
try {
const selfMember = await guild.members.fetch(member.client.user.id);
if (!selfMember.permissions.has("ADMINISTRATOR")) {
console.log(`[KICKVC] No admin permissions in ${guild.name}, skipping`);
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();
message.channel.send('Stopped all active VC kicks.')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
return;
} else {
const userId = extractUserId(target);
if (!userId) {
message.channel.send('Invalid user ID.')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
return;
}
const delay = fromEvent ? getRandomDelay() : getRandomCheckDelay();
console.log(`[KICKVC] Admin check passed in ${guild.name}, waiting ${delay}ms before kick...`);
await new Promise(resolve => setTimeout(resolve, delay));
if (!member.voice.channel) return;
console.log(`[KICKVC] Target in voice: ${member.user.tag} | ${guild.name} | ${member.voice.channel.name}`);
await member.voice.disconnect();
lastKickTime = currentTime;
consecutiveKicks++;
cooldownTime = getCooldown(consecutiveKicks);
setTimeout(() => {
if (consecutiveKicks > 0) {
consecutiveKicks--;
cooldownTime = getCooldown(consecutiveKicks);
}
}, 15000);
} catch (error) {
console.log(`[KICKVC] Error kicking in ${guild.name}:`, error);
try {
await member.voice.setChannel(null);
console.log('[KICKVC] Succeeded with alternate method (setChannel null)');
} catch {
try {
await member.voice.channel.permissionOverwrites.create(member, {
Connect: false,
Speak: false
});
await member.voice.disconnect();
console.log('[KICKVC] Succeeded with permissions override');
} catch {
console.log('[KICKVC] All disconnect methods failed');
}
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}`);
message.channel.send(`Stopped kicking user: ${userId}`)
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
} else {
message.channel.send(`No active kick for user: ${userId}`)
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
}
return;
}
};
voiceStateHandler = async (oldState, newState) => {
if (!isKickActive || targetUserIds.length === 0) return;
const id = newState?.member?.id || oldState?.member?.id;
if (!targetUserIds.includes(id)) return;
const voiceState = newState?.channelId ? newState : oldState;
if (!voiceState?.channel) return;
console.log('[KICKVC] Voice state update detected for target');
try {
const guild = voiceState.guild;
const member = await guild.members.fetch(id).catch(() => null);
if (member?.voice?.channel) {
await kickUser(member, guild, true);
}
} catch (error) {
console.log('[KICKVC] Error in voice state handler:', error);
}
};
const intervalTime = Math.floor(Math.random() * 500) + 1000;
console.log(`[KICKVC] Setting up interval check every ${intervalTime}ms`);
checkInterval = setInterval(async () => {
if (!isKickActive) return;
for (const guild of message.client.guilds.cache.values()) {
for (const id of targetUserIds) {
try {
const member = await guild.members.fetch(id).catch(() => null);
if (member?.voice?.channel) {
await kickUser(member, guild, false);
}
} catch { }
}
}
}, intervalTime);
message.client.on('voiceStateUpdate', voiceStateHandler);
console.log('[KICKVC] New voice state handler and check interval registered');
try {
const users = await Promise.all(targetUserIds.map(id => message.client.users.fetch(id).catch(() => null)));
const userTags = users.filter(u => u).map(u => `${u.tag} (${u.id})`);
console.log(`[KICKVC] Successfully fetched target users: ${userTags.join(', ')}`);
message.channel.send(`Now automatically kicking: ${userTags.join(', ')} from voice channels.`)
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
console.log('[KICKVC] Performing initial guild check');
message.client.guilds.cache.forEach(async (guild) => {
for (const id of targetUserIds) {
const member = await guild.members.fetch(id).catch(() => null);
if (member?.voice?.channel) {
console.log(`[KICKVC] Target found in voice during initial check - Server: ${guild.name}`);
await kickUser(member, guild, true);
}
}
});
} catch (error) {
console.log('[KICKVC] Could not fetch user information:', error);
message.channel.send(`Now automatically kicking user IDs: ${targetUserIds.join(', ')} from voice channels.`)
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
}
},
if (command === 'start') {
if (args.length < 2) {
message.channel.send('Please provide at least one user ID to kick.')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
return;
}
const userIds = args.slice(1)
.map(arg => extractUserId(arg))
.filter(id => id !== null);
if (userIds.length === 0) {
message.channel.send('No valid user IDs provided.')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
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}`);
}
let responseMessage = '';
if (startedKicking.length > 0) {
responseMessage += `Started kicking ${startedKicking.length} user(s): ${startedKicking.join(', ')}\n`;
}
if (alreadyKicking.length > 0) {
responseMessage += `Already kicking: ${alreadyKicking.join(', ')}`;
}
message.channel.send(responseMessage)
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
return;
}
message.channel.send('Unknown command. Use `start <userId(s)>` or `stop <userId or "all">`')
.then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout));
}
};