UNPKG

gratian-manager

Version:

Sistema de gerenciamento e hospedagem de múltiplos bots Discord.

519 lines (421 loc) 16.1 kB
// bot-manager.js const { execSync, spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const readline = require('readline'); function updateStatusFile(botName, data) { const botStatusDir = path.join(process.cwd(), 'bots', botName); const statusFilePath = path.join(botStatusDir, 'status.json'); // Garante que a pasta do bot existe if (!fs.existsSync(botStatusDir)) { fs.mkdirSync(botStatusDir, { recursive: true }); } // Escreve o status fs.writeFileSync(statusFilePath, JSON.stringify(data, null, 2)); } const config = { botsDirectory: './bots', }; if (!fs.existsSync(config.botsDirectory)) { fs.mkdirSync(config.botsDirectory, { recursive: true }); } const bots = {}; const stoppingBots = new Set(); // Bots sendo parados manualmente function createPackageJson(botDir, botName) { const packagePath = path.join(botDir, 'package.json'); if (fs.existsSync(packagePath)) { return true; } console.log(`Criando package.json para ${botName}...`); const packageJson = { name: botName, version: '1.0.0', description: `Bot ${botName}`, main: 'index.js', scripts: { start: 'node index.js' }, dependencies: {}, author: '', license: 'ISC' }; try { fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); console.log(`Package.json criado para ${botName}!`); return true; } catch (error) { console.error(`Erro ao criar package.json para ${botName}:`, error.message); return false; } } function createIndexJs(botDir, botName) { const indexPath = path.join(botDir, 'index.js'); if (fs.existsSync(indexPath)) { return true; } console.log(`Criando index.js básico para ${botName}...`); const basicIndexJs = `// Bot ${botName} console.log('Bot ${botName} iniciado com sucesso!'); // Seu código do bot aqui console.log('Configurando bot...'); console.log('Bot ${botName} em execução...'); // Exemplo de loop para manter o bot rodando setInterval(() => { }, 60000); // Log a cada minuto `; try { fs.writeFileSync(indexPath, basicIndexJs); console.log(`Index.js criado para ${botName}!`); return true; } catch (error) { console.error(`Erro ao criar index.js para ${botName}:`, error.message); return false; } } async function installDependencies(botDir, botName) { const hasPackageLock = fs.existsSync(path.join(botDir, 'package-lock.json')); console.log(`Instalando dependências para ${botName}...`); try { if (hasPackageLock) { execSync('npm ci --quiet', { cwd: botDir }); } else { execSync('npm install --quiet', { cwd: botDir }); } console.log(`Dependências instaladas para ${botName}!`); return true; } catch (error) { console.error(`Erro ao instalar dependências para ${botName}:`, error.message); return false; } } async function startBot(botName) { const botDir = path.join(config.botsDirectory, botName); if (!fs.existsSync(botDir)) { console.error(`Bot "${botName}" não encontrado.`); return false; } if (bots[botName] && bots[botName].process) { console.log(`Bot "${botName}" já está rodando.`); return false; } try { // Gera package.json se não existir if (!fs.existsSync(path.join(botDir, 'package.json'))) { const created = createPackageJson(botDir, botName); if (!created) return false; } // Gera index.js se não existir if (!fs.existsSync(path.join(botDir, 'index.js'))) { const created = createIndexJs(botDir, botName); if (!created) return false; } // Instala dependências if (!fs.existsSync(path.join(botDir, 'node_modules'))) { const installed = await installDependencies(botDir, botName); if (!installed) return false; } console.log(`Iniciando bot "${botName}"...`); const botProcess = spawn('node', ['.'], { cwd: botDir, stdio: 'inherit', detached: false }); const startedAt = new Date(); // Atualiza status.json updateStatusFile(botName, { status: 'running', pid: botProcess.pid, startedAt: startedAt.toISOString() }); // Registra processo do bot bots[botName] = { process: botProcess, status: 'running', startTime: startedAt, startedAt: startedAt, directory: botDir }; // Quando o processo encerrar botProcess.on('close', (code) => { console.log(`Bot "${botName}" encerrado com código ${code}.`); delete bots[botName]; // Verifica se foi uma parada manual if (stoppingBots.has(botName)) { stoppingBots.delete(botName); // remove do set updateStatusFile(botName, { status: 'stopped', pid: null, startedAt: null, uptime: 0 }); return; // ← Não reinicia! } // Se não foi manual, reinicia console.log(`Reiniciando bot "${botName}" em 5 segundos...`); updateStatusFile(botName, { status: 'restarting', pid: null, startedAt: null, uptime: 0 }); setTimeout(() => { startBot(botName); }, 5000); }); console.log(`Bot "${botName}" iniciado com sucesso!`); return true; } catch (error) { console.error(`Erro ao iniciar bot "${botName}":`, error.message); return false; } } function stopBot(botName) { const bot = bots[botName]; if (!bot || !bot.process) { console.error(`Bot "${botName}" não está rodando.`); return false; } console.log(`Parando bot "${botName}"...`); stoppingBots.add(botName); // ← marca como parada manual bot.process.kill('SIGTERM'); // Calcular o uptime com base no startedAt, se existir let uptime = 0; if (bot.startedAt) { const startTime = new Date(bot.startedAt).getTime(); uptime = Date.now() - startTime; } // Atualiza o arquivo de status imediatamente updateStatusFile(botName, { status: 'stopped', pid: null, startedAt: null, uptime }); // Após 3 segundos, força o encerramento se necessário setTimeout(() => { if (bot.process) { console.log(`Forçando encerramento do bot "${botName}"...`); bot.process.kill('SIGKILL'); } }, 3000); // Remove o bot da memória delete bots[botName]; return true; } async function restartBot(botName) { console.log(`Reiniciando bot "${botName}"...`); // Atualiza o status para "restarting" updateStatusFile(botName, { status: 'restarting', pid: null, startedAt: null, uptime: 0 }); const wasRunning = stopBot(botName); // Aguarda 2s apenas se ele estava rodando await new Promise(resolve => setTimeout(resolve, wasRunning ? 2000 : 0)); // Inicia novamente return startBot(botName); } function listAvailableBots() { try { const files = fs.readdirSync(config.botsDirectory); return files.filter(file => { const botDir = path.join(config.botsDirectory, file); return fs.statSync(botDir).isDirectory(); }); } catch (error) { console.error('Erro ao listar bots:', error.message); return []; } } function showStatus() { const availableBots = listAvailableBots(); console.log('\n===== STATUS DOS BOTS ====='); console.log(`Total de bots disponíveis: ${availableBots.length}`); let running = 0; let stopped = 0; availableBots.forEach(botName => { const status = bots[botName] ? 'RODANDO' : 'PARADO'; const statusEmoji = bots[botName] ? '🟢 ' : '🔴 '; if (bots[botName]) { running++; const uptime = Math.floor((new Date() - bots[botName].startTime) / 1000); console.log(`${statusEmoji} ${botName}: ${status} (uptime: ${formatUptime(uptime)})`); } else { stopped++; console.log(`${statusEmoji} ${botName}: ${status}`); } }); console.log(`\nBots rodando: ${running}`); console.log(`Bots parados: ${stopped}`); console.log('===========================\n'); } function formatUptime(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; return `${hours}h ${minutes}m ${secs}s`; } async function startBotsSequentially(botNames) { for (const botName of botNames) { console.log(`Iniciando bot: ${botName}`); await startBot(botName); await new Promise(resolve => setTimeout(resolve, 2000)); } } const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: 'Gratian Manager> ' }); console.log('=== GRATIAN MANAGER ==='); console.log('Comandos disponíveis:'); console.log('.start [botName] - Inicia um bot específico ou todos'); console.log('.stop [botName] - Para um bot específico ou todos'); console.log('.restart [botName] - Reinicia um bot específico ou todos'); console.log('.status - Mostra o status de todos os bots'); console.log('.npm [botName] - Instala dependências para um bot específico ou todos'); console.log('.list - Lista todos os bots disponíveis'); console.log('.help - Mostra esta ajuda'); console.log('.exit - Sai do gerenciador'); console.log('\nIniciando automaticamente todos os bots disponíveis...'); const allBots = listAvailableBots(); if (allBots.length > 0) { startBotsSequentially(allBots); } else { console.log('Nenhum bot encontrado para iniciar.'); } rl.prompt(); rl.on('line', async (line) => { const input = line.trim(); const [command, ...args] = input.split(' '); const botName = args.join(' '); switch (command.toLowerCase()) { case '.start': if (botName) { await startBot(botName); } else { const botsList = listAvailableBots(); if (botsList.length === 0) { console.log('Nenhum bot encontrado para iniciar.'); } else { console.log(`Iniciando todos os ${botsList.length} bots...`); await startBotsSequentially(botsList); } } break; case '.stop': if (botName) { stopBot(botName); } else { const runningBots = Object.keys(bots); if (runningBots.length === 0) { console.log('Nenhum bot está rodando.'); } else { console.log(`Parando todos os ${runningBots.length} bots...`); runningBots.forEach(bot => stopBot(bot)); } } break; case '.restart': if (botName) { await restartBot(botName); } else { const allBotsList = listAvailableBots(); if (allBotsList.length === 0) { console.log('Nenhum bot encontrado para reiniciar.'); } else { console.log(`Reiniciando todos os ${allBotsList.length} bots...`); for (const bot of allBotsList) { if (bots[bot]) { await restartBot(bot); } else { await startBot(bot); } // Pequeno intervalo entre reinicializações await new Promise(resolve => setTimeout(resolve, 1000)); } } } break; case '.status': showStatus(); break; case '.npm': case '.npm-i': case '.npm-install': if (botName) { const botDir = path.join(config.botsDirectory, botName); if (fs.existsSync(botDir)) { await installDependencies(botDir, botName); } else { console.error(`Bot "${botName}" não encontrado.`); } } else { const allBotsList = listAvailableBots(); if (allBotsList.length === 0) { console.log('Nenhum bot encontrado para instalar dependências.'); } else { console.log(`Instalando dependências para todos os ${allBotsList.length} bots...`); for (const bot of allBotsList) { const botDir = path.join(config.botsDirectory, bot); await installDependencies(botDir, bot); } } } break; case '.list': const availableBots = listAvailableBots(); console.log('\n=== BOTS DISPONÍVEIS ==='); if (availableBots.length === 0) { console.log('Nenhum bot encontrado.'); } else { availableBots.forEach(bot => { const status = bots[bot] ? '🟢 RODANDO' : '🔴 PARADO'; console.log(`${bot}: ${status}`); }); } console.log('=======================\n'); break; case '.help': console.log('\n=== AJUDA ==='); console.log('.start [botName] - Inicia um bot específico ou todos'); console.log('.stop [botName] - Para um bot específico ou todos'); console.log('.restart [botName] - Reinicia um bot específico ou todos'); console.log('.status - Mostra o status de todos os bots'); console.log('.npm [botName] - Instala dependências para um bot específico ou todos'); console.log('.list - Lista todos os bots disponíveis'); console.log('.help - Mostra esta ajuda'); console.log('.exit - Sai do gerenciador'); console.log('============\n'); break; case '.exit': console.log('Encerrando todos os bots antes de sair...'); Object.keys(bots).forEach(bot => stopBot(bot)); setTimeout(() => { console.log('Saindo do Gratian Manager. Até mais!'); rl.close(); process.exit(0); }, 2000); return; default: console.log(`Comando desconhecido: ${command}`); console.log('Use .help para ver a lista de comandos disponíveis.'); } rl.prompt(); }).on('close', () => { console.log('Saindo do Gratian Manager. Até mais!'); process.exit(0); }); process.on('SIGINT', () => { console.log('\nEncerrando Gratian Manager...'); Object.keys(bots).forEach(bot => stopBot(bot)); setTimeout(() => { process.exit(0); }, 2000); }); setTimeout(() => { showStatus(); rl.prompt(); }, 1000);