// findChannels.js // Parallelized server scanning to find chattable channels. // Uses servers.json (auto-located). // WARNING: Selfbots violate Discord TOS. Use at your own risk. const { Client, Intents } = require('discord.js-selfbot-v13'); const fs = require('fs'); const path = require('path'); /////////// CONFIG /////////// const TOKEN = ''; // <-- put your token here // Concurrency settings (tune to be slower to avoid rate limits/bans). const CONCURRENCY = 1; // Set to 1 to process servers one by one (safer) const CHANNEL_TEST_DELAY_MS = 2000; // Delay between testing each channel (in ms, e.g., 2000 = 2 seconds) const RETRY_ATTEMPTS = 3; // retry attempts on transient errors / 429 const RETRY_BASE_MS = 1000; // base backoff in ms const TEST_SEND = true; // Enabled to actually test sending "X" and deleting it to verify chattable (RISKY: can trigger anti-spam, rate limits, or bans! Run on small server lists first.) const LOG_CHANNELS = true; // Enabled to log each found/verified channel's name and ID to console const INTENTS = [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MEMBERS]; /////////// HELPERS /////////// function sleep(ms) { return new Promise(res => setTimeout(res, ms)); } /** * Locate servers.json in several sensible places so doubled folder problems won't block you. */ function locateServersJson() { const candidates = [ path.resolve(process.cwd(), 'servers.json'), path.join(__dirname, 'servers.json'), ]; for (const p of candidates) if (fs.existsSync(p)) return p; return { notFound: true, attempted: candidates }; } function loadServerIdsOrExit() { const located = locateServersJson(); if (located && located.notFound) { console.error('[error] Required file not found. Attempted the following paths:'); for (const p of located.attempted) console.error(' ' + p); console.error('\nCreate a servers.json in one of those locations using this format (array of guild ID strings):'); console.error(`[\n "349051257702580224",\n "587355301083349002",\n "1141515589924429869"\n]`); process.exit(1); } console.log(`[info] Using servers.json at: ${located}`); let parsed; try { parsed = JSON.parse(fs.readFileSync(located, 'utf8')); } catch (e) { console.error(`[error] Failed to parse ${located}: ${e.message}`); process.exit(1); } if (!Array.isArray(parsed)) { console.error(`[error] ${located} must contain a JSON array of guild ID strings.`); process.exit(1); } const ids = Array.from(new Set(parsed.map(String).map(s => s.trim()).filter(Boolean).filter(s => /^\d+$/.test(s)))); if (!ids.length) { console.error(`[error] ${located} contains no valid numeric guild IDs.`); process.exit(1); } return ids; } /** * Generic limited-concurrency runner. * items: array * limit: number of parallel workers * worker: async function(item, index) -> result */ async function runLimited(items, limit, worker) { let i = 0; const results = new Array(items.length); const workers = Array.from({ length: Math.min(limit, items.length) }, async () => { while (true) { const idx = i++; if (idx >= items.length) break; try { results[idx] = await worker(items[idx], idx); } catch (err) { results[idx] = { __error: err }; } } }); await Promise.all(workers); return results; } /** * Robust channel scan inside a guild. * Tries to fetch channels and check permissions with retries on errors. * If TEST_SEND is true, actually tries to send and delete a message to verify. * Skips threads as per user request. */ async function findChattableChannelsInGuildWithRetries(guild, client) { for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) { try { // Fetch self member const me = await guild.members.fetch(client.user.id); // Fetch all channels (cache should populate) - only gets channels visible to the user await guild.channels.fetch(); const channels = guild.channels.cache; const chattable = []; for (const channel of channels.values()) { if (!channel.isText() || channel.isThread()) continue; // Skip non-text and threads // Check permission first if (!channel.permissionsFor(me).has('SEND_MESSAGES')) continue; if (TEST_SEND) { // Actually test send "X" and delete try { const msg = await channel.send('X'); await msg.delete(); chattable.push(channel.id); if (LOG_CHANNELS) console.log(` - Verified by send: #${channel.name} (${channel.id})`); } catch (sendErr) { console.warn(`[warn] Cannot send in channel ${channel.id} (${channel.name}): ${sendErr.message || sendErr}`); continue; } } else { chattable.push(channel.id); if (LOG_CHANNELS) console.log(` - Found (perm check): #${channel.name} (${channel.id})`); } // Delay between channel tests to avoid rate limits await sleep(CHANNEL_TEST_DELAY_MS); } return chattable; } catch (err) { // Detect 429-like behavior if possible const is429 = (err && (err.status === 429 || /429|Too Many Requests/i.test(err.message || ''))); const waitMs = RETRY_BASE_MS * Math.pow(2, attempt); if (is429) { console.warn(`[warn] Rate limited in guild ${guild.id}. Backing off ${waitMs}ms (attempt ${attempt+1}/${RETRY_ATTEMPTS})`); } else { console.warn(`[warn] Error in guild ${guild.id}: ${err.message || err}. Retrying ${waitMs}ms (attempt ${attempt+1}/${RETRY_ATTEMPTS})`); } await sleep(waitMs); } } // All attempts failed -> treat as no channels for this guild return []; } /////////// MAIN /////////// (async () => { if (!TOKEN || TOKEN === 'PASTE_YOUR_TOKEN_HERE') { console.error('No token provided. Paste your token into the TOKEN constant at top of the script.'); process.exit(1); } const serverIds = loadServerIdsOrExit(); console.log(`Loaded ${serverIds.length} server ID(s) from servers.json.`); console.log(`Using concurrency: CONCURRENCY=${CONCURRENCY} (lowered for safety)`); console.log(`TEST_SEND mode: ${TEST_SEND ? 'ENABLED (will try sending "X" in each channel - risky, but verifies truly chattable)' : 'DISABLED (permission check only)'}`); console.log(`CHANNEL_TEST_DELAY_MS: ${CHANNEL_TEST_DELAY_MS}ms between each channel test`); console.log(`LOG_CHANNELS: ${LOG_CHANNELS ? 'ENABLED' : 'DISABLED'}`); const client = new Client({ intents: INTENTS }); client.once('ready', async () => { console.log(`\nLogged in as ${client.user.tag}`); const allChattableChannels = new Set(); // Use set to avoid duplicates // Process servers in parallel with CONCURRENCY await runLimited(serverIds, CONCURRENCY, async (sid, idx) => { console.log(`\n[${idx+1}/${serverIds.length}] Checking server ${sid} ...`); let guild; try { guild = await client.guilds.fetch(sid); } catch (err) { console.warn(`- Skipping guild ${sid}: cannot fetch (not in guild / invalid id).`); return; } try { const channels = await findChattableChannelsInGuildWithRetries(guild, client); if (channels.length) { console.log(`- Found ${channels.length} chattable channel(s) in ${guild.name} (${guild.id})`); channels.forEach(id => allChattableChannels.add(id)); } else { console.log(`- No chattable channels found in ${guild.name} (${guild.id})`); } } catch (err) { console.warn(`- Error scanning guild ${sid}: ${err.message || err}`); } }); // Write to channels.json const outputPath = path.resolve(process.cwd(), 'channels.json'); const channelArray = Array.from(allChattableChannels).sort(); // Sort for consistency const jsonContent = '[\n' + channelArray.map(id => ` "${id}"`).join(',\n') + '\n]'; fs.writeFileSync(outputPath, jsonContent, 'utf8'); console.log(`\n--- scan complete — summary ---`); console.log(`Found ${channelArray.length} chattable channel(s) across all servers.`); console.log(`Saved to: ${outputPath}`); client.destroy(); process.exit(0); }); client.on('error', (err) => { console.error('Client error:', err && err.message ? err.message : err); }); try { await client.login(TOKEN); } catch (err) { console.error('Failed to login:', err && err.message ? err.message : err); process.exit(1); } })();