From 1f8dced90eed738766bdb3af643fae46b261639c Mon Sep 17 00:00:00 2001 From: Wizzard <rich@bandaholics.cash> Date: Thu, 10 Apr 2025 15:09:11 -0400 Subject: [PATCH] Refactor normal delete --- commands/delete.js | 352 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 298 insertions(+), 54 deletions(-) diff --git a/commands/delete.js b/commands/delete.js index c4cfd9e..aa3e949 100644 --- a/commands/delete.js +++ b/commands/delete.js @@ -1,5 +1,15 @@ let isDeleting = false; 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})`); + deletedMessages.clear(); + } +}, CACHE_CLEANUP_INTERVAL); module.exports = { name: 'delete', @@ -21,15 +31,22 @@ 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 + } + const deleteCount = parseInt(args[0], 10); const targetGuildId = args[1]; if (!isNaN(deleteCount) && deleteCount > 0) { - await deleteMessagesFromChannel(message, deleteCount, deleteTimeout); + await deleteMessagesFromChannel(message, deleteCount, deleteTimeout, speed); } else if (targetGuildId) { - await deleteMessagesFromServer(message, targetGuildId, deleteTimeout); + await deleteMessagesFromServer(message, targetGuildId, deleteTimeout, speed); } else { - const errorMsg = await message.channel.send('Please provide a valid number of messages or server ID.'); + const errorMsg = await message.channel.send('Please specify how many messages to delete or a server ID. You can also set speed: `.delete [slow/medium/fast] [count/server]`'); setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); } @@ -37,51 +54,165 @@ module.exports = { }, }; -async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout) { - const getRandomDelay = () => Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000; - const getBatchDelay = () => Math.floor(Math.random() * (10000 - 5000 + 1)) + 5000; - const BATCH_SIZE = 10; +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) { + case 'slow': + deleteIntervalMin = 3000; + deleteIntervalMax = 6000; + jitterFactor = 0.5; + pauseChance = 0.25; + pauseLengthMin = 15000; + pauseLengthMax = 45000; + batchSize = 5; + break; + case 'fast': + deleteIntervalMin = 1500; + deleteIntervalMax = 3000; + jitterFactor = 0.3; + pauseChance = 0.05; + pauseLengthMin = 5000; + pauseLengthMax = 15000; + batchSize = 15; + break; + case 'medium': + default: + deleteIntervalMin = 2000; + deleteIntervalMax = 4500; + jitterFactor = 0.4; + pauseChance = 0.15; + pauseLengthMin = 10000; + pauseLengthMax = 30000; + batchSize = 10; + } + + // More human-like delay functions + const getHumanlikeDelay = () => { + const baseInterval = Math.floor(Math.random() * (deleteIntervalMax - deleteIntervalMin + 1)) + deleteIntervalMin; + const jitterAmount = baseInterval * jitterFactor; + const jitter = Math.random() * jitterAmount * 2 - jitterAmount; + return Math.max(1000, Math.floor(baseInterval + jitter)); + }; + + const getReadingDelay = () => Math.floor(Math.random() * 3000) + 1000; // 1-4 seconds try { - let remainingMessages = deleteCount; - - while (remainingMessages > 0 && !cancelDelete) { - const fetchLimit = Math.min(remainingMessages, BATCH_SIZE); - const messages = await message.channel.messages.fetch({ limit: fetchLimit + 1 }); - - const filteredMessages = messages.filter(msg => msg.author.id === message.author.id); + console.log(`[DELETE] Starting deletion of up to ${deleteCount} messages with ${speed} speed`); + let deletedCount = 0; + 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 filteredMessages = messages.filter(msg => + msg.author.id === message.author.id && + !deletedMessages.has(msg.id) + ); + + if (filteredMessages.size === 0) { + console.log(`[DELETE] No more messages found in this channel`); + break; + } + + // Process current batch + batchCount++; + let messagesInThisBatch = 0; + for (const msg of filteredMessages.values()) { - if (cancelDelete) return; + if (cancelDelete) { + console.log(`[DELETE] Operation canceled by user`); + return; + } + + // Exit the loop if we've deleted enough messages + if (deletedCount >= deleteCount) break; + try { - if (msg.deletable && !msg.deleted) { - const delay = getRandomDelay(); - await new Promise(resolve => setTimeout(resolve, delay)); + 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.error('Failed to delete message:', 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)); } } catch (error) { - console.error('Error deleting message:', error); + console.error('[DELETE] Error deleting message:', error); } } - - remainingMessages -= filteredMessages.size; - - if (remainingMessages > 0 && !cancelDelete) { - const batchDelay = getBatchDelay(); - await new Promise(resolve => setTimeout(resolve, batchDelay)); + + // 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 + } } } + + // 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); + } else { + const completedMsg = await message.channel.send(`Finished deleting ${deletedCount} messages.`); + setTimeout(() => completedMsg.delete().catch(console.error), deleteTimeout); + } } catch (error) { - console.error('Failed to delete messages:', error); + console.error('[DELETE] Failed to delete messages:', error); const errorMsg = await message.channel.send('There was an error while trying to delete messages.'); setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); } } -async function deleteMessagesFromServer(message, guildId, deleteTimeout) { +async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed = 'medium') { const guild = message.client.guilds.cache.get(guildId); if (!guild) { @@ -89,52 +220,165 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout) { setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); return; } + + // Human-like timing parameters based on speed setting + let deleteIntervalMin, deleteIntervalMax, jitterFactor, pauseChance, pauseLengthMin, pauseLengthMax, batchSize; + + switch(speed) { + case 'slow': + deleteIntervalMin = 3000; + deleteIntervalMax = 6000; + jitterFactor = 0.5; + pauseChance = 0.4; // Higher chance to pause between channels + pauseLengthMin = 30000; + pauseLengthMax = 90000; + batchSize = 5; + break; + case 'fast': + deleteIntervalMin = 1500; + deleteIntervalMax = 3000; + jitterFactor = 0.3; + pauseChance = 0.2; + pauseLengthMin = 15000; + pauseLengthMax = 45000; + batchSize = 15; + break; + case 'medium': + default: + deleteIntervalMin = 2000; + deleteIntervalMax = 4500; + jitterFactor = 0.4; + pauseChance = 0.3; + pauseLengthMin = 20000; + pauseLengthMax = 60000; + batchSize = 10; + } + + // More human-like delay functions + const getHumanlikeDelay = () => { + const baseInterval = Math.floor(Math.random() * (deleteIntervalMax - deleteIntervalMin + 1)) + deleteIntervalMin; + const jitterAmount = baseInterval * jitterFactor; + const jitter = Math.random() * jitterAmount * 2 - jitterAmount; + return Math.max(1000, Math.floor(baseInterval + jitter)); + }; - const getRandomDelay = () => Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000; - const getBatchDelay = () => Math.floor(Math.random() * (10000 - 5000 + 1)) + 5000; - const BATCH_SIZE = 10; + console.log(`[DELETE] Starting server-wide deletion in server: ${guild.name}`); + let totalDeleted = 0; try { const channels = guild.channels.cache.filter(channel => channel.isText()); + let processedChannels = 0; + for (const [channelId, channel] of channels) { - if (cancelDelete) return; + if (cancelDelete) { + console.log(`[DELETE] Operation canceled by user`); + break; + } + + processedChannels++; + console.log(`[DELETE] Processing channel ${processedChannels}/${channels.size}: ${channel.name}`); + let hasMoreMessages = true; + let messagesDeletedInChannel = 0; + let batchCount = 0; + while (hasMoreMessages && !cancelDelete) { - const messages = await channel.messages.fetch({ limit: BATCH_SIZE }); + const messages = await channel.messages.fetch({ limit: 100 }); if (messages.size === 0) { hasMoreMessages = false; continue; } - const filteredMessages = messages.filter(msg => msg.author.id === message.author.id); - for (const msg of filteredMessages.values()) { - if (cancelDelete) return; - try { - if (msg.deletable && !msg.deleted) { - const delay = getRandomDelay(); - await new Promise(resolve => setTimeout(resolve, delay)); - await msg.delete().catch(err => { - if (err.code !== 10008) { - console.error('Failed to delete message:', err); - } - }); - } - } catch (error) { - console.error('Error deleting message:', error); - } + const filteredMessages = messages.filter(msg => + msg.author.id === message.author.id && + !deletedMessages.has(msg.id) + ); + + if (filteredMessages.size === 0) { + hasMoreMessages = false; + continue; } - if (filteredMessages.size < BATCH_SIZE) { + // Process current batch + batchCount++; + let messagesInThisBatch = 0; + + for (const msg of filteredMessages.values()) { + if (cancelDelete) return; + + 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)); + } + } catch (error) { + 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 { - const batchDelay = getBatchDelay(); - await new Promise(resolve => setTimeout(resolve, batchDelay)); + // 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; + console.log(`[DELETE] Taking a short break for ${Math.round(pauseDuration/1000)} seconds in channel ${channel.name}. Deleted so far: ${messagesDeletedInChannel}`); + await new Promise(resolve => setTimeout(resolve, pauseDuration)); + } } } + + 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}`); + await new Promise(resolve => setTimeout(resolve, pauseDuration)); + } + } + + // 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); + } else { + const completedMsg = await message.channel.send(`Finished cleaning up ${guild.name}: ${totalDeleted} messages deleted across ${processedChannels} channels.`); + setTimeout(() => completedMsg.delete().catch(console.error), deleteTimeout); } } catch (error) { - console.error('Failed to delete messages in the server:', error); + console.error('[DELETE] Failed to delete messages in the server:', error); const errorMsg = await message.channel.send('There was an error while trying to delete messages from the server.'); setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); }