| const mineflayer = require("mineflayer"); |
| const mineflayerViewer = require('prismarine-viewer').mineflayer; |
| 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 getRecentLogs = loggers.getRecentLogs; |
|
|
| const https = require('https'); |
|
|
| |
| process.on('unhandledRejection', (reason) => { |
| logger.error(`[Küresel Güvenlik Ağı] Yakalanmamış Promise hatası (unhandledRejection): ${util.inspect(reason)}`); |
| }); |
|
|
| process.on('uncaughtException', (err) => { |
| logger.error(`[Küresel Güvenlik Ağı] Yakalanmamış istisna (uncaughtException): ${err && err.stack ? err.stack : err}`); |
| }); |
|
|
| |
| const HOME_ARRIVAL_DELAY_MS = 4000; |
| const WINDOW_OPEN_TIMEOUT_MS = 6000; |
| const DEFAULT_CHECK_INTERVAL_MS = 5 * 60 * 1000; |
| const FULL_FEED_COOLDOWN_MS = 50 * 60 * 1000; |
| const GO_ISLAND_COMMAND = "/is go"; |
| const BESLEYICI_NAMES_RAW = ["Besleyici", "ʙᴇsʟᴇʏiᴄi"]; |
| const BESLEYICI_NAMES = Array.isArray(BESLEYICI_NAMES_RAW) ? BESLEYICI_NAMES_RAW : [BESLEYICI_NAMES_RAW]; |
|
|
| const KAZICI_NAMES_RAW = (config.utils && (config.utils["kazici-names"] || config.utils["kazici-name"])) || ["Kazıcı", "ᴋᴀᴢıᴄı", "ᴋᴀᴢɪᴄɪ"]; |
| const KAZICI_NAMES = Array.isArray(KAZICI_NAMES_RAW) ? KAZICI_NAMES_RAW : [KAZICI_NAMES_RAW]; |
|
|
| const KAZICI_STORAGE_TRANSFER_SLOT = 17; |
| const KAZICI_BETWEEN_DELAY_MS = 5 * 1000; |
| const KAZICI_ACTION_SETTLE_DELAY_MS = 1200; |
|
|
| |
| const CIFTCI_NAMES_RAW = (config.utils && (config.utils["ciftci-names"] || config.utils["ciftci-name"])) || ["Çiftçi", "çiꜰᴛçi"]; |
| const CIFTCI_NAMES = Array.isArray(CIFTCI_NAMES_RAW) ? CIFTCI_NAMES_RAW : [CIFTCI_NAMES_RAW]; |
| const CIFTCI_SELL_ALL_SLOT = 45; |
| const CIFTCI_COMMAND = "/çiftçi menü"; |
| const CIFTCI_WINDOW_TIMEOUT_MS = 6000; |
| const CIFTCI_LORE_FALLBACK_KEYWORDS = ["hepsini sat"]; |
| const CIFTCI_CLICK_DELAY_AFTER_OPEN_MS = 1000; |
| const CIFTCI_CLICK_REPEAT_COUNT = 3; |
| const CIFTCI_CLICK_REPEAT_GAP_MS = 350; |
|
|
| const KAZICI_CYCLE_INTERVAL_MS = 5 * 60 * 1000; |
| const CIFTCI_CYCLE_INTERVAL_MS = 60 * 1000; |
|
|
| const CIFTCI_FILTER_MODES = [ |
| "Standart", |
| "Favoriler", |
| "Depo dolumuna göre", |
| "Jeneratör materyalleri", |
| "Satışı açık olanlar", |
| "Toplaması açık olanlar" |
| ]; |
| const CIFTCI_TARGET_FILTER_INDEX = 3; |
| const CIFTCI_ACTIVE_FILTER_COLOR = "#FCF988"; |
| const CIFTCI_FILTER_ITEM_NAME_KEYWORD = "Filtre"; |
| const CIFTCI_FILTER_STEP_WAIT_MS = 1000; |
| const CIFTCI_FILTER_RETRY_WAIT_MS = 1000; |
| const CIFTCI_FILTER_MAX_RETRIES = 10; |
| const STORAGE_SCAN_INTERVAL_MS = 15 * 60 * 1000; |
| const STORAGE_TRACKED_ITEMS = ["diamond", "emerald", "iron_block"]; |
|
|
| |
| const ORDER_COMMAND_PREFIX = "/order"; |
| const ORDER_WINDOW_TIMEOUT_MS = 8000; |
| const ORDER_WINDOW_SETTLE_DELAY_MS = 500; |
| const ORDER_AFTER_SELECT_DELAY_MS = 500; |
| const ORDER_IS_GO_SECOND_DELAY_MS = 5000; |
| const ORDER_ALLOWED_ITEMS = ["diamond", "emerald"]; |
| const ORDER_LIST_TITLE_KEYWORD = "ѕiᴘᴀʀişʟᴇʀ"; |
| const ORDER_DELIVERY_TITLE_KEYWORD = "ᴛᴇѕʟiᴍ ᴇᴛᴍᴇ"; |
| |
| |
| const ORDER_DELIVERY_WINDOW_WAIT_MS = 8000; |
|
|
| let storageInfoCache = { |
| diamond: null, |
| emerald: null, |
| iron_block: null, |
| lastUpdated: null |
| }; |
| let storageScanIntervalHandle = null; |
|
|
| let ciftciWindowPurpose = "sell"; |
|
|
| const STALL_CHECK_INTERVAL_MS = 15 * 1000; |
| const STALL_TIMEOUT_MS = 60 * 1000; |
| const FAILOVER_HOME_ARRIVAL_DELAY_MS = 4000; |
| const FAILOVER_LOGIN_DELAY_MS = 2500; |
| const FAILOVER_SKYBLOCK_DELAY_MS = 7500; |
| const FAILOVER_ISGO_DELAY_MS = 12500; |
| const FAILOVER_RECHECK_INTERVAL_MS = 20 * 1000; |
| const FAILOVER_MAX_WAIT_MS = 60 * 60 * 1000; |
| const FAILOVER_SPAWN_TIMEOUT_MS = 30 * 1000; |
|
|
| const WATCHED_KAZICI_SKIN_HASHES = new Set([ |
| "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDQ4ODcwY2E3N2FhNjk5ZDAxNzA5M2I4Mjc3OWQwYTU5OGRjNGYzZjQ5ZDM1MTUzMmFlNGQ5NjMzZGNjMmE1YSJ9fX0=", |
| ]); |
|
|
| 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 }); |
|
|
| let isConnecting = false; |
| let isDestroying = false; |
| let reconnectTimeout = null; |
| let currentSshClient = null; |
| let currentStream = null; |
| let currentSocket = null; |
| let windowOpenResolvers = []; |
|
|
| let botUptimeStart = null; |
| let lastFullFeedTimeStr = "Henüz besleme yapılmadı"; |
| let lastCheckTimeStr = "Henüz kontrol yapılmadı"; |
| let nextScheduledCheckTimeStr = "Botun spawn olması bekleniyor..."; |
|
|
| let lastKaziciCheckTimeStr = "Henüz kazıcı kontrolü yapılmadı"; |
| let nextKaziciTimeStr = "Botun spawn olması bekleniyor..."; |
| let nextKaziciAtMs = null; |
|
|
| let lastCiftciSellTimeStr = "Henüz çiftçi satışı yapılmadı"; |
| let nextCiftciTimeStr = "Botun spawn olması bekleniyor..."; |
| let nextCiftciAtMs = null; |
|
|
| let lastOrderTimeStr = "Henüz sipariş işlemi yapılmadı"; |
|
|
| let lastCheckAtMs = null; |
| let nextScheduledCheckAtMs = null; |
| let kaziciIntervalHandle = null; |
| let ciftciIntervalHandle = null; |
| let isMainCycleRunning = false; |
|
|
| let poseTracker = new Map(); |
| let stallCheckIntervalHandle = null; |
| let failoverModeActive = false; |
| let failoverBot = null; |
| let failoverInProgress = false; |
|
|
| let activeConnectionIp = "Bilinmiyor"; |
| let activeConnectionLocation = "Bilinmiyor"; |
| let localExternalIp = "Bilinmiyor"; |
|
|
| let periodicTimeoutHandle = null; |
| let activeTimeouts = []; |
|
|
| function setSafeTimeout(fn, delay) { |
| const t = setTimeout(fn, delay); |
| activeTimeouts.push(t); |
| return t; |
| } |
|
|
| class PriorityGuiMutex { |
| constructor() { |
| this._locked = false; |
| this._waiters = []; |
| } |
| isLocked() { return this._locked; } |
| tryAcquire() { |
| if (this._locked) return false; |
| this._locked = true; |
| return true; |
| } |
| acquire() { |
| if (!this._locked) { |
| this._locked = true; |
| return Promise.resolve(); |
| } |
| return new Promise((resolve) => { |
| this._waiters.push(resolve); |
| }); |
| } |
| release() { |
| if (this._waiters.length > 0) { |
| const next = this._waiters.shift(); |
| next(); |
| } else { |
| this._locked = false; |
| } |
| } |
| } |
| const guiMutex = new PriorityGuiMutex(); |
|
|
| let botActions = { |
| scanAndFeedManual: null, |
| scanAndCollectMinersManual: null, |
| openCiftciAndSellAllManual: null, |
| goToIslandManual: null, |
| sendSkyblockManual: null, |
| antiBotMove: null, |
| sendMoneyToNick: null, |
| scanStorageInfoManual: null, |
| processOrderManual: null |
| }; |
|
|
| function formatDurationMs(ms) { |
| if (ms == null || isNaN(ms) || ms < 0) return "0 saniye"; |
| let totalSeconds = Math.round(ms / 1000); |
|
|
| const days = Math.floor(totalSeconds / 86400); |
| totalSeconds -= days * 86400; |
| const hours = Math.floor(totalSeconds / 3600); |
| totalSeconds -= hours * 3600; |
| const minutes = Math.floor(totalSeconds / 60); |
| const seconds = totalSeconds - minutes * 60; |
|
|
| const parts = []; |
| if (days > 0) parts.push(`${days} gün`); |
| if (hours > 0) parts.push(`${hours} saat`); |
| if (minutes > 0) parts.push(`${minutes} dakika`); |
| if (seconds > 0 || parts.length === 0) parts.push(`${seconds} saniye`); |
|
|
| return parts.join(" "); |
| } |
|
|
| function stopPeriodicHomeChecks() { |
| if (periodicTimeoutHandle) { |
| clearTimeout(periodicTimeoutHandle); |
| periodicTimeoutHandle = null; |
| } |
| activeTimeouts.forEach(t => clearTimeout(t)); |
| activeTimeouts = []; |
|
|
| if (kaziciIntervalHandle) { |
| clearInterval(kaziciIntervalHandle); |
| kaziciIntervalHandle = null; |
| logger.info("[Kazıcı Periyodik] Döngü zamanlayıcısı durduruldu."); |
| } |
| if (ciftciIntervalHandle) { |
| clearInterval(ciftciIntervalHandle); |
| ciftciIntervalHandle = null; |
| logger.info("[Çiftçi Periyodik] Döngü zamanlayıcısı durduruldu."); |
| } |
| if (storageScanIntervalHandle) { |
| clearInterval(storageScanIntervalHandle); |
| storageScanIntervalHandle = null; |
| logger.info("[Storage Tarama] Döngü zamanlayıcısı durduruldu."); |
| } |
|
|
| nextScheduledCheckAtMs = null; |
| nextKaziciAtMs = null; |
| nextCiftciAtMs = null; |
| nextKaziciTimeStr = "Zamanlama durduruldu."; |
| nextCiftciTimeStr = "Zamanlama durduruldu."; |
| } |
|
|
| let manualStopRequested = false; |
|
|
| async function getIpLocation(ip) { |
| if (!ip || ip === "Bilinmiyor" || ip.startsWith("127.")) return "Bilinmiyor"; |
|
|
| 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); |
| if (geo.country && geo.status !== "fail") { |
| resolve(`${geo.country} / ${geo.city || geo.region || ""}`); |
| } else { |
| resolve(null); |
| } |
| } 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', () => {}); |
|
|
| function getEntitySkinHash(entity) { |
| try { |
| const equipment = entity.equipment || []; |
| for (const item of equipment) { |
| if (item && item.name === "player_head" && item.components) { |
| for (const comp of item.components) { |
| if (comp && comp.type === "profile" && comp.data && Array.isArray(comp.data.properties)) { |
| const texProp = comp.data.properties.find(p => p.name === "textures"); |
| if (texProp && texProp.value) { |
| return texProp.value; |
| } |
| } |
| } |
| } |
| } |
| } catch (e) {} |
| return null; |
| } |
|
|
| function hasIronPickaxeEquipped(entity) { |
| try { |
| const equipment = entity.equipment || []; |
| const mainHand = equipment[0]; |
| return !!(mainHand && mainHand.name === "iron_pickaxe"); |
| } catch (e) { |
| return false; |
| } |
| } |
|
|
| function isWatchedKaziciEntity(entity) { |
| if (!entity || entity.name !== "armor_stand") return false; |
| const skinHash = getEntitySkinHash(entity); |
| if (!skinHash || !WATCHED_KAZICI_SKIN_HASHES.has(skinHash)) return false; |
| return hasIronPickaxeEquipped(entity); |
| } |
|
|
| function getPoseKey(entity) { |
| try { |
| const md = entity.metadata || []; |
| const parts = []; |
| for (const m of md) { |
| if (m && typeof m === "object" && "pitch" in m && "yaw" in m && "roll" in m) { |
| parts.push(`${m.pitch.toFixed(2)},${m.yaw.toFixed(2)},${m.roll.toFixed(2)}`); |
| } |
| } |
| if (entity.position) { |
| parts.push(`${entity.position.x.toFixed(2)}_${entity.position.y.toFixed(2)}_${entity.position.z.toFixed(2)}`); |
| } |
| return parts.join("|"); |
| } catch (e) { |
| return null; |
| } |
| } |
|
|
| function trackKaziciPoseIfWatched(entity) { |
| if (!entity) return; |
| if (!isWatchedKaziciEntity(entity)) return; |
|
|
| const poseKey = getPoseKey(entity); |
| if (poseKey == null) return; |
|
|
| const prev = poseTracker.get(entity.id); |
| if (!prev) { |
| poseTracker.set(entity.id, { poseKey, lastChangeAt: Date.now(), uuid: entity.uuid }); |
| return; |
| } |
|
|
| if (prev.poseKey !== poseKey) { |
| prev.poseKey = poseKey; |
| prev.lastChangeAt = Date.now(); |
| } |
| } |
|
|
| function pruneStaleTrackedEntities() { |
| if (!bot || !bot.entities) return; |
| for (const id of poseTracker.keys()) { |
| if (!bot.entities[id]) poseTracker.delete(id); |
| } |
| } |
|
|
| function startStallWatcher() { |
| if (stallCheckIntervalHandle) return; |
| stallCheckIntervalHandle = setInterval(() => { |
| if (!bot || !botIsReady || failoverModeActive) return; |
| pruneStaleTrackedEntities(); |
|
|
| const now = Date.now(); |
| let anyStalled = false; |
| for (const [id, info] of poseTracker.entries()) { |
| if (now - info.lastChangeAt >= STALL_TIMEOUT_MS) { |
| anyStalled = true; |
| logger.warn(`[Failover] İzlenen kazıcı minyon (entityId:${id}) ${formatDurationMs(now - info.lastChangeAt)} boyunca hareketsiz.`); |
| } |
| } |
|
|
| if (anyStalled && !failoverInProgress) { |
| triggerFailoverSequence().catch(err => { |
| logger.error(`[Failover Hatası] ${err && err.stack ? err.stack : err}`); |
| failoverInProgress = false; |
| }); |
| } |
| }, STALL_CHECK_INTERVAL_MS); |
| logger.info("[Failover] Pose izleme döngüsü başlatıldı."); |
| } |
|
|
| function stopStallWatcher() { |
| if (stallCheckIntervalHandle) { |
| clearInterval(stallCheckIntervalHandle); |
| stallCheckIntervalHandle = null; |
| } |
| poseTracker.clear(); |
| } |
|
|
| async function triggerFailoverSequence() { |
| if (failoverInProgress) return; |
| failoverInProgress = true; |
| failoverModeActive = true; |
|
|
| const mainUsername = config["bot-account"]["username"]; |
| const backupUsername = `${mainUsername}1`; |
|
|
| logger.warn(`[Failover] İzlenen kazıcı(lar) durdu. Ana hesap (${mainUsername}) geçici olarak düşürülüp yedek hesap (${backupUsername}) devreye alınıyor.`); |
|
|
| manualStopRequested = true; |
| stopPeriodicHomeChecks(); |
| safeDestroyAllConnections(); |
|
|
| try { |
| await runBackupAccountAndWaitForRecovery(backupUsername); |
| } catch (err) { |
| logger.error(`[Failover] Yedek hesap akışında hata: ${err && err.stack ? err.stack : err}`); |
| } |
|
|
| logger.info(`[Failover] İzlenen kazıcı(lar) tekrar aktif (ya da azami bekleme süresi doldu). Ana hesap (${mainUsername}) yeniden bağlanıyor.`); |
| manualStopRequested = false; |
| failoverModeActive = false; |
| poseTracker.clear(); |
| createBot(); |
|
|
| failoverInProgress = false; |
| } |
|
|
| function runBackupAccountAndWaitForRecovery(backupUsername) { |
| return new Promise((resolve) => { |
| const backupPoseTracker = new Map(); |
| let recheckHandle = null; |
| let safetyTimeoutHandle = null; |
| let spawnTimeoutHandle = null; |
| let finished = false; |
|
|
| const finish = () => { |
| if (finished) return; |
| finished = true; |
| if (recheckHandle) clearInterval(recheckHandle); |
| if (safetyTimeoutHandle) clearTimeout(safetyTimeoutHandle); |
| if (spawnTimeoutHandle) clearTimeout(spawnTimeoutHandle); |
| try { |
| if (failoverBot) { |
| failoverBot.removeAllListeners(); |
| failoverBot.quit(); |
| } |
| } catch (e) {} |
| failoverBot = null; |
| resolve(); |
| }; |
|
|
| try { |
| failoverBot = mineflayer.createBot({ |
| username: backupUsername, |
| auth: config["bot-account"]["type"], |
| host: config.server.ip, |
| port: config.server.port, |
| version: config.server.version, |
| }); |
| } catch (err) { |
| logger.error(`[Failover - Yedek Hesap] Bot oluşturulamadı: ${err && err.message ? err.message : err}`); |
| finish(); |
| return; |
| } |
|
|
| spawnTimeoutHandle = setTimeout(() => { |
| logger.error(`[Failover - Yedek Hesap] ${backupUsername} ${formatDurationMs(FAILOVER_SPAWN_TIMEOUT_MS)} içinde spawn olamadı, vazgeçiliyor (bir sonraki kontrolde tekrar denenecek).`); |
| finish(); |
| }, FAILOVER_SPAWN_TIMEOUT_MS); |
|
|
| failoverBot.once("spawn", () => { |
| clearTimeout(spawnTimeoutHandle); |
| logger.info(`[Failover - Yedek Hesap] ${backupUsername} sunucuya girdi.`); |
|
|
| setTimeout(() => { |
| try { failoverBot.chat(`/login ${config.utils["auto-auth"].password}`); } catch (e) {} |
| logger.info(`[Failover - Yedek Hesap] ${backupUsername} Login komutu kullanıldı.`); |
| }, FAILOVER_LOGIN_DELAY_MS); |
|
|
| setTimeout(() => { |
| try { failoverBot.chat("/skyblock"); } catch (e) {} |
| logger.info(`[Failover - Yedek Hesap] ${backupUsername} Skyblock'a ışınlanma komutu kullanıldı.`); |
| }, FAILOVER_SKYBLOCK_DELAY_MS); |
|
|
| setTimeout(() => { |
| try { failoverBot.chat(GO_ISLAND_COMMAND); } catch (e) {} |
| logger.info(`[Failover - Yedek Hesap] ${GO_ISLAND_COMMAND} gönderildi, izlenen minyonların tekrar hareket etmesi bekleniyor...`); |
| }, FAILOVER_ISGO_DELAY_MS); |
|
|
| setTimeout(() => { |
| recheckHandle = setInterval(() => { |
| try { |
| const arr = Object.values(failoverBot.entities || {}); |
| let allRecovered = true; |
| let sawAnyWatched = false; |
|
|
| for (const e of arr) { |
| if (!isWatchedKaziciEntity(e)) continue; |
|
|
| sawAnyWatched = true; |
| const poseKey = getPoseKey(e); |
| const prev = backupPoseTracker.get(e.id); |
|
|
| if (!prev) { |
| backupPoseTracker.set(e.id, { poseKey, lastChangeAt: Date.now() }); |
| allRecovered = false; |
| continue; |
| } |
|
|
| if (prev.poseKey !== poseKey) { |
| prev.poseKey = poseKey; |
| prev.lastChangeAt = Date.now(); |
| } else if (Date.now() - prev.lastChangeAt < STALL_TIMEOUT_MS) { |
| |
| } else { |
| allRecovered = false; |
| } |
| } |
|
|
| if (sawAnyWatched && allRecovered) { |
| logger.info("[Failover - Yedek Hesap] İzlenen minyonlar tekrar hareket ediyor. Yedek hesaptan çıkılıyor."); |
| finish(); |
| } |
| } catch (e) {} |
| }, FAILOVER_RECHECK_INTERVAL_MS); |
| }, FAILOVER_HOME_ARRIVAL_DELAY_MS + FAILOVER_ISGO_DELAY_MS); |
| }); |
|
|
| failoverBot.on("error", (err) => { |
| logger.error(`[Failover - Yedek Hesap] Hata: ${err && err.message ? err.message : err}`); |
| }); |
|
|
| failoverBot.on("kicked", (reason) => { |
| logger.warn(`[Failover - Yedek Hesap] Sunucudan atıldı: ${util.inspect(reason)}`); |
| finish(); |
| }); |
|
|
| failoverBot.on("end", () => { |
| if (!finished) { |
| logger.warn("[Failover - Yedek Hesap] Bağlantı beklenmedik şekilde koptu."); |
| finish(); |
| } |
| }); |
|
|
| safetyTimeoutHandle = setTimeout(() => { |
| logger.warn(`[Failover - Yedek Hesap] Azami bekleme süresi (${formatDurationMs(FAILOVER_MAX_WAIT_MS)}) doldu, ana hesaba geri dönülüyor.`); |
| finish(); |
| }, FAILOVER_MAX_WAIT_MS); |
| }); |
| } |
|
|
| 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; |
|
|
| try { |
|
|
| 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 || isDestroying) 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(); |
| lastCheckAtMs = null; |
| safeDestroyAllConnections(); |
|
|
| if (!manualStopRequested && config.utils && config.utils["auto-reconnect"]) { |
| triggerSafeReconnect(); |
| } |
| }; |
|
|
| const connectDirectly = async (reason) => { |
| if (fallbackTriggered || isDestroying) 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ı"); |
| }); |
| }; |
|
|
| try { |
| 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 { |
| try { |
| const lookup = await dns.lookup(sshConfig.host); |
| activeConnectionIp = lookup.address; |
| } catch (e) { |
| activeConnectionIp = sshConfig.host; |
| } |
| activeConnectionLocation = await getIpLocation(activeConnectionIp); |
|
|
| let tunnelHandled = false; |
| const tunnelTimeout = setTimeout(() => { |
| if (tunnelHandled || isDestroying) return; |
| tunnelHandled = true; |
| connectDirectly("SSH tüneli (forwardOut) 10 saniye içinde açılamadı (sunucu/ağ lagı olabilir)"); |
| }, 10000); |
|
|
| sshClient.forwardOut( |
| '127.0.0.1', |
| 12345, |
| targetServerIp, |
| parseInt(targetServerPort) || 25565, |
| (err, stream) => { |
| if (tunnelHandled || isDestroying) return; |
| tunnelHandled = true; |
| clearTimeout(tunnelTimeout); |
|
|
| 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'); |
| } |
| } |
| ); |
| } catch (err) { |
| if (!isDestroying) { |
| connectDirectly(`SSH 'ready' işleyicisinde beklenmeyen hata: ${err && err.message ? err.message : err}`); |
| } |
| } |
| }); |
|
|
| 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}`); |
| } |
| } catch (err) { |
| logger.error(`[connect() Hatası] Beklenmeyen bir hata oluştu: ${err && err.stack ? err.stack : err}`); |
| if (!isDestroying) { |
| handleFinalFailure(`connect() içinde yakalanmayan hata: ${err && err.message ? err.message : err}`); |
| } |
| } |
| } |
| }); |
|
|
|
|
| let lastFullFeedAt = 0; |
|
|
| 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, ""); |
| try { |
| return t.toLocaleLowerCase('tr-TR'); |
| } catch (e) { |
| return t.toLowerCase(); |
| } |
| } |
|
|
| function normalizeVariants(s) { |
| if (!s || typeof s !== "string") return [""]; |
| let t = stripColorCodes(s); |
| if (t.normalize) t = t.normalize("NFKD").replace(/[\u0300-\u036f]/g, ""); |
| const variants = new Set(); |
| try { variants.add(t.toLocaleLowerCase('tr-TR')); } catch (e) {} |
| variants.add(t.toLowerCase()); |
| return Array.from(variants); |
| } |
|
|
| function matchesAnyName(sourceString, targetVariantsList) { |
| if (!sourceString) return false; |
| const sourceVariants = normalizeVariants(sourceString); |
| return sourceVariants.some(sv => targetVariantsList.some(t => sv.includes(t))); |
| } |
|
|
| const TARGET_NAME_NORMS = BESLEYICI_NAMES.flatMap(n => normalizeVariants(n)); |
|
|
| function isBesleyiciName(name) { |
| return matchesAnyName(name, TARGET_NAME_NORMS); |
| } |
|
|
| const TARGET_KAZICI_NORMS = KAZICI_NAMES.flatMap(n => normalizeVariants(n)); |
|
|
| function isKaziciName(name) { |
| return matchesAnyName(name, TARGET_KAZICI_NORMS); |
| } |
|
|
| const TARGET_CIFTCI_NORMS = CIFTCI_NAMES.flatMap(n => normalizeVariants(n)); |
|
|
| function isCiftciName(name) { |
| return matchesAnyName(name, TARGET_CIFTCI_NORMS); |
| } |
|
|
| 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 getComponentData(slot, type) { |
| try { |
| if (!slot || !Array.isArray(slot.components)) return undefined; |
| const comp = slot.components.find(c => c && c.type === type); |
| return comp ? comp.data : undefined; |
| } catch (e) { return undefined; } |
| } |
|
|
| function nbtUnwrap(node) { |
| if (node && typeof node === "object" && "type" in node && "value" in node) { |
| return node.value; |
| } |
| return node; |
| }; |
|
|
| function flattenTextComponent(node) { |
| if (node == null) return ""; |
| if (typeof node === "string") return node; |
| if (Array.isArray(node)) return node.map(flattenTextComponent).join(""); |
|
|
| if (typeof node === "object") { |
| if ("type" in node && "value" in node) { |
| const t = node.type, v = node.value; |
| if (t === "string") return typeof v === "string" ? v : ""; |
| if (t === "list" || t === "compound") return flattenTextComponent(v); |
| return ""; |
| } |
| let out = ""; |
| if ("text" in node) out += flattenTextComponent(node.text); |
| if ("extra" in node) out += flattenTextComponent(node.extra); |
| return out; |
| } |
| return ""; |
| }; |
|
|
| function parseComponentLoreLine(lineNode) { |
| let text = ""; |
| let color = null; |
|
|
| function walk(node) { |
| if (node == null) return; |
| if (typeof node === "string") { text += node; return; } |
| if (Array.isArray(node)) { node.forEach(walk); return; } |
| if (typeof node === "object") { |
| if ("type" in node && "value" in node) { |
| const t = node.type, v = node.value; |
| if (t === "string") { text += (typeof v === "string" ? v : ""); return; } |
| if (t === "list" || t === "compound") { walk(v); return; } |
| return; |
| } |
| if ("color" in node) { |
| const colVal = nbtUnwrap(node.color); |
| if (color == null && typeof colVal === "string") color = colVal; |
| } |
| if ("text" in node) walk(node.text); |
| if ("extra" in node) walk(node.extra); |
| } |
| } |
|
|
| walk(lineNode); |
| return { text: text.trim(), color }; |
| }; |
|
|
| function deepSearchForString(root, targetVariantsList) { |
| const results = []; |
| const visited = new WeakSet(); |
| function recurse(obj, path) { |
| if (obj == null || typeof obj === "function") return; |
| if (typeof obj === "string") { |
| if (matchesAnyName(obj, targetVariantsList)) 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; } |
| } |
|
|
| function getItemDisplayTexts(slot) { |
| const texts = []; |
| try { |
| if (!slot) return texts; |
|
|
| const customNameData = getComponentData(slot, "custom_name"); |
| if (customNameData !== undefined) { |
| const nameTxt = flattenTextComponent(customNameData); |
| if (nameTxt) texts.push(nameTxt); |
| } |
| const loreData = getComponentData(slot, "lore"); |
| if (Array.isArray(loreData)) { |
| for (const line of loreData) { |
| const { text } = parseComponentLoreLine(line); |
| if (text) texts.push(text); |
| } |
| } |
| if (texts.length > 0) return texts; |
|
|
| function extractPlain(raw) { |
| if (!raw || typeof raw !== "string") return null; |
| try { |
| const maybe = JSON.parse(raw); |
| if (maybe && typeof maybe === "object") { |
| if (maybe.extra && Array.isArray(maybe.extra)) { |
| return maybe.extra.map(x => (x && typeof x.text === "string") ? x.text : "").join(""); |
| } else if (typeof maybe.text === "string") { |
| return maybe.text; |
| } |
| } |
| } catch (e) {} |
| return raw; |
| } |
|
|
| if (slot.nbt) { |
| const name = safeGet(slot.nbt, ["value", "display", "value", "Name", "value"]); |
| const plainName = extractPlain(name); |
| if (plainName) texts.push(plainName); |
|
|
| const lore = safeGet(slot.nbt, ["value", "display", "value", "Lore", "value"]); |
| if (Array.isArray(lore)) { |
| for (const line of lore) { |
| const plainLine = extractPlain(line.value || line); |
| if (plainLine) texts.push(plainLine); |
| } |
| } |
| } |
|
|
| if (texts.length === 0) { |
| texts.push(util.inspect(slot, { depth: null })); |
| } |
| } catch (err) {} |
| return texts; |
| }; |
|
|
| function windowMatchesLoreKeyword(window, keywords) { |
| try { |
| if (!window || !Array.isArray(window.slots)) return false; |
| const normKeywords = keywords.flatMap(k => normalizeVariants(k)); |
| for (const slot of window.slots) { |
| if (!slot) continue; |
| const texts = getItemDisplayTexts(slot); |
| for (const t of texts) { |
| if (matchesAnyName(t, normKeywords)) return true; |
| } |
| } |
| } catch (e) {} |
| return false; |
| } |
|
|
| async function goToIsland() { |
| if (!bot || !botIsReady) return; |
| try { |
| logger.info(`[${currentBotUsername}] ${GO_ISLAND_COMMAND} komutu gönderiliyor.`); |
| bot.chat(GO_ISLAND_COMMAND); |
| } catch (err) { |
| logger.error(`[goToIsland Hatası] Komut gönderilemedi: ${err && err.message ? err.message : err}`); |
| } |
| } |
|
|
| async function sendSkyblockCommand() { |
| if (!bot || !botIsReady) return; |
| try { |
| logger.info(`[${currentBotUsername}] /skyblock komutu (manuel) gönderiliyor.`); |
| bot.chat("/skyblock"); |
| } catch (err) { |
| logger.error(`[sendSkyblockCommand Hatası] Komut gönderilemedi: ${err && err.message ? err.message : err}`); |
| } |
| } |
|
|
| const MONEY_BALANCE_REGEX = /bakiyen:\s*([\d.,]+)\s*TL/i; |
| const MONEY_RESPONSE_TIMEOUT_MS = 8000; |
|
|
| function parseBalanceToAdjustedAmount(rawBalanceStr) { |
| const noCommas = rawBalanceStr.replace(/,/g, ""); |
| const beforeDot = noCommas.split(".")[0]; |
| const intValue = parseInt(beforeDot, 10); |
| if (isNaN(intValue)) return null; |
| return intValue - 1000; |
| } |
|
|
| async function sendMoneyToNick(nick) { |
| if (!bot || !botIsReady) { |
| return { success: false, message: "Bot hazır değil." }; |
| } |
| if (!nick || typeof nick !== "string" || nick.trim().length === 0) { |
| return { success: false, message: "nick parametresi zorunlu." }; |
| } |
|
|
| return new Promise((resolve) => { |
| let finished = false; |
| let timeoutHandle = null; |
|
|
| const cleanup = () => { |
| if (finished) return; |
| finished = true; |
| clearTimeout(timeoutHandle); |
| bot.removeListener('message', onMessage); |
| }; |
|
|
| const onMessage = (jsonMsg) => { |
| try { |
| const chatMessage = bot.chat.ChatMessage ? new bot.chat.ChatMessage(jsonMsg) : jsonMsg; |
| const text = typeof chatMessage.toString === 'function' ? chatMessage.toString() : String(jsonMsg); |
| const plain = stripColorCodes(text); |
|
|
| const match = plain.match(MONEY_BALANCE_REGEX); |
| if (match) { |
| const adjusted = parseBalanceToAdjustedAmount(match[1]); |
| cleanup(); |
|
|
| if (adjusted == null) { |
| logger.error(`[Sendmoney] Bakiye ayrıştırılamadı: "${plain}"`); |
| resolve({ success: false, message: "Bakiye mesajı ayrıştırılamadı." }); |
| return; |
| } |
|
|
| try { |
| const payCommand = `/pay ${nick} ${adjusted}`; |
| bot.chat(payCommand); |
| logger.info(`[Sendmoney] Bakiye okundu: ${match[1]} -> Gönderilecek: ${adjusted}. Komut: ${payCommand}`); |
| resolve({ success: true, message: "Para gönderildi.", balance_raw: match[1], amount_sent: adjusted, nick }); |
| } catch (err) { |
| logger.error(`[Sendmoney] /pay komutu gönderilemedi: ${err && err.message ? err.message : err}`); |
| resolve({ success: false, message: "/pay komutu gönderilirken hata oluştu." }); |
| } |
| } |
| } catch (err) { |
| logger.error(`[Sendmoney] Mesaj işlenirken hata: ${err && err.message ? err.message : err}`); |
| } |
| }; |
|
|
| bot.on('message', onMessage); |
|
|
| timeoutHandle = setTimeout(() => { |
| cleanup(); |
| logger.warn(`[Sendmoney] ${formatDurationMs(MONEY_RESPONSE_TIMEOUT_MS)} içinde bakiye mesajı gelmedi.`); |
| resolve({ success: false, message: "Zaman aşımı: bakiye mesajı alınamadı." }); |
| }, MONEY_RESPONSE_TIMEOUT_MS); |
|
|
| try { |
| bot.chat("/money"); |
| logger.info(`[Sendmoney] /money komutu gönderildi, bakiye mesajı bekleniyor...`); |
| } catch (err) { |
| cleanup(); |
| logger.error(`[Sendmoney] /money komutu gönderilemedi: ${err && err.message ? err.message : err}`); |
| resolve({ success: false, message: "/money komutu gönderilirken hata oluştu." }); |
| } |
| }); |
| } |
|
|
| async function antiBotMove() { |
| if (!bot || !botIsReady) return; |
| try { |
| const moves = ['forward', 'left', 'right', 'jump']; |
|
|
| const moveCount = Math.floor(Math.random() * 4) + 2; |
|
|
| for (let i = 0; i < moveCount; i++) { |
| const move = moves[Math.floor(Math.random() * moves.length)]; |
| const duration = 150 + Math.floor(Math.random() * 500); |
|
|
| bot.setControlState(move, true); |
| await new Promise(r => setTimeout(r, duration)); |
| bot.setControlState(move, false); |
|
|
| await new Promise(r => setTimeout(r, 100 + Math.random() * 300)); |
|
|
| } |
|
|
| await bot.look( |
| bot.entity.yaw + ((Math.random() - 0.5) * 1.5), |
| bot.entity.pitch + ((Math.random() - 0.5) * 0.3), |
| true |
| ); |
| } catch (err) { |
| logger.error(`[antiBotMove Hatası] Komut gönderilemedi: ${err && err.message ? err.message : err}`); |
| } |
| }; |
|
|
| function scheduleNextCheck(delayMs) { |
| if (periodicTimeoutHandle) { |
| clearTimeout(periodicTimeoutHandle); |
| } |
|
|
| nextScheduledCheckAtMs = Date.now() + delayMs; |
| nextScheduledCheckTimeStr = `${getFutureDateTimeString(delayMs)} (${formatDurationMs(delayMs)} sonra)`; |
| logger.info(`[Planlayıcı] Bir sonraki besleyici kontrolü planlandı. Zaman: ${nextScheduledCheckTimeStr}`); |
|
|
| periodicTimeoutHandle = setTimeout(async () => { |
| if (!bot || !botIsReady) return; |
| isMainCycleRunning = true; |
| try { |
| await goToIsland(); |
| await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS)); |
| await scanAndFeed(); |
| } catch (err) { |
| logger.error(`[Planlayıcı Hatası] Periyodik döngüde beklenmeyen hata: ${err && err.stack ? err.stack : err}`); |
| try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {} |
| } finally { |
| isMainCycleRunning = false; |
| } |
| }, delayMs); |
| } |
|
|
| function activateEntityAtNoLook(entity, position) { |
| bot._client.write('use_entity', { |
| target: entity.id, |
| mouse: 2, |
| sneaking: false, |
| hand: 0, |
| x: position.x - entity.position.x, |
| y: position.y - entity.position.y, |
| z: position.z - entity.position.z |
| }); |
| } |
|
|
| async function scanAndFeedCore() { |
| if (!bot || !botIsReady) return; |
| if (!guiMutex.isLocked()) { |
| logger.error("[GÜVENLİK İHLALİ] scanAndFeedCore kilitsiz çağrıldı! Çakışma riski var, işlem iptal ediliyor."); |
| return; |
| } |
|
|
| if (Date.now() < (lastFullFeedAt + 30000)) { |
| logger.warn("[Besleyici] Çok kısa süre önce işlem yapıldı. Güvenlik kilidi devrede."); |
| return; |
| } |
|
|
| if (!bot.entities || !bot.entity || !bot.entity.position) { |
| logger.warn("[Besleyici] Bot durumu (entities/position) henüz hazır değil, muhtemelen lag veya senkronizasyon gecikmesi. 10 dk sonra tekrar denenecek."); |
| scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); |
| 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; |
| } |
| const deep = deepSearchForString(e, TARGET_NAME_NORMS); |
| if (deep.length > 0) { |
| best = { entity: e, path: deep[0].path }; |
| break; |
| } |
| } |
|
|
| if (!best) { |
| logger.info(`[${currentBotUsername}] /home sonrası tarama: besleyici minyon bulunamadı.`); |
| bot.chat("/skyblock"); |
| scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); |
| return; |
| } |
|
|
| if (!best.entity.position) { |
| logger.warn(`[${currentBotUsername}] Bulunan entity'nin pozisyon verisi yok (muhtemelen lag). 10 dk sonra tekrar denenecek.`); |
| 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; |
| } |
|
|
| await new Promise(r => setTimeout(r, 400)); |
|
|
| if (bot.currentWindow != null) { |
| logger.warn(`[${currentBotUsername}] Beklenmedik şekilde eski bir pencere referansı hâlâ duruyor, kapatılıp temizleniyor...`); |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| await new Promise(r => setTimeout(r, 300)); |
| } |
|
|
| const startTime = Date.now(); |
| const endTime = startTime + WINDOW_OPEN_TIMEOUT_MS; |
| let clickCount = 0; |
| let isLoopActive = true; |
|
|
| await new Promise((resolve) => { |
| let clickInterval = null; |
| const cleanFinish = () => { |
| isLoopActive = false; |
| clearInterval(clickInterval); |
| windowOpenResolvers = []; |
| resolve(); |
| }; |
|
|
| windowOpenResolvers.push(() => { |
| cleanFinish(); |
| }); |
|
|
| const sendClickPacket = () => { |
| if (!isLoopActive) return; |
|
|
| if (!bot || !botIsReady) { |
| logger.warn("[Tıklama Sistemi] Bot artık hazır değil (bağlantı koptu?). Döngü iptal ediliyor."); |
| 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(); |
| clickInterval = setInterval(sendClickPacket, 600); |
| }); |
| } |
|
|
| const FEED_LOCK_RETRY_DELAY_MS = 15 * 1000; |
| const FEED_LOCK_MAX_RETRIES = 8; |
|
|
| async function scanAndFeed(retryCount) { |
| retryCount = retryCount || 0; |
| if (!guiMutex.tryAcquire()) { |
| if (retryCount < FEED_LOCK_MAX_RETRIES) { |
| logger.warn(`[Besleyici] Zaten bir GUI işlemi sürüyor (kazıcı/çiftçi). ${formatDurationMs(FEED_LOCK_RETRY_DELAY_MS)} sonra tekrar denenecek (${retryCount + 1}/${FEED_LOCK_MAX_RETRIES}).`); |
| setSafeTimeout(() => { scanAndFeed(retryCount + 1); }, FEED_LOCK_RETRY_DELAY_MS); |
| } else { |
| logger.error("[Besleyici] Kilit uzun süredir meşgul, besleyici turu atlanıyor. Döngünün ölmemesi için normal süre sonra tekrar planlanıyor."); |
| try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {} |
| } |
| return; |
| } |
| try { |
| await scanAndFeedCore(); |
| } catch (err) { |
| logger.error(`[scanAndFeed Hatası] Beklenmeyen bir hata oluştu, döngü güvenle durduruldu: ${err && err.stack ? err.stack : err}`); |
| try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {} |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| async function scanAndFeedManual() { |
| logger.info("[Besleyici - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)..."); |
| await guiMutex.acquire(); |
| try { |
| logger.info("[Besleyici - Manuel] Kilit alındı, kontrol başlıyor."); |
| await scanAndFeedCore(); |
| } catch (err) { |
| logger.error(`[scanAndFeedManual Hatası] ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| async function activateEntityAndAwaitWindow(entity, labelForLogs) { |
| if (!bot || !botIsReady || !entity || !entity.position) { |
| return { success: false, reason: "Bot veya entity hazır değil" }; |
| } |
| if (!bot.entity || !bot.entity.position) { |
| return { success: false, reason: "Bot pozisyon verisi henüz hazır değil" }; |
| } |
|
|
| const dist = bot.entity.position.distanceTo(entity.position); |
| if (dist > 7) { |
| return { success: false, reason: `Çok uzak (${dist.toFixed(2)} blok, max 7)` }; |
| } |
|
|
| await new Promise(r => setTimeout(r, 400)); |
|
|
| if (bot.currentWindow != null) { |
| logger.warn(`[Tıklama Sistemi - ${labelForLogs}] Beklenmedik şekilde eski bir pencere referansı hâlâ duruyor, kapatılıp temizleniyor...`); |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| await new Promise(r => setTimeout(r, 300)); |
| } |
|
|
| const startTime = Date.now(); |
| const endTime = startTime + WINDOW_OPEN_TIMEOUT_MS; |
| let clickCount = 0; |
| let isLoopActive = true; |
| let timedOut = false; |
|
|
| await new Promise((resolve) => { |
| let clickInterval = null; |
| const cleanFinish = () => { |
| isLoopActive = false; |
| clearInterval(clickInterval); |
| windowOpenResolvers = []; |
| resolve(); |
| }; |
|
|
| windowOpenResolvers.push(() => { |
| cleanFinish(); |
| }); |
|
|
| const sendClickPacket = () => { |
| if (!isLoopActive) return; |
|
|
| if (!bot || !botIsReady) { |
| logger.warn(`[Tıklama Sistemi - ${labelForLogs}] Bot artık hazır değil. Döngü iptal ediliyor.`); |
| cleanFinish(); |
| return; |
| } |
|
|
| if (Date.now() > endTime) { |
| logger.warn(`[Tıklama Sistemi - ${labelForLogs}] ${WINDOW_OPEN_TIMEOUT_MS} ms doldu ancak menü sunucudan gelmedi. Pes ediliyor.`); |
| timedOut = true; |
| cleanFinish(); |
| return; |
| } |
|
|
| clickCount++; |
| logger.info(`[Tıklama Sistemi - ${labelForLogs}] Menü tespiti başarısız. Deneme #${clickCount} gönderiliyor... (id:${entity.id})`); |
|
|
| try { |
| activateEntityAtNoLook(entity, entity.position); |
| } catch (err) {} |
| }; |
|
|
| sendClickPacket(); |
| clickInterval = setInterval(sendClickPacket, 600); |
| }); |
|
|
| if (timedOut) { |
| return { success: false, reason: "Pencere zaman aşımına uğradı" }; |
| } |
| return { success: true }; |
| } |
|
|
| function findAllKaziciEntities() { |
| const arr = Object.values(bot.entities); |
| const matches = []; |
| const seenIds = new Set(); |
|
|
| for (const e of arr) { |
| const name = getEntityReadableName(e); |
| let matched = isKaziciName(name); |
|
|
| if (!matched) { |
| const deep = deepSearchForString(e, TARGET_KAZICI_NORMS); |
| matched = deep.length > 0; |
| } |
|
|
| if (matched && e && e.id != null && !seenIds.has(e.id)) { |
| seenIds.add(e.id); |
| matches.push(e); |
| } |
| } |
|
|
| return matches; |
| } |
|
|
| async function scanAndCollectMinersCore() { |
| if (!bot || !botIsReady) return; |
| if (!guiMutex.isLocked()) { |
| logger.error("[GÜVENLİK İHLALİ] scanAndCollectMinersCore kilitsiz çağrıldı! Çakışma riski var, işlem iptal ediliyor."); |
| return; |
| } |
|
|
| if (!bot.entities || !bot.entity || !bot.entity.position) { |
| logger.warn("[Kazıcı Toplama] Bot durumu (entities/position) henüz hazır değil, muhtemelen lag. Bu tur atlanıyor."); |
| return; |
| } |
|
|
| const miners = findAllKaziciEntities(); |
|
|
| if (miners.length === 0) { |
| logger.info(`[${currentBotUsername}] Kazıcı minyon bulunamadı, depo aktarım turu atlandı.`); |
| return; |
| } |
|
|
| logger.info(`[${currentBotUsername}] ${miners.length} adet kazıcı minyon bulundu. Sırayla depo aktarımı yapılacak (aralarda ${formatDurationMs(KAZICI_BETWEEN_DELAY_MS)} bekleme).`); |
|
|
| for (let i = 0; i < miners.length; i++) { |
| if (!bot || !botIsReady) { |
| logger.warn("[Kazıcı Toplama] Bot artık hazır değil, tur erken sonlandırılıyor."); |
| break; |
| } |
|
|
| const entity = miners[i]; |
| const label = `Kazıcı #${i + 1}/${miners.length}`; |
|
|
| if (!entity || !entity.position) { |
| logger.warn(`[Kazıcı Toplama] ${label} pozisyon verisi kayboldu (lag olabilir), atlanıyor.`); |
| } else { |
| logger.info(`[${currentBotUsername}] ${label} (id:${entity.id}) aktive ediliyor...`); |
| const result = await activateEntityAndAwaitWindow(entity, label); |
| if (!result.success) { |
| logger.warn(`[Kazıcı Toplama] ${label} başarısız: ${result.reason}`); |
| } else { |
| logger.info(`[Kazıcı Toplama] ${label} depo aktarım işlemi tamamlandı.`); |
| } |
| } |
|
|
| if (i < miners.length - 1) { |
| await new Promise(r => setTimeout(r, KAZICI_BETWEEN_DELAY_MS)); |
| } |
| } |
|
|
| logger.info(`[${currentBotUsername}] Kazıcı minyon depo aktarım turu tamamlandı.`); |
| } |
|
|
| async function scanAndCollectMiners() { |
| if (!guiMutex.tryAcquire()) { |
| logger.warn("[Kazıcı Toplama] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor."); |
| return; |
| } |
| try { |
| await scanAndCollectMinersCore(); |
| } catch (err) { |
| logger.error(`[scanAndCollectMiners Hatası] Beklenmeyen bir hata oluştu: ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| async function scanAndCollectMinersManual() { |
| logger.info("[Kazıcı Toplama - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)..."); |
| await guiMutex.acquire(); |
| try { |
| logger.info("[Kazıcı Toplama - Manuel] Kilit alındı, tur başlıyor."); |
| await scanAndCollectMinersCore(); |
| lastKaziciCheckTimeStr = getExtendedDateTimeString(); |
| } catch (err) { |
| logger.error(`[scanAndCollectMinersManual Hatası] ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| async function openCiftciAndSellAllCore() { |
| if (!bot || !botIsReady) return; |
| if (!guiMutex.isLocked()) { |
| logger.error("[GÜVENLİK İHLALİ] openCiftciAndSellAllCore kilitsiz çağrıldı! Çakışma riski var, işlem iptal ediliyor."); |
| return; |
| } |
|
|
| ciftciWindowPurpose = "sell"; |
|
|
| logger.info(`[${currentBotUsername}] Çiftçi (Hepsini Sat) menüsü açılıyor (${CIFTCI_COMMAND})...`); |
|
|
| if (bot.currentWindow != null) { |
| logger.warn(`[${currentBotUsername}] [Çiftçi] Beklenmedik şekilde eski bir pencere referansı hâlâ duruyor, kapatılıp temizleniyor...`); |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| await new Promise(r => setTimeout(r, 300)); |
| } |
|
|
| let timedOut = false; |
|
|
| await new Promise((resolve) => { |
| let timeoutHandle = null; |
| const cleanFinish = () => { |
| clearTimeout(timeoutHandle); |
| windowOpenResolvers = []; |
| resolve(); |
| }; |
|
|
| windowOpenResolvers.push(() => { |
| cleanFinish(); |
| }); |
|
|
| timeoutHandle = setTimeout(() => { |
| logger.warn(`[Çiftçi] ${formatDurationMs(CIFTCI_WINDOW_TIMEOUT_MS)} doldu ancak menü sunucudan gelmedi. Pes ediliyor.`); |
| timedOut = true; |
| cleanFinish(); |
| }, CIFTCI_WINDOW_TIMEOUT_MS); |
|
|
| if (!bot || !botIsReady) { |
| logger.warn("[Çiftçi] Bot artık hazır değil. İşlem iptal ediliyor."); |
| timedOut = true; |
| cleanFinish(); |
| return; |
| } |
|
|
| logger.info(`[Çiftçi] Menü komutu gönderiliyor (tek seferlik)...`); |
| try { bot.chat(CIFTCI_COMMAND); } catch (e) {} |
| }); |
|
|
| if (timedOut) { |
| logger.warn("[Çiftçi] Menü açılamadı, bu tur atlanıyor."); |
| } else { |
| logger.info(`[${currentBotUsername}] Çiftçi menü turu tamamlandı.`); |
| } |
| } |
|
|
| async function openCiftciAndSellAll() { |
| if (!guiMutex.tryAcquire()) { |
| logger.warn("[Çiftçi] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor."); |
| return; |
| } |
| try { |
| await openCiftciAndSellAllCore(); |
| } catch (err) { |
| logger.error(`[openCiftciAndSellAll Hatası] Beklenmeyen bir hata oluştu: ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| async function openCiftciAndSellAllManual() { |
| logger.info("[Çiftçi - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)..."); |
| await guiMutex.acquire(); |
| try { |
| logger.info("[Çiftçi - Manuel] Kilit alındı, satış başlıyor."); |
| await openCiftciAndSellAllCore(); |
| lastCiftciSellTimeStr = getExtendedDateTimeString(); |
| } catch (err) { |
| logger.error(`[openCiftciAndSellAllManual Hatası] ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| function stripColorAndGetLoreLines(window) { |
| if (!window || !Array.isArray(window.slots)) return null; |
| for (const slot of window.slots) { |
| if (!slot) continue; |
|
|
| let nameTxt = ""; |
|
|
| const customNameData = getComponentData(slot, "custom_name"); |
| if (customNameData !== undefined) { |
| nameTxt = flattenTextComponent(customNameData); |
| } |
|
|
| if (!nameTxt && slot.nbt) { |
| const nameRaw = safeGet(slot.nbt, ["value", "display", "value", "Name", "value"]); |
| try { |
| const maybe = JSON.parse(nameRaw); |
| if (maybe && maybe.extra) nameTxt = maybe.extra.map(x => x.text || "").join(""); |
| else if (maybe && maybe.text) nameTxt = maybe.text; |
| } catch (e) { nameTxt = nameRaw || ""; } |
| } |
|
|
| if (nameTxt.includes(CIFTCI_FILTER_ITEM_NAME_KEYWORD)) { |
| return { slot, index: window.slots.indexOf(slot) }; |
| } |
| } |
| return null; |
| }; |
|
|
| function getFilterItemLoreEntries(filterSlot) { |
| const entries = []; |
|
|
| const loreData = getComponentData(filterSlot, "lore"); |
| if (Array.isArray(loreData)) { |
| for (const line of loreData) { |
| entries.push(parseComponentLoreLine(line)); |
| } |
| if (entries.length > 0) return entries; |
| } |
|
|
| try { |
| const lore = safeGet(filterSlot.nbt, ["value", "display", "value", "Lore", "value"]); |
| if (!Array.isArray(lore)) return entries; |
| for (const line of lore) { |
| const raw = line.value || line; |
| let plain = ""; |
| let color = null; |
| try { |
| const maybe = JSON.parse(raw); |
| if (maybe && Array.isArray(maybe.extra)) { |
| for (const part of maybe.extra) { |
| if (part.text) plain += part.text; |
| if (part.color && !color) color = part.color; |
| } |
| } else if (maybe && maybe.text) { |
| plain = maybe.text; |
| } |
| } catch (e) { |
| plain = raw; |
| } |
| entries.push({ text: plain.trim(), color }); |
| } |
| } catch (e) {} |
| return entries; |
| }; |
|
|
| function findCurrentFilterIndex(loreEntries) { |
| for (let i = 0; i < CIFTCI_FILTER_MODES.length; i++) { |
| const modeName = CIFTCI_FILTER_MODES[i]; |
| const match = loreEntries.find(e => |
| e.text.includes(modeName) && |
| (e.text.includes("»") || e.color === CIFTCI_ACTIVE_FILTER_COLOR) |
| ); |
| if (match) return i; |
| } |
| const colorMatch = loreEntries.find(e => e.color === CIFTCI_ACTIVE_FILTER_COLOR); |
| if (colorMatch) { |
| for (let i = 0; i < CIFTCI_FILTER_MODES.length; i++) { |
| if (colorMatch.text.includes(CIFTCI_FILTER_MODES[i])) return i; |
| } |
| } |
| return -1; |
| } |
|
|
| async function waitForFilterItem(maxRetries) { |
| for (let attempt = 0; attempt <= maxRetries; attempt++) { |
| await new Promise(r => setTimeout(r, attempt === 0 ? CIFTCI_FILTER_STEP_WAIT_MS : CIFTCI_FILTER_RETRY_WAIT_MS)); |
| if (!bot || !bot.currentWindow) return null; |
| const found = stripColorAndGetLoreLines(bot.currentWindow); |
| if (found) return found; |
| } |
| return null; |
| } |
|
|
| function extractStorageItemInfo(window, internalName) { |
| if (!window || !Array.isArray(window.slots)) return null; |
| const slot = window.slots.find(s => s && s.name === internalName); |
| if (!slot) return null; |
|
|
| const texts = getItemDisplayTexts(slot); |
| const joined = texts.join(" | "); |
|
|
| function grab(label) { |
| const re = new RegExp(label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*:?\\s*([\\d.,%~]+)", "i"); |
| const m = joined.match(re); |
| return m ? m[1] : null; |
| } |
|
|
| return { |
| depo: grab("Depo"), |
| limit: grab("Limit"), |
| dolulukOrani: grab("Doluluk Oranı"), |
| birimFiyati: grab("Birim Fiyatı"), |
| anlikNetKazanc: grab("Anlık Net Kazanç"), |
| dakikalikUretim: grab("Dakikalık üretim"), |
| saatlikUretim: grab("Saatlik üretim"), |
| gunlukUretim: grab("Günlük üretim") |
| }; |
| } |
|
|
| function findSlotByInternalName(window, internalName) { |
| if (!window || !Array.isArray(window.slots)) return -1; |
| for (let i = 0; i < window.slots.length; i++) { |
| const s = window.slots[i]; |
| if (s && s.name === internalName) return i; |
| } |
| return -1; |
| } |
|
|
| async function navigateCiftciFilterToGenerator() { |
| if (!bot || !bot.currentWindow) return false; |
|
|
| let attempts = 0; |
| while (attempts < CIFTCI_FILTER_MAX_RETRIES) { |
| attempts++; |
|
|
| const foundItem = await waitForFilterItem(CIFTCI_FILTER_MAX_RETRIES); |
| if (!foundItem) { |
| logger.warn("[Çiftçi Filtre] Filtre item'ı bulunamadı, deneme durduruldu."); |
| return false; |
| } |
|
|
| const loreEntries = getFilterItemLoreEntries(foundItem.slot); |
| const currentIndex = findCurrentFilterIndex(loreEntries); |
|
|
| if (currentIndex === -1) { |
| logger.warn("[Çiftçi Filtre] Mevcut filtre modu tespit edilemedi."); |
| return false; |
| } |
|
|
| if (currentIndex === CIFTCI_TARGET_FILTER_INDEX) { |
| logger.info("[Çiftçi Filtre] Jeneratör materyalleri moduna ulaşıldı."); |
| return true; |
| } |
|
|
| const total = CIFTCI_FILTER_MODES.length; |
| const forwardDist = (CIFTCI_TARGET_FILTER_INDEX - currentIndex + total) % total; |
| const backwardDist = (currentIndex - CIFTCI_TARGET_FILTER_INDEX + total) % total; |
|
|
| const useLeftClick = forwardDist <= backwardDist; |
| const mouseButton = useLeftClick ? 0 : 1; |
|
|
| logger.info(`[Çiftçi Filtre] Mevcut: "${CIFTCI_FILTER_MODES[currentIndex]}" -> Hedef: "${CIFTCI_FILTER_MODES[CIFTCI_TARGET_FILTER_INDEX]}". ${useLeftClick ? "Sol tık (aşağı)" : "Sağ tık (yukarı)"} kullanılacak.`); |
|
|
| try { |
| await bot.clickWindow(foundItem.index, mouseButton, 0); |
| } catch (e) { |
| logger.error(`[Çiftçi Filtre] Tıklama hatası: ${e && e.message ? e.message : e}`); |
| return false; |
| } |
| } |
|
|
| logger.warn("[Çiftçi Filtre] Azami deneme sayısına ulaşıldı, Jeneratör materyallerine geçilemedi."); |
| return false; |
| } |
|
|
| async function scanStorageInfoCore() { |
| if (!bot || !botIsReady) return; |
| if (!guiMutex.isLocked()) { |
| logger.error("[GÜVENLİK İHLALİ] scanStorageInfoCore kilitsiz çağrıldı! İşlem iptal ediliyor."); |
| return; |
| } |
|
|
| ciftciWindowPurpose = "scan"; |
| logger.info(`[${currentBotUsername}] Storage bilgisi taraması başlıyor: çiftçi menüsü (${CIFTCI_COMMAND}) açılıyor...`); |
|
|
| if (bot.currentWindow != null) { |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| await new Promise(r => setTimeout(r, 300)); |
| } |
|
|
| let windowOpened = false; |
|
|
| await new Promise((resolve) => { |
| let timeoutHandle = null; |
| const cleanFinish = () => { |
| clearTimeout(timeoutHandle); |
| windowOpenResolvers = []; |
| resolve(); |
| }; |
|
|
| windowOpenResolvers.push(() => { |
| windowOpened = true; |
| cleanFinish(); |
| }); |
|
|
| timeoutHandle = setTimeout(() => { |
| logger.warn("[Storage Tarama] Menü açılamadı (zaman aşımı)."); |
| cleanFinish(); |
| }, CIFTCI_WINDOW_TIMEOUT_MS); |
|
|
| try { bot.chat(CIFTCI_COMMAND); } catch (e) {} |
| }); |
|
|
| if (!windowOpened || !bot.currentWindow) { |
| logger.warn("[Storage Tarama] Menü açılmadığı için tarama atlandı."); |
| ciftciWindowPurpose = "sell"; |
| return; |
| } |
|
|
| const reached = await navigateCiftciFilterToGenerator(); |
|
|
| if (reached && bot.currentWindow) { |
| const newCache = { |
| diamond: extractStorageItemInfo(bot.currentWindow, "diamond"), |
| emerald: extractStorageItemInfo(bot.currentWindow, "emerald"), |
| iron_block: extractStorageItemInfo(bot.currentWindow, "iron_block"), |
| lastUpdated: getExtendedDateTimeString() |
| }; |
| storageInfoCache = newCache; |
| logger.info(`[Storage Tarama] Bilgiler güncellendi: ${JSON.stringify(newCache)}`); |
| } else { |
| logger.warn("[Storage Tarama] Jeneratör materyallerine ulaşılamadığı için veri güncellenmedi."); |
| } |
|
|
| try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
| } |
|
|
| async function scanStorageInfo() { |
| if (!guiMutex.tryAcquire()) { |
| logger.info("[Storage Tarama] Başka bir GUI işlemi sürüyor, bu tur atlanıyor."); |
| return; |
| } |
| try { |
| await scanStorageInfoCore(); |
| } catch (err) { |
| logger.error(`[scanStorageInfo Hatası] ${err && err.stack ? err.stack : err}`); |
| ciftciWindowPurpose = "sell"; |
| } finally { |
| guiMutex.release(); |
| } |
| }; |
|
|
| async function scanStorageInfoManual() { |
| logger.info("[Storage Tarama - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)..."); |
| await guiMutex.acquire(); |
| try { |
| logger.info("[Storage Tarama - Manuel] Kilit alındı, tarama başlıyor."); |
| await scanStorageInfoCore(); |
| } catch (err) { |
| logger.error(`[scanStorageInfoManual Hatası] ${err && err.stack ? err.stack : err}`); |
| ciftciWindowPurpose = "sell"; |
| } finally { |
| guiMutex.release(); |
| } |
| }; |
|
|
| |
| |
| |
|
|
| async function waitForNextWindowOpen(timeoutMs) { |
| return new Promise((resolve) => { |
| let timeoutHandle = null; |
| const cleanFinish = (opened) => { |
| clearTimeout(timeoutHandle); |
| windowOpenResolvers = []; |
| resolve(opened); |
| }; |
|
|
| windowOpenResolvers.push(() => cleanFinish(true)); |
|
|
| timeoutHandle = setTimeout(() => { |
| cleanFinish(false); |
| }, timeoutMs); |
| }); |
| } |
|
|
| function findSlotContainingNick(window, nickNorm, itemInternalName) { |
| if (!window || !Array.isArray(window.slots)) return -1; |
| for (let i = 0; i < window.slots.length; i++) { |
| const slot = window.slots[i]; |
| if (!slot) continue; |
|
|
| |
| |
| |
| if (itemInternalName && slot.name !== itemInternalName) continue; |
|
|
| const texts = getItemDisplayTexts(slot); |
| for (const t of texts) { |
| if (matchesAnyName(t, [nickNorm])) return i; |
| } |
| } |
| return -1; |
| } |
|
|
| async function processOrderCore(itemInternalName, nick) { |
| if (!bot || !botIsReady) return { success: false, message: "Bot hazır değil." }; |
| if (!guiMutex.isLocked()) { |
| logger.error("[GÜVENLİK İHLALİ] processOrderCore kilitsiz çağrıldı! İşlem iptal ediliyor."); |
| return { success: false, message: "Kilitsiz çağrı engellendi." }; |
| } |
| if (!ORDER_ALLOWED_ITEMS.includes(itemInternalName)) { |
| return { success: false, message: `Geçersiz item: ${itemInternalName}. İzin verilenler: ${ORDER_ALLOWED_ITEMS.join(", ")}` }; |
| } |
| if (!nick || typeof nick !== "string" || nick.trim().length === 0) { |
| return { success: false, message: "nick parametresi zorunlu." }; |
| } |
|
|
| |
| ciftciWindowPurpose = "order"; |
| logger.info(`[Sipariş] Çiftçi menüsü açılıyor (${CIFTCI_COMMAND})...`); |
|
|
| if (bot.currentWindow != null) { |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| await new Promise(r => setTimeout(r, 300)); |
| } |
|
|
| let windowOpened = false; |
| await new Promise((resolve) => { |
| let timeoutHandle = null; |
| const cleanFinish = () => { clearTimeout(timeoutHandle); windowOpenResolvers = []; resolve(); }; |
| windowOpenResolvers.push(() => { windowOpened = true; cleanFinish(); }); |
| timeoutHandle = setTimeout(() => { |
| logger.warn("[Sipariş] Çiftçi menüsü açılamadı (zaman aşımı)."); |
| cleanFinish(); |
| }, CIFTCI_WINDOW_TIMEOUT_MS); |
| try { bot.chat(CIFTCI_COMMAND); } catch (e) {} |
| }); |
|
|
| if (!windowOpened || !bot.currentWindow) { |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: "Çiftçi menüsü açılamadı." }; |
| } |
|
|
| const reached = await navigateCiftciFilterToGenerator(); |
| if (!reached || !bot.currentWindow) { |
| try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: "Jeneratör materyalleri filtresine ulaşılamadı." }; |
| } |
|
|
| |
| const itemSlotIndex = findSlotByInternalName(bot.currentWindow, itemInternalName); |
| if (itemSlotIndex === -1) { |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: `'${itemInternalName}' slotu jeneratör materyalleri filtresinde bulunamadı.` }; |
| } |
|
|
| try { |
| logger.info(`[Sipariş] '${itemInternalName}' slotuna (idx:${itemSlotIndex}) shift+sağ tık gönderiliyor...`); |
| await bot.clickWindow(itemSlotIndex, 1, 1); |
| await new Promise(r => setTimeout(r, KAZICI_ACTION_SETTLE_DELAY_MS)); |
| } catch (e) { |
| logger.error(`[Sipariş] Shift+sağ tık hatası: ${e && e.message ? e.message : e}`); |
| } |
|
|
| |
| |
| |
| |
| |
| const itemsAfterWithdraw = bot.inventory.items().filter(it => it && it.name === itemInternalName); |
| const withdrawnCount = itemsAfterWithdraw.reduce((sum, it) => sum + (it.count || 0), 0); |
| logger.info(`[Sipariş] Çiftçiden çekildikten sonra envanterde '${itemInternalName}': ${withdrawnCount} adet.`); |
|
|
| if (withdrawnCount === 0) { |
| logger.warn(`[Sipariş] Çiftçi menüsünden '${itemInternalName}' envantere aktarılamadı (0 adet). İşlem iptal ediliyor.`); |
| try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: `Çiftçiden '${itemInternalName}' alınamadı (envanterde 0 adet). Depoda yeterli stok olmayabilir ya da tıklama sunucu tarafından işlenmedi.` }; |
| } |
|
|
| |
| try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {} |
| |
| |
| |
| await new Promise(r => setTimeout(r, 300)); |
|
|
| |
| try { await antiBotMove(); } catch (e) {} |
| try { bot.chat(GO_ISLAND_COMMAND); } catch (e) {} |
| await new Promise(r => setTimeout(r, ORDER_IS_GO_SECOND_DELAY_MS)); |
| try { bot.chat(GO_ISLAND_COMMAND); } catch (e) {} |
| await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS)); |
|
|
| |
| logger.info(`[Sipariş] ${ORDER_COMMAND_PREFIX} ${nick} komutu gönderiliyor...`); |
|
|
| let orderWindowOpened = false; |
| await new Promise((resolve) => { |
| let timeoutHandle = null; |
| const cleanFinish = () => { clearTimeout(timeoutHandle); windowOpenResolvers = []; resolve(); }; |
| windowOpenResolvers.push(() => { orderWindowOpened = true; cleanFinish(); }); |
| timeoutHandle = setTimeout(() => { |
| logger.warn("[Sipariş] /order penceresi açılamadı (zaman aşımı)."); |
| cleanFinish(); |
| }, ORDER_WINDOW_TIMEOUT_MS); |
| try { bot.chat(`${ORDER_COMMAND_PREFIX} ${nick}`); } catch (e) {} |
| }); |
|
|
| if (!orderWindowOpened || !bot.currentWindow) { |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: "/order penceresi açılmadı." }; |
| } |
|
|
| |
| await new Promise(r => setTimeout(r, ORDER_WINDOW_SETTLE_DELAY_MS)); |
| if (!bot.currentWindow) { |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: "Pencere beklenmedik şekilde kapandı." }; |
| } |
|
|
| |
| const nickVariants = normalizeVariants(nick); |
| let targetSlotIndex = -1; |
|
|
| |
| for (const nv of nickVariants) { |
| targetSlotIndex = findSlotContainingNick(bot.currentWindow, nv, itemInternalName); |
| if (targetSlotIndex !== -1) break; |
| } |
|
|
| if (targetSlotIndex === -1) { |
| logger.warn(`[Sipariş] '${nick}' + '${itemInternalName}' eşleşen item bulunamadı, pencere kapatılıyor.`); |
| try { bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: `'${nick}' kullanıcısının '${itemInternalName}' siparişi bulunamadı (belki farklı item için sipariş vermiş, ya da hiç sipariş yok).` }; |
| } |
|
|
| logger.info(`[Sipariş] '${nick}' içeren item bulundu (idx:${targetSlotIndex}), sol tık gönderiliyor...`); |
|
|
| |
| |
| |
| |
| |
| const windowBeforeSelect = bot.currentWindow; |
| try { |
| await bot.clickWindow(targetSlotIndex, 0, 0); |
| } catch (e) { |
| logger.error(`[Sipariş] Slot seçim tıklaması hatası: ${e && e.message ? e.message : e}`); |
| } |
|
|
| |
| const deliveryOpened = await waitForNextWindowOpen(ORDER_DELIVERY_WINDOW_WAIT_MS); |
|
|
| if (!deliveryOpened || !bot.currentWindow) { |
| logger.warn("[Sipariş] Teslim Etme penceresi zaman aşımına uğradı ya da açılmadı, işlem yarım kalmış olabilir."); |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: "Teslim Etme penceresi açılmadı (zaman aşımı)." }; |
| } |
|
|
| |
| |
| |
| if (bot.currentWindow === windowBeforeSelect) { |
| logger.warn("[Sipariş] windowOpen tetiklendi ama pencere referansı değişmedi, dikkatli devam ediliyor."); |
| } |
|
|
| const currentTitle = parseWindowTitleToString(bot.currentWindow.title); |
| const looksLikeDelivery = matchesAnyName(currentTitle, normalizeVariants(ORDER_DELIVERY_TITLE_KEYWORD)); |
| logger.info(`[Sipariş] Yeni pencere açıldı: "${currentTitle}" (Teslim Etme eşleşmesi: ${looksLikeDelivery}). Envanterdeki '${itemInternalName}' item'ları aktarılıyor...`); |
|
|
| if (!looksLikeDelivery) { |
| logger.warn(`[Sipariş] Açılan pencere Teslim Etme'ye benzemiyor, yine de mevcut pencereye aktarım denenecek: "${currentTitle}"`); |
| } |
|
|
| |
| |
| const invStartCheck = bot.currentWindow.inventoryStart; |
| const invCheckBeforeTransfer = bot.currentWindow.slots |
| .slice(invStartCheck) |
| .filter(it => it && it.name === itemInternalName); |
| if (invCheckBeforeTransfer.length === 0) { |
| logger.warn(`[Sipariş] Aktarım öncesi envanterde '${itemInternalName}' bulunamadı, aktarım atlanıyor.`); |
| try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
| return { success: false, message: `Envanterde '${itemInternalName}' kalmamış, Teslim Etme'ye aktarılacak item yok.` }; |
| } |
|
|
| const windowAtTransferStart = bot.currentWindow; |
| let transferredCount = 0; |
| let keepGoing = true; |
| let safetyCounter = 0; |
| let stuckSlot = null; |
| let stuckSlotRepeatCount = 0; |
|
|
| while (keepGoing && safetyCounter < 40) { |
| safetyCounter++; |
|
|
| |
| |
| if (!bot.currentWindow || bot.currentWindow !== windowAtTransferStart) { |
| logger.warn("[Sipariş] Aktarım sırasında pencere değişti/kapandı, döngü durduruluyor."); |
| break; |
| } |
|
|
| |
| |
| |
| |
| const invStart = bot.currentWindow.inventoryStart; |
| const winSlots = bot.currentWindow.slots; |
| let realSlotIndex = -1; |
|
|
| for (let i = invStart; i < winSlots.length; i++) { |
| if (winSlots[i] && winSlots[i].name === itemInternalName) { |
| realSlotIndex = i; |
| break; |
| } |
| } |
|
|
| if (realSlotIndex === -1) { keepGoing = false; break; } |
|
|
| if (stuckSlot === realSlotIndex) { |
| stuckSlotRepeatCount++; |
| if (stuckSlotRepeatCount >= 3) { |
| logger.warn(`[Sipariş] Slot ${realSlotIndex} 3 kez üst üste taşınamadı (Teslim Etme dolu/limitli olabilir), aktarım durduruluyor.`); |
| keepGoing = false; |
| break; |
| } |
| } else { |
| stuckSlot = realSlotIndex; |
| stuckSlotRepeatCount = 0; |
| } |
|
|
| try { |
| await bot.clickWindow(realSlotIndex, 0, 1); |
| await new Promise(r => setTimeout(r, 1000 + Math.floor(Math.random() * 200))); |
|
|
| const stillThere = bot.currentWindow.slots[realSlotIndex]; |
| if (!stillThere || stillThere.name !== itemInternalName) { |
| transferredCount++; |
| } else { |
| logger.warn(`[Sipariş] Slot ${realSlotIndex} tıklandı ama item hâlâ envanterde, taşınmamış olabilir.`); |
| } |
| } catch (e) { |
| logger.error(`[Sipariş] Envanterden aktarım tıklaması hatası: ${e && e.message ? e.message : e}`); |
| keepGoing = false; |
| } |
| } |
|
|
| if (safetyCounter >= 40) { |
| logger.warn(`[Sipariş] Güvenlik limitine (40 deneme) ulaşıldı. Teslim Etme kutusu dolu/limitli olabilir, transfer yarım kalmış olabilir.`); |
| } |
|
|
| |
| |
| try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {} |
| ciftciWindowPurpose = "sell"; |
|
|
| await new Promise(r => setTimeout(r, 300)); |
|
|
| const remainingAfterTransfer = bot.inventory.items() |
| .filter(it => it && it.name === itemInternalName) |
| .reduce((sum, it) => sum + (it.count || 0), 0); |
|
|
| const stoppedReason = safetyCounter >= 40 |
| ? "güvenlik limiti (40 deneme)" |
| : (remainingAfterTransfer > 0 ? "Teslim Etme dolu/limitli olabilir" : "envanter boşaldı"); |
|
|
| logger.info( |
| `[Sipariş] Envanterden Teslim Etme'ye aktarım tamamlandı: ` + |
| `${transferredCount} yığın (stack) gönderildi, ` + |
| `envanterde '${itemInternalName}' kaldı: ${remainingAfterTransfer} adet ` + |
| `(durma sebebi: ${stoppedReason}).` |
| ); |
| |
|
|
| return { |
| success: transferredCount > 0, |
| message: transferredCount > 0 ? "Sipariş işlemi tamamlandı." : "Hiçbir yığın Teslim Etme'ye aktarılamadı.", |
| item: itemInternalName, |
| nick, |
| withdrawn_from_ciftci: withdrawnCount, |
| inventory_transferred_batches: transferredCount, |
| remaining_in_inventory: remainingAfterTransfer |
| }; |
| } |
|
|
| async function processOrderManual(itemInternalName, nick) { |
| logger.info(`[Sipariş - Manuel] Panelden istek geldi (item:${itemInternalName}, nick:${nick}), sırasını bekliyor (öncelikli)...`); |
| await guiMutex.acquire(); |
| try { |
| logger.info("[Sipariş - Manuel] Kilit alındı, işlem başlıyor."); |
| const result = await processOrderCore(itemInternalName, nick); |
| lastOrderTimeStr = `${getExtendedDateTimeString()} (item:${itemInternalName}, nick:${nick}, sonuç:${result.success ? "başarılı" : "başarısız"})`; |
| return result; |
| } catch (err) { |
| logger.error(`[processOrderManual Hatası] ${err && err.stack ? err.stack : err}`); |
| return { success: false, message: "Beklenmeyen bir hata oluştu." }; |
| } finally { |
| guiMutex.release(); |
| } |
| } |
|
|
| function startStorageScanPeriodic() { |
| if (storageScanIntervalHandle) return; |
| storageScanIntervalHandle = setInterval(() => { |
| if (!bot || !botIsReady) return; |
| if (manualStopRequested || isDestroying) return; |
| scanStorageInfo(); |
| }, STORAGE_SCAN_INTERVAL_MS); |
| logger.info(`[Storage Tarama] Bağımsız döngü başlatıldı (her ${formatDurationMs(STORAGE_SCAN_INTERVAL_MS)}).`); |
| setSafeTimeout(() => { scanStorageInfo(); }, 30 * 1000); |
| } |
|
|
| function startKaziciPeriodic() { |
| if (kaziciIntervalHandle) return; |
|
|
| const runCycle = async () => { |
| if (!bot || !botIsReady) return; |
| if (manualStopRequested || isDestroying) return; |
|
|
| if (!guiMutex.tryAcquire()) { |
| logger.info("[Kazıcı Periyodik] Başka bir GUI işlemi sürüyor, bu tur atlanıyor."); |
| return; |
| } |
|
|
| try { |
| logger.info("[Kazıcı Periyodik] Tur başlıyor: kazıcı depo aktarımı."); |
| await scanAndCollectMinersCore(); |
| lastKaziciCheckTimeStr = getExtendedDateTimeString(); |
| logger.info(`[Kazıcı Periyodik] Tur tamamlandı. ${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)} sonra tekrar denenecek.`); |
| } catch (err) { |
| logger.error(`[Kazıcı Periyodik Hatası] Beklenmeyen bir hata oluştu: ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| nextKaziciAtMs = Date.now() + KAZICI_CYCLE_INTERVAL_MS; |
| nextKaziciTimeStr = `${getFutureDateTimeString(KAZICI_CYCLE_INTERVAL_MS)} (${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)} sonra)`; |
| } |
| }; |
|
|
| kaziciIntervalHandle = setInterval(runCycle, KAZICI_CYCLE_INTERVAL_MS); |
| nextKaziciAtMs = Date.now() + KAZICI_CYCLE_INTERVAL_MS; |
| nextKaziciTimeStr = `${getFutureDateTimeString(KAZICI_CYCLE_INTERVAL_MS)} (${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)} sonra)`; |
| logger.info(`[Kazıcı Periyodik] Bağımsız döngü başlatıldı (her ${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)}).`); |
| } |
|
|
| function startCiftciPeriodic() { |
| if (ciftciIntervalHandle) return; |
|
|
| const runCycle = async () => { |
| if (!bot || !botIsReady) return; |
| if (manualStopRequested || isDestroying) return; |
|
|
| if (!guiMutex.tryAcquire()) { |
| logger.info("[Çiftçi Periyodik] Başka bir GUI işlemi sürüyor, bu tur atlanıyor."); |
| return; |
| } |
|
|
| try { |
| logger.info("[Çiftçi Periyodik] Tur başlıyor: çiftçi hepsini-sat."); |
| await openCiftciAndSellAllCore(); |
| lastCiftciSellTimeStr = getExtendedDateTimeString(); |
| logger.info(`[Çiftçi Periyodik] Tur tamamlandı. ${formatDurationMs(CIFTCI_CYCLE_INTERVAL_MS)} sonra tekrar denenecek.`); |
| } catch (err) { |
| logger.error(`[Çiftçi Periyodik Hatası] Beklenmeyen bir hata oluştu: ${err && err.stack ? err.stack : err}`); |
| } finally { |
| guiMutex.release(); |
| nextCiftciAtMs = Date.now() + CIFTCI_CYCLE_INTERVAL_MS; |
| nextCiftciTimeStr = `${getFutureDateTimeString(CIFTCI_CYCLE_INTERVAL_MS)} (${formatDurationMs(CIFTCI_CYCLE_INTERVAL_MS)} sonra)`; |
| } |
| }; |
|
|
| ciftciIntervalHandle = setInterval(runCycle, CIFTCI_CYCLE_INTERVAL_MS); |
| nextCiftciAtMs = Date.now() + CIFTCI_CYCLE_INTERVAL_MS; |
| nextCiftciTimeStr = `${getFutureDateTimeString(CIFTCI_CYCLE_INTERVAL_MS)} (${formatDurationMs(CIFTCI_CYCLE_INTERVAL_MS)} sonra)`; |
| logger.info(`[Çiftçi Periyodik] Bağımsız döngü başlatıldı (her ${formatDurationMs(CIFTCI_CYCLE_INTERVAL_MS)}).`); |
| } |
|
|
| bot.once("spawn", async () => { |
| isConnecting = false; |
| if (!bot) return; |
|
|
| try { |
| logger.info("Bot sunucuya giriş yaptı."); |
| botIsReady = true; |
| botUptimeStart = Date.now(); |
|
|
| botActions.scanAndFeedManual = scanAndFeedManual; |
| botActions.scanAndCollectMinersManual = scanAndCollectMinersManual; |
| botActions.openCiftciAndSellAllManual = openCiftciAndSellAllManual; |
| botActions.goToIslandManual = goToIsland; |
| botActions.sendSkyblockManual = sendSkyblockCommand; |
| botActions.antiBotMove = antiBotMove; |
| botActions.sendMoneyToNick = sendMoneyToNick; |
| botActions.scanStorageInfoManual = scanStorageInfoManual; |
| botActions.processOrderManual = processOrderManual; |
|
|
| startKaziciPeriodic(); |
| startCiftciPeriodic(); |
| startStorageScanPeriodic(); |
| startStallWatcher(); |
|
|
| if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) { |
| const password = config.utils["auto-auth"].password; |
|
|
| setSafeTimeout(() => { |
| try { |
| if (bot && botIsReady) { |
| bot.chat(`/login ${password}`); |
| logger.info(`[${currentBotUsername}] Login komutu kullanıldı.`); |
| } |
| } catch (err) { |
| logger.error(`[Spawn Hatası] /login komutu gönderilemedi: ${err && err.message ? err.message : err}`); |
| } |
| }, 2500); |
|
|
| setSafeTimeout(() => { |
| try { |
| if (bot && botIsReady) { |
| bot.chat(`/skyblock`); |
| logger.info(`[${currentBotUsername}] Skyblock'a ışınlanma komutu kullanıldı.`); |
| } |
| } catch (err) { |
| logger.error(`[Spawn Hatası] /skyblock komutu gönderilemedi: ${err && err.message ? err.message : err}`); |
| } |
| }, 7500); |
|
|
| setSafeTimeout(async () => { |
| if (!bot || !botIsReady) return; |
| isMainCycleRunning = true; |
| try { |
| await goToIsland(); |
| await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS)); |
| await scanAndFeed(); |
| } catch (err) { |
| logger.error(`[Spawn Hatası] İlk kontrol döngüsünde beklenmeyen hata: ${err && err.stack ? err.stack : err}`); |
| try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {} |
| } finally { |
| isMainCycleRunning = false; |
| } |
| }, 12500); |
| } |
| } catch (err) { |
| logger.error(`[Spawn Hatası] Spawn işleyicisinde beklenmeyen hata: ${err && err.stack ? err.stack : err}`); |
| } |
| }); |
|
|
| bot.on("entityMoved", (entity) => { |
| try { trackKaziciPoseIfWatched(entity); } catch (e) {} |
| }); |
|
|
| bot.on("entitySpawn", (entity) => { |
| try { trackKaziciPoseIfWatched(entity); } catch (e) {} |
| }); |
|
|
| bot.on("windowOpen", async (window) => { |
| if (!bot) return; |
| try { |
| const titleRaw = parseWindowTitleToString(window.title); |
|
|
| const isBesleyiciWindow = matchesAnyName(titleRaw, TARGET_NAME_NORMS); |
| const isKaziciWindow = !isBesleyiciWindow && matchesAnyName(titleRaw, TARGET_KAZICI_NORMS); |
| const isCiftciWindow = !isBesleyiciWindow && !isKaziciWindow && |
| (matchesAnyName(titleRaw, TARGET_CIFTCI_NORMS) || windowMatchesLoreKeyword(window, CIFTCI_LORE_FALLBACK_KEYWORDS)); |
|
|
| if (!isBesleyiciWindow && !isKaziciWindow && !isCiftciWindow) { |
| if (ciftciWindowPurpose === "order") { |
| logger.info(`[${currentBotUsername}] Sipariş (/order) penceresi açıldı: ${titleRaw}`); |
| } else { |
| logger.info(`[${currentBotUsername}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${titleRaw}`); |
| try { bot.closeWindow(window); } catch(e){} |
| return; |
| } |
| } else if (isKaziciWindow) { |
| try { |
| logger.info(`[${currentBotUsername}] Kazıcı minyon paneli açıldı. Depo aktarım slotuna (oyun içi 18. slot / mineflayer idx ${KAZICI_STORAGE_TRANSFER_SLOT}) tıklanıyor...`); |
| await bot.clickWindow(KAZICI_STORAGE_TRANSFER_SLOT, 0, 0); |
| logger.info(`[${currentBotUsername}] Kazıcı depo aktarım tıklaması gönderildi.`); |
| await new Promise(r => setTimeout(r, KAZICI_ACTION_SETTLE_DELAY_MS)); |
| } catch (e) { |
| logger.error(`[Kazıcı Arayüz Hatası] Depo aktarım tıklaması sunucu tarafından onaylanmadı: ${e && e.message ? e.message : e}`); |
| } |
|
|
| try { bot.closeWindow(window); } catch(e){} |
|
|
| } else if (isCiftciWindow) { |
| if (ciftciWindowPurpose === "scan" || ciftciWindowPurpose === "order") { |
| |
| |
| } else { |
| try { |
| logger.info(`[${currentBotUsername}] Çiftçi (Hepsini Sat) menüsü açıldı. ${formatDurationMs(CIFTCI_CLICK_DELAY_AFTER_OPEN_MS)} bekleniyor, sonra slot ${CIFTCI_SELL_ALL_SLOT}'e ${CIFTCI_CLICK_REPEAT_COUNT} kez tıklanacak...`); |
| await new Promise(r => setTimeout(r, CIFTCI_CLICK_DELAY_AFTER_OPEN_MS)); |
|
|
| for (let i = 0; i < CIFTCI_CLICK_REPEAT_COUNT; i++) { |
| if (!bot || !botIsReady) break; |
| try { |
| await bot.clickWindow(CIFTCI_SELL_ALL_SLOT, 0, 0); |
| logger.info(`[${currentBotUsername}] Çiftçi hepsini-sat tıklaması gönderildi (${i + 1}/${CIFTCI_CLICK_REPEAT_COUNT}).`); |
| } catch (clickErr) { |
| logger.error(`[Çiftçi Arayüz Hatası] Tıklama #${i + 1} sunucu tarafından onaylanmadı: ${clickErr && clickErr.message ? clickErr.message : clickErr}`); |
| } |
| if (i < CIFTCI_CLICK_REPEAT_COUNT - 1) { |
| await new Promise(r => setTimeout(r, CIFTCI_CLICK_REPEAT_GAP_MS)); |
| } |
| } |
|
|
| await new Promise(r => setTimeout(r, KAZICI_ACTION_SETTLE_DELAY_MS)); |
| } catch (e) { |
| logger.error(`[Çiftçi Arayüz Hatası] Hepsini-sat tıklama turu sırasında beklenmeyen hata: ${e && e.message ? e.message : e}`); |
| } |
|
|
| try { bot.closeWindow(window); } catch(e){} |
| } |
|
|
| } else { |
| 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})`; |
| lastCheckAtMs = Date.now(); |
|
|
| if (can <= 2) { |
| 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 ${formatDurationMs(FULL_FEED_COOLDOWN_MS)} 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 { |
| let calculatedMinutes = (can - 2) * 5; |
| calculatedMinutes = Math.min(50, Math.max(5, calculatedMinutes)); |
|
|
| const dynamicDelayMs = calculatedMinutes * 60 * 1000; |
|
|
| logger.info(`[${currentBotUsername}] Minyon canı ${can} olarak okundu. Canın 2'ye düşmesi için ${formatDurationMs(dynamicDelayMs)} beklenecek.`); |
|
|
| if (can >= 10) { |
| lastFullFeedTimeStr = `${currentStamp} (Canı ${can} olduğu için besleme gerekmedi)`; |
| } |
|
|
| scheduleNextCheck(dynamicDelayMs); |
| } |
| } else { |
| logger.warn(`[${currentBotUsername}] Menü açıldı ama can okunamadı. ${formatDurationMs(DEFAULT_CHECK_INTERVAL_MS)} sonra tekrar denenecek.`); |
| scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); |
| } |
|
|
| try { bot.closeWindow(window); } catch(e){} |
| } |
| } catch (err) { |
| logger.error(`[windowOpen Hatası] Beklenmeyen bir hata oluştu: ${err && err.stack ? err.stack : err}`); |
| try { bot.closeWindow(window); } catch(e){} |
| try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {} |
| } |
|
|
| while (windowOpenResolvers.length > 0) { |
| const fn = windowOpenResolvers.shift(); |
| if (typeof fn === 'function') { |
| try { fn(); } catch (e) {} |
| } |
| } |
| }); |
|
|
|
|
| bot.on("end", () => { |
| try { |
| logger.warn(manualStopRequested ? "Bağlantı koptu. Manuel durdurma istendiği için yeniden bağlanılmayacak." : "Bağlantı koptu. Tüm zamanlayıcılar sıfırlanıyor ve YENİDEN BAĞLANMA SÜRECİ BAŞLIYOR..."); |
| stopPeriodicHomeChecks(); |
| stopStallWatcher(); |
| lastCheckAtMs = null; |
| botIsReady = false; |
| baglantiTuru = "Yok"; |
| botUptimeStart = null; |
| isListeningChat = false; |
| activeConnectionIp = "Bilinmiyor"; |
| activeConnectionLocation = "Bilinmiyor"; |
| nextScheduledCheckTimeStr = manualStopRequested ? "Bot manuel olarak durduruldu." : "Bağlantı koptuğu için planlama iptal edildi."; |
| nextKaziciTimeStr = manualStopRequested ? "Bot manuel olarak durduruldu." : "Bağlantı koptuğu için planlama iptal edildi."; |
| nextCiftciTimeStr = manualStopRequested ? "Bot manuel olarak durduruldu." : "Bağlantı koptuğu için planlama iptal edildi."; |
|
|
| botActions.scanAndFeedManual = null; |
| botActions.scanAndCollectMinersManual = null; |
| botActions.openCiftciAndSellAllManual = null; |
| botActions.goToIslandManual = null; |
| botActions.sendSkyblockManual = null; |
| botActions.antiBotMove = null; |
| botActions.sendMoneyToNick = null; |
| botActions.scanStorageInfoManual = null; |
| botActions.processOrderManual = null; |
|
|
| safeDestroyAllConnections(); |
|
|
| if (!manualStopRequested && config.utils && config.utils["auto-reconnect"]) { |
| triggerSafeReconnect(); |
| } |
| } catch (err) { |
| logger.error(`['end' Hatası] Bağlantı kopma işlemi sırasında beklenmeyen hata: ${err && err.stack ? err.stack : err}`); |
| } |
| }); |
|
|
| bot.on("kicked", (reason) => logger.warn(`Bot sunucudan atıldı: ${util.inspect(reason)}`)); |
| bot.on("error", (err) => logger.error(`Mineflayer Hatası: ${err}`)); |
|
|
| } catch (err) { |
| logger.error(`[createBot Hatası] Beklenmeyen bir hata oluştu, bağlantı denemesi güvenle iptal edildi: ${err && err.stack ? err.stack : err}`); |
| isConnecting = false; |
| botIsReady = false; |
| baglantiTuru = "Yok"; |
|
|
| if (!manualStopRequested && config.utils && config.utils["auto-reconnect"]) { |
| triggerSafeReconnect(); |
| } |
| } |
| } |
|
|
| 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(); |
| 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] ${formatDurationMs(delay)} sonra tekil bir hat üzerinden sistem sıfırdan başlatılacak...`); |
|
|
| reconnectTimeout = setTimeout(() => { |
| createBot(); |
| }, delay); |
| } |
|
|
| let pendingHardRestartTimeout = null; |
|
|
| function stopBotHard(reason) { |
| try { |
| logger.warn(`[Manuel Kontrol] Bot durduruluyor. Sebep: ${reason || "Elle durdurma istendi"}`); |
| manualStopRequested = true; |
|
|
| if (reconnectTimeout) { |
| clearTimeout(reconnectTimeout); |
| reconnectTimeout = null; |
| } |
| if (pendingHardRestartTimeout) { |
| clearTimeout(pendingHardRestartTimeout); |
| pendingHardRestartTimeout = null; |
| } |
|
|
| stopPeriodicHomeChecks(); |
| lastCheckAtMs = null; |
| safeDestroyAllConnections(); |
|
|
| botIsReady = false; |
| baglantiTuru = "Yok"; |
| botUptimeStart = null; |
| activeConnectionIp = "Bilinmiyor"; |
| activeConnectionLocation = "Bilinmiyor"; |
| nextScheduledCheckTimeStr = "Bot manuel olarak durduruldu."; |
| nextKaziciTimeStr = "Bot manuel olarak durduruldu."; |
| nextCiftciTimeStr = "Bot manuel olarak durduruldu."; |
| } catch (err) { |
| logger.error(`[stopBotHard Hatası] ${err && err.stack ? err.stack : err}`); |
| } |
| } |
|
|
| function startBotHard(reason) { |
| try { |
| logger.info(`[Manuel Kontrol] Bot başlatılıyor. Sebep: ${reason || "Elle başlatma istendi"}`); |
| manualStopRequested = false; |
|
|
| if (bot || isConnecting) { |
| safeDestroyAllConnections(); |
| } |
| stopPeriodicHomeChecks(); |
|
|
| createBot(); |
| } catch (err) { |
| logger.error(`[startBotHard Hatası] ${err && err.stack ? err.stack : err}`); |
| } |
| } |
|
|
| function restartBotHard(reason) { |
| logger.warn(`[Manuel Kontrol] Bot tamamen sıfırdan yeniden başlatılıyor (hard restart). Sebep: ${reason || "Elle restart istendi"}`); |
| stopBotHard(reason); |
|
|
| if (pendingHardRestartTimeout) { |
| clearTimeout(pendingHardRestartTimeout); |
| } |
|
|
| pendingHardRestartTimeout = setTimeout(() => { |
| pendingHardRestartTimeout = null; |
| startBotHard(reason); |
| }, 750); |
| } |
|
|
| 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(); |
|
|
| |
| const express = require("express"); |
| const app = express(); |
| const http = require('http'); |
|
|
| app.get("/logs", (req, res) => { |
| const logs = getRecentLogs(); |
|
|
| res.send({ |
| server_time: new Date().toLocaleString('tr-TR'), |
| range_minutes: 30, |
| count: logs.length, |
| logs: logs.map(l => ({ |
| time: new Date(l.time).toLocaleTimeString('tr-TR'), |
| level: l.level, |
| message: l.message |
| })) |
| }); |
| }); |
|
|
| app.get("/logs/view", (req, res) => { |
| const html = ` |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Sistem Logları</title> |
| <style> |
| body { background-color: #111; color: #eee; font-family: 'Courier New', Courier, monospace; padding: 20px; margin: 0; } |
| .container { max-width: 1100px; margin: 0 auto; background: #1a1a1a; padding: 15px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.5); } |
| h2 { margin-top: 0; border-bottom: 1px solid #333; padding-bottom: 10px; color: #fff; display: flex; justify-content: space-between; align-items: center; } |
| h2 span { font-size: 13px; color: #777; font-weight: normal; } |
| #log-box { height: 600px; overflow-y: auto; background: #000; border: 1px solid #333; padding: 10px; border-radius: 4px; display: flex; flex-direction: column; } |
| .log-line { margin-bottom: 4px; line-height: 1.5; font-size: 13px; white-space: pre-wrap; word-break: break-all; } |
| .time-tag { color: #555; margin-right: 8px; } |
| .lvl-INFO { color: #7fd1ff; } |
| .lvl-WARN { color: #ffcc66; } |
| .lvl-ERROR { color: #ff6b6b; } |
| .lvl-FATAL { color: #ff3333; font-weight: bold; } |
| .lvl-DEBUG, .lvl-TRACE { color: #999; } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h2>Sistem Logları <span id="meta">son 30 dakika</span></h2> |
| <div id="log-box"></div> |
| </div> |
| <script> |
| async function updateLogs() { |
| try { |
| const res = await fetch('/logs'); |
| if (!res.ok) return; |
| const data = await res.json(); |
| const box = document.getElementById('log-box'); |
| const meta = document.getElementById('meta'); |
| meta.textContent = 'son 30 dakika · ' + data.count + ' kayıt · güncelleme: ' + data.server_time; |
| |
| const wasAtBottom = box.scrollHeight - box.scrollTop - box.clientHeight < 30; |
| |
| box.innerHTML = ''; |
| if (data.logs && data.logs.length > 0) { |
| data.logs.forEach(l => { |
| const div = document.createElement('div'); |
| div.className = 'log-line'; |
| const lvl = (l.level || 'info').toUpperCase(); |
| div.innerHTML = '<span class="time-tag">[' + l.time + ']</span><span class="lvl-' + lvl + '">[' + lvl + ']</span> ' + l.message.replace(/</g, '<'); |
| box.appendChild(div); |
| }); |
| if (wasAtBottom) box.scrollTop = box.scrollHeight; |
| } else { |
| box.innerHTML = '<div style="color:#555;">Son 30 dakikada log kaydı yok.</div>'; |
| } |
| } catch (e) {} |
| } |
| setInterval(updateLogs, 3000); |
| updateLogs(); |
| </script> |
| </body> |
| </html> |
| `; |
| res.send(html); |
| }); |
|
|
| app.get("/status", (req, res) => { |
| let uptimeString = "Bağlı Değil"; |
| if (botIsReady && botUptimeStart) { |
| uptimeString = formatDurationMs(Date.now() - botUptimeStart); |
| } |
|
|
| 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, |
| last_kazici_check: lastKaziciCheckTimeStr, |
| next_kazici_check: nextKaziciTimeStr, |
| last_ciftci_sell: lastCiftciSellTimeStr, |
| next_ciftci_sell: nextCiftciTimeStr, |
| last_order: lastOrderTimeStr, |
| gui_lock_busy: guiMutex.isLocked(), |
| manually_stopped: manualStopRequested, |
| is_connecting: isConnecting, |
| failover_mode_active: failoverModeActive |
| }); |
| }); |
|
|
| app.get("/storageInfo", (req, res) => { |
| res.send({ |
| diamond: storageInfoCache.diamond, |
| emerald: storageInfoCache.emerald, |
| iron_block: storageInfoCache.iron_block, |
| lastUpdated: storageInfoCache.lastUpdated |
| }); |
| }); |
|
|
| app.get("/bot/stop", (req, res) => { |
| stopBotHard("Web paneli üzerinden /bot/stop isteği"); |
| res.send({ success: true, message: "Bot durduruldu. Tekrar başlatmak için /bot/start kullan." }); |
| }); |
|
|
| app.get("/bot/start", (req, res) => { |
| if (bot && botIsReady) { |
| return res.send({ success: false, message: "Bot zaten çalışıyor. Sıfırdan başlatmak için /bot/restart kullan." }); |
| } |
| startBotHard("Web paneli üzerinden /bot/start isteği"); |
| res.send({ success: true, message: "Bot başlatılıyor." }); |
| }); |
|
|
| app.get("/bot/restart", (req, res) => { |
| restartBotHard("Web paneli üzerinden /bot/restart isteği"); |
| res.send({ success: true, message: "Bot tamamen sıfırdan yeniden başlatılıyor (soket/SSH dahil)." }); |
| }); |
|
|
| app.get("/bot/kazici-kontrol", (req, res) => { |
| if (!bot || !botIsReady || !botActions.scanAndCollectMinersManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
| res.send({ success: true, message: "Kazıcı kontrolü öncelikli olarak kuyruğa alındı, işleniyor." }); |
| botActions.scanAndCollectMinersManual().catch(e => logger.error(`[/bot/kazici-kontrol Hatası] ${e && e.stack ? e.stack : e}`)); |
| }); |
|
|
| app.get("/bot/ciftci-satis", (req, res) => { |
| if (!bot || !botIsReady || !botActions.openCiftciAndSellAllManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
| res.send({ success: true, message: "Çiftçi satışı öncelikli olarak kuyruğa alındı, işleniyor." }); |
| botActions.openCiftciAndSellAllManual().catch(e => logger.error(`[/bot/ciftci-satis Hatası] ${e && e.stack ? e.stack : e}`)); |
| }); |
|
|
| app.get("/bot/checkFarmer", (req, res) => { |
| if (!bot || !botIsReady || !botActions.scanStorageInfoManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
| res.send({ success: true, message: "Çiftçi depo/storage kontrolü öncelikli olarak kuyruğa alındı, işleniyor." }); |
| botActions.scanStorageInfoManual().catch(e => logger.error(`[/bot/checkFarmer Hatası] ${e && e.stack ? e.stack : e}`)); |
| }); |
|
|
| app.get("/bot/besleyici-kontrol", (req, res) => { |
| if (!bot || !botIsReady || !botActions.scanAndFeedManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
| res.send({ success: true, message: "Besleyici kontrolü öncelikli olarak kuyruğa alındı, işleniyor." }); |
| botActions.scanAndFeedManual().catch(e => logger.error(`[/bot/besleyici-kontrol Hatası] ${e && e.stack ? e.stack : e}`)); |
| }); |
|
|
| app.get("/bot/skyblock", (req, res) => { |
| if (!bot || !botIsReady || !botActions.sendSkyblockManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
| botActions.sendSkyblockManual().catch(e => logger.error(`[/bot/skyblock Hatası] ${e && e.stack ? e.stack : e}`)); |
| res.send({ success: true, message: "/skyblock komutu gönderildi." }); |
| }); |
|
|
| app.get("/bot/is-go", (req, res) => { |
| if (!bot || !botIsReady || !botActions.goToIslandManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
| botActions.goToIslandManual().catch(e => logger.error(`[/bot/is-go Hatası] ${e && e.stack ? e.stack : e}`)); |
| res.send({ success: true, message: `${GO_ISLAND_COMMAND} komutu gönderildi.` }); |
| }); |
|
|
| app.get("/bot/sendmsg", (req, res) => { |
| if (!bot || !botIsReady) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
|
|
| const message = req.query.message; |
| if (!message || typeof message !== "string" || message.trim().length === 0) { |
| return res.status(400).send({ success: false, message: "message query parametresi zorunlu." }); |
| } |
|
|
| try { |
| bot.chat(message); |
| logger.info(`[${bot.username}] /sendmsg üzerinden chat mesajı gönderildi: ${message}`); |
| res.send({ success: true, message: "Mesaj gönderildi.", sent: message }); |
| } catch (err) { |
| logger.error(`[/bot/sendmsg Hatası] ${err && err.message ? err.message : err}`); |
| res.status(500).send({ success: false, message: "Mesaj gönderilirken hata oluştu." }); |
| } |
| }); |
|
|
| app.get("/bot/antibotmove", (req, res) => { |
| if (!bot || !botIsReady) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
|
|
| try { |
| botActions.antiBotMove().catch(e => logger.error(`[/bot/antibotmove Hatası] ${e && e.stack ? e.stack : e}`)); |
| res.send({ success: true, message: "Anti Bot Yürüyüşü Yapıldı."}); |
| } catch (err) { |
| logger.error(`[/bot/antibotmove Hatası] ${err && err.message ? err.message : err}`); |
| res.status(500).send({ success: false, message: "Anti Bot Move sırasında hata oluştu." }); |
| } |
| }); |
|
|
| app.get("/bot/sendmoney", (req, res) => { |
| if (!bot || !botIsReady || !botActions.sendMoneyToNick) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
|
|
| const nick = req.query.nick; |
| if (!nick || typeof nick !== "string" || nick.trim().length === 0) { |
| return res.status(400).send({ success: false, message: "nick query parametresi zorunlu." }); |
| } |
|
|
| botActions.sendMoneyToNick(nick) |
| .then((result) => { |
| if (result.success) { |
| res.send(result); |
| } else { |
| res.status(500).send(result); |
| } |
| }) |
| .catch((err) => { |
| logger.error(`[/bot/sendmoney Hatası] ${err && err.stack ? err.stack : err}`); |
| res.status(500).send({ success: false, message: "Beklenmeyen bir hata oluştu." }); |
| }); |
| }); |
|
|
| app.get("/bot/order", (req, res) => { |
| if (!bot || !botIsReady || !botActions.processOrderManual) { |
| return res.status(503).send({ success: false, message: "Bot hazır değil." }); |
| } |
|
|
| const item = req.query.item; |
| const nick = req.query.nick; |
|
|
| if (!item || typeof item !== "string" || !ORDER_ALLOWED_ITEMS.includes(item)) { |
| return res.status(400).send({ success: false, message: `item query parametresi zorunlu ve şunlardan biri olmalı: ${ORDER_ALLOWED_ITEMS.join(", ")}` }); |
| } |
| if (!nick || typeof nick !== "string" || nick.trim().length === 0) { |
| return res.status(400).send({ success: false, message: "nick query parametresi zorunlu." }); |
| } |
|
|
| res.send({ success: true, message: "Sipariş işlemi öncelikli olarak kuyruğa alındı, işleniyor." }); |
| botActions.processOrderManual(item, nick) |
| .catch(e => logger.error(`[/bot/order Hatası] ${e && e.stack ? e.stack : e}`)); |
| }); |
|
|
| 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 = ` |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Canlı Minecraft Sohbeti</title> |
| <style> |
| body { background-color: #111; color: #eee; font-family: 'Courier New', Courier, monospace; padding: 20px; margin: 0; } |
| .container { max-width: 900px; margin: 0 auto; background: #1a1a1a; padding: 15px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.5); } |
| h2 { margin-top: 0; border-bottom: 1px solid #333; padding-bottom: 10px; color: #fff; } |
| #chat-box { height: 500px; overflow-y: auto; background: #000; border: 1px solid #333; padding: 10px; border-radius: 4px; display: flex; flex-direction: column; } |
| .msg-line { margin-bottom: 6px; line-height: 1.4; font-size: 14px; word-break: break-all; } |
| .time-tag { color: #555; margin-right: 8px; font-size: 12px; } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h2>Canlı Oyun Sohbeti</h2> |
| <div id="chat-box"></div> |
| </div> |
| <script> |
| async function updateChat() { |
| try { |
| const res = await fetch('/messages'); |
| if (!res.ok) return; |
| const data = await res.json(); |
| const chatBox = document.getElementById('chat-box'); |
| chatBox.innerHTML = ''; |
| if (data.logs && data.logs.length > 0) { |
| data.logs.forEach(msg => { |
| const div = document.createElement('div'); |
| div.className = 'msg-line'; |
| div.innerHTML = '<span class="time-tag">[' + msg.time + ']</span>' + msg.text; |
| chatBox.appendChild(div); |
| }); |
| chatBox.scrollTop = chatBox.scrollHeight; |
| } else { |
| chatBox.innerHTML = '<div style="color:#555;">Henüz yeni mesaj yok veya havuz temizlendi...</div>'; |
| } |
| } catch(e) {} |
| } |
| setInterval(updateChat, 2000); |
| updateChat(); |
| </script> |
| </body> |
| </html> |
| `; |
| res.send(html); |
| }); |
|
|
| const checkAndStartViewer = (req, res, next) => { |
| if (viewerTimeout) { |
| clearTimeout(viewerTimeout); |
| viewerTimeout = null; |
| } |
|
|
| const isBotReady = bot && botIsReady; |
|
|
| if (!viewerInstance && isBotReady) { |
| try { |
| mineflayerViewer(bot, { port: 3000, firstPerson: true }); |
| viewerInstance = bot.viewer; |
| logger.info("[Viewer] 3D Harita motoru arka planda (3000) başlatıldı."); |
| setTimeout(() => next(), 600); |
| return; |
| } catch (err) { |
| logger.error("Görüntüleyici başlatılamadı: " + err); |
| } |
| } |
|
|
| if (!isBotReady && !viewerInstance) { |
| return res.status(503).send("Bot henüz sunucuya giriş yapmadığı için ekran görüntülenemiyor."); |
| } |
|
|
| next(); |
| }; |
|
|
| app.use('/3dview', checkAndStartViewer, (req, res) => { |
| if (req.originalUrl === '/3dview' && !req.originalUrl.endsWith('/')) { |
| return res.redirect(301, '/3dview/'); |
| } |
|
|
| const targetPath = req.url; |
| const proxyReq = http.request({ |
| host: '127.0.0.1', |
| port: 3000, |
| path: targetPath, |
| method: req.method, |
| headers: req.headers |
| }, (proxyRes) => { |
| res.writeHead(proxyRes.statusCode, proxyRes.headers); |
| proxyRes.pipe(res, { end: true }); |
| }); |
|
|
| proxyReq.on('error', (err) => { |
| logger.error("[Asset Hatası] Veri 3000 portundan çekilemedi: " + err.message); |
| if (!res.headersSent) res.status(502).send("Harita yükleniyor, lütfen sayfayı yenileyin."); |
| }); |
|
|
| req.pipe(proxyReq, { end: true }); |
| }); |
|
|
| app.get("/restart", (req, res) => { |
| logger.info("[Sistem] Web paneli üzerinden tam restart isteği geldi. Sistem felç ediliyor..."); |
|
|
| if (config.utils) { |
| config.utils["auto-reconnect"] = false; |
| } |
| isDestroying = true; |
|
|
| if (bot && botIsReady) { |
| try { bot.chat("Bot web paneli uzerinden tum kodlariyla yeniden baslatiliyor..."); } catch(e){} |
| res.send({ success: true, message: "Tüm proje kapatıldı. Konteyner birkaç saniye içinde sıfırdan başlayacak." }); |
|
|
| setTimeout(() => { |
| try { bot.quit(); } catch(e){} |
| safeDestroyAllConnections(); |
| logger.info("[Sistem] process.exit(0) çağrılıyor."); |
| process.exit(0); |
| }, 1000); |
| } else { |
| res.send({ success: true, message: "Bot bağlı değildi ancak konteyner kökten kapatılıyor." }); |
| setTimeout(() => { |
| process.exit(0); |
| }, 1000); |
| } |
| }); |
|
|
| const server = app.listen(process.env.PORT || 7860, () => { |
| logger.info("Ana Express sunucusu 7860 portunda aktif."); |
| }); |
|
|
| server.on('upgrade', (req, socket, head) => { |
| if (req.url.includes('socket.io') || req.url.startsWith('/3dview')) { |
| if (viewerTimeout) { |
| clearTimeout(viewerTimeout); |
| viewerTimeout = null; |
| } |
|
|
| let targetWsPath = req.url; |
| if (targetWsPath.startsWith('/3dview')) { |
| targetWsPath = targetWsPath.replace('/3dview', ''); |
| } |
| if (!targetWsPath.startsWith('/')) { |
| targetWsPath = '/' + targetWsPath; |
| } |
|
|
| const proxyReq = http.request({ |
| host: '127.0.0.1', |
| port: 3000, |
| path: targetWsPath, |
| method: req.method, |
| headers: req.headers |
| }); |
|
|
| proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => { |
| socket.write('HTTP/1.1 101 Switching Protocols\r\n' + |
| Object.keys(proxyRes.headers).map(key => `${key}: ${proxyRes.headers[key]}`).join('\r\n') + |
| '\r\n\r\n'); |
|
|
| proxySocket.pipe(socket); |
| socket.pipe(proxySocket); |
|
|
| socket.on('close', () => { |
| if (viewerTimeout) clearTimeout(viewerTimeout); |
| viewerTimeout = setTimeout(() => { |
| if (viewerInstance && typeof viewerInstance.close === 'function') { |
| try { |
| if (viewerInstance.ws && typeof viewerInstance.ws.close === 'function') { |
| viewerInstance.ws.close(); |
| } |
| viewerInstance.close(); |
| if (bot) bot.viewer = null; |
| viewerInstance = null; |
| logger.info("[RAM Tasarrufu] 10 saniyedir kimse izlemiyor. Harita kapatıldı, RAM serbest!"); |
| } catch (e) { |
| logger.error("Viewer kapatılırken hata: " + e.message); |
| } |
| } |
| }, 10000); |
| }); |
| }); |
|
|
| proxyReq.on('error', (err) => { |
| logger.error("[WS Tünel Hatası] Websocket aktarılamadı: " + err.message); |
| }); |
|
|
| proxyReq.end(); |
| } |
| }); |
|
|
| const originalStderrWrite = process.stderr.write; |
| process.stderr.write = function (chunk, encoding, callback) { |
| const message = chunk.toString(); |
| if (message.includes("Ignoring block entities as chunk failed to load")) { |
| if (typeof callback === 'function') callback(); |
| return true; |
| } |
| return originalStderrWrite.apply(process.stderr, arguments); |
| }; |