diff --git a/commands/autodelete.js b/commands/autodelete.js index 695faa1..2e42f86 100644 --- a/commands/autodelete.js +++ b/commands/autodelete.js @@ -6,6 +6,7 @@ let ignoredMessages = new Set(); let isFirstDeletion = true; let deletedMessages = new Set(); const CACHE_CLEANUP_INTERVAL = 30 * 60 * 1000; +const { sendCommandResponse } = require('../utils/messageUtils'); const DELETION_DELAY = 5 * 60 * 1000; let DELETE_INTERVAL_MIN = 8000; @@ -198,93 +199,116 @@ module.exports = { async execute(message, args, deleteTimeout) { ignoredMessages.add(message.id); - if (args[0]?.toLowerCase() === 'stop') { - isAutoDeleteActive = false; + if (args.length === 0 || args[0].toLowerCase() === 'status') { + const statusText = isAutoDeleteActive + ? `Auto-delete is ON - Messages will be deleted after approximately ${Math.round(DELETION_DELAY / 1000 / 60)} minutes.` + : 'Auto-delete is OFF.'; - for (const [messageId, timer] of messageTimers) { - clearTimeout(timer); - console.log(`[AUTODELETE] Cleared timer for message: ${messageId}`); - } - - messageTimers.clear(); - deleteQueue = []; - isProcessingQueue = false; - isFirstDeletion = true; - - DELETE_INTERVAL_MIN = 8000; - DELETE_INTERVAL_MAX = 15000; - currentBatchCount = 0; - - console.log('[AUTODELETE] System deactivated - All timers cleared'); - message.channel.send('Auto-delete has been deactivated.') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + await sendCommandResponse( + message, + `${statusText}\nQueue size: ${deleteQueue.length} messages | Tracked messages: ${messageTimers.size}`, + deleteTimeout, + true + ); return; } - if (args[0]?.toLowerCase() === 'delay' && args[1]) { - const newDelay = parseInt(args[1], 10); - if (!isNaN(newDelay) && newDelay >= 1) { - const oldDelay = Math.round(DELETION_DELAY / 1000 / 60); - DELETION_DELAY = newDelay * 1000 * 60; - message.channel.send(`Auto-delete delay changed from ${oldDelay} to ${newDelay} minutes.`) - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); - return; - } else { - message.channel.send('Please provide a valid delay in minutes (minimum 1 minute).') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + 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; } - } - if (args[0]?.toLowerCase() === 'speed') { - if (args[1]?.toLowerCase() === 'slow') { - DELETE_INTERVAL_MIN = 15000; - DELETE_INTERVAL_MAX = 30000; - PAUSE_CHANCE = 0.25; - message.channel.send('Auto-delete speed set to slow mode (very human-like with frequent pauses).') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); - return; - } else if (args[1]?.toLowerCase() === 'medium') { - DELETE_INTERVAL_MIN = 8000; - DELETE_INTERVAL_MAX = 15000; - PAUSE_CHANCE = 0.15; - message.channel.send('Auto-delete speed set to medium mode (balanced human-like behavior).') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); - return; - } else if (args[1]?.toLowerCase() === 'fast') { - DELETE_INTERVAL_MIN = 5000; - DELETE_INTERVAL_MAX = 10000; - PAUSE_CHANCE = 0.05; - message.channel.send('Auto-delete speed set to fast mode (less human-like but quicker progress).') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); - return; - } else { - message.channel.send('Please specify a valid speed: slow, medium, or fast.') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); - return; - } - } - - if (!isAutoDeleteActive) { isAutoDeleteActive = true; - isFirstDeletion = true; - currentBatchCount = 0; - console.log('[AUTODELETE] System activated - Now tracking new messages'); - - message.client.removeListener('messageCreate', handleNewMessage); message.client.on('messageCreate', handleNewMessage); - const delayInMinutes = Math.round(DELETION_DELAY / 1000 / 60); - message.channel.send( - `Auto-delete activated. Messages will be deleted after ~${delayInMinutes} minutes ` + - `with human-like timing. Use \`.autodelete speed slow/medium/fast\` to adjust deletion speed.` - ).then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); - } else { - const delayInMinutes = Math.round(DELETION_DELAY / 1000 / 60); - message.channel.send( - `Auto-delete is already active. Current delay: ~${delayInMinutes} minutes. ` + - `Use \`.autodelete speed slow/medium/fast\` to adjust deletion speed.` - ).then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + 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; + } + + await sendCommandResponse( + message, + 'Unknown command. Available options: on/off, status, clear, speed [slow/normal/fast]', + deleteTimeout, + true + ); }, }; \ No newline at end of file diff --git a/commands/delete.js b/commands/delete.js index e2615f4..f681a64 100644 --- a/commands/delete.js +++ b/commands/delete.js @@ -2,6 +2,7 @@ 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) { @@ -16,14 +17,12 @@ 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; } @@ -44,8 +43,7 @@ module.exports = { } else if (targetGuildId) { await deleteMessagesFromServer(message, targetGuildId, deleteTimeout, speed); } else { - const errorMsg = await message.channel.send('Please specify how many messages to delete or a server ID. You can also set speed: `.delete [slow/medium/fast] [count/server]`'); - setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); + 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; @@ -182,16 +180,13 @@ async function deleteMessagesFromChannel(message, deleteCount, deleteTimeout, sp } if (cancelDelete) { - const canceledMsg = await message.channel.send(`Delete operation canceled after removing ${deletedCount} messages.`); - setTimeout(() => canceledMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, `Delete operation canceled after removing ${deletedCount} messages.`, deleteTimeout, true); } else { - const completedMsg = await message.channel.send(`Finished deleting ${deletedCount} messages.`); - setTimeout(() => completedMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, `Finished deleting ${deletedCount} messages.`, deleteTimeout, true); } } catch (error) { console.error('[DELETE] Failed to delete messages:', error); - const errorMsg = await message.channel.send('There was an error while trying to delete messages.'); - setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, 'There was an error while trying to delete messages.', deleteTimeout, true); } } @@ -199,8 +194,7 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed = 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; } @@ -341,15 +335,12 @@ async function deleteMessagesFromServer(message, guildId, deleteTimeout, speed = } if (cancelDelete) { - const canceledMsg = await message.channel.send(`Server cleanup canceled after removing ${totalDeleted} messages across ${processedChannels} channels.`); - setTimeout(() => canceledMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, `Delete operation canceled after removing ${totalDeleted} messages across ${processedChannels} channels.`, deleteTimeout, true); } else { - const completedMsg = await message.channel.send(`Finished cleaning up ${guild.name}: ${totalDeleted} messages deleted across ${processedChannels} channels.`); - setTimeout(() => completedMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, `Finished cleaning up ${guild.name}: ${totalDeleted} messages deleted across ${processedChannels} channels.`, deleteTimeout, true); } } catch (error) { console.error('[DELETE] Failed to delete messages in the server:', error); - const errorMsg = await message.channel.send('There was an error while trying to delete messages from the server.'); - setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, 'There was an error while trying to delete messages from the server.', deleteTimeout, true); } } \ No newline at end of file diff --git a/commands/groupadd.js b/commands/groupadd.js index 68479b4..b206366 100644 --- a/commands/groupadd.js +++ b/commands/groupadd.js @@ -4,6 +4,7 @@ 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; @@ -28,8 +29,7 @@ module.exports = { const { extractUserId } = require('../utils/userUtils'); if (message.channel.type !== 'GROUP_DM') { - message.channel.send('This command only works in group DMs.') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + await sendCommandResponse(message, 'This command only works in group DMs.', deleteTimeout, true); return; } @@ -42,8 +42,7 @@ module.exports = { channelToWatch = null; console.log('[GROUPADD] System deactivated'); - message.channel.send('Group auto-add deactivated.') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + await sendCommandResponse(message, 'Group auto-add deactivated.', deleteTimeout, true); return; } @@ -52,8 +51,7 @@ module.exports = { .filter(id => id !== null); if (validIds.length === 0) { - message.channel.send('Please provide at least one valid user ID or @mention.') - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + await sendCommandResponse(message, 'Please provide at least one valid user ID or @mention.', deleteTimeout, true); return; } @@ -156,7 +154,6 @@ module.exports = { message.client.on('channelRecipientRemove', handleRecipientRemove); const targetCount = targetUserIds.size; - message.channel.send(`Now watching for ${targetCount} user${targetCount > 1 ? 's' : ''} to leave the group.`) - .then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout)); + await sendCommandResponse(message, `Now watching for ${targetCount} user${targetCount > 1 ? 's' : ''} to leave the group.`, deleteTimeout, true); }, }; \ No newline at end of file diff --git a/commands/help.js b/commands/help.js index d84d20b..449b728 100644 --- a/commands/help.js +++ b/commands/help.js @@ -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); }, }; diff --git a/commands/ip.js b/commands/ip.js index 9a8da22..e526dbf 100644 --- a/commands/ip.js +++ b/commands/ip.js @@ -1,4 +1,5 @@ let vpnRangesCache = null; +const { sendCommandResponse } = require('../utils/messageUtils'); function ipToInt(ip) { return ip.split('.').reduce((acc, oct) => (acc << 8) + parseInt(oct, 10), 0) >>> 0; @@ -63,12 +64,10 @@ Organization: ${org} AS: ${as} VPN: ${vpnCheck ? "True" : "False"}`; - message.channel.send(`\`\`\`\n${output}\n\`\`\``) - .then(sentMsg => setTimeout(() => sentMsg.delete().catch(console.error), 30000)); + await sendCommandResponse(message, `\`\`\`\n${output}\n\`\`\``, 30000, true); } catch (error) { console.error("Error fetching IP info:", error); - message.channel.send("Error fetching IP info.") - .then(sentMsg => setTimeout(() => sentMsg.delete().catch(console.error), deleteTimeout)); + await sendCommandResponse(message, "Error fetching IP info.", deleteTimeout, true); } }, }; \ No newline at end of file diff --git a/commands/kickvc.js b/commands/kickvc.js index 9984473..38492ea 100644 --- a/commands/kickvc.js +++ b/commands/kickvc.js @@ -1,4 +1,5 @@ const { extractUserId } = require('../utils/userUtils'); +const { sendCommandResponse } = require('../utils/messageUtils'); let activeKicks = new Map(); let cooldowns = new Map(); @@ -111,8 +112,7 @@ module.exports = { description: 'Automatically kicks specified users from voice channels.', async execute(message, args, deleteTimeout) { if (args.length === 0) { - message.channel.send('Please provide a command: `start <userId(s)>` or `stop <userId or "all">`') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'Please provide a command: `start <userId(s)>` or `stop <userId or "all">`', deleteTimeout, true); return; } @@ -120,8 +120,7 @@ module.exports = { if (command === 'stop') { if (args.length < 2) { - message.channel.send('Please specify a user ID or "all" to stop kicking.') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'Please specify a user ID or "all" to stop kicking.', deleteTimeout, true); return; } @@ -138,15 +137,13 @@ module.exports = { } voiceStateUpdateHandlers.clear(); - message.channel.send('Stopped all active VC kicks.') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'Stopped all active VC kicks.', deleteTimeout, true); return; } else { const userId = extractUserId(target); if (!userId) { - message.channel.send('Invalid user ID.') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'Invalid user ID.', deleteTimeout, true); return; } @@ -158,11 +155,9 @@ module.exports = { checkIntervals.delete(userId); console.log(`[KICKVC] Stopped kicking user: ${userId}`); - message.channel.send(`Stopped kicking user: ${userId}`) - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, `Stopped kicking user: ${userId}`, deleteTimeout, true); } else { - message.channel.send(`No active kick for user: ${userId}`) - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, `No active kick for user: ${userId}`, deleteTimeout, true); } return; } @@ -170,8 +165,7 @@ module.exports = { if (command === 'start') { if (args.length < 2) { - message.channel.send('Please provide at least one user ID to kick.') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'Please provide at least one user ID to kick.', deleteTimeout, true); return; } @@ -180,8 +174,7 @@ module.exports = { .filter(id => id !== null); if (userIds.length === 0) { - message.channel.send('No valid user IDs provided.') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'No valid user IDs provided.', deleteTimeout, true); return; } @@ -257,22 +250,19 @@ module.exports = { console.log(`[KICKVC] Started kicking user: ${userId}`); } - let responseMessage = ''; - if (startedKicking.length > 0) { - responseMessage += `Started kicking ${startedKicking.length} user(s): ${startedKicking.join(', ')}\n`; + 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); } - - if (alreadyKicking.length > 0) { - responseMessage += `Already kicking: ${alreadyKicking.join(', ')}`; - } - - message.channel.send(responseMessage) - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); return; } - message.channel.send('Unknown command. Use `start <userId(s)>` or `stop <userId or "all">`') - .then(msg => setTimeout(() => msg.delete().catch(() => {}), deleteTimeout)); + await sendCommandResponse(message, 'Unknown command. Use `start <userId(s)>` or `stop <userId or "all">`.', deleteTimeout, true); } }; \ No newline at end of file diff --git a/commands/react.js b/commands/react.js index d83de1d..1f3fff2 100644 --- a/commands/react.js +++ b/commands/react.js @@ -1,3 +1,5 @@ +const { sendCommandResponse } = require('../utils/messageUtils'); + module.exports = { name: 'react', description: `Automatically react with specified emojis to multiple users' messages, or stop reacting.`, @@ -6,15 +8,16 @@ module.exports = { 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 => `User ID: ${id}`) - .join(', ')} with the following emojis: ${message.client.reactEmojis.join(' ')}.` + .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; } @@ -26,11 +29,9 @@ 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; } @@ -39,37 +40,69 @@ module.exports = { const emojis = args.slice(1); if (targetIds.length === 0 || emojis.length === 0) { - const errorMsg = await message.channel.send('Please provide valid user IDs or @mentions and at least one emoji.'); - setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, 'Please provide valid user IDs or @mentions and at least one emoji.', deleteTimeout, false); return; } message.client.targetReactUserIds = targetIds; message.client.reactEmojis = emojis; - const confirmationMsg = await message.channel.send( + await sendCommandResponse( + message, `I will now react to messages from the following users: ${targetIds .map(id => `User ID: ${id}`) - .join(', ')} with the following emojis: ${emojis.join(' ')}.` + .join(', ')} with the following emojis: ${emojis.join(' ')}.`, + 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 emojis) { + 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); } } }; diff --git a/commands/reply.js b/commands/reply.js index 7800f2b..22b0d95 100644 --- a/commands/reply.js +++ b/commands/reply.js @@ -1,3 +1,5 @@ +const { sendCommandResponse } = require('../utils/messageUtils'); + module.exports = { name: 'reply', description: `Automatically reply with a specified message to multiple users' messages, or stop replying.`, @@ -6,15 +8,16 @@ module.exports = { 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 => `User ID: ${id}`) - .join(', ')} with the message: "${message.client.replyMessage}".` + .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; } @@ -26,11 +29,9 @@ 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; } @@ -39,20 +40,21 @@ module.exports = { const replyMessage = args.slice(1).join(' '); if (targetIds.length === 0 || !replyMessage) { - const errorMsg = await message.channel.send('Please provide valid user IDs or @mentions and a message to reply with.'); - setTimeout(() => errorMsg.delete().catch(console.error), deleteTimeout); + await sendCommandResponse(message, 'Please provide valid user IDs or @mentions and a message to reply with.', deleteTimeout, false); return; } message.client.targetReplyUserIds = targetIds; message.client.replyMessage = replyMessage; - const confirmationMsg = await message.channel.send( + await sendCommandResponse( + message, `I will now reply to messages from the following users: ${targetIds .map(id => `User ID: ${id}`) - .join(', ')} with the message: "${replyMessage}".` + .join(', ')} with the message: "${replyMessage}".`, + deleteTimeout, + false ); - setTimeout(() => confirmationMsg.delete().catch(console.error), deleteTimeout); if (message.client.replyListener) { message.client.off('messageCreate', message.client.replyListener); diff --git a/utils/messageUtils.js b/utils/messageUtils.js new file mode 100644 index 0000000..b4b6ee2 --- /dev/null +++ b/utils/messageUtils.js @@ -0,0 +1,54 @@ +const getHumanizedDeleteDelay = (baseDelay = 5000) => { + // Add randomness to deletion timing for more human-like behavior + const jitter = Math.floor(Math.random() * 2000) - 1000; // -1000 to +1000ms jitter + return Math.max(1500, baseDelay + jitter); +}; + +const sendTempMessage = async (channel, content, baseDeleteDelay = 5000) => { + try { + const deleteDelay = getHumanizedDeleteDelay(baseDeleteDelay); + const sentMessage = await channel.send(content); + + // Log what's happening for debugging + console.log(`[MESSAGE] Sending temp message in ${channel.id}, will delete in ${deleteDelay}ms`); + + // Simulate a human, occasionally delayed deletion + setTimeout(() => { + if (Math.random() < 0.1) { + // 10% chance for an extra delay (simulates forgetting to delete immediately) + 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) => { + // Delete original command message if requested + if (deleteOriginal) { + try { + // Add small delay before deleting command, like a human would + const cmdDeleteDelay = Math.floor(Math.random() * 1000) + 500; + setTimeout(() => message.delete().catch(() => {}), cmdDeleteDelay); + } catch (error) { + console.error('[MESSAGE] Error deleting original command:', error); + } + } + + // Send and schedule deletion of response + return await sendTempMessage(message.channel, content, baseDeleteDelay); +}; + +module.exports = { + getHumanizedDeleteDelay, + sendTempMessage, + sendCommandResponse +}; \ No newline at end of file