const mineflayer = require("mineflayer"); const mineflayerViewer = require('prismarine-viewer').mineflayer; const Movements = require("mineflayer-pathfinder").Movements; const pathfinder = require("mineflayer-pathfinder").pathfinder; const { GoalBlock, GoalXZ } = require("mineflayer-pathfinder").goals; const util = require("util"); const { SocksClient } = require('socks'); const { Client } = require('ssh2'); const dns = require('dns').promises; const config = require("./settings.json"); const loggers = require("./logging.js"); const logger = loggers.logger; const https = require('https'); // --- AYARLAR --- const HOME_ARRIVAL_DELAY_MS = 2000; const POST_ACTION_DELAY_MS = 4000; const WINDOW_OPEN_TIMEOUT_MS = 6000; const DEFAULT_CHECK_INTERVAL_MS = 10 * 60 * 1000; // Varsayılan 10 dk const FULL_FEED_COOLDOWN_MS = 50 * 60 * 1000; // Besleme sonrası sabit 50 dk const GO_HOME_COMMAND = "/is go"; const RETURN_ISLAND_COMMAND = "/is go"; const BESLEYICI_NAMES = (config.utils && config.utils["besleyici-names"]) || ["Besleyici", "ʙᴇsʟᴇʏiᴄi"]; let viewerInstance = null; let viewerTimeout = null; let bot = null; let botIsReady = false; let baglantiTuru = "Yok"; let activeMessages = []; let messagesTimeout = null; let isListeningChat = false; const ansiToHtmlConvert = new (require('ansi-to-html'))({ newline: true }); // --- GÜVENLİ BAĞLANTI DEĞİŞKENLERİ --- let isConnecting = false; let isDestroying = false; let reconnectTimeout = null; let currentSshClient = null; let currentStream = null; let currentSocket = null; let windowOpenResolvers = []; // --- PANEL METRİKLERİ --- let botUptimeStart = null; let lastFullFeedTimeStr = "Henüz besleme yapılmadı"; let lastCheckTimeStr = "Henüz kontrol yapılmadı"; let nextScheduledCheckTimeStr = "Botun spawn olması bekleniyor..."; // --- DİNAMİK IP VE KONUM BİLGİLERİ --- let activeConnectionIp = "Bilinmiyor"; let activeConnectionLocation = "Bilinmiyor"; let localExternalIp = "Bilinmiyor"; // --- DİNAMİK ZAMANLAYICI KONTROLÜ --- let periodicTimeoutHandle = null; async function getIpLocation(ip) { if (!ip || ip === "Bilinmiyor" || ip.startsWith("127.")) return "Bilinmiyor"; // Denenecek servisler listesi const services = [ `https://ip-api.com/json/${ip}?fields=status,country,city`, `https://ipapi.co/${ip}/json/` ]; for (const url of services) { try { const location = await new Promise((resolve) => { https.get(url, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const geo = JSON.parse(data); // Servise göre cevap yapısı değişebilir, burada basit bir kontrol yapıyoruz if (geo.country && geo.status !== "fail") { resolve(`${geo.country} / ${geo.city || geo.region || ""}`); } else { resolve(null); // Bir sonraki servise geç } } catch (e) { resolve(null); } }); }).on('error', () => resolve(null)); }); if (location) return location; } catch (e) { continue; } } return "Konum Alınamadı"; } https.get('https://api.ipify.org?format=json', (res) => { let data = ''; res.on('data', chunk => { data += chunk; }); res.on('end', () => { try { const ipInfo = JSON.parse(data); localExternalIp = ipInfo.ip; console.log(`Dış IP adresin: ${localExternalIp}`); } catch (err) {} }); }).on('error', () => {}); async function createBot() { if (isConnecting) { logger.warn("[Sistem] Zaten aktif bir bağlantı süreci yürütülüyor. Mükerrer istek engellendi."); return; } isConnecting = true; isDestroying = false; const net = require('net'); const sshClient = new Client(); currentSshClient = sshClient; const targetServerIp = config.server.ip; const targetServerPort = config.server.port; const currentBotUsername = config["bot-account"]["username"]; bot = mineflayer.createBot({ username: currentBotUsername, auth: config["bot-account"]["type"], host: targetServerIp, port: targetServerPort, version: config.server.version, connect: async (client) => { let fallbackTriggered = false; let isFinalFailureHandled = false; const handleFinalFailure = (reason) => { if (isFinalFailureHandled) return; isFinalFailureHandled = true; logger.error(`[Kritik Hata] ${reason}. Bağlantı sağlanamadı! Sistem en başa döndürülüyor...`); baglantiTuru = "Yok"; botIsReady = false; botUptimeStart = null; activeConnectionIp = "Bilinmiyor"; activeConnectionLocation = "Bilinmiyor"; stopPeriodicHomeChecks(); safeDestroyAllConnections(); if (config.utils && config.utils["auto-reconnect"]) { triggerSafeReconnect(); } }; const connectDirectly = async (reason) => { if (fallbackTriggered) return; fallbackTriggered = true; logger.warn(`[Bağlantı Yedekleme] Doğrudan IP moduna geçiliyor (Sebep: ${reason}). Kendi (Yurt dışı) IP'miz üzerinden GERÇEK TCP bağlantısı kuruluyor...`); if (sshClient) { try { sshClient.removeAllListeners('ready'); sshClient.removeAllListeners('error'); sshClient.destroy(); } catch (e) {} } activeConnectionIp = localExternalIp !== "Bilinmiyor" ? localExternalIp : "Kendi Dış IP'niz"; activeConnectionLocation = await getIpLocation(localExternalIp); const socket = net.connect({ host: targetServerIp, port: parseInt(targetServerPort) || 25565 }); currentSocket = socket; let isDirectHandled = false; const directTimeout = setTimeout(() => { if (isDirectHandled) return; isDirectHandled = true; socket.destroy(); handleFinalFailure("Kendi IP bağlantısı 1 dakikalık süre içinde zaman aşımına uğradı (Sunucu yanıt vermedi)"); }, 60000); socket.on('connect', () => { if (isDirectHandled) return; isDirectHandled = true; clearTimeout(directTimeout); logger.info("[Bağlantı Yedekleme] Kendi IP'miz üzerinden GERÇEK TCP bağlantısı başarıyla kuruldu!"); baglantiTuru = "Normal"; if (client && typeof client.setSocket === 'function') { client.setSocket(socket); client.emit('connect'); } }); socket.on('error', (err) => { if (isDirectHandled) return; isDirectHandled = true; clearTimeout(directTimeout); logger.error(`[Bağlantı Yedekleme Hatası] Sunucu bağlantıyı reddetti veya olumsuz yanıt verdi: ${err.message}`); handleFinalFailure(`Kendi IP hatası: ${err.message}`); }); socket.on('close', () => { if (isDirectHandled) return; isDirectHandled = true; clearTimeout(directTimeout); handleFinalFailure("Normal TCP soketi kapandı"); }); }; const githubRawUrl = 'https://raw.githubusercontent.com/suvarileader/che/refs/heads/main/doc/ower/doc.json'; logger.info("[GitHub Config] Güncel SSH tünel bilgileri GitHub'dan çekiliyor..."); let remoteData = null; try { remoteData = await new Promise((resolve, reject) => { const reqTimeout = setTimeout(() => { reject(new Error("GitHub bağlantısı 6 saniye içinde yanıt vermedi (Timeout)")); }, 6000); https.get(githubRawUrl, (res) => { clearTimeout(reqTimeout); let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { resolve(JSON.parse(body)); } catch (e) { reject(new Error("JSON Ayrıştırma Hatası")); } }); }).on('error', (err) => { clearTimeout(reqTimeout); reject(err); }); }); } catch (err) { connectDirectly(`GitHub verisi alınamadı veya sayfa çöktü (${err.message})`); return; } let sshConfig = null; if (remoteData && Array.isArray(remoteData)) { const myConfig = remoteData.find(item => item && item.account === currentBotUsername); if (myConfig) { sshConfig = { host: (myConfig.serverName && myConfig.serverName !== "empty") ? myConfig.serverName : null, port: 22, username: (myConfig.username && myConfig.username !== "empty") ? myConfig.username : null, password: (myConfig.pass && myConfig.pass !== "empty") ? myConfig.pass : config.utils["auto-auth"].password }; if (!sshConfig.host || !sshConfig.username) { sshConfig = null; } } } if (!sshConfig) { connectDirectly(`GitHub listesinde '${currentBotUsername}' botu ile uyumlu geçerli SSH verisi bulunamadı`); return; } logger.info(`[SSH Tünel] GitHub'dan alınan ${sshConfig.host} sunucusuna (${sshConfig.username}) bağlanılıyor...`); const connectionTimeout = setTimeout(() => { connectDirectly("SSH Bağlantı Zaman Aşımı (10 saniye yanıt alınamadı)"); }, 10000); sshClient.on('ready', async () => { clearTimeout(connectionTimeout); logger.info('[SSH Tünel] SSH bağlantısı başarılı. Minecraft sunucusuna tünel açılıyor...'); try { const lookup = await dns.lookup(sshConfig.host); activeConnectionIp = lookup.address; } catch (e) { activeConnectionIp = sshConfig.host; } activeConnectionLocation = await getIpLocation(activeConnectionIp); sshClient.forwardOut( '127.0.0.1', 12345, targetServerIp, parseInt(targetServerPort) || 25565, (err, stream) => { if (err) { connectDirectly(`Tünel yönlendirme hatası: ${err.message}`); return; } logger.info("[SSH Tünel] Tünel başarıyla kuruldu! Mineflayer sokete bağlanıyor."); baglantiTuru = "SSH"; currentStream = stream; stream.once('close', () => { if (!fallbackTriggered && !isDestroying) { connectDirectly("Tünel stream kapandı"); } }); stream.once('error', (e) => { if (!fallbackTriggered && !isDestroying) { connectDirectly(`Stream hatası: ${e.message}`); } }); if (client && typeof client.setSocket === 'function') { client.setSocket(stream); client.emit('connect'); } } ); }); sshClient.on('error', (err) => { clearTimeout(connectionTimeout); if (!fallbackTriggered && !isDestroying) { connectDirectly(`SSH Sunucu Hatası: ${err.message}`); } }); try { sshClient.connect(sshConfig); } catch (err) { clearTimeout(connectionTimeout); connectDirectly(`Sistem Hatası: ${err.message}`); } } }); bot.loadPlugin(pathfinder); let lastFullFeedAt = 0; let activeTimeouts = []; function setSafeTimeout(fn, delay) { const t = setTimeout(fn, delay); activeTimeouts.push(t); return t; } function stopPeriodicHomeChecks() { if (periodicTimeoutHandle) { clearTimeout(periodicTimeoutHandle); periodicTimeoutHandle = null; } activeTimeouts.forEach(t => clearTimeout(t)); activeTimeouts = []; } function getExtendedDateTimeString() { const now = new Date(); const dateStr = now.toLocaleDateString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric' }); const timeStr = now.toLocaleTimeString('tr-TR'); return `${dateStr} - ${timeStr}`; } function getFutureDateTimeString(delayMs) { const future = new Date(Date.now() + delayMs); return future.toLocaleTimeString('tr-TR'); } function stripColorCodes(s) { if (!s || typeof s !== "string") return s; return s.replace(/§[0-9a-fk-or]/gi, ""); } function safeDisplayNameToString(e) { try { if (e && e.displayName && typeof e.displayName.toString === "function") { return e.displayName.toString(); } } catch (err) {} return null; } function getEntityReadableName(e) { if (!e) return ""; const disp = safeDisplayNameToString(e); if (disp) return stripColorCodes(disp); if (e.name && typeof e.name === "string") return stripColorCodes(e.name); if (e.username && typeof e.username === "string") return stripColorCodes(e.username); return ""; } function normalizeForMatch(s) { if (!s || typeof s !== "string") return ""; let t = stripColorCodes(s); if (t.normalize) t = t.normalize("NFKD").replace(/[\u0300-\u036f]/g, ""); return t.toLowerCase(); } const TARGET_NAME_NORMS = BESLEYICI_NAMES.map(n => normalizeForMatch(n)); function isBesleyiciName(name) { if (!name) return false; const norm = normalizeForMatch(name); return TARGET_NAME_NORMS.some(t => norm.includes(t)); } function safeGet(obj, pathArray) { try { let cur = obj; for (const p of pathArray) { if (cur == null) return undefined; cur = cur[p]; } return cur; } catch (e) { return undefined; } } function deepSearchForString(root, targetLower) { const results = []; const visited = new WeakSet(); function recurse(obj, path) { if (obj == null || typeof obj === "function") return; if (typeof obj === "string") { const norm = normalizeForMatch(obj); if (norm.includes(targetLower)) results.push({ path, value: obj }); return; } if (typeof obj !== "object") return; if (visited.has(obj)) return; visited.add(obj); for (const key of Object.keys(obj)) { recurse(obj[key], `${path}.${key}`); } } try { recurse(root, "root"); } catch (e) {} return results; } function parseWindowTitleToString(titleObj) { try { if (!titleObj) return ""; if (typeof titleObj === "string") return titleObj; const s = titleObj.toString(); if (s && s !== "[object Object]") return s; return JSON.stringify(titleObj); } catch (err) { return ""; } } function extractMinyonCanFromSlot(slot) { try { if (!slot) return null; function tryParseLoreText(text) { try { if (!text || typeof text !== "string") return null; let plain = text; try { const maybe = JSON.parse(text); if (maybe && typeof maybe === "object") { if (maybe.extra && Array.isArray(maybe.extra) && maybe.extra.length > 0) { if (typeof maybe.extra[0].text === "string") plain = maybe.extra[0].text; } else if (typeof maybe.text === "string") { plain = maybe.text; } } } catch (e) {} const xyMatch = plain.match(/(\d{1,4})\s*\/\s*(\d{1,4})/); if (xyMatch) return Number(xyMatch[1]); const numMatch = plain.match(/(\d{1,4})/); if (numMatch) return Number(numMatch[1]); return null; } catch (e) { return null; } } if (slot.nbt) { const lore = safeGet(slot.nbt, ["value", "display", "value", "Lore", "value"]); if (Array.isArray(lore)) { for (const line of lore) { const res = tryParseLoreText(line.value || line); if (res != null) return res; } } } const full = util.inspect(slot, { depth: null }); return tryParseLoreText(full); } catch (err) { return null; } } async function goHome() { if (!bot || !botIsReady) return; logger.info(`[${currentBotUsername}] ${GO_HOME_COMMAND} komutu gönderiliyor.`); bot.chat(GO_HOME_COMMAND); } async function returnIsland() { if (!bot || !botIsReady) return; logger.info(`[${currentBotUsername}] ${RETURN_ISLAND_COMMAND} komutu gönderiliyor.`); bot.chat(RETURN_ISLAND_COMMAND); } // --- DİNAMİK PLANLAMA MOTORU --- function scheduleNextCheck(delayMs) { if (periodicTimeoutHandle) { clearTimeout(periodicTimeoutHandle); } nextScheduledCheckTimeStr = `${getFutureDateTimeString(delayMs)} (${Math.round(delayMs / 60 / 1000)} dk sonra)`; logger.info(`[Planlayıcı] Bir sonraki döngü planlandı. Zaman: ${nextScheduledCheckTimeStr}`); periodicTimeoutHandle = setTimeout(async () => { if (!bot || !botIsReady) return; await goHome(); await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS)); await scanAndFeed(); await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS)); await returnIsland(); }, delayMs); } function activateEntityAtNoLook(entity, position) { bot._client.write('use_entity', { target: entity.id, mouse: 2, // interact at sneaking: false, hand: 0, x: position.x - entity.position.x, y: position.y - entity.position.y, z: position.z - entity.position.z }); } async function scanAndFeed() { if (!bot || !botIsReady) return; // Sunucu lagı yüzünden döngü üst üste tetiklenirse koruma if (Date.now() < (lastFullFeedAt + 30000)) { logger.warn("[Planlayıcı] Çok kısa süre önce işlem yapıldı. Güvenlik kilidi devrede."); return; } const arr = Object.values(bot.entities); let best = null; for (const e of arr) { const name = getEntityReadableName(e); if (isBesleyiciName(name)) { best = { entity: e, path: "displayName/name" }; break; } for (const target of TARGET_NAME_NORMS) { const deep = deepSearchForString(e, target); if (deep.length > 0) { best = { entity: e, path: deep[0].path }; break; } } if (best) break; } if (!best) { logger.info(`[${currentBotUsername}] /home sonrası tarama: besleyici minyon bulunamadı.`); bot.chat("/skyblock"); // Okuyamadığımız için 10 dakika sonra tekrar deneyecek şekilde planla scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); return; } const dist = bot.entity.position.distanceTo(best.entity.position); logger.info(`[${currentBotUsername}] '${best.entity.name || 'Armor Stand'}' bulundu id:${best.entity.id} path:${best.path} dist:${dist.toFixed(2)} — aktive ediliyor.`); if (dist > 7) { logger.warn(`safeActivateEntityPreferAt: entity id:${best.entity.id} uzak (${dist.toFixed(2)}). Max 7 blok.`); logger.warn(`[${currentBotUsername}] 'Besleyici' aktive edilemedi (id:${best.entity.id}).`); scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); return; } try { await new Promise(r => setTimeout(r, 400)); const startTime = Date.now(); const endTime = startTime + WINDOW_OPEN_TIMEOUT_MS; let clickCount = 0; let isLoopActive = true; await new Promise((resolve) => { const cleanFinish = () => { isLoopActive = false; clearInterval(clickInterval); windowOpenResolvers = []; resolve(); }; windowOpenResolvers.push(() => { cleanFinish(); }); const sendClickPacket = () => { if (!isLoopActive) return; if (bot.currentWindow != null) { cleanFinish(); return; } if (Date.now() > endTime) { logger.warn(`[Tıklama Sistemi] ${WINDOW_OPEN_TIMEOUT_MS} ms doldu ancak menü sunucudan gelmedi. Pes ediliyor.`); cleanFinish(); scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); return; } clickCount++; logger.info(`[Tıklama Sistemi] Menü tespiti başarısız. Deneme #${clickCount} gönderiliyor... (id:${best.entity.id})`); try { activateEntityAtNoLook(best.entity, best.entity.position); } catch (err) {} }; sendClickPacket(); const clickInterval = setInterval(sendClickPacket, 600); }); } catch (err) {} } bot.once("spawn", async () => { isConnecting = false; if (!bot) return; bot.pathfinder.setMovements(new Movements(bot)); logger.info("Bot sunucuya giriş yaptı."); botIsReady = true; botUptimeStart = Date.now(); if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) { const password = config.utils["auto-auth"].password; setSafeTimeout(() => { if (bot && botIsReady) { bot.chat(`/login ${password}`); logger.info(`[${currentBotUsername}] Login komutu kullanıldı.`); } }, 2500); setSafeTimeout(() => { if (bot && botIsReady) { bot.chat(`/skyblock`); logger.info(`[${currentBotUsername}] Skyblock'a ışınlanma komutu kullanıldı.`); } }, 7500); // İlk çalıştırma: Girişten 12.5 saniye sonra ilk kontrolü yap setSafeTimeout(async () => { if (!bot || !botIsReady) return; await goHome(); await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS)); await scanAndFeed(); await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS)); await returnIsland(); }, 12500); } }); bot.on("windowOpen", async (window) => { if (!bot) return; const title = parseWindowTitleToString(window.title).toLowerCase(); if (!title.includes("besleyici") && !parseWindowTitleToString(window.title).includes("ʙᴇsʟᴇʏiᴄi")) { logger.info(`[${currentBotUsername}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${parseWindowTitleToString(window.title)}`); try { bot.closeWindow(window); } catch(e){} return; } const beefSlot = window.slots.find(s => s?.name === "cooked_beef"); const can = extractMinyonCanFromSlot(beefSlot); if (can != null) { const currentStamp = getExtendedDateTimeString(); lastCheckTimeStr = `${currentStamp} (Okunan Can: ${can})`; if (can <= 2) { // CAN 2 VEYA ALTINDA: BESLE VE SABİT 50 DK PLANLA try { logger.info(`[${currentBotUsername}] Minyon canı ${can} olduğu için besleniyor...`); await bot.clickWindow(36, 0, 0); lastFullFeedAt = Date.now(); lastFullFeedTimeStr = `${currentStamp} (Minyon canı ${can} iken başarıyla beslendi)`; logger.info(`[${currentBotUsername}] Tıklama onaylandı. Fixed 50dk bekleme aktif.`); } catch(e) { logger.error(`[Arayüz Hatası] Tıklama işlemi sunucu tarafından onaylanmadı: ${e.message}`); } scheduleNextCheck(FULL_FEED_COOLDOWN_MS); } else { // CAN 3 VEYA DAHA YÜKSEK: DİNAMİK PLANLAMA YAP // Formül: (Can - 2) * 15 dakika. Maksimum 65 dakika. let calculatedMinutes = (can - 2) * 15; if (calculatedMinutes > 50) calculatedMinutes = 65; if (calculatedMinutes < 15) calculatedMinutes = 15; const dynamicDelayMs = calculatedMinutes * 60 * 1000; logger.info(`[${currentBotUsername}] Minyon canı ${can} olarak okundu. Canın 2'ye düşmesi için ${calculatedMinutes} dakika beklenecek.`); if (can >= 10) { lastFullFeedTimeStr = `${currentStamp} (Canı ${can} olduğu için besleme gerekmedi)`; } scheduleNextCheck(dynamicDelayMs); } } else { // Can okunamadıysa güvenlik amacıyla 10 dk sonra tekrar kontrol et logger.warn(`[${currentBotUsername}] Menü açıldı ama can okunamadı. 10 dk sonra tekrar denenecek.`); scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } try { bot.closeWindow(window); } catch(e){} while (windowOpenResolvers.length > 0) { const fn = windowOpenResolvers.shift(); if (typeof fn === 'function') fn(); } }); bot.on("end", () => { logger.warn("Bağlantı koptu. Tüm zamanlayıcılar sıfırlanıyor ve YENİDEN BAĞLANMA SÜRECİ BAŞLIYOR..."); stopPeriodicHomeChecks(); botIsReady = false; baglantiTuru = "Yok"; botUptimeStart = null; isListeningChat = false; activeConnectionIp = "Bilinmiyor"; activeConnectionLocation = "Bilinmiyor"; nextScheduledCheckTimeStr = "Bağlantı koptuğu için planlama iptal edildi."; safeDestroyAllConnections(); if (config.utils && config.utils["auto-reconnect"]) { triggerSafeReconnect(); } }); bot.on("kicked", (reason) => logger.warn(`Bot sunucudan atıldı: ${util.inspect(reason)}`)); bot.on("error", (err) => logger.error(`Mineflayer Hatası: ${err}`)); } function safeDestroyAllConnections() { if (isDestroying) return; isDestroying = true; isConnecting = false; windowOpenResolvers = []; if (viewerInstance && typeof viewerInstance.close === 'function') { try { if (viewerInstance.ws && typeof viewerInstance.ws.close === 'function') { viewerInstance.ws.close(); } viewerInstance.close(); } catch(e){} } viewerInstance = null; if (bot) { bot.viewer = null; } if (currentStream && typeof currentStream.destroy === 'function') { try { currentStream.destroy(); } catch(e){} } if (currentSshClient && typeof currentSshClient.destroy === 'function') { try { currentSshClient.destroy(); } catch(e){} } else { if (currentSocket && typeof currentSocket.destroy === 'function') { try { currentSocket.destroy(); } catch(e){} } } currentStream = null; currentSocket = null; currentSshClient = null; if (bot) { try { bot.removeAllListeners('message'); bot.removeAllListeners('spawn'); bot.removeAllListeners('windowOpen'); bot.removeAllListeners('end'); bot.quit(); } catch(e){} bot = null; } } function triggerSafeReconnect() { if (reconnectTimeout) { clearTimeout(reconnectTimeout); } const delay = (config.utils && config.utils["auto-reconnect-delay"]) || 5000; logger.info(`[Sistem] ${delay} ms sonra tekil bir hat üzerinden sistem sıfırdan başlatılacak...`); reconnectTimeout = setTimeout(() => { createBot(); }, delay); } function globalMinecraftMessageListener(jsonMsg) { try { if (!bot) return; const chatMessage = bot.chat.ChatMessage ? new bot.chat.ChatMessage(jsonMsg) : jsonMsg; const coloredAnsi = typeof chatMessage.toAnsi === 'function' ? chatMessage.toAnsi() : chatMessage.toString(); const htmlMessage = ansiToHtmlConvert.toHtml(coloredAnsi); const timeStr = new Date().toLocaleTimeString('tr-TR'); activeMessages.push({ time: timeStr, text: htmlMessage }); if (activeMessages.length > 50) { activeMessages.shift(); } } catch (err) { logger.error("Mesaj işlenirken hata oluştu: " + err.message); } } createBot(); // --- EXPRESS SUNUCUSU VE PANEL ALTYAPISI --- const express = require("express"); const app = express(); const http = require('http'); app.get("/status", (req, res) => { let uptimeString = "Bağlı Değil"; if (botIsReady && botUptimeStart) { const diffMs = Date.now() - botUptimeStart; const diffSecs = Math.floor(diffMs / 1000); const days = Math.floor(diffSecs / 86400); const hours = Math.floor((diffSecs % 86400) / 3600); const mins = Math.floor((diffSecs % 3600) / 60); const secs = diffSecs % 60; uptimeString = `${days} Gün, ${hours} Saat, ${mins} Dakika, ${secs} Saniye`; } res.send({ ready: botIsReady, username: bot ? bot.username : null, connection_type: baglantiTuru, connection_ip: activeConnectionIp, connection_geo: activeConnectionLocation, bot_uptime: uptimeString, last_minyon_check: lastCheckTimeStr, last_minyon_feed: lastFullFeedTimeStr, next_scheduled_check: nextScheduledCheckTimeStr // Panel için sonraki kontrol saati }); }); app.get("/", (req, res) => res.send("Bot Aktif")); app.get("/messages", (req, res) => { if (messagesTimeout) { clearTimeout(messagesTimeout); messagesTimeout = null; } if (!bot || !botIsReady) { return res.status(503).send("Bot hazır olmadığı için mesajlar dinlenemiyor."); } if (!isListeningChat) { isListeningChat = true; bot.on('message', globalMinecraftMessageListener); logger.info("[Sohbet Tasarrufu] Panel aktif edildi. Minecraft mesaj dinleyicisi BAŞLATILDI."); } res.send({ server_time: new Date().toLocaleTimeString('tr-TR'), logs: activeMessages }); messagesTimeout = setTimeout(() => { activeMessages = []; messagesTimeout = null; if (bot && isListeningChat) { bot.removeListener('message', globalMinecraftMessageListener); isListeningChat = false; logger.info("[Sohbet Tasarrufu] 1 dakikadır izlenmiyor. Minecraft mesaj dinleyicisi DURDURULDU ve havuz temizlendi."); } }, 60000); }); app.get("/chat", (req, res) => { const html = `