First upload
This commit is contained in:
174
scripts/bulk-lookup-users.js
Normal file
174
scripts/bulk-lookup-users.js
Normal file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Bulk lookup Discord user information
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/bulk-lookup-users.js <input_file> <output_file>
|
||||
*
|
||||
* Input file: Text file with one user ID per line
|
||||
* Output file: JSON file with user lookup results
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
|
||||
// Load environment variables from repo root
|
||||
const envPath = path.join(__dirname, '../../.env');
|
||||
console.log(`Loading .env from: ${envPath}`);
|
||||
const result = require('dotenv').config({ path: envPath });
|
||||
if (result.error) {
|
||||
console.error(`Error loading .env: ${result.error.message}`);
|
||||
// Try broccolini-bot/.env as fallback
|
||||
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
||||
}
|
||||
|
||||
const TOKEN = process.env.DISCORD_BOT_TOKEN || process.env.DISCORD_TOKEN;
|
||||
const GUILD_ID = process.env.GUILD_ID || process.env.SERVER_ID;
|
||||
|
||||
if (!TOKEN) {
|
||||
console.error('Error: DISCORD_BOT_TOKEN must be set in .env');
|
||||
console.error('Available env vars:', Object.keys(process.env).filter(k => k.includes('DISCORD')));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Parse command line args
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error('Usage: node scripts/bulk-lookup-users.js <input_file> <output_file>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const inputFile = args[0];
|
||||
const outputFile = args[1];
|
||||
|
||||
// Read user IDs from input file
|
||||
const userIds = fs.readFileSync(inputFile, 'utf-8')
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0);
|
||||
|
||||
console.log(`Loaded ${userIds.length} user IDs from ${inputFile}`);
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers
|
||||
]
|
||||
});
|
||||
|
||||
const results = {};
|
||||
let processed = 0;
|
||||
let errors = 0;
|
||||
|
||||
async function lookupUser(userId) {
|
||||
try {
|
||||
// Add timeout to prevent hanging
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Lookup timeout')), 10000)
|
||||
);
|
||||
|
||||
const fetchPromise = client.users.fetch(userId);
|
||||
const user = await Promise.race([fetchPromise, timeoutPromise]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
globalName: user.globalName || user.username,
|
||||
tag: user.tag,
|
||||
bot: user.bot,
|
||||
avatar: user.displayAvatarURL()
|
||||
};
|
||||
} catch (error) {
|
||||
// Handle errors (not found, timeout, rate limit)
|
||||
if (error.message.includes('429')) {
|
||||
console.log(` ⚠️ Rate limit hit for user ${userId}, will retry`);
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
id: userId,
|
||||
error: error.message,
|
||||
username: null,
|
||||
globalName: null,
|
||||
tag: null,
|
||||
bot: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processUsers() {
|
||||
console.log('\nStarting bulk lookup...');
|
||||
console.log('This will take a few minutes for 2,428 users\n');
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
// Process in batches to avoid rate limits
|
||||
const BATCH_SIZE = 3; // Very small batches to avoid rate limits
|
||||
const DELAY_MS = 2000; // 2 seconds between batches
|
||||
|
||||
for (let i = 0; i < userIds.length; i += BATCH_SIZE) {
|
||||
const batch = userIds.slice(i, i + BATCH_SIZE);
|
||||
|
||||
// Lookup batch in parallel
|
||||
const promises = batch.map(userId => lookupUser(userId));
|
||||
const batchResults = await Promise.all(promises);
|
||||
|
||||
// Store results
|
||||
batchResults.forEach(result => {
|
||||
if (!result.success) {
|
||||
errors++;
|
||||
}
|
||||
results[result.id] = result;
|
||||
processed++;
|
||||
});
|
||||
|
||||
// Log every batch for debugging
|
||||
if (processed <= 50) {
|
||||
console.log(` Batch complete: ${processed} users processed`);
|
||||
}
|
||||
|
||||
// Progress update every 100 users
|
||||
if (processed % 100 === 0 || processed === userIds.length) {
|
||||
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
const rate = (processed / elapsed).toFixed(1);
|
||||
const remaining = ((userIds.length - processed) / rate).toFixed(0);
|
||||
console.log(`Progress: ${processed}/${userIds.length} (${errors} errors) - ${elapsed}s elapsed, ~${remaining}s remaining`);
|
||||
}
|
||||
|
||||
// Wait before next batch to avoid rate limits
|
||||
if (i + BATCH_SIZE < userIds.length) {
|
||||
await new Promise(resolve => setTimeout(resolve, DELAY_MS));
|
||||
}
|
||||
}
|
||||
|
||||
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
console.log(`\n✅ Completed in ${totalTime}s`);
|
||||
console.log(` Successful: ${processed - errors}`);
|
||||
console.log(` Errors: ${errors}`);
|
||||
|
||||
// Save results
|
||||
const output = {
|
||||
timestamp: new Date().toISOString(),
|
||||
total_users: userIds.length,
|
||||
successful: processed - errors,
|
||||
errors: errors,
|
||||
users: results
|
||||
};
|
||||
|
||||
fs.writeFileSync(outputFile, JSON.stringify(output, null, 2));
|
||||
console.log(`\n💾 Saved results to ${outputFile}`);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
client.once('ready', () => {
|
||||
console.log(`✅ Bot logged in as ${client.user.tag}`);
|
||||
processUsers();
|
||||
});
|
||||
|
||||
client.on('error', (error) => {
|
||||
console.error('Discord client error:', error);
|
||||
});
|
||||
|
||||
client.login(TOKEN);
|
||||
Reference in New Issue
Block a user