diff --git a/scripts/lookup-with-roles.js b/scripts/lookup-with-roles.js index bb31757..7af72ba 100644 --- a/scripts/lookup-with-roles.js +++ b/scripts/lookup-with-roles.js @@ -1,237 +1,237 @@ -#!/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); +#!/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);