Team Ai
Apppublic

Stryker-SC/Discord-Bot-VCMaster-JavaScript-Revamped

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
index.js149 linesDownload Raw Back to root
1const fs = require('fs');2const path = require('path');3const { Client, Collection, GatewayIntentBits, Partials } = require('discord.js');4const config = require('./config');5const db = require('./database');6const logger = require('./utils/logger');7 8const clientOptions = {9    intents: [10        GatewayIntentBits.Guilds,11        GatewayIntentBits.GuildVoiceStates,12        GatewayIntentBits.GuildMessages,13        GatewayIntentBits.MessageContent,14        GatewayIntentBits.GuildMembers, // Needed for moving members15        GatewayIntentBits.GuildPresences, // Needed for dynamic channel naming based on user activity16    ],17    partials: [Partials.Channel] // Sometimes needed for dm channels, but good practice18};19 20// If a REST proxy URL is provided via environment variables, configure the client to use it.21// This is essential for Hugging Face Spaces to bypass aggressive outbound TCP blocking.22if (process.env.REST_PROXY_URL) {23    const cleanProxyUrl = process.env.REST_PROXY_URL.replace(/\/+$/, '');24    clientOptions.rest = {25        api: cleanProxyUrl26    };27    logger.info(`[Proxy Setup] Using Custom REST API Proxy: ${cleanProxyUrl}`);28}29 30const client = new Client(clientOptions);31 32client.commands = new Collection();33client.db = db; // Attach db to client for easy access34client.roleConfigCache = new Map(); // Global cache for auto-role configuration35 36// Load Commands37const foldersPath = path.join(__dirname, 'commands');38const commandFolders = fs.readdirSync(foldersPath);39 40for (const folder of commandFolders) {41    const commandsPath = path.join(foldersPath, folder);42    const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));43    for (const file of commandFiles) {44        const filePath = path.join(commandsPath, file);45        const command = require(filePath);46        if ('data' in command && 'execute' in command) {47            client.commands.set(command.data.name, command);48        } else {49            logger.warn(`[Command Loader] The command at ${filePath} is missing a required "data" or "execute" property.`);50        }51    }52}53 54// Load Events55const eventsPath = path.join(__dirname, 'events');56const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));57 58for (const file of eventFiles) {59    const filePath = path.join(eventsPath, file);60    const event = require(filePath);61    if (event.once) {62        client.once(event.name, (...args) => event.execute(...args, client));63    } else {64        client.on(event.name, (...args) => event.execute(...args, client));65    }66}67 68const dns = require('dns');69 70// 1. Override DNS servers for Node's internal resolver (dns.resolve)71if (process.env.SPACE_ID) {72    dns.setServers(['8.8.8.8', '8.8.4.4']);73    dns.setDefaultResultOrder('ipv4first');74 75    // 2. MONKEY-PATCH dns.lookup76    // Hugging Face breaks OS-level resolution for discord.com but blocks root from fixing /etc/resolv.conf.77    // Furthermore, Node 20's undici/net algorithms use "Happy Eyeballs" to connect to ALL resolved IPs simultaneously.78    // Hugging Face's strict outgoing burst firewall drops simultaneous connections as abuse, causing the dreaded EPERM.79    // This patch intercepts all DNS lookups:80    // A) Forces discord domains through Google DNS to solve ENOTFOUND.81    // B) Forces ALL lookups to return ONLY ONE IP, preventing Happy Eyeballs burst EPERMs.82    const originalLookup = dns.lookup;83    dns.lookup = function (hostname, options, callback) {84        if (typeof options === 'function') {85            callback = options;86            options = {};87        }88 89        if (hostname && hostname.includes('discord')) {90            dns.resolve4(hostname, (err, addresses) => {91                if (err || !addresses || addresses.length === 0) {92                    return originalLookup(hostname, options, callback);93                }94                // CRITICAL FIX: Return ONLY 1 IP. Prevents 5 concurrent TCP SYN packets.95                if (options.all) {96                    callback(null, [{ address: addresses[0], family: 4 }]);97                } else {98                    callback(null, addresses[0], 4);99                }100            });101        } else {102            // Let other domains pass through, but still strictly limit to 1 IP if options.all is requested103            originalLookup(hostname, options, (err, address, family) => {104                if (err) return callback(err);105                if (options.all && Array.isArray(address) && address.length > 0) {106                    callback(null, [address[0]]);107                } else {108                    callback(null, address, family);109                }110            });111        }112    };113}114 115// Connect to Database116db.connect(config.mongoURI);117 118// Diagnostics: Test Outbound HTTP Connections119const https = require('https');120 121function testConnection(url, name) {122    logger.info(`[Diagnostic] Testing connection to ${name} (${url})...`);123    https.get(url, (res) => {124        logger.info(`[Diagnostic] Success! ${name} responded with status: ${res.statusCode}`);125    }).on('error', (e) => {126        logger.error(`[Diagnostic] FAILED! Connection to ${name} rejected:`, e.message);127    });128}129 130// Run tests before discord login131// testConnection('https://www.google.com', 'Google');132// testConnection('https://discord.com/api/v10/gateway', 'Discord API');133 134const login = async (retries = 5) => {135    try {136        await client.login(config.token);137    } catch (error) {138        if (retries === 0) {139            logger.error('[Discord API] Failed to login after multiple attempts:', error);140            process.exit(1);141        }142        logger.warn(`[Discord API] Login failed, retrying in 5 seconds... (${retries} attempts left)`);143        logger.error('[Discord API]', error); // Log the specific error144        setTimeout(() => login(retries - 1), 5000);145    }146};147 148login();149