#!/usr/bin/env node /** * Discord user lookup WITH ROLES * * Fetches: * - User info (username, display name, avatar) * - Guild member info (roles, join date, server nickname) * - All Palpocalypse server roles * * Requires: Server Members Intent enabled in Discord Developer Portal */ const fs = require('fs'); const path = require('path'); const { Client, GatewayIntentBits } = require('discord.js'); require('dotenv').config({ path: path.join(__dirname, '../../.env') }); const TOKEN = process.env.MEMBER_BOT_TOKEN || process.env.DISCORD_BOT_TOKEN; const GUILD_ID = '798321161082896395'; // Indifferent Broccoli server if (!TOKEN) { console.error('āŒ Error: No bot token found'); process.exit(1); } const args = process.argv.slice(2); if (args.length < 2) { console.error('Usage: node scripts/lookup-with-roles.js '); process.exit(1); } const inputFile = args[0]; const outputFile = args[1]; 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`); let results = {}; let processed = 0; let errors = 0; if (fs.existsSync(outputFile)) { try { const existing = JSON.parse(fs.readFileSync(outputFile, 'utf-8')); results = existing.users || {}; processed = Object.keys(results).length; errors = existing.errors || 0; console.log(`šŸ“‚ Found existing: ${processed} users`); } catch (e) { console.log(`āš ļø Starting fresh`); } } const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers // Required for roles! ] }); let guild = null; async function lookupUserWithRoles(userId) { if (results[userId]) return results[userId]; try { // Fetch basic user info const user = await client.users.fetch(userId); // Try to fetch guild member (for roles) let roles = []; let serverNickname = null; let joinedAt = null; let isInServer = false; try { const member = await guild.members.fetch(userId); isInServer = true; serverNickname = member.nickname; joinedAt = member.joinedAt ? member.joinedAt.toISOString() : null; // Get all roles except @everyone roles = member.roles.cache .filter(role => role.name !== '@everyone') .map(role => ({ id: role.id, name: role.name, color: role.hexColor, position: role.position })) .sort((a, b) => b.position - a.position); // Highest role first } catch (memberError) { // User exists but not in this server isInServer = false; } return { success: true, id: user.id, username: user.username, globalName: user.globalName || user.username, tag: user.tag, bot: user.bot, avatar: user.displayAvatarURL(), // Server-specific data server_nickname: serverNickname, joined_at: joinedAt, in_server: isInServer, roles: roles, role_names: roles.map(r => r.name), highest_role: roles[0]?.name || null }; } catch (error) { return { success: false, id: userId, error: error.message, username: null, globalName: null, roles: [] }; } } function saveResults() { const output = { timestamp: new Date().toISOString(), total_users: userIds.length, processed: processed, successful: processed - errors, errors: errors, guild_id: GUILD_ID, includes_roles: true, users: results }; fs.writeFileSync(outputFile, JSON.stringify(output, null, 2)); } async function processUsers() { console.log('\nšŸŽ­ Starting lookups WITH ROLES...'); console.log(` Guild ID: ${GUILD_ID}`); console.log(` Rate: 1 user/second\n`); // Fetch guild guild = await client.guilds.fetch(GUILD_ID); console.log(`āœ… Connected to: ${guild.name}\n`); const startTime = Date.now(); const toProcess = userIds.filter(id => !results[id]); console.log(` ${toProcess.length} users remaining\n`); for (let i = 0; i < toProcess.length; i++) { const userId = toProcess[i]; const result = await lookupUserWithRoles(userId); results[result.id] = result; if (!result.success) errors++; processed++; // Save every 10 users if (processed % 10 === 0) { saveResults(); const elapsed = (Date.now() - startTime) / 1000; const rate = (processed - (userIds.length - toProcess.length)) / elapsed; const remaining = (toProcess.length - i - 1) / rate; // Show sample with roles if (result.success && result.roles.length > 0) { const rolePreview = result.role_names.slice(0, 2).join(', '); console.log(`šŸ’¾ ${processed}/${userIds.length} - ${result.globalName} [${rolePreview}] - ~${remaining.toFixed(0)}s left`); } else { console.log(`šŸ’¾ ${processed}/${userIds.length} (${errors} errors) - ~${remaining.toFixed(0)}s left`); } } await new Promise(resolve => setTimeout(resolve, 1000)); } saveResults(); const totalTime = (Date.now() - startTime) / 1000; // Stats const usersWithRoles = Object.values(results).filter(u => u.success && u.roles.length > 0).length; const allRoleNames = new Set(); Object.values(results).forEach(u => { if (u.success) { u.role_names?.forEach(r => allRoleNames.add(r)); } }); console.log(`\n${'='.repeat(70)}`); console.log(`āœ… Complete with Roles!`); console.log(`${'='.repeat(70)}`); console.log(` Time: ${totalTime.toFixed(1)}s`); console.log(` Processed: ${processed}/${userIds.length}`); console.log(` Successful: ${processed - errors}`); console.log(` Users with roles: ${usersWithRoles}`); console.log(` Unique roles found: ${allRoleNames.size}`); console.log(`\nšŸ’¾ Saved to: ${outputFile}\n`); // Show some roles if (allRoleNames.size > 0) { console.log('šŸ“‹ Sample roles found:'); Array.from(allRoleNames).slice(0, 10).forEach(r => console.log(` • ${r}`)); } process.exit(0); } client.once('ready', () => { console.log(`āœ… Logged in as ${client.user.tag}\n`); processUsers(); }); client.on('error', (error) => { console.error('āŒ Error:', error.message); }); process.on('SIGINT', () => { console.log('\n\nāš ļø Interrupted! Saving...'); saveResults(); console.log('āœ… Saved. Resume by running same command.\n'); process.exit(0); }); console.log('šŸ”Œ Connecting to Discord...'); client.login(TOKEN);