Compare commits
20 Commits
92e1984cbf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d8017e5b5 | |||
| 887ebb0c64 | |||
| 73149b8d6d | |||
| 5f750b0666 | |||
| b15f7097f8 | |||
| 7ca36a4938 | |||
| 235398d200 | |||
| 1f8dced90e | |||
| ddf4c6ae15 | |||
| c16e55d56c | |||
| 38147c46a2 | |||
| 90df6177ed | |||
| 26e7b8f706 | |||
| 4d2521441d | |||
| 3c055c53d2 | |||
| 259ed424dd | |||
| 59570259d6 | |||
| c287382bd2 | |||
| af3dbe625b | |||
| 4b1740ac07 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +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.
|
||||
|
||||
@@ -3,13 +3,70 @@ 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;
|
||||
const MIN_DELETE_INTERVAL = 2500;
|
||||
const MAX_DELETE_INTERVAL = 3500;
|
||||
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) {
|
||||
@@ -18,8 +75,28 @@ setInterval(() => {
|
||||
}
|
||||
}, CACHE_CLEANUP_INTERVAL);
|
||||
|
||||
const getRandomInterval = () => {
|
||||
return Math.floor(Math.random() * (MAX_DELETE_INTERVAL - MIN_DELETE_INTERVAL)) + MIN_DELETE_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 () => {
|
||||
@@ -33,30 +110,26 @@ const processDeleteQueue = async () => {
|
||||
console.log(`[AUTODELETE] Message ${messageToDelete.id} already deleted (cached), skipping`);
|
||||
|
||||
if (deleteQueue.length > 0 && isProcessingQueue) {
|
||||
const nextInterval = getRandomInterval();
|
||||
console.log(`[AUTODELETE] Next deletion in ${nextInterval}ms | Queue size: ${deleteQueue.length}`);
|
||||
setTimeout(processDeleteQueue, nextInterval);
|
||||
scheduleNextDeletion();
|
||||
} else {
|
||||
isProcessingQueue = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteDelay = Math.floor(Math.random() * 100) + 50;
|
||||
await new Promise(resolve => setTimeout(resolve, deleteDelay));
|
||||
console.log(`[AUTODELETE] Waited ${deleteDelay}ms before processing`);
|
||||
const preDeleteDelay = Math.floor(Math.random() * 1500) + 500; // 500-2000ms
|
||||
await new Promise(resolve => setTimeout(resolve, preDeleteDelay));
|
||||
|
||||
if (isFirstDeletion || Math.random() < 0.2) {
|
||||
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) {
|
||||
const nextInterval = getRandomInterval();
|
||||
console.log(`[AUTODELETE] Next deletion in ${nextInterval}ms | Queue size: ${deleteQueue.length}`);
|
||||
setTimeout(processDeleteQueue, nextInterval);
|
||||
scheduleNextDeletion();
|
||||
} else {
|
||||
isProcessingQueue = false;
|
||||
}
|
||||
@@ -64,10 +137,24 @@ const processDeleteQueue = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -75,8 +162,14 @@ const processDeleteQueue = async () => {
|
||||
|
||||
if (!deletedMessages.has(messageToDelete.id)) {
|
||||
deletedMessages.add(messageToDelete.id);
|
||||
console.log(`[AUTODELETE] Processed and cached message ${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) {
|
||||
@@ -84,17 +177,36 @@ const processDeleteQueue = async () => {
|
||||
}
|
||||
|
||||
if (deleteQueue.length > 0 && isProcessingQueue) {
|
||||
const nextInterval = getRandomInterval();
|
||||
console.log(`[AUTODELETE] Next deletion in ${nextInterval}ms | Queue size: ${deleteQueue.length}`);
|
||||
setTimeout(processDeleteQueue, nextInterval);
|
||||
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();
|
||||
}
|
||||
};
|
||||
@@ -108,64 +220,344 @@ const handleNewMessage = (message) => {
|
||||
}
|
||||
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}`);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}, DELETION_DELAY);
|
||||
}, variableDelay);
|
||||
|
||||
messageTimers.set(message.id, timer);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'autodelete',
|
||||
description: 'Automatically deletes your messages after 5 minutes',
|
||||
description: 'Automatically deletes your messages after a set time',
|
||||
async execute(message, args, deleteTimeout) {
|
||||
ignoredMessages.add(message.id);
|
||||
|
||||
if (args[0]?.toLowerCase() === 'stop') {
|
||||
isAutoDeleteActive = false;
|
||||
|
||||
for (const [messageId, timer] of messageTimers) {
|
||||
clearTimeout(timer);
|
||||
console.log(`[AUTODELETE] Cleared timer for message: ${messageId}`);
|
||||
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`;
|
||||
}
|
||||
|
||||
messageTimers.clear();
|
||||
deleteQueue = [];
|
||||
isProcessingQueue = false;
|
||||
isFirstDeletion = true;
|
||||
const statusText = isAutoDeleteActive
|
||||
? `Auto-delete is ON - Messages will be deleted after approximately ${Math.round(DELETION_DELAY / 1000 / 60)} minutes.`
|
||||
: 'Auto-delete is OFF.';
|
||||
|
||||
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}${ignoreStatus}`,
|
||||
deleteTimeout,
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAutoDeleteActive) {
|
||||
isAutoDeleteActive = true;
|
||||
isFirstDeletion = true;
|
||||
console.log('[AUTODELETE] System activated - Now tracking new messages');
|
||||
const command = args[0].toLowerCase();
|
||||
|
||||
message.client.removeListener('messageCreate', handleNewMessage);
|
||||
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);
|
||||
|
||||
message.channel.send('Auto-delete activated. Each message will be deleted after exactly 5 minutes.')
|
||||
.then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
} else {
|
||||
message.channel.send('Auto-delete is already active.')
|
||||
.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;
|
||||
}
|
||||
|
||||
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;
|
||||
console.log(`[DELETE] Starting deletion of up to ${deleteCount} messages with ${speed} speed`);
|
||||
let deletedCount = 0;
|
||||
let batchCount = 0;
|
||||
|
||||
while (remainingMessages > 0 && !cancelDelete) {
|
||||
const fetchLimit = Math.min(remainingMessages, BATCH_SIZE);
|
||||
const messages = await message.channel.messages.fetch({ limit: fetchLimit + 1 });
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
remainingMessages -= filteredMessages.size;
|
||||
|
||||
if (remainingMessages > 0 && !cancelDelete) {
|
||||
const batchDelay = getBatchDelay();
|
||||
await new Promise(resolve => setTimeout(resolve, batchDelay));
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteMessagesFromServer(message, guildId, deleteTimeout) {
|
||||
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);
|
||||
if (cancelDelete) {
|
||||
console.log(`[DELETE] Operation canceled by user`);
|
||||
return;
|
||||
}
|
||||
|
||||
const getRandomDelay = () => Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000;
|
||||
const getBatchDelay = () => Math.floor(Math.random() * (10000 - 5000 + 1)) + 5000;
|
||||
const BATCH_SIZE = 10;
|
||||
if (deletedCount >= deleteCount) break;
|
||||
|
||||
try {
|
||||
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.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('[DELETE] Error deleting message:', error);
|
||||
}
|
||||
}
|
||||
|
||||
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('[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, speed = 'medium') {
|
||||
const guild = message.client.guilds.cache.get(guildId);
|
||||
|
||||
if (!guild) {
|
||||
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));
|
||||
};
|
||||
|
||||
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);
|
||||
const filteredMessages = messages.filter(msg =>
|
||||
msg.author.id === message.author.id &&
|
||||
!deletedMessages.has(msg.id)
|
||||
);
|
||||
|
||||
if (filteredMessages.size === 0) {
|
||||
hasMoreMessages = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
batchCount++;
|
||||
let messagesInThisBatch = 0;
|
||||
|
||||
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));
|
||||
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.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 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('Error deleting message:', error);
|
||||
console.error('[DELETE] Error deleting message:', error);
|
||||
}
|
||||
|
||||
if (messagesInThisBatch >= batchSize) break;
|
||||
}
|
||||
|
||||
if (messagesInThisBatch < batchSize) {
|
||||
hasMoreMessages = false;
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredMessages.size < BATCH_SIZE) {
|
||||
hasMoreMessages = false;
|
||||
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 {
|
||||
const batchDelay = getBatchDelay();
|
||||
await new Promise(resolve => setTimeout(resolve, batchDelay));
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,34 @@ 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 getRandomDelay = () => {
|
||||
return Math.floor(Math.random() * 200) + 150;
|
||||
const getBackoffDelay = (userId) => {
|
||||
const attempts = failedAttempts.get(userId) || 0;
|
||||
|
||||
if (attempts <= 1) return 2000;
|
||||
if (attempts <= 3) return 4000;
|
||||
if (attempts <= 5) return 7000;
|
||||
if (attempts <= 10) return 15000;
|
||||
return 30000;
|
||||
};
|
||||
|
||||
const getAddDelay = () => {
|
||||
const baseDelay = Math.floor(Math.random() * 700) + 800;
|
||||
const jitter = Math.floor(Math.random() * 300) - 150;
|
||||
return Math.max(500, baseDelay + jitter);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'groupadd',
|
||||
description: 'Automatically re-adds users to group when they leave. Use multiple IDs for multiple targets.',
|
||||
description: 'Automatically re-adds users to group when they leave.',
|
||||
async execute(message, args, deleteTimeout) {
|
||||
const { extractUserId } = require('../utils/userUtils');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -21,24 +37,29 @@ module.exports = {
|
||||
isActive = false;
|
||||
targetUserIds.clear();
|
||||
lastAddTimes.clear();
|
||||
failedAttempts.clear();
|
||||
recentAdds.clear();
|
||||
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;
|
||||
}
|
||||
|
||||
const validIds = args.filter(id => /^\d{17,19}$/.test(id));
|
||||
const validIds = args
|
||||
.map(arg => extractUserId(arg))
|
||||
.filter(id => id !== null);
|
||||
|
||||
if (validIds.length === 0) {
|
||||
message.channel.send('Please provide at least one valid user ID.')
|
||||
.then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
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(', ')}`);
|
||||
|
||||
@@ -46,42 +67,93 @@ module.exports = {
|
||||
try {
|
||||
if (!channelToWatch.recipients.has(userId)) {
|
||||
console.log(`[GROUPADD] Target ${userId} not in group, attempting initial add`);
|
||||
|
||||
const initialDelay = Math.floor(Math.random() * 500) + 300;
|
||||
await new Promise(resolve => setTimeout(resolve, initialDelay));
|
||||
|
||||
await channelToWatch.addUser(userId);
|
||||
lastAddTimes.set(userId, Date.now());
|
||||
recentAdds.set(userId, true);
|
||||
|
||||
setTimeout(() => {
|
||||
recentAdds.delete(userId);
|
||||
}, 10000);
|
||||
|
||||
console.log(`[GROUPADD] Initial add successful for ${userId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[GROUPADD] Initial add failed for ${userId}:`, error);
|
||||
failedAttempts.set(userId, (failedAttempts.get(userId) || 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
message.client.on('channelRecipientRemove', async (channel, user) => {
|
||||
const handleRecipientRemove = async (channel, user) => {
|
||||
if (!isActive || channel.id !== channelToWatch.id || !targetUserIds.has(user.id)) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
const lastAddTime = lastAddTimes.get(user.id) || 0;
|
||||
const timeSinceLastAdd = currentTime - lastAddTime;
|
||||
|
||||
if (timeSinceLastAdd < 1000) {
|
||||
console.log(`[GROUPADD] Rate limiting for ${user.id}, waiting...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 - timeSinceLastAdd));
|
||||
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));
|
||||
}
|
||||
|
||||
const delay = getRandomDelay();
|
||||
console.log(`[GROUPADD] User ${user.id} left, waiting ${delay}ms before re-adding`);
|
||||
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));
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
const addDelay = getAddDelay();
|
||||
console.log(`[GROUPADD] Will readd user ${user.id} after ${addDelay}ms`);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, addDelay));
|
||||
|
||||
if (!isActive) {
|
||||
console.log(`[GROUPADD] Command was deactivated during delay, cancelling re-add for ${user.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await channel.addUser(user.id);
|
||||
lastAddTimes.set(user.id, Date.now());
|
||||
recentAdds.set(user.id, true);
|
||||
|
||||
setTimeout(() => {
|
||||
recentAdds.delete(user.id);
|
||||
}, 10000);
|
||||
|
||||
console.log(`[GROUPADD] Successfully re-added user ${user.id}`);
|
||||
|
||||
if (failedAttempts.get(user.id) > 0) {
|
||||
failedAttempts.set(user.id, Math.max(0, failedAttempts.get(user.id) - 1));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[GROUPADD] Failed to re-add user ${user.id}:`, error);
|
||||
failedAttempts.set(user.id, (failedAttempts.get(user.id) || 0) + 1);
|
||||
|
||||
if (Math.random() < 0.4 && timeSinceLastAdd > 5000) {
|
||||
console.log(`[GROUPADD] Will try again after a pause`);
|
||||
setTimeout(() => {
|
||||
if (isActive && !channel.recipients.has(user.id)) {
|
||||
channel.addUser(user.id).catch(e =>
|
||||
console.log(`[GROUPADD] Retry failed for ${user.id}:`, e)
|
||||
);
|
||||
}
|
||||
});
|
||||
}, 3000 + Math.random() * 2000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
message.client.on('channelRecipientRemove', handleRecipientRemove);
|
||||
|
||||
const targetCount = targetUserIds.size;
|
||||
message.channel.send(`Now watching for ${targetCount} user${targetCount > 1 ? 's' : ''} to leave the group.`)
|
||||
.then(msg => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
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,201 +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 = () => {
|
||||
const delay = Math.floor(Math.random() * 250) + 100;
|
||||
console.log(`[KICKVC] Generated event delay: ${delay}ms`);
|
||||
return delay;
|
||||
let activeKicks = new Map();
|
||||
let cooldowns = new Map();
|
||||
let voiceStateUpdateHandlers = new Map();
|
||||
let checkIntervals = new Map();
|
||||
|
||||
const getKickDelay = () => {
|
||||
const baseDelay = Math.floor(Math.random() * 400) + 600;
|
||||
const jitter = Math.floor(Math.random() * 200) - 100;
|
||||
return Math.max(400, baseDelay + jitter);
|
||||
};
|
||||
|
||||
const getRandomCheckDelay = () => {
|
||||
const delay = Math.floor(Math.random() * 250) + 200;
|
||||
console.log(`[KICKVC] Generated interval check delay: ${delay}ms`);
|
||||
return delay;
|
||||
const calculateCooldown = (kickCount) => {
|
||||
if (kickCount <= 2) return 0;
|
||||
if (kickCount <= 5) return Math.min((kickCount - 2) * 300, 900);
|
||||
if (kickCount <= 10) return Math.min(900 + (kickCount - 5) * 400, 2900);
|
||||
return Math.min(2900 + (kickCount - 10) * 500, 5000);
|
||||
};
|
||||
|
||||
const getCooldown = (kicks) => {
|
||||
let cooldown;
|
||||
if (kicks <= 3) cooldown = 200;
|
||||
else if (kicks <= 5) cooldown = 500;
|
||||
else if (kicks <= 10) cooldown = 1000;
|
||||
else cooldown = 2500;
|
||||
const getSafetyPause = () => {
|
||||
if (Math.random() < 0.25) {
|
||||
return Math.floor(Math.random() * 4000) + 3000;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
console.log(`[KICKVC] New cooldown calculated for ${kicks} kicks: ${cooldown}ms`);
|
||||
return cooldown;
|
||||
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)) {
|
||||
console.log('[KICKVC] Invalid user ID provided');
|
||||
message.channel.send('Please provide a valid user ID.')
|
||||
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
|
||||
const command = args[0].toLowerCase();
|
||||
|
||||
if (command === 'stop') {
|
||||
if (args.length < 2) {
|
||||
await sendCommandResponse(message, 'Please specify a user ID or "all" to stop kicking.', deleteTimeout, true);
|
||||
return;
|
||||
}
|
||||
|
||||
targetUserId = userId;
|
||||
isKickActive = true;
|
||||
console.log(`[KICKVC] System activated - Targeting user ID: ${userId}`);
|
||||
const target = args[1].toLowerCase();
|
||||
|
||||
if (voiceStateHandler) {
|
||||
message.client.removeListener('voiceStateUpdate', voiceStateHandler);
|
||||
console.log('[KICKVC] Removed old voice state handler');
|
||||
}
|
||||
if (checkInterval) {
|
||||
clearInterval(checkInterval);
|
||||
console.log('[KICKVC] Cleared old check interval');
|
||||
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();
|
||||
|
||||
const kickUser = async (member, guild, fromEvent = false) => {
|
||||
if (!isKickActive) return;
|
||||
await sendCommandResponse(message, 'Stopped all active VC kicks.', deleteTimeout, true);
|
||||
return;
|
||||
} else {
|
||||
const userId = extractUserId(target);
|
||||
|
||||
const currentTime = Date.now();
|
||||
const timeSinceLastKick = currentTime - lastKickTime;
|
||||
|
||||
if (timeSinceLastKick < cooldownTime) {
|
||||
console.log(`[KICKVC] On cooldown - ${cooldownTime - timeSinceLastKick}ms remaining`);
|
||||
if (!userId) {
|
||||
await sendCommandResponse(message, 'Invalid user ID.', deleteTimeout, true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const delay = fromEvent ? getRandomDelay() : getRandomCheckDelay();
|
||||
console.log(`[KICKVC] Waiting ${delay}ms before kick attempt...`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
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}`);
|
||||
|
||||
if (!member.voice.channel) {
|
||||
console.log('[KICKVC] Target no longer in voice after delay');
|
||||
await sendCommandResponse(message, `Stopped kicking user: ${userId}`, deleteTimeout, true);
|
||||
} else {
|
||||
await sendCommandResponse(message, `No active kick for user: ${userId}`, deleteTimeout, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (command === 'start') {
|
||||
if (args.length < 2) {
|
||||
await sendCommandResponse(message, 'Please provide at least one user ID to kick.', deleteTimeout, true);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[KICKVC] Target detected in voice: ${member.user.tag} | Server: ${guild.name} | Channel: ${member.voice.channel.name}`);
|
||||
const userIds = args.slice(1)
|
||||
.map(arg => extractUserId(arg))
|
||||
.filter(id => id !== null);
|
||||
|
||||
await member.voice.disconnect();
|
||||
lastKickTime = currentTime;
|
||||
consecutiveKicks++;
|
||||
|
||||
const oldCooldown = cooldownTime;
|
||||
cooldownTime = getCooldown(consecutiveKicks);
|
||||
|
||||
console.log(`[KICKVC] Kick successful - Consecutive kicks: ${consecutiveKicks} | New cooldown: ${cooldownTime}ms (was ${oldCooldown}ms)`);
|
||||
|
||||
setTimeout(() => {
|
||||
if (consecutiveKicks > 0) {
|
||||
const oldKicks = consecutiveKicks;
|
||||
const oldCd = cooldownTime;
|
||||
consecutiveKicks--;
|
||||
cooldownTime = getCooldown(consecutiveKicks);
|
||||
console.log(`[KICKVC] Decay timer - Kicks reduced: ${oldKicks} -> ${consecutiveKicks} | Cooldown: ${oldCd}ms -> ${cooldownTime}ms`);
|
||||
if (userIds.length === 0) {
|
||||
await sendCommandResponse(message, 'No valid user IDs provided.', deleteTimeout, true);
|
||||
return;
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
} catch (error) {
|
||||
console.log('[KICKVC] Primary disconnect failed, trying alternate methods');
|
||||
try {
|
||||
await member.voice.setChannel(null);
|
||||
console.log('[KICKVC] Alternate method 1 successful (setChannel null)');
|
||||
} catch {
|
||||
try {
|
||||
await member.voice.channel.permissionOverwrites.create(member, {
|
||||
Connect: false,
|
||||
Speak: false
|
||||
});
|
||||
await member.voice.disconnect();
|
||||
console.log('[KICKVC] Alternate method 2 successful (permissions + disconnect)');
|
||||
} catch {
|
||||
console.log('[KICKVC] All disconnection methods failed');
|
||||
const startedKicking = [];
|
||||
const alreadyKicking = [];
|
||||
|
||||
for (const userId of userIds) {
|
||||
if (activeKicks.has(userId)) {
|
||||
alreadyKicking.push(userId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
console.log('[KICKVC] Voice state update detected for target');
|
||||
|
||||
try {
|
||||
const guild = voiceState.guild;
|
||||
const member = await guild.members.fetch(targetUserId).catch(() => null);
|
||||
if (member?.voice?.channel) {
|
||||
await kickUser(member, guild, true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[KICKVC] Error in voice state handler:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const intervalTime = Math.floor(Math.random() * 500) + 1000;
|
||||
console.log(`[KICKVC] Setting up interval check every ${intervalTime}ms`);
|
||||
|
||||
checkInterval = setInterval(async () => {
|
||||
if (!isKickActive) return;
|
||||
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(targetUserId).catch(() => null);
|
||||
if (member?.voice?.channel) {
|
||||
await kickUser(member, guild, false);
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
}, intervalTime);
|
||||
|
||||
message.client.on('voiceStateUpdate', voiceStateHandler);
|
||||
console.log('[KICKVC] New voice state handler and check interval registered');
|
||||
|
||||
try {
|
||||
const user = await message.client.users.fetch(userId);
|
||||
console.log(`[KICKVC] Successfully fetched target user: ${user.tag}`);
|
||||
message.channel.send(`Now automatically kicking ${user.tag} (${userId}) from voice channels.`)
|
||||
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
|
||||
|
||||
console.log('[KICKVC] Performing initial guild check');
|
||||
message.client.guilds.cache.forEach(async (guild) => {
|
||||
const member = await guild.members.fetch(userId).catch(() => null);
|
||||
if (member?.voice?.channel) {
|
||||
console.log(`[KICKVC] Target found in voice during initial check - Server: ${guild.name}`);
|
||||
await kickUser(member, guild, true);
|
||||
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] Could not fetch user information:', error);
|
||||
message.channel.send(`Now automatically kicking user ID ${userId} from voice channels.`)
|
||||
.then(msg => setTimeout(() => msg.delete().catch(console.error), deleteTimeout));
|
||||
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);
|
||||
}
|
||||
},
|
||||
};
|
||||
139
commands/react.js
vendored
139
commands/react.js
vendored
@@ -1,18 +1,23 @@
|
||||
const { sendCommandResponse } = require('../utils/messageUtils');
|
||||
|
||||
module.exports = {
|
||||
name: 'react',
|
||||
description: `Automatically react with specified emojis to multiple users’ messages, or stop reacting.`,
|
||||
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;
|
||||
}
|
||||
@@ -24,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;
|
||||
}
|
||||
|
||||
// All arguments before emojiStartIndex are user IDs
|
||||
const userInput = args.slice(0, emojiStartIndex).join(' ');
|
||||
const emojis = args.slice(emojiStartIndex);
|
||||
|
||||
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 = emojis;
|
||||
message.client.reactEmojis = processedEmojis;
|
||||
|
||||
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(' ')}.`
|
||||
// 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);
|
||||
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,18 +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.`,
|
||||
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;
|
||||
}
|
||||
@@ -24,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 (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);
|
||||
if (messageStartIndex === -1) {
|
||||
await sendCommandResponse(message, 'Please provide a message to reply with.', deleteTimeout, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
135
commands/ssh.js
135
commands/ssh.js
@@ -1,135 +0,0 @@
|
||||
const { Client: SSHClient } = require('ssh2');
|
||||
|
||||
let sshConnection = null;
|
||||
|
||||
module.exports = {
|
||||
name: 'ssh',
|
||||
description: 'Manage an SSH connection. Subcommands: connect, exec, disconnect',
|
||||
async execute(message, args, deleteTimeout) {
|
||||
if (!args.length) {
|
||||
return message.channel
|
||||
.send("Usage: `.ssh <connect|exec|disconnect> ...`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
const subcommand = args.shift().toLowerCase();
|
||||
|
||||
if (subcommand === 'connect') {
|
||||
if (args.length < 3) {
|
||||
return message.channel
|
||||
.send("Usage: `.ssh connect <host> <username> <password> [port]`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
if (sshConnection) {
|
||||
return message.channel
|
||||
.send("Already connected. Disconnect first using `.ssh disconnect`.")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
const host = args[0];
|
||||
const username = args[1];
|
||||
const password = args[2];
|
||||
const port = args[3] ? parseInt(args[3]) : 22;
|
||||
|
||||
sshConnection = new SSHClient();
|
||||
|
||||
sshConnection
|
||||
.on('ready', () => {
|
||||
message.channel
|
||||
.send(`Connected to ${host}`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
})
|
||||
.on('error', (err) => {
|
||||
message.channel
|
||||
.send(`SSH Connection error: ${err.message}`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
sshConnection = null;
|
||||
})
|
||||
.on('close', () => {
|
||||
message.channel
|
||||
.send(`SSH Connection closed.`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
sshConnection = null;
|
||||
});
|
||||
|
||||
sshConnection.connect({ host, port, username, password });
|
||||
return;
|
||||
}
|
||||
|
||||
else if (subcommand === 'exec') {
|
||||
if (!sshConnection) {
|
||||
return message.channel
|
||||
.send("No active SSH connection. Connect first using `.ssh connect ...`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
if (!args.length) {
|
||||
return message.channel
|
||||
.send("Usage: `.ssh exec <command>`")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
const cmd = args.join(' ');
|
||||
|
||||
sshConnection.exec(cmd, (err, stream) => {
|
||||
if (err) {
|
||||
return message.channel
|
||||
.send(`Error executing command: ${err.message}`)
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
let outputBuffer = '';
|
||||
|
||||
message.channel.send(`Executing command: \`${cmd}\`\n\`\`\`\n...\n\`\`\``)
|
||||
.then((sentMsg) => {
|
||||
const updateInterval = setInterval(() => {
|
||||
let display = outputBuffer;
|
||||
if (display.length > 1900) {
|
||||
display = display.slice(-1900);
|
||||
}
|
||||
sentMsg.edit(`Executing command: \`${cmd}\`\n\`\`\`\n${display}\n\`\`\``)
|
||||
.catch(() => { });
|
||||
}, 2000);
|
||||
|
||||
stream.on('data', (data) => {
|
||||
outputBuffer += data.toString();
|
||||
});
|
||||
stream.stderr.on('data', (data) => {
|
||||
outputBuffer += data.toString();
|
||||
});
|
||||
stream.on('close', (code, signal) => {
|
||||
clearInterval(updateInterval);
|
||||
outputBuffer += `\nProcess exited with code ${code}${signal ? ' and signal ' + signal : ''}`;
|
||||
let display = outputBuffer;
|
||||
if (display.length > 1900) {
|
||||
display = display.slice(-1900);
|
||||
}
|
||||
sentMsg.edit(`Executing command: \`${cmd}\`\n\`\`\`\n${display}\n\`\`\``)
|
||||
.catch(() => { });
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
else if (subcommand === 'disconnect') {
|
||||
if (!sshConnection) {
|
||||
return message.channel
|
||||
.send("No active SSH connection.")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
sshConnection.end();
|
||||
sshConnection = null;
|
||||
return message.channel
|
||||
.send("Disconnecting SSH...")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
|
||||
else {
|
||||
return message.channel
|
||||
.send("Unknown subcommand. Use `connect`, `exec`, or `disconnect`.")
|
||||
.then((msg) => setTimeout(() => msg.delete().catch(() => { }), deleteTimeout));
|
||||
}
|
||||
},
|
||||
};
|
||||
53
index.js
53
index.js
@@ -2,6 +2,7 @@ require('dotenv').config();
|
||||
const { Client } = require('discord.js-selfbot-v13');
|
||||
const client = new Client();
|
||||
const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
|
||||
const PREFIX = process.env.PREFIX || '.';
|
||||
const MESSAGE_DELETE_TIMEOUT = 10000
|
||||
@@ -15,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 => {
|
||||
|
||||
391
package-lock.json
generated
391
package-lock.json
generated
@@ -6,22 +6,24 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"csv-parse": "^5.5.6",
|
||||
"discord.js-selfbot-v13": "^3.5.1",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.10.0.tgz",
|
||||
"integrity": "sha512-ikVZsZP+3shmVJ5S1oM+7SveUCK3L9fTyfA8aJ7uD9cNQlTqF+3Irbk2Y22KXTb3C3RNUahRkSInClJMkHrINg==",
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.10.1.tgz",
|
||||
"integrity": "sha512-OWo1fY4ztL1/M/DUyRPShB4d/EzVfuUvPTRRHRIt/YxBrUYSz0a+JicD5F5zHFoNs2oTuWavxCOVFV1UljHTng==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@discordjs/formatters": "^0.6.0",
|
||||
"@discordjs/util": "^1.1.1",
|
||||
"@sapphire/shapeshift": "^4.0.0",
|
||||
"discord-api-types": "^0.37.114",
|
||||
"discord-api-types": "^0.37.119",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"ts-mixer": "^6.0.4",
|
||||
"tslib": "^2.6.3"
|
||||
@@ -82,6 +84,12 @@
|
||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/@minhducsun2002/leb128": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@minhducsun2002/leb128/-/leb128-1.0.0.tgz",
|
||||
"integrity": "sha512-eFrYUPDVHeuwWHluTG1kwNQUEUcFjVKYwPkU8z9DR1JH3AW7JtJsG9cRVGmwz809kKtGfwGJj58juCZxEvnI/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sapphire/async-queue": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
|
||||
@@ -105,6 +113,30 @@
|
||||
"node": ">=v16"
|
||||
}
|
||||
},
|
||||
"node_modules/@shinyoshiaki/binary-data": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@shinyoshiaki/binary-data/-/binary-data-0.6.1.tgz",
|
||||
"integrity": "sha512-7HDb/fQAop2bCmvDIzU5+69i+UJaFgIVp99h1VzK1mpg1JwSODOkjbqD7ilTYnqlnadF8C4XjpwpepxDsGY6+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"generate-function": "^2.3.1",
|
||||
"is-plain-object": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@shinyoshiaki/jspack": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@shinyoshiaki/jspack/-/jspack-0.0.6.tgz",
|
||||
"integrity": "sha512-SdsNhLjQh4onBlyPrn4ia1Pdx5bXT88G/LIEpOYAjx2u4xeY/m/HB5yHqlkJB1uQR3Zw4R3hBWLj46STRAN0rg=="
|
||||
},
|
||||
"node_modules/aes-js": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
|
||||
"integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
@@ -129,6 +161,77 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buildcheck": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
|
||||
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
@@ -192,12 +295,52 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/csv-parse": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz",
|
||||
"integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
@@ -214,15 +357,15 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.37.119",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz",
|
||||
"integrity": "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg==",
|
||||
"version": "0.37.120",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.120.tgz",
|
||||
"integrity": "sha512-7xpNK0EiWjjDFp2nAhHXezE4OUWm7s1zhc/UXXN6hnFFU8dfoPHgV0Hx0RPiCa3ILRpdeh152icc68DGCyXYIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/discord.js-selfbot-v13": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/discord.js-selfbot-v13/-/discord.js-selfbot-v13-3.5.1.tgz",
|
||||
"integrity": "sha512-AG4bYnymw8Ji0fsjVKG0f3gKoH/xuGTKn2TpxGtLhmiL8JwYywsoF7IFQ/Cl3avOPY+MKmeaR0JNScImQvG0tw==",
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/discord.js-selfbot-v13/-/discord.js-selfbot-v13-3.6.1.tgz",
|
||||
"integrity": "sha512-0RVv3u2sJZaGUKYPsUDtl4Zdbas5XuNGuR6409Qdm0/LLIcVm+677OydcCMDu9njwstwYMNWhl/8Zqpm5OrtnQ==",
|
||||
"license": "GNU General Public License v3.0",
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "^1.6.3",
|
||||
@@ -237,10 +380,11 @@
|
||||
"tough-cookie": "^4.1.4",
|
||||
"tree-kill": "^1.2.2",
|
||||
"undici": "^6.21.0",
|
||||
"werift-rtp": "^0.8.4",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
"node": ">=20.18"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
@@ -267,6 +411,29 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-cookie": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz",
|
||||
@@ -304,6 +471,27 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/generate-function": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
@@ -322,6 +510,26 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
@@ -331,6 +539,33 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"isobject": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/libsodium": {
|
||||
"version": "0.7.15",
|
||||
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.15.tgz",
|
||||
@@ -377,6 +612,62 @@
|
||||
"url": "https://tidelift.com/funding/github/npm/loglevel"
|
||||
}
|
||||
},
|
||||
"node_modules/mp4box": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/mp4box/-/mp4box-0.5.4.tgz",
|
||||
"integrity": "sha512-GcCH0fySxBurJtvr0dfhz0IxHZjc1RP+F+I8xw+LIwkU1a+7HJx8NCDiww1I5u4Hz6g4eR1JlGADEGJ9r4lSfA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
|
||||
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/opusscript": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz",
|
||||
@@ -528,6 +819,17 @@
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rx.mini": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/rx.mini/-/rx.mini-1.4.0.tgz",
|
||||
"integrity": "sha512-8w5cSc1mwNja7fl465DXOkVvIOkpvh2GW4jo31nAIvX4WTXCsRnKJGUfiDBzWtYRInEcHAUYIZfzusjIrea8gA=="
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
@@ -540,6 +842,23 @@
|
||||
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
|
||||
"integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
"bcrypt-pbkdf": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.10",
|
||||
"nan": "^2.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
@@ -614,10 +933,16 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.21.1",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
|
||||
"integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
|
||||
"version": "6.21.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz",
|
||||
"integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
@@ -642,6 +967,34 @@
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/werift-rtp": {
|
||||
"version": "0.8.4",
|
||||
"resolved": "https://registry.npmjs.org/werift-rtp/-/werift-rtp-0.8.4.tgz",
|
||||
"integrity": "sha512-n2FqQoSZnrS6ztMFkMMUee0ORh4JqdkEaaXwJ3NlemCoshcX3bfdKo4HukLwH2oBomfHFRIAvrqGRpo5JdYTzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@minhducsun2002/leb128": "^1.0.0",
|
||||
"@shinyoshiaki/binary-data": "^0.6.1",
|
||||
"@shinyoshiaki/jspack": "^0.0.6",
|
||||
"aes-js": "^3.1.2",
|
||||
"buffer": "^6.0.3",
|
||||
"debug": "^4.3.4",
|
||||
"mp4box": "^0.5.2",
|
||||
"rx.mini": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
@@ -663,9 +1016,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"csv-parse": "^5.5.6",
|
||||
"discord.js-selfbot-v13": "^3.5.1",
|
||||
"discord.js-selfbot-v13": "^3.6.1",
|
||||
"dotenv": "^16.4.1",
|
||||
"libsodium-wrappers": "^0.7.13",
|
||||
"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