Hamus / bot.js
Suvarii's picture
Update bot.js
afa2b33 verified
Raw
History Blame Contribute Delete
119 kB
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');
// --- KÜRESEL GÜVENLİK AĞI ---
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}`);
});
// --- AYARLAR ---
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;
// --- ÇİFTÇİ (HEPSİNİ SAT) MENÜSÜ İÇİN AYARLAR ---
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"];
// --- SİPARİŞ (ORDER) SİSTEMİ AYARLARI ---
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ᴍ ᴇᴛᴍᴇ";
// Nick seçildikten sonra Teslim Etme penceresinin gerçekten açılmasını beklemek için
// kullanılan zaman aşımı (sabit sleep yerine).
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) {
// ok
} 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();
}
};
// =====================================================================
// SİPARİŞ (ORDER) SİSTEMİ
// =====================================================================
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;
// ÖNEMLİ: hem item tipi hem de nick eşleşmeli.
// itemInternalName verilmişse (örn. "diamond"), slotun gerçek
// Minecraft item id'si ile karşılaştırıyoruz (dilden bağımsız, kesin).
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." };
}
// --- 1) Çiftçi menüsünü aç, jeneratör materyalleri filtresine geç ---
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ı." };
}
// --- 2) İstenen item'ın slotuna shift+sağ tık (envanter dolana kadar verir) ---
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); // mouseButton=1 (sağ), mode=1 (shift)
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}`);
}
// --- YENİ: Çiftçiden gerçekten item alınmış mı doğrula ---
// clickWindow hata fırlatmasa bile sunucu isteği görmezden gelmiş olabilir.
// Envanterde ilgili item yoksa devam etmenin anlamı yok (Teslim Etme'ye zaten
// aktarılacak bir şey olmaz), bu yüzden burada erkenden kontrol edip
// anlamlı bir hata dönüyoruz.
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.` };
}
// --- 3) Menüyü kapat ---
try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
// ÖNEMLİ: purpose burada "sell"e ÇEVRİLMEZ! /order akışı (Siparişler +
// Teslim Etme pencereleri) henüz açılmadı. purpose "order" kaldığı sürece
// windowOpen handler bu pencereleri otomatik kapatmayacak.
await new Promise(r => setTimeout(r, 300));
// --- 4) antiBotMove -> /is go -> 5sn -> /is go ---
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));
// --- 5) /order <nick> gönder ---
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ı." };
}
// --- 6) +500ms bekle, güncel pencereyi oku (windowOpen anındaki değil) ---
await new Promise(r => setTimeout(r, ORDER_WINDOW_SETTLE_DELAY_MS));
if (!bot.currentWindow) {
ciftciWindowPurpose = "sell";
return { success: false, message: "Pencere beklenmedik şekilde kapandı." };
}
// --- 7) Lore'da nick geçen slotu bul, sol tık ile seç ---
const nickVariants = normalizeVariants(nick);
let targetSlotIndex = -1;
// 1. ÖNCELİK: hem item tipi hem nick birlikte eşleşsin
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...`);
// --- YENİ: nick'e tıkladıktan sonra sabit 500ms yerine GERÇEK yeni
// pencere açılışını (Teslim Etme) bekliyoruz. Bu, mevcut buğun ana
// sebebiydi: sunucu 500ms'den geç yanıt verirse eski (Siparişler)
// penceresine göre hesaplanmış slotlara tıklanıyor ve hiçbir şey
// taşınmıyordu (sunucu paketi reddetmediği için hata da görünmüyordu).
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}`);
}
// --- 8) Teslim Etme penceresinin gerçekten açılmasını bekle ---
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ı)." };
}
// Pencere objesinin gerçekten DEĞİŞTİĞİNİ doğrula (bazı sunucular aynı id'yi
// update edip windowOpen yerine sadece slot update gönderebilir; bu durumda
// en azından title kontrolüyle devam ediyoruz).
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}"`);
}
// Ek güvenlik: envanterde gerçekten aktarılacak item var mı, tekrar kontrol et
// (nick seçme / /is go arasında item'lar herhangi bir sebeple kaybolmuş olabilir).
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++;
// Pencere döngü sırasında değişti/kapandıysa (ör. sunucu Teslim Etme'yi
// kapattıysa) devam etmenin bir anlamı yok, güvenle çık.
if (!bot.currentWindow || bot.currentWindow !== windowAtTransferStart) {
logger.warn("[Sipariş] Aktarım sırasında pencere değişti/kapandı, döngü durduruluyor.");
break;
}
// ÖNEMLİ FIX: bot.inventory.items() custom pencere açıkken YANLIŞ slot
// numarası döndürebiliyor (inventoryStart offsetini hesaba katmıyor).
// Bunun yerine doğrudan currentWindow.slots üzerinden, gerçek (offsetli)
// slot index'ini buluyoruz.
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); // mouseButton=0 (sol), mode=1 (shift)
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.`);
}
// --- 9) Pencereyi önce kapat, sonra sayım yap (pencere açıkken bot.inventory.items()
// yanlış slot offseti hesaplayabildiği için, güvenilir sayım için kapanmasını bekliyoruz) ---
try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
ciftciWindowPurpose = "sell"; // akış tamamen bitti, artık normal davranışa dön
await new Promise(r => setTimeout(r, 300)); // pencere kapanışının client tarafında senkronize olmasını bekle
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") {
// Storage tarama / sipariş modu: burada otomatik tıklama/kapatma yapma,
// ilgili core fonksiyon kendi akışını yönetiyor. Sadece resolver'ı tetikle.
} 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();
// --- EXPRESS SUNUCUSU VE PANEL ALTYAPISI ---
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, '&lt;');
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);
};