const express = require('express'); const bodyParser = require('body-parser'); const mineflayer = require('mineflayer'); const app = express(); const PORT = process.env.PORT || 7860; // ====================================================================== // --- КОНФИГУРАЦИЯ БОТА И API --- const TELEGRAM_TOKEN = '8596622001:AAE7NxgyUEQ-mZqTMolt7Kgs2ouM0QyjdIE'; const BASE_TELEGRAM_URL = `https://api.telegram.org/bot${TELEGRAM_TOKEN}`; // ====================================================================== // ---------------------------------------------------------------------- // --- КОНФИГУРАЦИЯ ПРОКСИ --- const PROXY_LIST_URL = null; // Отключено let PROXY_LIST = [ { host: '203.25.208.163', port: 1100 }, { host: '13.231.213.224', port: 1080 }, { host: '47.82.117.31', port: 1100 }, { host: '203.25.208.163', port: 1111 }, { host: '46.146.220.180', port: 1080 }, { host: '109.168.173.173', port: 1080 }, { host: '78.140.46.48', port: 1080 }, { host: '47.82.117.31', port: 1011 }, { host: '89.148.196.156', port: 1080 }, { host: '37.192.133.82', port: 1080 }, { host: '121.169.46.116', port: 1090 }, { host: '192.241.156.17', port: 1080 }, { host: '38.183.144.18', port: 1080 }, { host: '143.110.217.153', port: 1080 } ]; // ---------------------------------------------------------------------- const activeBots = {}; // --- КОНФИГУРАЦИЯ EXPRESS --- app.use(bodyParser.json()); app.get('/', (req, res) => { res.send(`Worker API is running. Currently loaded ${PROXY_LIST.length} proxies.`); }); // --- ФУНКЦИИ УВЕДОМЛЕНИЙ (Оставлены без изменений) --- async function sendNotification(chatId, message) { // ... (код sendNotification) const data = activeBots[chatId]; if (data && data.isStopping) { return; } try { const { default: fetch } = await import('node-fetch'); if (!TELEGRAM_TOKEN) return console.error(`[Chat ${chatId}] Ошибка: TELEGRAM_TOKEN не установлен.`); const escapedMessage = message.replace(/[().!]/g, '\\$&'); const url = `${BASE_TELEGRAM_URL}/sendMessage`; const payload = { chat_id: chatId, text: escapedMessage, parse_mode: 'MarkdownV2' }; let response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok && response.status === 400) { console.warn(`[Chat ${chatId}] Ошибка MarkdownV2, отправляю обычный текст.`); const plainPayload = { chat_id: chatId, text: `[RAW] ${message}` }; response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(plainPayload) }); } } catch (e) { console.error(`[Chat ${chatId}] Критическая ошибка сети при отправке уведомления: ${e.message}`); } } function cleanupBot(chatId) { if (activeBots[chatId]) { console.log(`[Chat ${chatId}] Ресурсы бота очищены.`); delete activeBots[chatId]; } } // --- ФУНКЦИИ ПАРСИНГА И ЗАГРУЗКИ ПРОКСИ (Оставлены без изменений) --- async function fetchAndParseProxyList() { if (!PROXY_LIST_URL) return PROXY_LIST; return []; } // --- ОСНОВНАЯ ЛОГИКА MINEFLAYER (Оставлены без изменений) --- async function setupMineflayerBot(chatId, host, port, username, version) { const maxAttempts = 5; if (PROXY_LIST.length === 0) { PROXY_LIST = await fetchAndParseProxyList(); if (PROXY_LIST.length === 0) { console.log(`[Chat ${chatId}] Нет доступных прокси. Отключение.`); sendNotification(chatId, `🛑 Не удалось найти прокси-лист\\.`, 'MarkdownV2'); return cleanupBot(chatId); } } let data = activeBots[chatId]; if (data && data.bot) { console.log(`[Chat ${chatId}] Обнаружен старый бот. Отключаю: ${data.host}:${data.port}`); data.bot.quit('disconnect.cleanup'); data.bot = null; } if (!data) { data = { bot: null, host, port, username, version, reconnectAttempts: 0, currentProxyIndex: 0, isProxyFailure: false, isStopping: false }; activeBots[chatId] = data; } else { data.host = host; data.port = port; data.username = username; data.version = version; // Добавлено сохранение версии data.bot = null; data.isStopping = false; } const currentIndex = data.currentProxyIndex; if (currentIndex >= PROXY_LIST.length) { console.log(`[Chat ${chatId}] Все ${PROXY_LIST.length} прокси были испробованы. Отключение.`); sendNotification(chatId, `🛑 Бот отключен окончательно\\. Все ${PROXY_LIST.length} прокси были испробованы\\.`, 'MarkdownV2'); cleanupBot(chatId); return; } const currentProxy = PROXY_LIST[currentIndex]; console.log(`[Chat ${chatId}] Запуск Mineflayer с: Host=${host}, Port=${port}, Username=${username}, Version=${version} | ПРОКСИ: ${currentProxy.host}:${currentProxy.port} (№${currentIndex + 1}/${PROXY_LIST.length})`); const bot = mineflayer.createBot({ host: host, port: parseInt(port), username: username, version: version, // Использование динамической версии proxy: { host: currentProxy.host, port: currentProxy.port, type: 5 } }); data.bot = bot; bot.on('login', () => { console.log(`[Chat ${chatId}] Бот ${username} подключился к ${host}:${port}`); sendNotification(chatId, `✅ Бот ${username} успешно подключился к ${host}:${port}`, 'MarkdownV2'); if (activeBots[chatId]) { activeBots[chatId].reconnectAttempts = 0; activeBots[chatId].currentProxyIndex = 0; } }); bot.on('error', (err) => { const errorMessage = err.message || 'Неизвестная ошибка подключения'; console.error(`[Chat ${chatId}] Ошибка бота: ${errorMessage}`); const data = activeBots[chatId]; if (data) { if (errorMessage.includes('ECONNRESET') || errorMessage.includes('ETIMEDOUT') || errorMessage.includes('socketClosed') || errorMessage.includes('Failed to connect') || errorMessage.includes('EACCES')) { data.isProxyFailure = true; } data.bot.quit('disconnect.error'); } }); bot.on('end', (reason) => { console.log(`[Chat ${chatId}] Бот отключен. Причина: ${reason}`); const data = activeBots[chatId]; if (!data) return; if (data.isStopping) { return cleanupBot(chatId); } if (reason === 'disconnect.cleanup') { return cleanupBot(chatId); } if (data.isProxyFailure || reason === 'socketClosed') { data.isProxyFailure = false; data.currentProxyIndex++; if (data.currentProxyIndex < PROXY_LIST.length) { const nextProxyIndex = data.currentProxyIndex; sendNotification(chatId, `⚠️ Прокси не сработал\\. Попытка переподключения с ПРОКСИ №${nextProxyIndex + 1}/${PROXY_LIST.length}\\.`, 'MarkdownV2'); setTimeout(() => { console.log(`[Chat ${chatId}] Попытка переподключения с новым прокси...`); setupMineflayerBot(chatId, data.host, data.port, data.username, data.version); // Передаем версию }, 5000); return; } else { sendNotification(chatId, `🛑 Бот отключен окончательно\\. Все ${PROXY_LIST.length} прокси были испробованы\\.`, 'MarkdownV2'); return cleanupBot(chatId); } } data.reconnectAttempts++; if (data.reconnectAttempts < maxAttempts) { sendNotification(chatId, `⚠️ Бот был отключен \\(${reason}\\)\\. Попытка переподключения \\(${data.reconnectAttempts}/${maxAttempts}\\)\\.\\.\\.`, 'MarkdownV2'); setTimeout(() => { console.log(`[Chat ${chatId}] Попытка переподключения...`); setupMineflayerBot(chatId, data.host, data.port, data.username, data.version); // Передаем версию }, 5000 * data.reconnectAttempts); } else { sendNotification(chatId, `🛑 Бот отключен окончательно \\(${reason}\\)\\. Достигнут лимит попыток переподключения\\.`, 'MarkdownV2'); cleanupBot(chatId); } }); bot.on('spawn', () => { console.log(`[Chat ${chatId}] Бот заспавнился. Готов к работе.`); sendNotification(chatId, `🌍 Бот заспавнился и готов к работе\\.`, 'MarkdownV2'); }); } // --- API ЭНДПОИНТЫ --- app.get('/api/status/:chatId', (req, res) => { // ❗❗❗ ЭТОТ МАРШРУТ БЫЛ ДОБАВЛЕН ❗❗❗ const chatId = req.params.chatId; // Проверяем, существует ли бот, не является ли он 'null' и не находится ли в процессе остановки const isRunning = !!activeBots[chatId] && !!activeBots[chatId].bot && !activeBots[chatId].isStopping; res.status(200).send({ isRunning: isRunning }); }); app.post('/api/start', async (req, res) => { const { chatId, host, port, username, version } = req.body; if (!chatId || !host || !port || !username || !version) { return res.status(400).send({ error: "Missing required parameters: chatId, host, port, username, or version." }); } try { if (activeBots[chatId]) { activeBots[chatId].reconnectAttempts = 0; activeBots[chatId].currentProxyIndex = 0; activeBots[chatId].isStopping = false; } await setupMineflayerBot(chatId, host, port, username, version); res.status(200).send({ message: "Bot start command received." }); } catch (e) { res.status(500).send({ error: e.message }); } }); app.post('/api/stop', (req, res) => { const { chatId } = req.body; if (!chatId) { return res.status(400).send({ error: "Missing required parameter: chatId." }); } if (activeBots[chatId] && activeBots[chatId].bot) { activeBots[chatId].isStopping = true; activeBots[chatId].bot.quit('disconnect.quitting'); res.status(200).send({ message: "Bot stop command received. Disconnecting." }); } else { res.status(404).send({ message: "Bot not found or not running for this chat." }); cleanupBot(chatId); } }); app.post('/api/command', (req, res) => { const { chatId, command } = req.body; if (!chatId || !command) { return res.status(400).send({ error: "Missing required parameters: chatId or command." }); } if (activeBots[chatId] && activeBots[chatId].bot) { try { activeBots[chatId].bot.chat(command); res.status(200).send({ message: `Command '${command}' sent to bot.` }); } catch (e) { console.error(`[Chat ${chatId}] Failed to send command: ${e.message}`); res.status(500).send({ error: `Failed to send command: ${e.message}` }); } } else { res.status(404).send({ message: "Bot not found or not running." }); } }); app.listen(PORT, () => { console.log(`Worker service running on port ${PORT}`); });