Files
broccolini-bot/scripts/bulk-lookup-users.js
2026-02-17 21:49:58 -06:00

175 lines
5.3 KiB
JavaScript

#!/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);