Compare commits
	
		
			33 Commits
		
	
	
		
			50438bf2c8
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1d8017e5b5 | |||
| 887ebb0c64 | |||
| 73149b8d6d | |||
| 5f750b0666 | |||
| b15f7097f8 | |||
| 7ca36a4938 | |||
| 235398d200 | |||
| 1f8dced90e | |||
| ddf4c6ae15 | |||
| c16e55d56c | |||
| 38147c46a2 | |||
| 90df6177ed | |||
| 26e7b8f706 | |||
| 4d2521441d | |||
| 3c055c53d2 | |||
| 259ed424dd | |||
| 59570259d6 | |||
| c287382bd2 | |||
| af3dbe625b | |||
| 4b1740ac07 | |||
| 92e1984cbf | |||
| 0e32bc2fb4 | |||
| c42d519a06 | |||
| 5912b1fbca | |||
| a2acaa42f1 | |||
| 13b31d4fc3 | |||
| 52b6e1b148 | |||
| 47eb919c0a | |||
| db63ef6827 | |||
| 305b6cc683 | |||
| 4943964b77 | |||
| 129a20c894 | |||
| 4bc2a55a39 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,3 @@ | ||||
| .env | ||||
| node_modules/* | ||||
| *.json | ||||
							
								
								
									
										33
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,6 +2,39 @@ | ||||
|  | ||||
| `discord-selfbot` is a simple selfbot for discord using [discord.js-selfbot-v13](https://github.com/aiko-chan-ai/discord.js-selfbot-v13). | ||||
|  | ||||
| # Discord Selfbot Setup | ||||
|  | ||||
| ## 1. Clone the Repository | ||||
| Open your terminal and run: | ||||
| ```bash | ||||
| git clone https://git.deadzone.lol/Wizzard/discord-selfbot/ | ||||
| cd discord-selfbot | ||||
| ``` | ||||
| Run start.sh / start.bat | ||||
|  | ||||
| ## 2. Get your token | ||||
| Open the file `tokengrab.js` in a text editor. | ||||
|  | ||||
| Copy its entire contents. | ||||
|  | ||||
| Open the Discord client and press Ctrl+Shift+I to open Developer Tools. | ||||
|  | ||||
| Navigate to the Console tab. | ||||
|  | ||||
| Paste the copied contents into the console and press Enter. | ||||
|  | ||||
| ## 3. Configuration | ||||
| Create your configuration file by copying the example file: | ||||
|  | ||||
| `cp EXAMPLE.env .env` (copy file on windows) | ||||
|  | ||||
| Replace the placeholder for DISCORD_TOKEN with your actual Discord token. For example: | ||||
|  | ||||
| ``` | ||||
| DISCORD_TOKEN=discord_token | ||||
| PREFIX=. | ||||
| ``` | ||||
|  | ||||
| ## Disclaimer | ||||
|  | ||||
| I don't take any responsibility for blocked Discord accounts that used this program. | ||||
|   | ||||
							
								
								
									
										563
									
								
								commands/autodelete.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										563
									
								
								commands/autodelete.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,563 @@ | ||||
| let isAutoDeleteActive = false; | ||||
| let deleteQueue = []; | ||||
| let isProcessingQueue = false; | ||||
| let messageTimers = new Map(); | ||||
| let ignoredMessages = new Set(); | ||||
| let ignoredChannels = new Set(); | ||||
| let ignoredUsers = new Set(); | ||||
| let ignoredGuilds = new Set(); | ||||
| let isFirstDeletion = true; | ||||
| let deletedMessages = new Set(); | ||||
| const CACHE_CLEANUP_INTERVAL = 30 * 60 * 1000; | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
| const fs = require('fs'); | ||||
| const path = require('path'); | ||||
|  | ||||
| const DATA_DIR = path.join(__dirname, '..', 'data'); | ||||
| const IGNORE_FILE = path.join(DATA_DIR, 'autodelete_ignores.json'); | ||||
|  | ||||
| if (!fs.existsSync(DATA_DIR)) { | ||||
|     try { | ||||
|         fs.mkdirSync(DATA_DIR, { recursive: true }); | ||||
|         console.log('[AUTODELETE] Created data directory'); | ||||
|     } catch (error) { | ||||
|         console.error('[AUTODELETE] Error creating data directory:', error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function loadIgnoreLists() { | ||||
|     try { | ||||
|         if (fs.existsSync(IGNORE_FILE)) { | ||||
|             const data = JSON.parse(fs.readFileSync(IGNORE_FILE, 'utf8')); | ||||
|              | ||||
|             if (data.ignoredChannels) ignoredChannels = new Set(data.ignoredChannels); | ||||
|             if (data.ignoredUsers) ignoredUsers = new Set(data.ignoredUsers); | ||||
|             if (data.ignoredGuilds) ignoredGuilds = new Set(data.ignoredGuilds); | ||||
|              | ||||
|             console.log('[AUTODELETE] Loaded ignore lists from file'); | ||||
|         } | ||||
|     } catch (error) { | ||||
|         console.error('[AUTODELETE] Error loading ignore lists:', error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function saveIgnoreLists() { | ||||
|     try { | ||||
|         const data = { | ||||
|             ignoredChannels: Array.from(ignoredChannels), | ||||
|             ignoredUsers: Array.from(ignoredUsers), | ||||
|             ignoredGuilds: Array.from(ignoredGuilds) | ||||
|         }; | ||||
|          | ||||
|         fs.writeFileSync(IGNORE_FILE, JSON.stringify(data, null, 2), 'utf8'); | ||||
|         console.log('[AUTODELETE] Saved ignore lists to file'); | ||||
|     } catch (error) { | ||||
|         console.error('[AUTODELETE] Error saving ignore lists:', error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| loadIgnoreLists(); | ||||
|  | ||||
| const DELETION_DELAY = 5 * 60 * 1000; | ||||
| let DELETE_INTERVAL_MIN = 8000; | ||||
| let DELETE_INTERVAL_MAX = 15000; | ||||
| let JITTER_FACTOR = 0.4; | ||||
| let PAUSE_CHANCE = 0.15; | ||||
| let PAUSE_LENGTH_MIN = 30000; | ||||
| let PAUSE_LENGTH_MAX = 120000; | ||||
| let BATCH_SIZE = 3; | ||||
| let currentBatchCount = 0; | ||||
|  | ||||
| setInterval(() => { | ||||
|     if (deletedMessages.size > 1000) { | ||||
|         console.log(`[AUTODELETE] Cleaning message cache (size: ${deletedMessages.size})`); | ||||
|         deletedMessages.clear(); | ||||
|     } | ||||
| }, CACHE_CLEANUP_INTERVAL); | ||||
|  | ||||
| const getHumanlikeDelay = () => { | ||||
|     const baseInterval = Math.floor(Math.random() * (DELETE_INTERVAL_MAX - DELETE_INTERVAL_MIN + 1)) + DELETE_INTERVAL_MIN; | ||||
|  | ||||
|     const jitterAmount = baseInterval * JITTER_FACTOR; | ||||
|     const jitter = Math.random() * jitterAmount * 2 - jitterAmount; | ||||
|  | ||||
|     return Math.max(1000, Math.floor(baseInterval + jitter)); | ||||
| }; | ||||
|  | ||||
| const shouldTakePause = () => { | ||||
|     currentBatchCount++; | ||||
|  | ||||
|     if (currentBatchCount >= BATCH_SIZE) { | ||||
|         currentBatchCount = 0; | ||||
|         return Math.random() < PAUSE_CHANCE; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| }; | ||||
|  | ||||
| const getPauseDuration = () => { | ||||
|     return Math.floor(Math.random() * (PAUSE_LENGTH_MAX - PAUSE_LENGTH_MIN + 1)) + PAUSE_LENGTH_MIN; | ||||
| }; | ||||
|  | ||||
| const processDeleteQueue = async () => { | ||||
|     if (!isProcessingQueue || deleteQueue.length === 0) return; | ||||
|  | ||||
|     try { | ||||
|         const messageToDelete = deleteQueue.shift(); | ||||
|         if (!messageToDelete) return; | ||||
|  | ||||
|         if (deletedMessages.has(messageToDelete.id)) { | ||||
|             console.log(`[AUTODELETE] Message ${messageToDelete.id} already deleted (cached), skipping`); | ||||
|  | ||||
|             if (deleteQueue.length > 0 && isProcessingQueue) { | ||||
|                 scheduleNextDeletion(); | ||||
|             } else { | ||||
|                 isProcessingQueue = false; | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const preDeleteDelay = Math.floor(Math.random() * 1500) + 500; // 500-2000ms | ||||
|         await new Promise(resolve => setTimeout(resolve, preDeleteDelay)); | ||||
|  | ||||
|         if (isFirstDeletion || Math.random() < 0.35) { | ||||
|             console.log(`[AUTODELETE] Checking message ${messageToDelete.id} existence${isFirstDeletion ? ' (first deletion)' : ''}`); | ||||
|             const exists = await messageToDelete.fetch().catch(() => null); | ||||
|             if (!exists) { | ||||
|                 console.log(`[AUTODELETE] Message ${messageToDelete.id} no longer exists, adding to cache`); | ||||
|                 deletedMessages.add(messageToDelete.id); | ||||
|                 isFirstDeletion = false; | ||||
|  | ||||
|                 if (deleteQueue.length > 0 && isProcessingQueue) { | ||||
|                     scheduleNextDeletion(); | ||||
|                 } else { | ||||
|                     isProcessingQueue = false; | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (Math.random() < 0.25) { | ||||
|             const readingDelay = Math.floor(Math.random() * 3000) + 1000; // 1-4 seconds "reading" delay | ||||
|             console.log(`[AUTODELETE] Taking ${readingDelay}ms to "read" before deleting`); | ||||
|             await new Promise(resolve => setTimeout(resolve, readingDelay)); | ||||
|         } | ||||
|  | ||||
|         await messageToDelete.delete().catch((error) => { | ||||
|             if (error.code === 10008) { | ||||
|                 console.log(`[AUTODELETE] Message ${messageToDelete.id} already deleted, adding to cache`); | ||||
|                 deletedMessages.add(messageToDelete.id); | ||||
|             } else if (error.code === 429) { | ||||
|                 console.log(`[AUTODELETE] Rate limited when deleting ${messageToDelete.id}. Will retry later.`); | ||||
|  | ||||
|                 deleteQueue.push(messageToDelete); | ||||
|  | ||||
|                 DELETE_INTERVAL_MIN = Math.min(DELETE_INTERVAL_MIN * 1.5, 25000); | ||||
|                 DELETE_INTERVAL_MAX = Math.min(DELETE_INTERVAL_MAX * 1.5, 45000); | ||||
|                 console.log(`[AUTODELETE] Increased deletion intervals to ${DELETE_INTERVAL_MIN}-${DELETE_INTERVAL_MAX}ms`); | ||||
|             } else { | ||||
|                 console.log(`[AUTODELETE] Couldn't delete message ${messageToDelete.id}:`, error); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         if (!deletedMessages.has(messageToDelete.id)) { | ||||
|             deletedMessages.add(messageToDelete.id); | ||||
|             console.log(`[AUTODELETE] Successfully deleted message ${messageToDelete.id}`); | ||||
|  | ||||
|             if (Math.random() < 0.1) { | ||||
|                 DELETE_INTERVAL_MIN = Math.max(DELETE_INTERVAL_MIN * 0.95, 8000); | ||||
|                 DELETE_INTERVAL_MAX = Math.max(DELETE_INTERVAL_MAX * 0.95, 15000); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         isFirstDeletion = false; | ||||
|  | ||||
|     } catch (error) { | ||||
|         console.log('[AUTODELETE] Error processing queue:', error); | ||||
|     } | ||||
|  | ||||
|     if (deleteQueue.length > 0 && isProcessingQueue) { | ||||
|         scheduleNextDeletion(); | ||||
|     } else { | ||||
|         isProcessingQueue = false; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const scheduleNextDeletion = () => { | ||||
|     if (shouldTakePause()) { | ||||
|         const pauseDuration = getPauseDuration(); | ||||
|         console.log(`[AUTODELETE] Taking a break for ${Math.round(pauseDuration / 1000)} seconds before continuing deletion. Queue size: ${deleteQueue.length}`); | ||||
|         setTimeout(processDeleteQueue, pauseDuration); | ||||
|     } else { | ||||
|         let nextInterval = getHumanlikeDelay(); | ||||
|  | ||||
|         if (deleteQueue.length > 15) { | ||||
|             nextInterval = Math.max(Math.floor(nextInterval * 0.8), 5000); | ||||
|         } | ||||
|         else if (deleteQueue.length <= 2) { | ||||
|             nextInterval = Math.floor(nextInterval * 1.2); | ||||
|         } | ||||
|  | ||||
|         console.log(`[AUTODELETE] Next deletion in ${nextInterval}ms | Queue size: ${deleteQueue.length}`); | ||||
|         setTimeout(processDeleteQueue, nextInterval); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const startQueueProcessing = () => { | ||||
|     if (!isProcessingQueue && deleteQueue.length > 0) { | ||||
|         isProcessingQueue = true; | ||||
|         currentBatchCount = 0; | ||||
|         processDeleteQueue(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const handleNewMessage = (message) => { | ||||
|     if (!isAutoDeleteActive || message.author.id !== message.client.user.id) return; | ||||
|  | ||||
|     if (ignoredMessages.has(message.id) || deletedMessages.has(message.id)) { | ||||
|         console.log(`[AUTODELETE] Skipping cached/ignored message: ${message.id}`); | ||||
|         return; | ||||
|     } | ||||
|     if (message.content.startsWith('.autodelete')) { | ||||
|         console.log(`[AUTODELETE] Skipping command message: ${message.id}`); | ||||
|         ignoredMessages.add(message.id); | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     if (message.content.includes('```') && message.content.length > 100) { | ||||
|         console.log(`[AUTODELETE] Skipping command response message: ${message.id}`); | ||||
|         ignoredMessages.add(message.id); | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     if (message.scheduledForDeletion) { | ||||
|         console.log(`[AUTODELETE] Skipping message already scheduled for deletion: ${message.id}`); | ||||
|         ignoredMessages.add(message.id); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (message.channel && ignoredChannels.has(message.channel.id)) { | ||||
|         console.log(`[AUTODELETE] Skipping message in ignored channel: ${message.channel.id}`); | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     if (message.guild && ignoredGuilds.has(message.guild.id)) { | ||||
|         console.log(`[AUTODELETE] Skipping message in ignored guild: ${message.guild.id}`); | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     if (!message.guild && message.channel && ignoredUsers.has(message.channel.recipient?.id)) { | ||||
|         console.log(`[AUTODELETE] Skipping message to ignored user: ${message.channel.recipient.id}`); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     console.log(`[AUTODELETE] New message tracked: ${message.id}`); | ||||
|     console.log(`[AUTODELETE] Content preview: ${message.content.slice(0, 30)}...`); | ||||
|  | ||||
|     const variableDelay = DELETION_DELAY + (Math.random() * 60000) - 30000; // +/- 30 seconds | ||||
|  | ||||
|     const timer = setTimeout(() => { | ||||
|         if (isAutoDeleteActive) { | ||||
|             console.log(`[AUTODELETE] Timer completed for message: ${message.id} after ~${Math.round(variableDelay / 1000 / 60)} minutes`); | ||||
|             if (!deletedMessages.has(message.id)) { | ||||
|                 deleteQueue.push(message); | ||||
|                 messageTimers.delete(message.id); | ||||
|                 startQueueProcessing(); | ||||
|             } | ||||
|         } | ||||
|     }, variableDelay); | ||||
|  | ||||
|     messageTimers.set(message.id, timer); | ||||
| }; | ||||
|  | ||||
| module.exports = { | ||||
|     name: 'autodelete', | ||||
|     description: 'Automatically deletes your messages after a set time', | ||||
|     async execute(message, args, deleteTimeout) { | ||||
|         ignoredMessages.add(message.id); | ||||
|  | ||||
|         if (args.length === 0 || args[0].toLowerCase() === 'status') { | ||||
|             let ignoreStatus = ''; | ||||
|             if (ignoredChannels.size > 0 || ignoredGuilds.size > 0 || ignoredUsers.size > 0) { | ||||
|                 ignoreStatus = `\nIgnored: ${ignoredChannels.size} channels, ${ignoredGuilds.size} servers, ${ignoredUsers.size} users`; | ||||
|             } | ||||
|              | ||||
|             const statusText = isAutoDeleteActive | ||||
|                 ? `Auto-delete is ON - Messages will be deleted after approximately ${Math.round(DELETION_DELAY / 1000 / 60)} minutes.` | ||||
|                 : 'Auto-delete is OFF.'; | ||||
|  | ||||
|             await sendCommandResponse( | ||||
|                 message, | ||||
|                 `${statusText}\nQueue size: ${deleteQueue.length} messages | Tracked messages: ${messageTimers.size}${ignoreStatus}`, | ||||
|                 deleteTimeout,  | ||||
|                 true | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const command = args[0].toLowerCase(); | ||||
|  | ||||
|         if (command === 'on' || command === 'start' || command === 'enable') { | ||||
|             if (isAutoDeleteActive) { | ||||
|                 await sendCommandResponse(message, 'Auto-delete is already active.', deleteTimeout, true); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             isAutoDeleteActive = true; | ||||
|             message.client.on('messageCreate', handleNewMessage); | ||||
|  | ||||
|             await sendCommandResponse( | ||||
|                 message, | ||||
|                 `Auto-delete enabled. Your messages will be deleted after approximately ${Math.round(DELETION_DELAY / 1000 / 60)} minutes.`, | ||||
|                 deleteTimeout, | ||||
|                 true | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (command === 'off' || command === 'stop' || command === 'disable') { | ||||
|             if (!isAutoDeleteActive) { | ||||
|                 await sendCommandResponse(message, 'Auto-delete is not active.', deleteTimeout, true); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             isAutoDeleteActive = false; | ||||
|             message.client.off('messageCreate', handleNewMessage); | ||||
|  | ||||
|             for (const timer of messageTimers.values()) { | ||||
|                 clearTimeout(timer); | ||||
|             } | ||||
|             messageTimers.clear(); | ||||
|  | ||||
|             await sendCommandResponse(message, 'Auto-delete disabled. Messages will no longer be automatically deleted.', deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (command === 'clear') { | ||||
|             const queueSize = deleteQueue.length; | ||||
|             const trackCount = messageTimers.size; | ||||
|              | ||||
|             deleteQueue = []; | ||||
|             for (const timer of messageTimers.values()) { | ||||
|                 clearTimeout(timer); | ||||
|             } | ||||
|             messageTimers.clear(); | ||||
|             currentBatchCount = 0; | ||||
|              | ||||
|             await sendCommandResponse(message, `Cleared auto-delete queue (${queueSize} pending, ${trackCount} tracked).`, deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (command === 'speed') { | ||||
|             const speedOption = args[1]?.toLowerCase(); | ||||
|              | ||||
|             if (!speedOption || !['slow', 'normal', 'fast'].includes(speedOption)) { | ||||
|                 await sendCommandResponse(message, 'Please specify a valid speed: slow, normal, or fast.', deleteTimeout, true); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             if (speedOption === 'slow') { | ||||
|                 DELETE_INTERVAL_MIN = 12000; | ||||
|                 DELETE_INTERVAL_MAX = 25000; | ||||
|                 JITTER_FACTOR = 0.5; | ||||
|                 PAUSE_CHANCE = 0.25; | ||||
|                 PAUSE_LENGTH_MIN = 45000; | ||||
|                 PAUSE_LENGTH_MAX = 180000; | ||||
|                 BATCH_SIZE = 2; | ||||
|             } else if (speedOption === 'fast') { | ||||
|                 DELETE_INTERVAL_MIN = 5000; | ||||
|                 DELETE_INTERVAL_MAX = 10000; | ||||
|                 JITTER_FACTOR = 0.3; | ||||
|                 PAUSE_CHANCE = 0.1; | ||||
|                 PAUSE_LENGTH_MIN = 15000; | ||||
|                 PAUSE_LENGTH_MAX = 60000; | ||||
|                 BATCH_SIZE = 5; | ||||
|             } else { | ||||
|                 DELETE_INTERVAL_MIN = 8000; | ||||
|                 DELETE_INTERVAL_MAX = 15000; | ||||
|                 JITTER_FACTOR = 0.4; | ||||
|                 PAUSE_CHANCE = 0.15; | ||||
|                 PAUSE_LENGTH_MIN = 30000; | ||||
|                 PAUSE_LENGTH_MAX = 120000; | ||||
|                 BATCH_SIZE = 3; | ||||
|             } | ||||
|              | ||||
|             await sendCommandResponse(message, `Auto-delete speed set to ${speedOption}.`, deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (command === 'ignore') { | ||||
|             if (args.length < 2) { | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     'Please specify what to ignore. Usage: `.autodelete ignore [channel/server/user] [ID]`', | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             const ignoreType = args[1].toLowerCase(); | ||||
|             const id = args[2]; | ||||
|              | ||||
|             if (!id || !/^\d{17,19}$/.test(id)) { | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     'Please provide a valid ID (channel, server, or user ID).', | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             if (ignoreType === 'channel' || ignoreType === 'c') { | ||||
|                 ignoredChannels.add(id); | ||||
|                 saveIgnoreLists(); | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     `Channel ${id} will now be ignored by auto-delete.`, | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|             } else if (ignoreType === 'server' || ignoreType === 'guild' || ignoreType === 's' || ignoreType === 'g') { | ||||
|                 ignoredGuilds.add(id); | ||||
|                 saveIgnoreLists(); | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     `Server ${id} will now be ignored by auto-delete.`, | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|             } else if (ignoreType === 'user' || ignoreType === 'u' || ignoreType === 'dm') { | ||||
|                 ignoredUsers.add(id); | ||||
|                 saveIgnoreLists(); | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     `User ${id} (DMs) will now be ignored by auto-delete.`, | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|             } else { | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     'Invalid ignore type. Use "channel", "server", or "user".', | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         if (command === 'unignore') { | ||||
|             if (args.length < 2) { | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     'Please specify what to unignore. Usage: `.autodelete unignore [channel/server/user] [ID]`', | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             const ignoreType = args[1].toLowerCase(); | ||||
|             const id = args[2]; | ||||
|              | ||||
|             if (!id || !/^\d{17,19}$/.test(id)) { | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     'Please provide a valid ID (channel, server, or user ID).', | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             if (ignoreType === 'channel' || ignoreType === 'c') { | ||||
|                 if (ignoredChannels.has(id)) { | ||||
|                     ignoredChannels.delete(id); | ||||
|                     saveIgnoreLists(); | ||||
|                     await sendCommandResponse( | ||||
|                         message, | ||||
|                         `Channel ${id} will no longer be ignored by auto-delete.`, | ||||
|                         deleteTimeout, | ||||
|                         true | ||||
|                     ); | ||||
|                 } else { | ||||
|                     await sendCommandResponse( | ||||
|                         message, | ||||
|                         `Channel ${id} is not in the ignore list.`, | ||||
|                         deleteTimeout, | ||||
|                         true | ||||
|                     ); | ||||
|                 } | ||||
|             } else if (ignoreType === 'server' || ignoreType === 'guild' || ignoreType === 's' || ignoreType === 'g') { | ||||
|                 if (ignoredGuilds.has(id)) { | ||||
|                     ignoredGuilds.delete(id); | ||||
|                     saveIgnoreLists(); | ||||
|                     await sendCommandResponse( | ||||
|                         message, | ||||
|                         `Server ${id} will no longer be ignored by auto-delete.`, | ||||
|                         deleteTimeout, | ||||
|                         true | ||||
|                     ); | ||||
|                 } else { | ||||
|                     await sendCommandResponse( | ||||
|                         message, | ||||
|                         `Server ${id} is not in the ignore list.`, | ||||
|                         deleteTimeout, | ||||
|                         true | ||||
|                     ); | ||||
|                 } | ||||
|             } else if (ignoreType === 'user' || ignoreType === 'u' || ignoreType === 'dm') { | ||||
|                 if (ignoredUsers.has(id)) { | ||||
|                     ignoredUsers.delete(id); | ||||
|                     saveIgnoreLists(); | ||||
|                     await sendCommandResponse( | ||||
|                         message, | ||||
|                         `User ${id} (DMs) will no longer be ignored by auto-delete.`, | ||||
|                         deleteTimeout, | ||||
|                         true | ||||
|                     ); | ||||
|                 } else { | ||||
|                     await sendCommandResponse( | ||||
|                         message, | ||||
|                         `User ${id} is not in the ignore list.`, | ||||
|                         deleteTimeout, | ||||
|                         true | ||||
|                     ); | ||||
|                 } | ||||
|             } else { | ||||
|                 await sendCommandResponse( | ||||
|                     message, | ||||
|                     'Invalid ignore type. Use "channel", "server", or "user".', | ||||
|                     deleteTimeout, | ||||
|                     true | ||||
|                 ); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         if (command === 'ignorelist' || command === 'list') { | ||||
|             let ignoredChannelsList = Array.from(ignoredChannels).join(', '); | ||||
|             let ignoredGuildsList = Array.from(ignoredGuilds).join(', '); | ||||
|             let ignoredUsersList = Array.from(ignoredUsers).join(', '); | ||||
|              | ||||
|             const ignoredInfo = `**Ignored Channels:** ${ignoredChannels.size > 0 ? ignoredChannelsList : 'None'}\n**Ignored Servers:** ${ignoredGuilds.size > 0 ? ignoredGuildsList : 'None'}\n**Ignored Users (DMs):** ${ignoredUsers.size > 0 ? ignoredUsersList : 'None'}`; | ||||
|              | ||||
|             await sendCommandResponse( | ||||
|                 message, | ||||
|                 `**Auto-delete Ignore List**\n\n${ignoredInfo}`, | ||||
|                 deleteTimeout, | ||||
|                 true | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         await sendCommandResponse( | ||||
|             message, | ||||
|             'Unknown command. Valid commands: on, off, status, ignore, unignore, ignorelist', | ||||
|             deleteTimeout, | ||||
|             true | ||||
|         ); | ||||
|     }, | ||||
| }; | ||||
| @@ -1,5 +1,15 @@ | ||||
| let isDeleting = false; | ||||
| let cancelDelete = false; | ||||
| let deletedMessages = new Set(); | ||||
| const CACHE_CLEANUP_INTERVAL = 30 * 60 * 1000; | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| setInterval(() => { | ||||
|   if (deletedMessages.size > 1000) { | ||||
|     console.log(`[DELETE] Cleaning message cache (size: ${deletedMessages.size})`); | ||||
|     deletedMessages.clear(); | ||||
|   } | ||||
| }, CACHE_CLEANUP_INTERVAL); | ||||
|  | ||||
| module.exports = { | ||||
|   name: 'delete', | ||||
| @@ -7,135 +17,330 @@ module.exports = { | ||||
|   async execute(message, args, deleteTimeout) { | ||||
|     if (args[0] && args[0].toLowerCase() === 'cancel') { | ||||
|       cancelDelete = true; | ||||
|       const cancelMsg = await message.channel.send('Delete operation canceled.'); | ||||
|       setTimeout(() => cancelMsg.delete().catch(console.error), deleteTimeout); | ||||
|       await sendCommandResponse(message, 'Delete operation canceled.', deleteTimeout, true); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (isDeleting) { | ||||
|       const inProgressMsg = await message.channel.send('A delete operation is already in progress. Please wait or cancel it with `.delete cancel`.'); | ||||
|       setTimeout(() => inProgressMsg.delete().catch(console.error), deleteTimeout); | ||||
|       await sendCommandResponse(message, 'A delete operation is already in progress. Please wait or cancel it with `.delete cancel`.', deleteTimeout, true); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     isDeleting = true; | ||||
|     cancelDelete = false; | ||||
|  | ||||
|     let speed = 'medium'; | ||||
|     if (args[0] && ['slow', 'medium', 'fast'].includes(args[0].toLowerCase())) { | ||||
|       speed = args[0].toLowerCase(); | ||||
|       args.shift(); | ||||
|     } | ||||
|  | ||||
|     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.'); | ||||
|       setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); | ||||
|       await sendCommandResponse(message, 'Please specify how many messages to delete or a server ID. You can also set speed: `.delete [slow/medium/fast] [count/server]`', deleteTimeout, true); | ||||
|     } | ||||
|  | ||||
|     isDeleting = false; | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| 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') { | ||||
|   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; | ||||
|   } | ||||
|    | ||||
|   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; | ||||
|  | ||||
|   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) { | ||||
|       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 }); | ||||
|        | ||||
|       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; | ||||
|       } | ||||
|        | ||||
|       batchCount++; | ||||
|       let messagesInThisBatch = 0; | ||||
|        | ||||
|       for (const msg of filteredMessages.values()) { | ||||
|         if (cancelDelete) return; | ||||
|         if (cancelDelete) { | ||||
|           console.log(`[DELETE] Operation canceled by user`); | ||||
|           return; | ||||
|         } | ||||
|          | ||||
|         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)) { | ||||
|             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)); | ||||
|             } | ||||
|              | ||||
|             const preDeleteDelay = Math.floor(Math.random() * 1000) + 250; | ||||
|             await new Promise(resolve => setTimeout(resolve, preDeleteDelay)); | ||||
|              | ||||
|             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...`); | ||||
|                 return; | ||||
|               } else { | ||||
|                 console.error(`[DELETE] Failed to delete message:`, err); | ||||
|               } | ||||
|             }); | ||||
|              | ||||
|             deletedMessages.add(msg.id); | ||||
|             deletedCount++; | ||||
|             messagesInThisBatch++; | ||||
|              | ||||
|             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)); | ||||
|        | ||||
|       if (messagesInThisBatch === 0) { | ||||
|         console.log(`[DELETE] No deletable messages found in batch`); | ||||
|         break; | ||||
|       } | ||||
|        | ||||
|       if (!cancelDelete && deletedCount < deleteCount) { | ||||
|         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; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     if (cancelDelete) { | ||||
|       await sendCommandResponse(message, `Delete operation canceled after removing ${deletedCount} messages.`, deleteTimeout, true); | ||||
|     } else { | ||||
|       await sendCommandResponse(message, `Finished deleting ${deletedCount} messages.`, deleteTimeout, true); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     console.error('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); | ||||
|     console.error('[DELETE] Failed to delete messages:', error); | ||||
|     await sendCommandResponse(message, 'There was an error while trying to delete messages.', deleteTimeout, true); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function deleteMessagesFromServer(message, guildId, deleteTimeout) { | ||||
| async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed = 'medium') { | ||||
|   const guild = message.client.guilds.cache.get(guildId); | ||||
|  | ||||
|   if (!guild) { | ||||
|     const errorMsg = await message.channel.send('I am not in the server with the specified ID.'); | ||||
|     setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); | ||||
|     await sendCommandResponse(message, `Guild with ID ${guildId} not found.`, deleteTimeout, true); | ||||
|     return; | ||||
|   } | ||||
|    | ||||
|   let deleteIntervalMin, deleteIntervalMax, jitterFactor, pauseChance, pauseLengthMin, pauseLengthMax, batchSize; | ||||
|    | ||||
|   switch(speed) { | ||||
|     case 'slow': | ||||
|       deleteIntervalMin = 3000; | ||||
|       deleteIntervalMax = 6000; | ||||
|       jitterFactor = 0.5; | ||||
|       pauseChance = 0.4; | ||||
|       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; | ||||
|   } | ||||
|    | ||||
|   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) { | ||||
|         batchCount++; | ||||
|         let messagesInThisBatch = 0; | ||||
|          | ||||
|         for (const msg of filteredMessages.values()) { | ||||
|           if (cancelDelete) return; | ||||
|            | ||||
|           try { | ||||
|             if (msg.deletable && !msg.deleted && !deletedMessages.has(msg.id)) { | ||||
|               const preDeleteDelay = Math.floor(Math.random() * 1000) + 250; | ||||
|               await new Promise(resolve => setTimeout(resolve, preDeleteDelay)); | ||||
|                | ||||
|               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...`); | ||||
|                   return new Promise(resolve => setTimeout(resolve, 30000 + Math.random() * 30000)); | ||||
|                 } else { | ||||
|                   console.error(`[DELETE] Failed to delete message:`, err); | ||||
|                 } | ||||
|               }); | ||||
|                | ||||
|               deletedMessages.add(msg.id); | ||||
|               totalDeleted++; | ||||
|               messagesDeletedInChannel++; | ||||
|               messagesInThisBatch++; | ||||
|                | ||||
|               const delay = getHumanlikeDelay(); | ||||
|               await new Promise(resolve => setTimeout(resolve, delay)); | ||||
|             } | ||||
|           } catch (error) { | ||||
|             console.error('[DELETE] Error deleting message:', error); | ||||
|           } | ||||
|            | ||||
|           if (messagesInThisBatch >= batchSize) break; | ||||
|         } | ||||
|  | ||||
|         if (messagesInThisBatch < batchSize) { | ||||
|           hasMoreMessages = false; | ||||
|         } else { | ||||
|           const batchDelay = getBatchDelay(); | ||||
|           await new Promise(resolve => setTimeout(resolve, batchDelay)); | ||||
|           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`); | ||||
|        | ||||
|       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)); | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     if (cancelDelete) { | ||||
|       await sendCommandResponse(message, `Delete operation canceled after removing ${totalDeleted} messages across ${processedChannels} channels.`, deleteTimeout, true); | ||||
|     } else { | ||||
|       await sendCommandResponse(message, `Finished cleaning up ${guild.name}: ${totalDeleted} messages deleted across ${processedChannels} channels.`, deleteTimeout, true); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     console.error('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); | ||||
|     console.error('[DELETE] Failed to delete messages in the server:', error); | ||||
|     await sendCommandResponse(message, 'There was an error while trying to delete messages from the server.', deleteTimeout, true); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										159
									
								
								commands/groupadd.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								commands/groupadd.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| let targetUserIds = new Set(); | ||||
| let isActive = false; | ||||
| let channelToWatch = null; | ||||
| let lastAddTimes = new Map(); | ||||
| let failedAttempts = new Map(); | ||||
| let recentAdds = new Map(); | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| 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.', | ||||
|     async execute(message, args, deleteTimeout) { | ||||
|         const { extractUserId } = require('../utils/userUtils'); | ||||
|          | ||||
|         if (message.channel.type !== 'GROUP_DM') { | ||||
|             await sendCommandResponse(message, 'This command only works in group DMs.', deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (args[0]?.toLowerCase() === 'stop') { | ||||
|             isActive = false; | ||||
|             targetUserIds.clear(); | ||||
|             lastAddTimes.clear(); | ||||
|             failedAttempts.clear(); | ||||
|             recentAdds.clear(); | ||||
|             channelToWatch = null; | ||||
|             console.log('[GROUPADD] System deactivated'); | ||||
|  | ||||
|             await sendCommandResponse(message, 'Group auto-add deactivated.', deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const validIds = args | ||||
|             .map(arg => extractUserId(arg)) | ||||
|             .filter(id => id !== null); | ||||
|              | ||||
|         if (validIds.length === 0) { | ||||
|             await sendCommandResponse(message, 'Please provide at least one valid user ID or @mention.', deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         channelToWatch = message.channel; | ||||
|         targetUserIds = new Set(validIds); | ||||
|         isActive = true; | ||||
|         failedAttempts.clear(); | ||||
|         recentAdds.clear(); | ||||
|  | ||||
|         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`); | ||||
|                      | ||||
|                     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); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|  | ||||
|             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 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; | ||||
|         await sendCommandResponse(message, `Now watching for ${targetCount} user${targetCount > 1 ? 's' : ''} to leave the group.`, deleteTimeout, true); | ||||
|     }, | ||||
| }; | ||||
| @@ -1,7 +1,9 @@ | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| module.exports = { | ||||
|   name: 'help', | ||||
|   description: 'List all of my commands or info about a specific command.', | ||||
|   execute(message, args, deleteTimeout) { | ||||
|   async execute(message, args, deleteTimeout) { | ||||
|     let reply = '```'; | ||||
|     reply += 'Here are the available commands:\n\n'; | ||||
|  | ||||
| @@ -12,9 +14,7 @@ module.exports = { | ||||
|  | ||||
|     reply += '```'; | ||||
|  | ||||
|     message.channel.send(reply).then(sentMessage => { | ||||
|       setTimeout(() => sentMessage.delete().catch(console.error), deleteTimeout); | ||||
|     }); | ||||
|     await sendCommandResponse(message, reply, deleteTimeout, false); | ||||
|   }, | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										98
									
								
								commands/ip.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								commands/ip.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| let vpnRangesCache = null; | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| function ipToInt(ip) { | ||||
|     return ip.split('.').reduce((acc, oct) => (acc << 8) + parseInt(oct, 10), 0) >>> 0; | ||||
| } | ||||
|  | ||||
| function cidrContains(cidr, ip) { | ||||
|     const [range, bitsStr] = cidr.split('/'); | ||||
|     const bits = parseInt(bitsStr, 10); | ||||
|     const ipInt = ipToInt(ip); | ||||
|     const rangeInt = ipToInt(range); | ||||
|     const mask = ~(2 ** (32 - bits) - 1) >>> 0; | ||||
|     return (ipInt & mask) === (rangeInt & mask); | ||||
| } | ||||
|  | ||||
| function isVpnIp(ip) { | ||||
|     if (!vpnRangesCache) return false; | ||||
|     for (const cidr of vpnRangesCache) { | ||||
|         if (cidrContains(cidr, ip)) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| function isValidIp(ip) { | ||||
|     const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; | ||||
|     return ipRegex.test(ip); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     name: 'ip', | ||||
|     description: 'Fetches IP info and checks if the IP is a VPN. Usage: .ip [ip_address]', | ||||
|     async execute(message, args, deleteTimeout) { | ||||
|         const { default: fetch } = await import('node-fetch'); | ||||
|         try { | ||||
|             let targetIp; | ||||
|              | ||||
|             if (args.length > 0) { | ||||
|                 targetIp = args[0]; | ||||
|                 if (!isValidIp(targetIp)) { | ||||
|                     await sendCommandResponse(message, "Invalid IP address format. Please use format: x.x.x.x", deleteTimeout, true); | ||||
|                     return; | ||||
|                 } | ||||
|             } else { | ||||
|                 const ipRes = await fetch('http://ip-api.com/json/'); | ||||
|                 const data = await ipRes.json(); | ||||
|                 targetIp = data.query; | ||||
|             } | ||||
|  | ||||
|             if (!vpnRangesCache) { | ||||
|                 const vpnRes = await fetch('https://raw.githubusercontent.com/X4BNet/lists_vpn/main/ipv4.txt'); | ||||
|                 const vpnText = await vpnRes.text(); | ||||
|                 vpnRangesCache = vpnText.split('\n').map(line => line.trim()).filter(line => line); | ||||
|             } | ||||
|  | ||||
|             const ipRes = await fetch(`http://ip-api.com/json/${targetIp}`); | ||||
|             const data = await ipRes.json(); | ||||
|  | ||||
|             if (data.status === 'fail') { | ||||
|                 await sendCommandResponse(message, `Error: ${data.message}`, deleteTimeout, true); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const ip = data.query || "Unknown"; | ||||
|             const vpnCheck = isVpnIp(ip); | ||||
|  | ||||
|             const hostname = data.hostname || "Unknown"; | ||||
|             const city = data.city || "Unknown"; | ||||
|             const region = data.regionName || "Unknown"; | ||||
|             const country = data.country || "Unknown"; | ||||
|             const timezone = data.timezone || "Unknown"; | ||||
|             const zip = data.zip || "Unknown"; | ||||
|             const isp = data.isp || "Unknown"; | ||||
|             const org = data.org || "Unknown"; | ||||
|             const as = data.as || "Unknown"; | ||||
|  | ||||
|             const output = | ||||
|                 `IP: ${ip} | ||||
| Hostname: ${hostname} | ||||
| City: ${city} | ||||
| Region: ${region} | ||||
| Country: ${country} | ||||
| Time Zone: ${timezone} | ||||
| ZIP: ${zip} | ||||
| ISP: ${isp} | ||||
| Organization: ${org} | ||||
| AS: ${as} | ||||
| VPN: ${vpnCheck ? "True" : "False"}`; | ||||
|  | ||||
|             await sendCommandResponse(message, `\`\`\`\n${output}\n\`\`\``, 30000, true); | ||||
|         } catch (error) { | ||||
|             console.error("Error fetching IP info:", error); | ||||
|             await sendCommandResponse(message, "Error fetching IP info.", deleteTimeout, true); | ||||
|         } | ||||
|     }, | ||||
| }; | ||||
| @@ -1,163 +1,268 @@ | ||||
| let targetUserId = null; | ||||
| let isKickActive = false; | ||||
| let voiceStateHandler = null; | ||||
| let lastKickTime = 0; | ||||
| let consecutiveKicks = 0; | ||||
| let cooldownTime = 0; | ||||
| let checkInterval = null; | ||||
| const { extractUserId } = require('../utils/userUtils'); | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| const getRandomDelay = () => { | ||||
|     return Math.floor(Math.random() * 250) + 100; | ||||
| 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 = () => { | ||||
|     return Math.floor(Math.random() * 250) + 200; | ||||
| 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) => { | ||||
|     if (kicks <= 3) return 200; | ||||
|     if (kicks <= 5) return 500; | ||||
|     if (kicks <= 10) return 1000; | ||||
|     return 2000; | ||||
| 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 a specified user from voice channels.', | ||||
|     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; | ||||
|             targetUserId = null; | ||||
|             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) { | ||||
|             await sendCommandResponse(message, 'Please provide a command: `start <userId(s)>` or `stop <userId or "all">`', deleteTimeout, true); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const userId = args[0]; | ||||
|         if (!userId || !/^\d{17,19}$/.test(userId)) { | ||||
|             message.channel.send('Please provide a valid user ID.') | ||||
|                 .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|             return; | ||||
|         } | ||||
|         const command = args[0].toLowerCase(); | ||||
|  | ||||
|         targetUserId = userId; | ||||
|         isKickActive = true; | ||||
|         console.log(`[KICKVC] System activated - Targeting user ID: ${userId}`); | ||||
|  | ||||
|         if (voiceStateHandler) { | ||||
|             message.client.removeListener('voiceStateUpdate', voiceStateHandler); | ||||
|         } | ||||
|         if (checkInterval) { | ||||
|             clearInterval(checkInterval); | ||||
|         } | ||||
|  | ||||
|         const kickUser = async (member, guild, fromEvent = false) => { | ||||
|             if (!isKickActive) return; | ||||
|  | ||||
|             const currentTime = Date.now(); | ||||
|             const timeSinceLastKick = currentTime - lastKickTime; | ||||
|  | ||||
|             if (timeSinceLastKick < cooldownTime) { | ||||
|         if (command === 'stop') { | ||||
|             if (args.length < 2) { | ||||
|                 await sendCommandResponse(message, 'Please specify a user ID or "all" to stop kicking.', deleteTimeout, true); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             try { | ||||
|                 const delay = fromEvent ? getRandomDelay() : getRandomCheckDelay(); | ||||
|                 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}`); | ||||
|             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 member.voice.disconnect(); | ||||
|                 lastKickTime = currentTime; | ||||
|                 consecutiveKicks++; | ||||
|                 await sendCommandResponse(message, 'Stopped all active VC kicks.', deleteTimeout, true); | ||||
|                 return; | ||||
|             } else { | ||||
|                 const userId = extractUserId(target); | ||||
|                  | ||||
|                 cooldownTime = getCooldown(consecutiveKicks); | ||||
|  | ||||
|                 setTimeout(() => { | ||||
|                     if (consecutiveKicks > 0) { | ||||
|                         consecutiveKicks--; | ||||
|                         cooldownTime = getCooldown(consecutiveKicks); | ||||
|                     } | ||||
|                 }, 15000); | ||||
|  | ||||
|             } catch { | ||||
|                 try { | ||||
|                     await member.voice.setChannel(null); | ||||
|                 } catch { | ||||
|                     try { | ||||
|                         await member.voice.channel.permissionOverwrites.create(member, { | ||||
|                             Connect: false, | ||||
|                             Speak: false | ||||
|                         }); | ||||
|                         await member.voice.disconnect(); | ||||
|                     } catch {} | ||||
|                 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; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         voiceStateHandler = async (oldState, newState) => { | ||||
|             if (!isKickActive || !targetUserId) return; | ||||
|  | ||||
|             const isTargetUser = newState?.member?.id === targetUserId || oldState?.member?.id === targetUserId; | ||||
|             if (!isTargetUser) return; | ||||
|  | ||||
|             const voiceState = newState?.channelId ? newState : oldState; | ||||
|             if (!voiceState?.channel) return; | ||||
|  | ||||
|             try { | ||||
|                 const guild = voiceState.guild; | ||||
|                 const member = await guild.members.fetch(targetUserId).catch(() => null); | ||||
|                 if (member?.voice?.channel) { | ||||
|                     await kickUser(member, guild, true); | ||||
|                 } | ||||
|             } catch {} | ||||
|         }; | ||||
|  | ||||
|         const intervalTime = Math.floor(Math.random() * 500) + 1000; | ||||
|         checkInterval = setInterval(async () => { | ||||
|             if (!isKickActive) return; | ||||
|  | ||||
|             for (const guild of message.client.guilds.cache.values()) { | ||||
|                 try { | ||||
|                     const member = await guild.members.fetch(targetUserId).catch(() => null); | ||||
|                     if (member?.voice?.channel) { | ||||
|                         await kickUser(member, guild, false); | ||||
|                     } | ||||
|                 } catch {} | ||||
|             } | ||||
|         }, intervalTime); | ||||
|  | ||||
|         message.client.on('voiceStateUpdate', voiceStateHandler); | ||||
|  | ||||
|         try { | ||||
|             const user = await message.client.users.fetch(userId); | ||||
|             message.channel.send(`Now automatically kicking ${user.tag} (${userId}) from voice channels.`) | ||||
|                 .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|  | ||||
|             message.client.guilds.cache.forEach(async (guild) => { | ||||
|                 const member = await guild.members.fetch(userId).catch(() => null); | ||||
|                 if (member?.voice?.channel) { | ||||
|                     await kickUser(member, guild, true); | ||||
|                 } | ||||
|             }); | ||||
|         } catch { | ||||
|             message.channel.send(`Now automatically kicking user ID ${userId} from voice channels.`) | ||||
|                 .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|         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); | ||||
|     } | ||||
| }; | ||||
| @@ -1,88 +0,0 @@ | ||||
| const { DiscordStreamClient } = require('discord-stream-client'); | ||||
|  | ||||
| module.exports = { | ||||
|   name: 'livestream', | ||||
|   description: 'Starts or stops a live stream in a voice channel with a provided video link.', | ||||
|   async execute(message, args, deleteTimeout) { | ||||
|     if (args[0] === 'stop') { | ||||
|       if (message.client.voiceConnection) { | ||||
|         message.client.voiceConnection.disconnect(); | ||||
|         message.client.voiceConnection = null; | ||||
|  | ||||
|         if (message.client.currentPlayer) { | ||||
|           message.client.currentPlayer.stop(); | ||||
|           message.client.currentPlayer.removeAllListeners('end'); | ||||
|           message.client.currentPlayer = null; | ||||
|         } | ||||
|  | ||||
|         return message.channel.send('Livestream stopped.').then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|       } else { | ||||
|         return message.channel.send('No active livestream to stop.').then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (args.length < 2) { | ||||
|       return message.channel.send('Usage: .livestream <channelId> <videoLink> | .livestream stop') | ||||
|         .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|     } | ||||
|  | ||||
|     const channelId = args[0]; | ||||
|     const videoLink = args[1]; | ||||
|     const channel = message.client.channels.cache.get(channelId); | ||||
|      | ||||
|     if (!channel) { | ||||
|       return message.channel.send('Channel not found.') | ||||
|         .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|     } | ||||
|  | ||||
|     const voiceState = message.guild.members.me.voice; | ||||
|     if (voiceState.channel) { | ||||
|     	console.log('Already in a voice channel, leaving...'); | ||||
|   	await voiceState.disconnect(); | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       if (message.client.currentPlayer) { | ||||
|         message.client.currentPlayer.stop(); | ||||
|       } | ||||
|  | ||||
|       const connection = await message.client.streamClient.joinVoiceChannel(channel, { | ||||
|         selfDeaf: true, | ||||
|         selfMute: true, | ||||
|         selfVideo: false, | ||||
|       }); | ||||
|  | ||||
|       const stream = await connection.createStream(); | ||||
|       const player = message.client.streamClient.createPlayer(videoLink, stream.udp); | ||||
|       message.client.currentPlayer = player; | ||||
|  | ||||
|       player.on('error', err => console.error(err)); | ||||
|  | ||||
|       const playStream = () => { | ||||
|         player.play(videoLink, { | ||||
|           kbpsVideo: 7000, | ||||
|           fps: 60, | ||||
|           hwaccel: true, | ||||
|           kbpsAudio: 128, | ||||
|           volume: 1, | ||||
|         }); | ||||
|       }; | ||||
|  | ||||
|       player.on('finish', () => { | ||||
|         console.log('Media ended, replaying...'); | ||||
|         playStream(); | ||||
|       }); | ||||
|  | ||||
|       playStream();  | ||||
|  | ||||
|       message.channel.send('Livestream started with the provided video link.') | ||||
|         .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|  | ||||
|     } catch (error) { | ||||
|       console.error(error); | ||||
|       message.channel.send('Failed to start the livestream.') | ||||
|         .then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout)); | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
|  | ||||
							
								
								
									
										147
									
								
								commands/react.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										147
									
								
								commands/react.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,22 +1,23 @@ | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| module.exports = { | ||||
|   name: 'react', | ||||
|   description: `Automatically react with specified emojis to multiple users’ messages, or stop reacting.\n | ||||
|   Usage: | ||||
|   .react <userID1,userID2,...> <emoji1> <emoji2> ... - React to messages from multiple users with specified emojis.  | ||||
|   Example: \`.react 12345,67890 :smile: :thumbsup:\` | ||||
|   .react stop - Stop reacting to users' messages.`, | ||||
|   description: `Automatically react with specified emojis to multiple users' messages, or stop reacting. Usage: .react [user1,user2,...] [emoji1] [emoji2] ...`, | ||||
|   async execute(message, args, deleteTimeout) { | ||||
|     const { processUserInput } = require('../utils/userUtils'); | ||||
|      | ||||
|     if (args.length === 0) { | ||||
|       if (message.client.targetReactUserIds && message.client.reactEmojis) { | ||||
|         const statusMsg = await message.channel.send( | ||||
|         await sendCommandResponse( | ||||
|           message, | ||||
|           `Currently reacting to messages from the following users: ${message.client.targetReactUserIds | ||||
|             .map(id => `<@${id}>`) | ||||
|             .join(', ')} with the following emojis: ${message.client.reactEmojis.join(' ')}.` | ||||
|             .map(id => `User ID: ${id}`) | ||||
|             .join(', ')} with the following emojis: ${message.client.reactEmojis.join(' ')}.`, | ||||
|           deleteTimeout, | ||||
|           false | ||||
|         ); | ||||
|         setTimeout(() => statusMsg.delete().catch(console.error), deleteTimeout); | ||||
|       } else { | ||||
|         const noTargetMsg = await message.channel.send('No active reaction target.'); | ||||
|         setTimeout(() => noTargetMsg.delete().catch(console.error), deleteTimeout); | ||||
|         await sendCommandResponse(message, 'No active reaction target.', deleteTimeout, false); | ||||
|       } | ||||
|       return; | ||||
|     } | ||||
| @@ -28,50 +29,126 @@ module.exports = { | ||||
|         message.client.targetReactUserIds = null; | ||||
|         message.client.reactEmojis = null; | ||||
|  | ||||
|         const stopMsg = await message.channel.send('Stopped reacting to messages.'); | ||||
|         setTimeout(() => stopMsg.delete().catch(console.error), deleteTimeout); | ||||
|         await sendCommandResponse(message, 'Stopped reacting to messages.', deleteTimeout, false); | ||||
|       } else { | ||||
|         const noActiveReactMsg = await message.channel.send('No active reactions to stop.'); | ||||
|         setTimeout(() => noActiveReactMsg.delete().catch(console.error), deleteTimeout); | ||||
|         await sendCommandResponse(message, 'No active reactions to stop.', deleteTimeout, false); | ||||
|       } | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const targetIds = args[0].split(',').map(id => id.trim()); | ||||
|     const emojis = args.slice(1); | ||||
|     // Find where the emojis start | ||||
|     let emojiStartIndex = -1; | ||||
|     for (let i = 0; i < args.length; i++) { | ||||
|       // Check if this argument looks like an emoji (contains : or is a single character) | ||||
|       if (args[i].includes(':') || args[i].length <= 2) { | ||||
|         emojiStartIndex = i; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (targetIds.length === 0 || emojis.length === 0) { | ||||
|       const errorMsg = await message.channel.send('Please provide valid user IDs and at least one emoji.'); | ||||
|       setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); | ||||
|     if (emojiStartIndex === -1) { | ||||
|       await sendCommandResponse(message, 'Please provide at least one emoji to react with.', deleteTimeout, false); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     message.client.targetReactUserIds = targetIds; | ||||
|     message.client.reactEmojis = emojis; | ||||
|     // All arguments before emojiStartIndex are user IDs | ||||
|     const userInput = args.slice(0, emojiStartIndex).join(' '); | ||||
|     const emojis = args.slice(emojiStartIndex); | ||||
|  | ||||
|     const confirmationMsg = await message.channel.send( | ||||
|       `I will now react to messages from the following users: ${targetIds | ||||
|         .map(id => `<@${id}>`) | ||||
|         .join(', ')} with the following emojis: ${emojis.join(' ')}.` | ||||
|     console.log(`[REACT] Processing user input: "${userInput}"`); | ||||
|     const targetIds = processUserInput(userInput); | ||||
|     console.log(`[REACT] Extracted user IDs: ${targetIds.join(', ')}`); | ||||
|      | ||||
|     if (targetIds.length === 0) { | ||||
|       await sendCommandResponse(message, 'Please provide valid user IDs or @mentions. You can use multiple users separated by spaces or commas.', deleteTimeout, false); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Process emojis to handle custom emojis | ||||
|     const processedEmojis = emojis.map(emoji => { | ||||
|       // Check if it's a custom emoji (format: :name:) | ||||
|       const customEmojiMatch = emoji.match(/^:([a-zA-Z0-9_]+):$/); | ||||
|       if (customEmojiMatch) { | ||||
|         // For custom emojis, we need to find the emoji ID from the guild | ||||
|         const emojiName = customEmojiMatch[1]; | ||||
|         const customEmoji = message.guild?.emojis.cache.find(e => e.name === emojiName); | ||||
|         if (customEmoji) { | ||||
|           return customEmoji.id; | ||||
|         } | ||||
|       } | ||||
|       // For standard emojis, just return as is | ||||
|       return emoji; | ||||
|     }); | ||||
|  | ||||
|     message.client.targetReactUserIds = targetIds; | ||||
|     message.client.reactEmojis = processedEmojis; | ||||
|  | ||||
|     // Create a more detailed confirmation message with a different format | ||||
|     let userListText = ''; | ||||
|     if (targetIds.length === 1) { | ||||
|       userListText = `User ID: ${targetIds[0]}`; | ||||
|     } else { | ||||
|       userListText = targetIds.map((id, index) => `User ID ${index + 1}: ${id}`).join('\n'); | ||||
|     } | ||||
|      | ||||
|     const confirmationMessage = `I will now react to messages from:\n${userListText}\n\nWith the following emojis: ${emojis.join(' ')}`; | ||||
|      | ||||
|     console.log(`[REACT] Confirmation message: ${confirmationMessage}`); | ||||
|      | ||||
|     await sendCommandResponse( | ||||
|       message, | ||||
|       confirmationMessage, | ||||
|       deleteTimeout, | ||||
|       false | ||||
|     ); | ||||
|     setTimeout(() => confirmationMsg.delete().catch(console.error), deleteTimeout); | ||||
|  | ||||
|     if (message.client.reactListener) { | ||||
|       message.client.off('messageCreate', message.client.reactListener); | ||||
|     } | ||||
|  | ||||
|     const getRandomDelay = () => Math.floor(Math.random() * (5000 - 2000 + 1)) + 2000; | ||||
|     const getHumanizedDelay = () => { | ||||
|       const baseDelay = Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000; | ||||
|       const jitter = Math.floor(Math.random() * 1000) - 500;  | ||||
|       return Math.max(800, baseDelay + jitter); | ||||
|     }; | ||||
|  | ||||
|     message.client.reactListener = async (msg) => { | ||||
|       if (message.client.targetReactUserIds && message.client.targetReactUserIds.includes(msg.author.id)) { | ||||
|         for (const emoji of emojis) { | ||||
|           try { | ||||
|             const delay = getRandomDelay(); | ||||
|             await new Promise((resolve) => setTimeout(resolve, delay)); | ||||
|             await msg.react(emoji); | ||||
|           } catch (error) { | ||||
|             console.error('Failed to react:', error); | ||||
|         try { | ||||
|           const shouldReact = Math.random() < 0.95;  | ||||
|            | ||||
|           if (!shouldReact) { | ||||
|             console.log(`[REACT] Randomly skipping reaction to message ${msg.id}`); | ||||
|             return; | ||||
|           } | ||||
|            | ||||
|           const initialDelay = getHumanizedDelay(); | ||||
|           await new Promise(resolve => setTimeout(resolve, initialDelay)); | ||||
|            | ||||
|           for (const emoji of processedEmojis) { | ||||
|             if (Math.random() < 0.05) { | ||||
|               console.log(`[REACT] Skipping emoji ${emoji} for more human-like behavior`); | ||||
|               continue; | ||||
|             } | ||||
|              | ||||
|             try { | ||||
|               const reactDelay = getHumanizedDelay(); | ||||
|                | ||||
|               if (Math.random() < 0.08) { | ||||
|                 const extraDelay = Math.floor(Math.random() * 4000) + 1000; | ||||
|                 console.log(`[REACT] Adding ${extraDelay}ms extra delay before reacting with ${emoji}`); | ||||
|                 await new Promise(resolve => setTimeout(resolve, extraDelay)); | ||||
|               } | ||||
|                | ||||
|               await new Promise(resolve => setTimeout(resolve, reactDelay)); | ||||
|               await msg.react(emoji); | ||||
|                | ||||
|             } catch (error) { | ||||
|               console.error(`[REACT] Failed to react with ${emoji}:`, error); | ||||
|             } | ||||
|           } | ||||
|         } catch (error) { | ||||
|           console.error('[REACT] Error in reaction handler:', error); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   | ||||
| @@ -1,22 +1,23 @@ | ||||
| const { sendCommandResponse } = require('../utils/messageUtils'); | ||||
|  | ||||
| module.exports = { | ||||
|     name: 'reply', | ||||
|     description: `Automatically reply with a specified message to multiple users’ messages, or stop replying.\n | ||||
|     Usage: | ||||
|     .reply <userID1,userID2,...> <message> - Automatically reply to messages from multiple users with the specified message. | ||||
|     Example: \`.reply 12345,67890 Hello there!\` | ||||
|     .reply stop - Stop replying to users' messages.`, | ||||
|     description: `Automatically reply with a specified message to multiple users' messages, or stop replying. Usage: .reply [user1,user2,...] [message]`, | ||||
|     async execute(message, args, deleteTimeout) { | ||||
|       const { processUserInput } = require('../utils/userUtils'); | ||||
|        | ||||
|       if (args.length === 0) { | ||||
|         if (message.client.targetReplyUserIds && message.client.replyMessage) { | ||||
|           const statusMsg = await message.channel.send( | ||||
|           await sendCommandResponse( | ||||
|             message, | ||||
|             `Currently replying to messages from the following users: ${message.client.targetReplyUserIds | ||||
|               .map(id => `<@${id}>`) | ||||
|               .join(', ')} with the message: "${message.client.replyMessage}".` | ||||
|               .map(id => `User ID: ${id}`) | ||||
|               .join(', ')} with the message: "${message.client.replyMessage}".`, | ||||
|             deleteTimeout, | ||||
|             false | ||||
|           ); | ||||
|           setTimeout(() => statusMsg.delete().catch(console.error), deleteTimeout); | ||||
|         } else { | ||||
|           const noTargetMsg = await message.channel.send('No active reply target.'); | ||||
|           setTimeout(() => noTargetMsg.delete().catch(console.error), deleteTimeout); | ||||
|           await sendCommandResponse(message, 'No active reply target.', deleteTimeout, false); | ||||
|         } | ||||
|         return; | ||||
|       } | ||||
| @@ -28,33 +29,62 @@ module.exports = { | ||||
|           message.client.targetReplyUserIds = null; | ||||
|           message.client.replyMessage = null; | ||||
|    | ||||
|           const stopMsg = await message.channel.send('Stopped replying to messages.'); | ||||
|           setTimeout(() => stopMsg.delete().catch(console.error), deleteTimeout); | ||||
|           await sendCommandResponse(message, 'Stopped replying to messages.', deleteTimeout, false); | ||||
|         } else { | ||||
|           const noActiveReplyMsg = await message.channel.send('No active replies to stop.'); | ||||
|           setTimeout(() => noActiveReplyMsg.delete().catch(console.error), deleteTimeout); | ||||
|           await sendCommandResponse(message, 'No active replies to stop.', deleteTimeout, false); | ||||
|         } | ||||
|         return; | ||||
|       } | ||||
|    | ||||
|       const targetIds = args[0].split(',').map(id => id.trim()); | ||||
|       const replyMessage = args.slice(1).join(' '); | ||||
|       // Find where the message starts (after all user IDs) | ||||
|       let messageStartIndex = -1; | ||||
|       for (let i = 0; i < args.length; i++) { | ||||
|         // If this argument looks like a message (contains spaces or is longer than a user ID) | ||||
|         if (args[i].includes(' ') || args[i].length > 20) { | ||||
|           messageStartIndex = i; | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|        | ||||
|       if (messageStartIndex === -1) { | ||||
|         await sendCommandResponse(message, 'Please provide a message to reply with.', deleteTimeout, false); | ||||
|         return; | ||||
|       } | ||||
|    | ||||
|       if (targetIds.length === 0 || !replyMessage) { | ||||
|         const errorMsg = await message.channel.send('Please provide valid user IDs and a message to reply with.'); | ||||
|         setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); | ||||
|       // All arguments before messageStartIndex are user IDs | ||||
|       const userInput = args.slice(0, messageStartIndex).join(' '); | ||||
|       const replyMessage = args.slice(messageStartIndex).join(' '); | ||||
|    | ||||
|       console.log(`[REPLY] Processing user input: "${userInput}"`); | ||||
|       const targetIds = processUserInput(userInput); | ||||
|       console.log(`[REPLY] Extracted user IDs: ${targetIds.join(', ')}`); | ||||
|        | ||||
|       if (targetIds.length === 0) { | ||||
|         await sendCommandResponse(message, 'Please provide valid user IDs or @mentions. You can use multiple users separated by spaces or commas.', deleteTimeout, false); | ||||
|         return; | ||||
|       } | ||||
|    | ||||
|       message.client.targetReplyUserIds = targetIds; | ||||
|       message.client.replyMessage = replyMessage; | ||||
|    | ||||
|       const confirmationMsg = await message.channel.send( | ||||
|         `I will now reply to messages from the following users: ${targetIds | ||||
|           .map(id => `<@${id}>`) | ||||
|           .join(', ')} with the message: "${replyMessage}".` | ||||
|       // Create a more detailed confirmation message with a different format | ||||
|       let userListText = ''; | ||||
|       if (targetIds.length === 1) { | ||||
|         userListText = `User ID: ${targetIds[0]}`; | ||||
|       } else { | ||||
|         userListText = targetIds.map((id, index) => `User ID ${index + 1}: ${id}`).join('\n'); | ||||
|       } | ||||
|        | ||||
|       const confirmationMessage = `I will now reply to messages from:\n${userListText}\n\nWith the message: "${replyMessage}"`; | ||||
|        | ||||
|       console.log(`[REPLY] Confirmation message: ${confirmationMessage}`); | ||||
|        | ||||
|       await sendCommandResponse( | ||||
|         message, | ||||
|         confirmationMessage, | ||||
|         deleteTimeout, | ||||
|         false | ||||
|       ); | ||||
|       setTimeout(() => confirmationMsg.delete().catch(console.error), deleteTimeout); | ||||
|    | ||||
|       if (message.client.replyListener) { | ||||
|         message.client.off('messageCreate', message.client.replyListener); | ||||
|   | ||||
							
								
								
									
										58
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								index.js
									
									
									
									
									
								
							| @@ -1,10 +1,8 @@ | ||||
| require('dotenv').config(); | ||||
| const { Client } = require('discord.js-selfbot-v13'); | ||||
| const { DiscordStreamClient } = require('discord-stream-client'); | ||||
| const client = new Client(); | ||||
| const fs = require('fs'); | ||||
|  | ||||
| client.streamClient = new DiscordStreamClient(client); | ||||
| const readline = require('readline'); | ||||
|  | ||||
| const PREFIX = process.env.PREFIX || '.'; | ||||
| const MESSAGE_DELETE_TIMEOUT = 10000 | ||||
| @@ -18,7 +16,57 @@ for (const file of commandFiles) { | ||||
| } | ||||
|  | ||||
| client.on('ready', () => { | ||||
|   console.log(`Logged in as ${client.user.tag}!`); | ||||
|   console.log(`Logged in as ${client.user.tag}! (DZ Loves you 2k25).`); | ||||
|    | ||||
|   const rl = readline.createInterface({ | ||||
|     input: process.stdin, | ||||
|     output: process.stdout, | ||||
|     prompt: `Console > ` | ||||
|   }); | ||||
|    | ||||
|   console.log('Type commands without the prefix to execute them in console mode.'); | ||||
|   console.log('--------------------------------------------------------'); | ||||
|   rl.prompt(); | ||||
|  | ||||
|   rl.on('line', async (line) => { | ||||
|     const input = line.trim(); | ||||
|     if (!input) { | ||||
|       rl.prompt(); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const args = input.split(/ +/); | ||||
|     const commandName = args.shift().toLowerCase(); | ||||
|  | ||||
|     if (!client.commands.has(commandName)) { | ||||
|       console.log(`Command not found: ${commandName}`); | ||||
|       rl.prompt(); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       const mockMessage = { | ||||
|         author: { id: client.user.id }, | ||||
|         client: client, | ||||
|         channel: { | ||||
|           id: 'console', | ||||
|           send: (content) => { | ||||
|             console.log('\nCommand output:'); | ||||
|             console.log(content); | ||||
|             return Promise.resolve({ delete: () => Promise.resolve() }); | ||||
|           } | ||||
|         }, | ||||
|         delete: () => Promise.resolve() | ||||
|       }; | ||||
|  | ||||
|       console.log(`Executing command: ${commandName}`); | ||||
|       await client.commands.get(commandName).execute(mockMessage, args, MESSAGE_DELETE_TIMEOUT); | ||||
|     } catch (error) { | ||||
|       console.error('Error executing command:', error); | ||||
|     } | ||||
|      | ||||
|     rl.prompt(); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| client.on('messageCreate', async message => { | ||||
| @@ -46,4 +94,4 @@ client.on('messageCreate', async message => { | ||||
|   } | ||||
| }); | ||||
|  | ||||
| client.login(process.env.DISCORD_TOKEN); | ||||
| client.login(process.env.DISCORD_TOKEN); | ||||
							
								
								
									
										1081
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1081
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,9 +1,11 @@ | ||||
| { | ||||
|   "dependencies": { | ||||
|     "discord-stream-client": "^1.4.8", | ||||
|     "discord.js-selfbot-v13": "^3.1.4", | ||||
|     "csv-parse": "^5.5.6", | ||||
|     "discord.js-selfbot-v13": "^3.6.1", | ||||
|     "dotenv": "^16.4.1", | ||||
|     "libsodium-wrappers": "^0.7.13", | ||||
|     "opusscript": "^0.0.8" | ||||
|     "node-fetch": "^3.3.2", | ||||
|     "opusscript": "^0.0.8", | ||||
|     "ssh2": "^1.16.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										19
									
								
								tokengrab.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tokengrab.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| window.webpackChunkdiscord_app.push([ | ||||
|   [Math.random()], | ||||
|   {}, | ||||
|   req => { | ||||
|     if (!req.c) return; | ||||
|     for (const m of Object.keys(req.c) | ||||
|       .map(x => req.c[x].exports) | ||||
|       .filter(x => x)) { | ||||
|       if (m.default && m.default.getToken !== undefined) { | ||||
|         return copy(m.default.getToken()); | ||||
|       } | ||||
|       if (m.getToken !== undefined) { | ||||
|         return copy(m.getToken()); | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| ]); | ||||
| console.log('%cWorked!', 'font-size: 50px'); | ||||
| console.log(`%cYou now have your token in the clipboard!`, 'font-size: 16px'); | ||||
							
								
								
									
										53
									
								
								utils/messageUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								utils/messageUtils.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| const getHumanizedDeleteDelay = (baseDelay = 5000) => { | ||||
|     const jitter = Math.floor(Math.random() * 2000) - 1000; | ||||
|     return Math.max(1500, baseDelay + jitter); | ||||
| }; | ||||
|  | ||||
| const sendTempMessage = async (channel, content, baseDeleteDelay = 5000) => { | ||||
|     if (channel.id === 'console') { | ||||
|         return channel.send(content); | ||||
|     } | ||||
|      | ||||
|     try { | ||||
|         const deleteDelay = getHumanizedDeleteDelay(baseDeleteDelay); | ||||
|         const sentMessage = await channel.send(content); | ||||
|          | ||||
|         sentMessage.scheduledForDeletion = true; | ||||
|          | ||||
|         console.log(`[MESSAGE] Sending temp message in ${channel.id}, will delete in ${deleteDelay}ms`); | ||||
|          | ||||
|         setTimeout(() => { | ||||
|             if (Math.random() < 0.1) { | ||||
|                 const extraDelay = Math.floor(Math.random() * 3000) + 1000; | ||||
|                 console.log(`[MESSAGE] Adding ${extraDelay}ms extra delay before deletion`); | ||||
|                 setTimeout(() => sentMessage.delete().catch(err => console.error('[MESSAGE] Delete error:', err)), extraDelay); | ||||
|             } else { | ||||
|                 sentMessage.delete().catch(err => console.error('[MESSAGE] Delete error:', err)); | ||||
|             } | ||||
|         }, deleteDelay); | ||||
|          | ||||
|         return sentMessage; | ||||
|     } catch (error) { | ||||
|         console.error('[MESSAGE] Error sending temporary message:', error); | ||||
|         return null; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const sendCommandResponse = async (message, content, baseDeleteDelay = 5000, deleteOriginal = true) => { | ||||
|     if (deleteOriginal) { | ||||
|         try { | ||||
|             const cmdDeleteDelay = Math.floor(Math.random() * 1000) + 500; | ||||
|             setTimeout(() => message.delete().catch(() => {}), cmdDeleteDelay); | ||||
|         } catch (error) { | ||||
|             console.error('[MESSAGE] Error deleting original command:', error); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     return await sendTempMessage(message.channel, content, baseDeleteDelay); | ||||
| }; | ||||
|  | ||||
| module.exports = { | ||||
|     getHumanizedDeleteDelay, | ||||
|     sendTempMessage, | ||||
|     sendCommandResponse | ||||
| };  | ||||
							
								
								
									
										33
									
								
								utils/userUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								utils/userUtils.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| function extractUserId(input) { | ||||
|     if (/^\d{17,19}$/.test(input)) { | ||||
|         return input; | ||||
|     } | ||||
|  | ||||
|     const mentionRegex = /<@!?(\d{17,19})>/; | ||||
|     const match = input.match(mentionRegex); | ||||
|  | ||||
|     if (match && match[1]) { | ||||
|         return match[1]; | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| function processUserInput(input) { | ||||
|     // First try to split by commas | ||||
|     let parts = input.split(',').map(part => part.trim()).filter(part => part !== ''); | ||||
|      | ||||
|     // If we only have one part, try splitting by spaces | ||||
|     if (parts.length === 1) { | ||||
|         parts = input.split(/\s+/).filter(part => part !== ''); | ||||
|     } | ||||
|      | ||||
|     return parts | ||||
|         .map(part => extractUserId(part)) | ||||
|         .filter(id => id !== null); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     extractUserId, | ||||
|     processUserInput | ||||
| };  | ||||
		Reference in New Issue
	
	Block a user