Refactor groupadd and delete
This commit is contained in:
parent
1f8dced90e
commit
235398d200
@ -3,7 +3,6 @@ let cancelDelete = false;
|
||||
let deletedMessages = new Set();
|
||||
const CACHE_CLEANUP_INTERVAL = 30 * 60 * 1000;
|
||||
|
||||
// Cleanup deleted message cache periodically
|
||||
setInterval(() => {
|
||||
if (deletedMessages.size > 1000) {
|
||||
console.log(`[DELETE] Cleaning message cache (size: ${deletedMessages.size})`);
|
||||
@ -31,11 +30,10 @@ module.exports = {
|
||||
isDeleting = true;
|
||||
cancelDelete = false;
|
||||
|
||||
// Check for speed settings
|
||||
let speed = 'medium';
|
||||
if (args[0] && ['slow', 'medium', 'fast'].includes(args[0].toLowerCase())) {
|
||||
speed = args[0].toLowerCase();
|
||||
args.shift(); // Remove the speed argument
|
||||
args.shift();
|
||||
}
|
||||
|
||||
const deleteCount = parseInt(args[0], 10);
|
||||
@ -55,7 +53,6 @@ module.exports = {
|
||||
};
|
||||
|
||||
async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, speed = 'medium') {
|
||||
// Human-like timing parameters based on speed setting
|
||||
let deleteIntervalMin, deleteIntervalMax, jitterFactor, pauseChance, pauseLengthMin, pauseLengthMax, batchSize;
|
||||
|
||||
switch(speed) {
|
||||
@ -88,7 +85,6 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp
|
||||
batchSize = 10;
|
||||
}
|
||||
|
||||
// More human-like delay functions
|
||||
const getHumanlikeDelay = () => {
|
||||
const baseInterval = Math.floor(Math.random() * (deleteIntervalMax - deleteIntervalMin + 1)) + deleteIntervalMin;
|
||||
const jitterAmount = baseInterval * jitterFactor;
|
||||
@ -96,7 +92,7 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp
|
||||
return Math.max(1000, Math.floor(baseInterval + jitter));
|
||||
};
|
||||
|
||||
const getReadingDelay = () => Math.floor(Math.random() * 3000) + 1000; // 1-4 seconds
|
||||
const getReadingDelay = () => Math.floor(Math.random() * 3000) + 1000;
|
||||
|
||||
try {
|
||||
console.log(`[DELETE] Starting deletion of up to ${deleteCount} messages with ${speed} speed`);
|
||||
@ -104,13 +100,12 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp
|
||||
let batchCount = 0;
|
||||
|
||||
while (deletedCount < deleteCount && !cancelDelete) {
|
||||
// Show progress occasionally
|
||||
if (deletedCount > 0 && deletedCount % 25 === 0) {
|
||||
console.log(`[DELETE] Progress: ${deletedCount}/${deleteCount} messages deleted`);
|
||||
}
|
||||
|
||||
const fetchLimit = Math.min(deleteCount - deletedCount, batchSize);
|
||||
const messages = await message.channel.messages.fetch({ limit: 100 }); // Fetch more to ensure we find user messages
|
||||
const messages = await message.channel.messages.fetch({ limit: 100 });
|
||||
|
||||
const filteredMessages = messages.filter(msg =>
|
||||
msg.author.id === message.author.id &&
|
||||
@ -122,7 +117,6 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp
|
||||
break;
|
||||
}
|
||||
|
||||
// Process current batch
|
||||
batchCount++;
|
||||
let messagesInThisBatch = 0;
|
||||
|
||||
@ -132,42 +126,35 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit the loop if we've deleted enough messages
|
||||
if (deletedCount >= deleteCount) break;
|
||||
|
||||
try {
|
||||
if (msg.deletable && !msg.deleted && !deletedMessages.has(msg.id)) {
|
||||
// Simulate reading the message occasionally (25% chance)
|
||||
if (Math.random() < 0.25) {
|
||||
const readingDelay = getReadingDelay();
|
||||
console.log(`[DELETE] Taking ${readingDelay}ms to "read" before deleting message ${msg.id}`);
|
||||
await new Promise(resolve => setTimeout(resolve, readingDelay));
|
||||
}
|
||||
|
||||
// Variable pre-delete delay
|
||||
const preDeleteDelay = Math.floor(Math.random() * 1000) + 250;
|
||||
await new Promise(resolve => setTimeout(resolve, preDeleteDelay));
|
||||
|
||||
// Attempt to delete
|
||||
await msg.delete().catch(err => {
|
||||
if (err.code === 10008) {
|
||||
console.log(`[DELETE] Message ${msg.id} already deleted`);
|
||||
deletedMessages.add(msg.id);
|
||||
} else if (err.code === 429) {
|
||||
console.log(`[DELETE] Rate limited. Taking a longer break...`);
|
||||
// Don't count this one, we'll try again later
|
||||
return;
|
||||
} else {
|
||||
console.error(`[DELETE] Failed to delete message:`, err);
|
||||
}
|
||||
});
|
||||
|
||||
// Successfully deleted
|
||||
deletedMessages.add(msg.id);
|
||||
deletedCount++;
|
||||
messagesInThisBatch++;
|
||||
|
||||
// Add human-like delay between deletions
|
||||
const delay = getHumanlikeDelay();
|
||||
console.log(`[DELETE] Waiting ${delay}ms before next deletion`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
@ -177,27 +164,23 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp
|
||||
}
|
||||
}
|
||||
|
||||
// Break if we couldn't delete any messages in this batch
|
||||
if (messagesInThisBatch === 0) {
|
||||
console.log(`[DELETE] No deletable messages found in batch`);
|
||||
break;
|
||||
}
|
||||
|
||||
// Take a natural pause between batches (with higher chance after several batches)
|
||||
if (!cancelDelete && deletedCount < deleteCount) {
|
||||
// More likely to pause after several consecutive batches
|
||||
const adjustedPauseChance = pauseChance * (1 + (Math.min(batchCount, 5) / 10));
|
||||
|
||||
if (Math.random() < adjustedPauseChance) {
|
||||
const pauseDuration = Math.floor(Math.random() * (pauseLengthMax - pauseLengthMin + 1)) + pauseLengthMin;
|
||||
console.log(`[DELETE] Taking a break for ${Math.round(pauseDuration/1000)} seconds. Progress: ${deletedCount}/${deleteCount}`);
|
||||
await new Promise(resolve => setTimeout(resolve, pauseDuration));
|
||||
batchCount = 0; // Reset batch count after a pause
|
||||
batchCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final status message
|
||||
if (cancelDelete) {
|
||||
const canceledMsg = await message.channel.send(`Delete operation canceled after removing ${deletedCount} messages.`);
|
||||
setTimeout(() => canceledMsg.delete().catch(console.error), deleteTimeout);
|
||||
@ -221,7 +204,6 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
return;
|
||||
}
|
||||
|
||||
// Human-like timing parameters based on speed setting
|
||||
let deleteIntervalMin, deleteIntervalMax, jitterFactor, pauseChance, pauseLengthMin, pauseLengthMax, batchSize;
|
||||
|
||||
switch(speed) {
|
||||
@ -229,7 +211,7 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
deleteIntervalMin = 3000;
|
||||
deleteIntervalMax = 6000;
|
||||
jitterFactor = 0.5;
|
||||
pauseChance = 0.4; // Higher chance to pause between channels
|
||||
pauseChance = 0.4;
|
||||
pauseLengthMin = 30000;
|
||||
pauseLengthMax = 90000;
|
||||
batchSize = 5;
|
||||
@ -254,7 +236,6 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
batchSize = 10;
|
||||
}
|
||||
|
||||
// More human-like delay functions
|
||||
const getHumanlikeDelay = () => {
|
||||
const baseInterval = Math.floor(Math.random() * (deleteIntervalMax - deleteIntervalMin + 1)) + deleteIntervalMin;
|
||||
const jitterAmount = baseInterval * jitterFactor;
|
||||
@ -300,7 +281,6 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process current batch
|
||||
batchCount++;
|
||||
let messagesInThisBatch = 0;
|
||||
|
||||
@ -309,31 +289,26 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
|
||||
try {
|
||||
if (msg.deletable && !msg.deleted && !deletedMessages.has(msg.id)) {
|
||||
// Variable pre-delete delay
|
||||
const preDeleteDelay = Math.floor(Math.random() * 1000) + 250;
|
||||
await new Promise(resolve => setTimeout(resolve, preDeleteDelay));
|
||||
|
||||
// Attempt to delete
|
||||
await msg.delete().catch(err => {
|
||||
if (err.code === 10008) {
|
||||
console.log(`[DELETE] Message ${msg.id} already deleted`);
|
||||
deletedMessages.add(msg.id);
|
||||
} else if (err.code === 429) {
|
||||
console.log(`[DELETE] Rate limited. Taking a longer break...`);
|
||||
// Take an extra long break on rate limits
|
||||
return new Promise(resolve => setTimeout(resolve, 30000 + Math.random() * 30000));
|
||||
} else {
|
||||
console.error(`[DELETE] Failed to delete message:`, err);
|
||||
}
|
||||
});
|
||||
|
||||
// Successfully deleted
|
||||
deletedMessages.add(msg.id);
|
||||
totalDeleted++;
|
||||
messagesDeletedInChannel++;
|
||||
messagesInThisBatch++;
|
||||
|
||||
// Add human-like delay between deletions
|
||||
const delay = getHumanlikeDelay();
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
@ -341,15 +316,12 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
console.error('[DELETE] Error deleting message:', error);
|
||||
}
|
||||
|
||||
// Break batch processing after certain number of messages to avoid long loops
|
||||
if (messagesInThisBatch >= batchSize) break;
|
||||
}
|
||||
|
||||
// If we deleted fewer messages than the batch size, assume we've reached the end
|
||||
if (messagesInThisBatch < batchSize) {
|
||||
hasMoreMessages = false;
|
||||
} else {
|
||||
// Take a natural pause between batches within a channel
|
||||
const shouldPause = Math.random() < pauseChance;
|
||||
if (shouldPause && !cancelDelete) {
|
||||
const pauseDuration = Math.floor(Math.random() * (pauseLengthMin - pauseLengthMin/2 + 1)) + pauseLengthMin/2;
|
||||
@ -361,7 +333,6 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
|
||||
console.log(`[DELETE] Completed channel ${channel.name}: ${messagesDeletedInChannel} messages deleted`);
|
||||
|
||||
// Take a longer pause between channels
|
||||
if (!cancelDelete && processedChannels < channels.size) {
|
||||
const pauseDuration = Math.floor(Math.random() * (pauseLengthMax - pauseLengthMin + 1)) + pauseLengthMin;
|
||||
console.log(`[DELETE] Moving to next channel in ${Math.round(pauseDuration/1000)} seconds. Total deleted so far: ${totalDeleted}`);
|
||||
@ -369,7 +340,6 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed =
|
||||
}
|
||||
}
|
||||
|
||||
// Final status message
|
||||
if (cancelDelete) {
|
||||
const canceledMsg = await message.channel.send(`Server cleanup canceled after removing ${totalDeleted} messages across ${processedChannels} channels.`);
|
||||
setTimeout(() => canceledMsg.delete().catch(console.error), deleteTimeout);
|
||||
|
@ -2,14 +2,28 @@ let targetUserIds = new Set();
|
||||
let isActive = false;
|
||||
let channelToWatch = null;
|
||||
let lastAddTimes = new Map();
|
||||
let failedAttempts = new Map();
|
||||
let recentAdds = new Map();
|
||||
|
||||
const getRandomDelay = () => {
|
||||
return Math.floor(Math.random() * 200) + 150;
|
||||
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);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'groupadd',
|
||||
description: 'Automatically re-adds users to group when they leave. Use multiple IDs for multiple targets.',
|
||||
description: 'Automatically re-adds users to group when they leave.',
|
||||
async execute(message, args, deleteTimeout) {
|
||||
const { extractUserId } = require('../utils/userUtils');
|
||||
|
||||
@ -23,6 +37,8 @@ module.exports = {
|
||||
isActive = false;
|
||||
targetUserIds.clear();
|
||||
lastAddTimes.clear();
|
||||
failedAttempts.clear();
|
||||
recentAdds.clear();
|
||||
channelToWatch = null;
|
||||
console.log('[GROUPADD] System deactivated');
|
||||
|
||||
@ -44,6 +60,8 @@ module.exports = {
|
||||
channelToWatch = message.channel;
|
||||
targetUserIds = new Set(validIds);
|
||||
isActive = true;
|
||||
failedAttempts.clear();
|
||||
recentAdds.clear();
|
||||
|
||||
console.log(`[GROUPADD] System activated - Targeting users: ${Array.from(targetUserIds).join(', ')}`);
|
||||
|
||||
@ -51,39 +69,91 @@ module.exports = {
|
||||
try {
|
||||
if (!channelToWatch.recipients.has(userId)) {
|
||||
console.log(`[GROUPADD] Target ${userId} not in group, attempting initial add`);
|
||||
|
||||
const initialDelay = Math.floor(Math.random() * 500) + 300;
|
||||
await new Promise(resolve => setTimeout(resolve, initialDelay));
|
||||
|
||||
await channelToWatch.addUser(userId);
|
||||
lastAddTimes.set(userId, Date.now());
|
||||
recentAdds.set(userId, true);
|
||||
|
||||
setTimeout(() => {
|
||||
recentAdds.delete(userId);
|
||||
}, 10000);
|
||||
|
||||
console.log(`[GROUPADD] Initial add successful for ${userId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[GROUPADD] Initial add failed for ${userId}:`, error);
|
||||
failedAttempts.set(userId, (failedAttempts.get(userId) || 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
message.client.on('channelRecipientRemove', async (channel, user) => {
|
||||
const handleRecipientRemove = async (channel, user) => {
|
||||
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;
|
||||
|
||||
const isRecentlyAdded = recentAdds.has(user.id);
|
||||
const failCount = failedAttempts.get(user.id) || 0;
|
||||
|
||||
if (timeSinceLastAdd < 1000) {
|
||||
console.log(`[GROUPADD] Rate limiting for ${user.id}, waiting...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 - timeSinceLastAdd));
|
||||
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));
|
||||
}
|
||||
|
||||
const delay = getRandomDelay();
|
||||
console.log(`[GROUPADD] User ${user.id} left, waiting ${delay}ms before re-adding`);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
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;
|
||||
}
|
||||
|
||||
try {
|
||||
await channel.addUser(user.id);
|
||||
lastAddTimes.set(user.id, Date.now());
|
||||
recentAdds.set(user.id, true);
|
||||
|
||||
setTimeout(() => {
|
||||
recentAdds.delete(user.id);
|
||||
}, 10000);
|
||||
|
||||
console.log(`[GROUPADD] Successfully re-added user ${user.id}`);
|
||||
|
||||
if (failedAttempts.get(user.id) > 0) {
|
||||
failedAttempts.set(user.id, Math.max(0, failedAttempts.get(user.id) - 1));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[GROUPADD] Failed to re-add user ${user.id}:`, error);
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
message.client.on('channelRecipientRemove', handleRecipientRemove);
|
||||
|
||||
const targetCount = targetUserIds.size;
|
||||
message.channel.send(`Now watching for ${targetCount} user${targetCount > 1 ? 's' : ''} to leave the group.`)
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
135
commands/ssh.js
135
commands/ssh.js
@ -1,135 +0,0 @@
|
||||
const { Client: SSHClient } = require('ssh2');
|
||||
|
||||
let sshConnection = null;
|
||||
|
||||
module.exports = {
|
||||
name: 'ssh',
|
||||
description: 'Manage an SSH connection. Subcommands: connect, exec, disconnect',
|
||||
async execute(message, args, deleteTimeout) {
|
||||
if (!args.length) {
|
||||
return message.channel
|
||||
.send("Usage: `.ssh <connect|exec|disconnect> ...`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
const subcommand = args.shift().toLowerCase();
|
||||
|
||||
if (subcommand === 'connect') {
|
||||
if (args.length < 3) {
|
||||
return message.channel
|
||||
.send("Usage: `.ssh connect <host> <username> <password> [port]`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
if (sshConnection) {
|
||||
return message.channel
|
||||
.send("Already connected. Disconnect first using `.ssh disconnect`.")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
const host = args[0];
|
||||
const username = args[1];
|
||||
const password = args[2];
|
||||
const port = args[3] ? parseInt(args[3]) : 22;
|
||||
|
||||
sshConnection = new SSHClient();
|
||||
|
||||
sshConnection
|
||||
.on('ready', () => {
|
||||
message.channel
|
||||
.send(`Connected to ${host}`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
})
|
||||
.on('error', (err) => {
|
||||
message.channel
|
||||
.send(`SSH Connection error: ${err.message}`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
sshConnection = null;
|
||||
})
|
||||
.on('close', () => {
|
||||
message.channel
|
||||
.send(`SSH Connection closed.`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
sshConnection = null;
|
||||
});
|
||||
|
||||
sshConnection.connect({ host, port, username, password });
|
||||
return;
|
||||
}
|
||||
|
||||
else if (subcommand === 'exec') {
|
||||
if (!sshConnection) {
|
||||
return message.channel
|
||||
.send("No active SSH connection. Connect first using `.ssh connect ...`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
if (!args.length) {
|
||||
return message.channel
|
||||
.send("Usage: `.ssh exec <command>`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
const cmd = args.join(' ');
|
||||
|
||||
sshConnection.exec(cmd, (err, stream) => {
|
||||
if (err) {
|
||||
return message.channel
|
||||
.send(`Error executing command: ${err.message}`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
let outputBuffer = '';
|
||||
|
||||
message.channel.send(`Executing command: \`${cmd}\`\n\`\`\`\n...\n\`\`\``)
|
||||
.then((sentMsg) => {
|
||||
const updateInterval = setInterval(() => {
|
||||
let display = outputBuffer;
|
||||
if (display.length > 1900) {
|
||||
display = display.slice(-1900);
|
||||
}
|
||||
sentMsg.edit(`Executing command: \`${cmd}\`\n\`\`\`\n${display}\n\`\`\``)
|
||||
.catch(() => { });
|
||||
}, 2000);
|
||||
|
||||
stream.on('data', (data) => {
|
||||
outputBuffer += data.toString();
|
||||
});
|
||||
stream.stderr.on('data', (data) => {
|
||||
outputBuffer += data.toString();
|
||||
});
|
||||
stream.on('close', (code, signal) => {
|
||||
clearInterval(updateInterval);
|
||||
outputBuffer += `\nProcess exited with code ${code}${signal ? ' and signal ' + signal : ''}`;
|
||||
let display = outputBuffer;
|
||||
if (display.length > 1900) {
|
||||
display = display.slice(-1900);
|
||||
}
|
||||
sentMsg.edit(`Executing command: \`${cmd}\`\n\`\`\`\n${display}\n\`\`\``)
|
||||
.catch(() => { });
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
else if (subcommand === 'disconnect') {
|
||||
if (!sshConnection) {
|
||||
return message.channel
|
||||
.send("No active SSH connection.")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
sshConnection.end();
|
||||
sshConnection = null;
|
||||
return message.channel
|
||||
.send("Disconnecting SSH...")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
else {
|
||||
return message.channel
|
||||
.send("Unknown subcommand. Use `connect`, `exec`, or `disconnect`.")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user