Suvarii commited on
Commit
deaaee5
·
verified ·
1 Parent(s): 466652a

Update bot.js

Browse files
Files changed (1) hide show
  1. bot.js +507 -75
bot.js CHANGED
@@ -56,6 +56,23 @@ const KAZICI_CYCLE_INTERVAL_MS = 5 * 60 * 1000; // 5 dakika
56
  // --- ÇİFTÇİ PERİYODİK DÖNGÜ AYARLARI (bağımsız, 1 dakikada bir) ---
57
  const CIFTCI_CYCLE_INTERVAL_MS = 60 * 1000; // 1 dakika
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  let viewerInstance = null;
60
  let viewerTimeout = null;
61
  let bot = null;
@@ -98,6 +115,13 @@ let kaziciIntervalHandle = null; // Kazıcı bağımsız döngü zamanlayıcı
98
  let ciftciIntervalHandle = null; // Çiftçi bağımsız döngü zamanlayıcısı (1 dk)
99
  let isMainCycleRunning = false; // Besleyici (goToIsland->besle) ana döngüsü sürüyor mu
100
 
 
 
 
 
 
 
 
101
  // --- DİNAMİK IP VE KONUM BİLGİLERİ ---
102
  let activeConnectionIp = "Bilinmiyor";
103
  let activeConnectionLocation = "Bilinmiyor";
@@ -114,20 +138,10 @@ function setSafeTimeout(fn, delay) {
114
  }
115
 
116
  // --- ÖNCELİKLİ GUI KİLİDİ (MUTEX) ---
117
- // Besleyici, kazıcı ve çiftçi menüleri hepsi aynı windowOpen/tıklama mekanizmasını
118
- // paylaştığı için ASLA aynı anda çalışmamalılar. Bu kilit ikisi arasında ayrım yapar:
119
- // - tryAcquire(): periyodik/otomatik döngüler için. Kilit doluysa BEKLEMEZ, hemen
120
- // false döner ve o tur sessizce atlanır (bir sonraki periyotta tekrar dener).
121
- // - acquire(): Express panelinden (manuel) gelen istekler için. Kilit doluysa
122
- // KUYRUĞA GİRER ve önceliklidir — mevcut işlem bitince, kuyrukta bekleyen varsa
123
- // bile (ki otomatik döngüler hiç kuyruğa girmediği için pratikte tek bekleyen
124
- // genelde manuel istek olur) ilk manuel istek işi devralır. Böylece kullanıcı
125
- // panelden bir istek yaptığında, o sırada süren otomatik bir işlem varsa önce
126
- // o tamamlanır, hemen ardından kullanıcının isteği en öncelikli şekilde işlenir.
127
  class PriorityGuiMutex {
128
  constructor() {
129
  this._locked = false;
130
- this._waiters = []; // manuel (öncelikli) bekleyenler, FIFO
131
  }
132
  isLocked() { return this._locked; }
133
  tryAcquire() {
@@ -147,7 +161,6 @@ class PriorityGuiMutex {
147
  release() {
148
  if (this._waiters.length > 0) {
149
  const next = this._waiters.shift();
150
- // Kilit devrediliyor (locked=true kalır), bir sonraki bekleyen devralır.
151
  next();
152
  } else {
153
  this._locked = false;
@@ -156,22 +169,16 @@ class PriorityGuiMutex {
156
  }
157
  const guiMutex = new PriorityGuiMutex();
158
 
159
- // Express panelinden diğer bot fonksiyonlarına erişim için (createBot() closure'ı
160
- // dışından çağrılabilmeleri için köprü). Bot her (yeniden) bağlandığında doldurulur.
161
  let botActions = {
162
  scanAndFeedManual: null,
163
  scanAndCollectMinersManual: null,
164
  openCiftciAndSellAllManual: null,
165
  goToIslandManual: null,
166
  sendSkyblockManual: null,
 
 
167
  };
168
 
169
- // --- ORTAK SÜRE FORMATLAMA FONKSİYONU ---
170
- // Tüm loglardaki "X dk sonra", "Y saat sonra" gibi elle yazılmış ifadelerin yerine
171
- // bu fonksiyon kullanılır. Böylece yukarıdaki ayar sabitleri (KAZICI_CYCLE_INTERVAL_MS,
172
- // CIFTCI_CYCLE_INTERVAL_MS, DEFAULT_CHECK_INTERVAL_MS, vb.) değiştirildiğinde,
173
- // loglara yansıyan süre ifadeleri OTOMATİK olarak güncellenir; elle metin
174
- // değiştirmeye gerek kalmaz.
175
  function formatDurationMs(ms) {
176
  if (ms == null || isNaN(ms) || ms < 0) return "0 saniye";
177
  let totalSeconds = Math.round(ms / 1000);
@@ -267,6 +274,295 @@ https.get('https://api.ipify.org?format=json', (res) => {
267
  });
268
  }).on('error', () => {});
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  async function createBot() {
271
  if (isConnecting) {
272
  logger.warn("[Sistem] Zaten aktif bir bağlantı süreci yürütülüyor. Mükerrer istek engellendi.");
@@ -765,7 +1061,119 @@ async function createBot() {
765
  }
766
  }
767
 
768
- // --- DİNAMİK PLANLAMA MOTORU (SADECE BESLEYİCİ İÇİN) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
  function scheduleNextCheck(delayMs) {
770
  if (periodicTimeoutHandle) {
771
  clearTimeout(periodicTimeoutHandle);
@@ -782,9 +1190,6 @@ async function createBot() {
782
  await goToIsland();
783
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
784
  await scanAndFeed();
785
- // NOT: Kazıcı ve çiftçi toplama artık BURADA çağrılmıyor. Her ikisi de
786
- // besleyiciden tamamen bağımsız kendi zamanlayıcılarında çalışır
787
- // (bkz. startKaziciPeriodic / startCiftciPeriodic).
788
  } catch (err) {
789
  logger.error(`[Planlayıcı Hatası] Periyodik döngüde beklenmeyen hata: ${err && err.stack ? err.stack : err}`);
790
  try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {}
@@ -806,7 +1211,6 @@ function activateEntityAtNoLook(entity, position) {
806
  });
807
  }
808
 
809
- // --- BESLEYİCİ (scanAndFeedCore = asıl mantık, kilit içermez) ---
810
  async function scanAndFeedCore() {
811
  if (!bot || !botIsReady) return;
812
  if (!guiMutex.isLocked()) {
@@ -919,15 +1323,9 @@ function activateEntityAtNoLook(entity, position) {
919
  });
920
  }
921
 
922
- // Kilit meşgulken (kazıcı/çiftçi turu sürerken) besleyicinin bu kısa süreliğine
923
- // beklemesi için yeniden deneme aralığı. Gerçek besleme zamanlamasını bozmaz,
924
- // sadece kilidin boşalmasını birkaç kez kontrol eder.
925
  const FEED_LOCK_RETRY_DELAY_MS = 15 * 1000;
926
- const FEED_LOCK_MAX_RETRIES = 8; // 8 x 15sn = 2 dk boyunca dener
927
 
928
- // Periyodik (otomatik) kullanım: kilit doluysa hemen pes etmez, kısa aralıklarla
929
- // yeniden dener (kazıcı/çiftçi turu birkaç saniye içinde biter). Sonuçta kilidi
930
- // hâlâ alamazsa, döngünün tamamen ölmemesi için normal aralıkla tekrar planlar.
931
  async function scanAndFeed(retryCount) {
932
  retryCount = retryCount || 0;
933
  if (!guiMutex.tryAcquire()) {
@@ -950,8 +1348,6 @@ function activateEntityAtNoLook(entity, position) {
950
  }
951
  }
952
 
953
- // Manuel (Express panelinden) kullanım: kilit doluysa ÖNCELİKLİ olarak kuyruğa
954
- // girer, mevcut işlem bitince hemen devralır.
955
  async function scanAndFeedManual() {
956
  logger.info("[Besleyici - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
957
  await guiMutex.acquire();
@@ -965,7 +1361,6 @@ function activateEntityAtNoLook(entity, position) {
965
  }
966
  }
967
 
968
- // --- KAZICI MİNYON DEPO AKTARIMI ---
969
  async function activateEntityAndAwaitWindow(entity, labelForLogs) {
970
  if (!bot || !botIsReady || !entity || !entity.position) {
971
  return { success: false, reason: "Bot veya entity hazır değil" };
@@ -1063,8 +1458,6 @@ function activateEntityAtNoLook(entity, position) {
1063
  return matches;
1064
  }
1065
 
1066
- // scanAndCollectMinersCore: asıl mantık, kilit İÇERMEZ (dışarıdan kilitlenmiş
1067
- // olarak çağrılması beklenir).
1068
  async function scanAndCollectMinersCore() {
1069
  if (!bot || !botIsReady) return;
1070
  if (!guiMutex.isLocked()) {
@@ -1115,7 +1508,6 @@ function activateEntityAtNoLook(entity, position) {
1115
  logger.info(`[${currentBotUsername}] Kazıcı minyon depo aktarım turu tamamlandı.`);
1116
  }
1117
 
1118
- // Tekil/periyodik kullanım (kendi başına çağrılırsa kilidi kendisi alır)
1119
  async function scanAndCollectMiners() {
1120
  if (!guiMutex.tryAcquire()) {
1121
  logger.warn("[Kazıcı Toplama] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor.");
@@ -1130,7 +1522,6 @@ function activateEntityAtNoLook(entity, position) {
1130
  }
1131
  }
1132
 
1133
- // Manuel (Express panelinden) kullanım: öncelikli kuyruk.
1134
  async function scanAndCollectMinersManual() {
1135
  logger.info("[Kazıcı Toplama - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1136
  await guiMutex.acquire();
@@ -1145,11 +1536,6 @@ function activateEntityAtNoLook(entity, position) {
1145
  }
1146
  }
1147
 
1148
- // --- ÇİFTÇİ (HEPSİNİ SAT) MENÜSÜ ---
1149
- // openCiftciAndSellAllCore: asıl mantık, kilit İÇERMEZ (dışarıdan kilitli çağrılır).
1150
- // NOT: Asıl "hepsini sat" tıklaması artık burada DEĞİL, windowOpen olayında,
1151
- // pencere açıldıktan CIFTCI_CLICK_DELAY_AFTER_OPEN_MS kadar bekledikten sonra
1152
- // ve CIFTCI_CLICK_REPEAT_COUNT kez tekrar tıklanarak yapılıyor (bkz. windowOpen).
1153
  async function openCiftciAndSellAllCore() {
1154
  if (!bot || !botIsReady) return;
1155
  if (!guiMutex.isLocked()) {
@@ -1165,10 +1551,6 @@ function activateEntityAtNoLook(entity, position) {
1165
  await new Promise(r => setTimeout(r, 300));
1166
  }
1167
 
1168
- // Komut yalnızca BİR KEZ gönderilir. Eskiden birkaç saniyede bir tekrar
1169
- // gönderiliyordu; bu, sunucunun aynı komutu art arda işleyip menüyü birden
1170
- // fazla kez açmasına (ve dolayısıyla tıklama/satış tutarsızlıklarına) yol
1171
- // açabiliyordu. Şimdi sadece pencerenin açılmasını timeout süresince bekliyoruz.
1172
  let timedOut = false;
1173
 
1174
  await new Promise((resolve) => {
@@ -1207,7 +1589,6 @@ function activateEntityAtNoLook(entity, position) {
1207
  }
1208
  }
1209
 
1210
- // Tekil/periyodik kullanım.
1211
  async function openCiftciAndSellAll() {
1212
  if (!guiMutex.tryAcquire()) {
1213
  logger.warn("[Çiftçi] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor.");
@@ -1222,7 +1603,6 @@ function activateEntityAtNoLook(entity, position) {
1222
  }
1223
  }
1224
 
1225
- // Manuel (Express panelinden) kullanım: öncelikli kuyruk.
1226
  async function openCiftciAndSellAllManual() {
1227
  logger.info("[Çiftçi - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1228
  await guiMutex.acquire();
@@ -1237,11 +1617,8 @@ function activateEntityAtNoLook(entity, position) {
1237
  }
1238
  }
1239
 
1240
- // --- KAZICI BAĞIMSIZ PERİYODİK DÖNGÜ (5 dakikada bir) ---
1241
- // Besleyici ve çiftçi durumundan hiçbir şekilde etkilenmez. Sadece paylaşılan
1242
- // guiMutex ile çakışmayı önler; kilit doluysa bu turu sessizce atlar.
1243
  function startKaziciPeriodic() {
1244
- if (kaziciIntervalHandle) return; // zaten çalışıyor
1245
 
1246
  const runCycle = async () => {
1247
  if (!bot || !botIsReady) return;
@@ -1272,9 +1649,8 @@ function activateEntityAtNoLook(entity, position) {
1272
  logger.info(`[Kazıcı Periyodik] Bağımsız döngü başlatıldı (her ${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)}).`);
1273
  }
1274
 
1275
- // --- ÇİFTÇİ BAĞIMSIZ PERİYODİK DÖNGÜ (1 dakikada bir) ---
1276
  function startCiftciPeriodic() {
1277
- if (ciftciIntervalHandle) return; // zaten çalışıyor
1278
 
1279
  const runCycle = async () => {
1280
  if (!bot || !botIsReady) return;
@@ -1314,17 +1690,17 @@ function activateEntityAtNoLook(entity, position) {
1314
  botIsReady = true;
1315
  botUptimeStart = Date.now();
1316
 
1317
- // Express panelinin bu bot örneğine erişebilmesi için köprüyü doldur.
1318
  botActions.scanAndFeedManual = scanAndFeedManual;
1319
  botActions.scanAndCollectMinersManual = scanAndCollectMinersManual;
1320
  botActions.openCiftciAndSellAllManual = openCiftciAndSellAllManual;
1321
  botActions.goToIslandManual = goToIsland;
1322
  botActions.sendSkyblockManual = sendSkyblockCommand;
 
 
1323
 
1324
- // Kazıcı ve Çiftçi bağımsız döngülerini başlat (birbirlerinden ve besleyiciden
1325
- // bağımsız zamanlayıcılar; sadece guiMutex ile çakışma önlenir).
1326
  startKaziciPeriodic();
1327
  startCiftciPeriodic();
 
1328
 
1329
  if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) {
1330
  const password = config.utils["auto-auth"].password;
@@ -1351,7 +1727,6 @@ function activateEntityAtNoLook(entity, position) {
1351
  }
1352
  }, 7500);
1353
 
1354
- // İlk çalıştırma: Girişten 12.5 saniye sonra ilk besleyici kontrolünü yap
1355
  setSafeTimeout(async () => {
1356
  if (!bot || !botIsReady) return;
1357
  isMainCycleRunning = true;
@@ -1359,8 +1734,6 @@ function activateEntityAtNoLook(entity, position) {
1359
  await goToIsland();
1360
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
1361
  await scanAndFeed();
1362
- // NOT: Kazıcı ve çiftçi toplama burada da çağrılmıyor; kendi bağımsız
1363
- // döngüleri zaten spawn olur olmaz başladı.
1364
  } catch (err) {
1365
  logger.error(`[Spawn Hatası] İlk kontrol döngüsünde beklenmeyen hata: ${err && err.stack ? err.stack : err}`);
1366
  try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {}
@@ -1374,6 +1747,14 @@ function activateEntityAtNoLook(entity, position) {
1374
  }
1375
  });
1376
 
 
 
 
 
 
 
 
 
1377
  bot.on("windowOpen", async (window) => {
1378
  if (!bot) return;
1379
  try {
@@ -1403,11 +1784,6 @@ function activateEntityAtNoLook(entity, position) {
1403
  try { bot.closeWindow(window); } catch(e){}
1404
 
1405
  } else if (isCiftciWindow) {
1406
- // Pencere açılır açılmaz hemen tıklamak yerine, sunucunun menü içeriğini
1407
- // (lore/slot verisini) tam yerleştirmesi için önce kısa bir süre beklenir,
1408
- // ardından "hepsini sat" slotuna birkaç kez art arda tıklanır. Bu, bazen
1409
- // satışın hiç gerçekleşmemesi sorununu (erken tıklama nedeniyle sunucunun
1410
- // isteği görmezden gelmesi) engellemek içindir.
1411
  try {
1412
  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...`);
1413
  await new Promise(r => setTimeout(r, CIFTCI_CLICK_DELAY_AFTER_OPEN_MS));
@@ -1492,6 +1868,7 @@ function activateEntityAtNoLook(entity, position) {
1492
  try {
1493
  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...");
1494
  stopPeriodicHomeChecks();
 
1495
  lastCheckAtMs = null;
1496
  botIsReady = false;
1497
  baglantiTuru = "Yok";
@@ -1508,6 +1885,8 @@ function activateEntityAtNoLook(entity, position) {
1508
  botActions.openCiftciAndSellAllManual = null;
1509
  botActions.goToIslandManual = null;
1510
  botActions.sendSkyblockManual = null;
 
 
1511
 
1512
  safeDestroyAllConnections();
1513
 
@@ -1778,7 +2157,8 @@ app.get("/status", (req, res) => {
1778
  next_ciftci_sell: nextCiftciTimeStr,
1779
  gui_lock_busy: guiMutex.isLocked(),
1780
  manually_stopped: manualStopRequested,
1781
- is_connecting: isConnecting
 
1782
  });
1783
  });
1784
 
@@ -1801,12 +2181,6 @@ app.get("/bot/restart", (req, res) => {
1801
  res.send({ success: true, message: "Bot tamamen sıfırdan yeniden başlatılıyor (soket/SSH dahil)." });
1802
  });
1803
 
1804
- // --- YENİ: MANUEL GUI/AKSİYON TETİKLEME ENDPOINT'LERİ ---
1805
- // Bu endpoint'ler istek geldiği anda hemen yanıt döner (fire-and-forget), asıl
1806
- // işlem arka planda ÖNCELİKLİ olarak (guiMutex.acquire ile kuyruğa öncelikli
1807
- // girerek) yürütülür. O sırada otomatik bir döngü (besleyici, kazıcı veya çiftçi)
1808
- // zaten çalışıyorsa, önce o tamamlanır, HEMEN ARDINDAN bu istek işlenir.
1809
-
1810
  app.get("/bot/kazici-kontrol", (req, res) => {
1811
  if (!bot || !botIsReady || !botActions.scanAndCollectMinersManual) {
1812
  return res.status(503).send({ success: false, message: "Bot hazır değil." });
@@ -1847,6 +2221,64 @@ app.get("/bot/is-go", (req, res) => {
1847
  res.send({ success: true, message: `${GO_ISLAND_COMMAND} komutu gönderildi.` });
1848
  });
1849
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1850
  app.get("/", (req, res) => res.send("Bot Aktif"));
1851
 
1852
  app.get("/messages", (req, res) => {
@@ -2082,4 +2514,4 @@ process.stderr.write = function (chunk, encoding, callback) {
2082
  return true;
2083
  }
2084
  return originalStderrWrite.apply(process.stderr, arguments);
2085
- };
 
56
  // --- ÇİFTÇİ PERİYODİK DÖNGÜ AYARLARI (bağımsız, 1 dakikada bir) ---
57
  const CIFTCI_CYCLE_INTERVAL_MS = 60 * 1000; // 1 dakika
58
 
59
+ // --- KAZICI FAILOVER (YEDEK HESAP) AYARLARI ---
60
+ const STALL_CHECK_INTERVAL_MS = 15 * 1000; // pose kontrolü ne sıklıkla yapılsın
61
+ const STALL_TIMEOUT_MS = 60 * 1000; // bu süre hareketsiz kalırsa "durdu" say
62
+ const FAILOVER_HOME_ARRIVAL_DELAY_MS = 4000; // yedek hesapta /is go sonrası varış beklemesi
63
+ const FAILOVER_LOGIN_DELAY_MS = 2500;
64
+ const FAILOVER_SKYBLOCK_DELAY_MS = 7500;
65
+ const FAILOVER_ISGO_DELAY_MS = 12500;
66
+ const FAILOVER_RECHECK_INTERVAL_MS = 20 * 1000; // yedek hesapta minyonları tekrar kontrol aralığı
67
+ const FAILOVER_MAX_WAIT_MS = 60 * 60 * 1000; // güvenlik: 1 saatte hâlâ aktifleşmediyse pes et, ana hesaba dön
68
+ const FAILOVER_SPAWN_TIMEOUT_MS = 30 * 1000; // yedek hesap 30 sn içinde spawn olmazsa vazgeç
69
+
70
+ // İzlenen (KAZICI_NAMES ile eşleşmeyen, başka hesaptan konan) kazıcı minyonların
71
+ // skin texture hash'i. Bu skin + elde iron_pickaxe olan armor stand'ler izlenir.
72
+ const WATCHED_KAZICI_SKIN_HASHES = new Set([
73
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDQ4ODcwY2E3N2FhNjk5ZDAxNzA5M2I4Mjc3OWQwYTU5OGRjNGYzZjQ5ZDM1MTUzMmFlNGQ5NjMzZGNjMmE1YSJ9fX0=",
74
+ ]);
75
+
76
  let viewerInstance = null;
77
  let viewerTimeout = null;
78
  let bot = null;
 
115
  let ciftciIntervalHandle = null; // Çiftçi bağımsız döngü zamanlayıcısı (1 dk)
116
  let isMainCycleRunning = false; // Besleyici (goToIsland->besle) ana döngüsü sürüyor mu
117
 
118
+ // --- KAZICI FAILOVER (YEDEK HESAP) DURUM DEĞİŞKENLERİ ---
119
+ let poseTracker = new Map(); // entityId -> { poseKey, lastChangeAt, uuid }
120
+ let stallCheckIntervalHandle = null;
121
+ let failoverModeActive = false; // true iken ana botun periyodik işlemleri fiilen durur (bot=null olduğu için)
122
+ let failoverBot = null; // yedek hesabın mineflayer instance'ı
123
+ let failoverInProgress = false; // aynı anda 2 kez tetiklenmesin diye kilit
124
+
125
  // --- DİNAMİK IP VE KONUM BİLGİLERİ ---
126
  let activeConnectionIp = "Bilinmiyor";
127
  let activeConnectionLocation = "Bilinmiyor";
 
138
  }
139
 
140
  // --- ÖNCELİKLİ GUI KİLİDİ (MUTEX) ---
 
 
 
 
 
 
 
 
 
 
141
  class PriorityGuiMutex {
142
  constructor() {
143
  this._locked = false;
144
+ this._waiters = [];
145
  }
146
  isLocked() { return this._locked; }
147
  tryAcquire() {
 
161
  release() {
162
  if (this._waiters.length > 0) {
163
  const next = this._waiters.shift();
 
164
  next();
165
  } else {
166
  this._locked = false;
 
169
  }
170
  const guiMutex = new PriorityGuiMutex();
171
 
 
 
172
  let botActions = {
173
  scanAndFeedManual: null,
174
  scanAndCollectMinersManual: null,
175
  openCiftciAndSellAllManual: null,
176
  goToIslandManual: null,
177
  sendSkyblockManual: null,
178
+ antiBotMove: null,
179
+ sendMoneyToNick: null
180
  };
181
 
 
 
 
 
 
 
182
  function formatDurationMs(ms) {
183
  if (ms == null || isNaN(ms) || ms < 0) return "0 saniye";
184
  let totalSeconds = Math.round(ms / 1000);
 
274
  });
275
  }).on('error', () => {});
276
 
277
+ // =====================================================================
278
+ // KAZICI FAILOVER (YEDEK HESAP) YARDIMCI FONKSİYONLARI
279
+ // Bunlar module-level (createBot() dışında) çünkü bot bağlantısı kesilip
280
+ // yeniden kurulduğunda bile (createBot() tekrar çağrıldığında) hayatta
281
+ // kalmaları gerekiyor.
282
+ // =====================================================================
283
+
284
+ // Entity'nin skin texture değerini (player_head equipment'inden) çıkarır.
285
+ function getEntitySkinHash(entity) {
286
+ try {
287
+ const equipment = entity.equipment || [];
288
+ for (const item of equipment) {
289
+ if (item && item.name === "player_head" && item.components) {
290
+ for (const comp of item.components) {
291
+ if (comp && comp.type === "profile" && comp.data && Array.isArray(comp.data.properties)) {
292
+ const texProp = comp.data.properties.find(p => p.name === "textures");
293
+ if (texProp && texProp.value) {
294
+ return texProp.value;
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+ } catch (e) {}
301
+ return null;
302
+ }
303
+
304
+ // Entity'nin elinde (equipment[0] = main hand) iron_pickaxe olup olmadığını kontrol eder.
305
+ function hasIronPickaxeEquipped(entity) {
306
+ try {
307
+ const equipment = entity.equipment || [];
308
+ const mainHand = equipment[0];
309
+ return !!(mainHand && mainHand.name === "iron_pickaxe");
310
+ } catch (e) {
311
+ return false;
312
+ }
313
+ }
314
+
315
+ // Skin + iron_pickaxe ikisi birden tutuyorsa bu entity izlenen kazıcı minyondur.
316
+ function isWatchedKaziciEntity(entity) {
317
+ if (!entity || entity.name !== "armor_stand") return false;
318
+ const skinHash = getEntitySkinHash(entity);
319
+ if (!skinHash || !WATCHED_KAZICI_SKIN_HASHES.has(skinHash)) return false;
320
+ return hasIronPickaxeEquipped(entity);
321
+ }
322
+
323
+ // metadata içindeki pose (kol/kafa açıları) + pozisyonu birleştirip bir imza çıkarır.
324
+ function getPoseKey(entity) {
325
+ try {
326
+ const md = entity.metadata || [];
327
+ const parts = [];
328
+ for (const m of md) {
329
+ if (m && typeof m === "object" && "pitch" in m && "yaw" in m && "roll" in m) {
330
+ parts.push(`${m.pitch.toFixed(2)},${m.yaw.toFixed(2)},${m.roll.toFixed(2)}`);
331
+ }
332
+ }
333
+ if (entity.position) {
334
+ parts.push(`${entity.position.x.toFixed(2)}_${entity.position.y.toFixed(2)}_${entity.position.z.toFixed(2)}`);
335
+ }
336
+ return parts.join("|");
337
+ } catch (e) {
338
+ return null;
339
+ }
340
+ }
341
+
342
+ // bot.on('entityMoved', ...) ve bot.on('entitySpawn', ...) içinden çağrılır.
343
+ // İzlenen kazıcı minyonun pose'unu takip eder.
344
+ function trackKaziciPoseIfWatched(entity) {
345
+ if (!entity) return;
346
+ if (!isWatchedKaziciEntity(entity)) return;
347
+
348
+ const poseKey = getPoseKey(entity);
349
+ if (poseKey == null) return;
350
+
351
+ const prev = poseTracker.get(entity.id);
352
+ if (!prev) {
353
+ poseTracker.set(entity.id, { poseKey, lastChangeAt: Date.now(), uuid: entity.uuid });
354
+ return;
355
+ }
356
+
357
+ if (prev.poseKey !== poseKey) {
358
+ prev.poseKey = poseKey;
359
+ prev.lastChangeAt = Date.now();
360
+ }
361
+ // poseKey aynıysa lastChangeAt GÜNCELLENMEZ -> hareketsizlik süresi büyümeye devam eder.
362
+ }
363
+
364
+ // Artık dünyada olmayan entity kayıtlarını temizler.
365
+ function pruneStaleTrackedEntities() {
366
+ if (!bot || !bot.entities) return;
367
+ for (const id of poseTracker.keys()) {
368
+ if (!bot.entities[id]) poseTracker.delete(id);
369
+ }
370
+ }
371
+
372
+ function startStallWatcher() {
373
+ if (stallCheckIntervalHandle) return;
374
+ stallCheckIntervalHandle = setInterval(() => {
375
+ if (!bot || !botIsReady || failoverModeActive) return;
376
+ pruneStaleTrackedEntities();
377
+
378
+ const now = Date.now();
379
+ let anyStalled = false;
380
+ for (const [id, info] of poseTracker.entries()) {
381
+ if (now - info.lastChangeAt >= STALL_TIMEOUT_MS) {
382
+ anyStalled = true;
383
+ logger.warn(`[Failover] İzlenen kazıcı minyon (entityId:${id}) ${formatDurationMs(now - info.lastChangeAt)} boyunca hareketsiz.`);
384
+ }
385
+ }
386
+
387
+ if (anyStalled && !failoverInProgress) {
388
+ triggerFailoverSequence().catch(err => {
389
+ logger.error(`[Failover Hatası] ${err && err.stack ? err.stack : err}`);
390
+ failoverInProgress = false;
391
+ });
392
+ }
393
+ }, STALL_CHECK_INTERVAL_MS);
394
+ logger.info("[Failover] Pose izleme döngüsü başlatıldı.");
395
+ }
396
+
397
+ function stopStallWatcher() {
398
+ if (stallCheckIntervalHandle) {
399
+ clearInterval(stallCheckIntervalHandle);
400
+ stallCheckIntervalHandle = null;
401
+ }
402
+ poseTracker.clear();
403
+ }
404
+
405
+ // Ana hesabı geçici düşürüp yedek hesabı (kullaniciadi1) devreye alan, izlenen
406
+ // kazıcı minyonlar tekrar hareket edene kadar bekleyen, sonra ana hesabı
407
+ // yeniden bağlayan tam akış.
408
+ async function triggerFailoverSequence() {
409
+ if (failoverInProgress) return;
410
+ failoverInProgress = true;
411
+ failoverModeActive = true;
412
+
413
+ const mainUsername = config["bot-account"]["username"];
414
+ const backupUsername = `${mainUsername}1`;
415
+
416
+ 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.`);
417
+
418
+ manualStopRequested = true; // mevcut auto-reconnect mantığını geçici olarak devre dışı bırakır
419
+ stopPeriodicHomeChecks();
420
+ safeDestroyAllConnections();
421
+
422
+ try {
423
+ await runBackupAccountAndWaitForRecovery(backupUsername);
424
+ } catch (err) {
425
+ logger.error(`[Failover] Yedek hesap akışında hata: ${err && err.stack ? err.stack : err}`);
426
+ }
427
+
428
+ logger.info(`[Failover] İzlenen kazıcı(lar) tekrar aktif (ya da azami bekleme süresi doldu). Ana hesap (${mainUsername}) yeniden bağlanıyor.`);
429
+ manualStopRequested = false;
430
+ failoverModeActive = false;
431
+ poseTracker.clear();
432
+ createBot();
433
+
434
+ failoverInProgress = false;
435
+ }
436
+
437
+ // Yedek hesapla ayrı bir mineflayer bot'u başlatır: /login, /skyblock, /is go
438
+ // atar (ana hesaptaki ile AYNI gecikme süreleriyle), sonra izlenen minyonların
439
+ // hareketine bakar. Hareket geri gelince bağlantıyı kapatıp resolve eder.
440
+ function runBackupAccountAndWaitForRecovery(backupUsername) {
441
+ return new Promise((resolve) => {
442
+ const backupPoseTracker = new Map();
443
+ let recheckHandle = null;
444
+ let safetyTimeoutHandle = null;
445
+ let spawnTimeoutHandle = null;
446
+ let finished = false;
447
+
448
+ const finish = () => {
449
+ if (finished) return;
450
+ finished = true;
451
+ if (recheckHandle) clearInterval(recheckHandle);
452
+ if (safetyTimeoutHandle) clearTimeout(safetyTimeoutHandle);
453
+ if (spawnTimeoutHandle) clearTimeout(spawnTimeoutHandle);
454
+ try {
455
+ if (failoverBot) {
456
+ failoverBot.removeAllListeners();
457
+ failoverBot.quit();
458
+ }
459
+ } catch (e) {}
460
+ failoverBot = null;
461
+ resolve();
462
+ };
463
+
464
+ try {
465
+ failoverBot = mineflayer.createBot({
466
+ username: backupUsername,
467
+ auth: config["bot-account"]["type"],
468
+ host: config.server.ip,
469
+ port: config.server.port,
470
+ version: config.server.version,
471
+ });
472
+ } catch (err) {
473
+ logger.error(`[Failover - Yedek Hesap] Bot oluşturulamadı: ${err && err.message ? err.message : err}`);
474
+ finish();
475
+ return;
476
+ }
477
+
478
+ spawnTimeoutHandle = setTimeout(() => {
479
+ logger.error(`[Failover - Yedek Hesap] ${backupUsername} ${formatDurationMs(FAILOVER_SPAWN_TIMEOUT_MS)} içinde spawn olamadı, vazgeçiliyor (bir sonraki kontrolde tekrar denenecek).`);
480
+ finish();
481
+ }, FAILOVER_SPAWN_TIMEOUT_MS);
482
+
483
+ failoverBot.once("spawn", () => {
484
+ clearTimeout(spawnTimeoutHandle);
485
+ logger.info(`[Failover - Yedek Hesap] ${backupUsername} sunucuya girdi.`);
486
+
487
+ setTimeout(() => {
488
+ try { failoverBot.chat(`/login ${config.utils["auto-auth"].password}`); } catch (e) {}
489
+ logger.info(`[Failover - Yedek Hesap] ${backupUsername} Login komutu kullanıldı.`);
490
+ }, FAILOVER_LOGIN_DELAY_MS);
491
+
492
+ setTimeout(() => {
493
+ try { failoverBot.chat("/skyblock"); } catch (e) {}
494
+ logger.info(`[Failover - Yedek Hesap] ${backupUsername} Skyblock'a ışınlanma komutu kullanıldı.`);
495
+ }, FAILOVER_SKYBLOCK_DELAY_MS);
496
+
497
+ setTimeout(() => {
498
+ try { failoverBot.chat(GO_ISLAND_COMMAND); } catch (e) {}
499
+ logger.info(`[Failover - Yedek Hesap] ${GO_ISLAND_COMMAND} gönderildi, izlenen minyonların tekrar hareket etmesi bekleniyor...`);
500
+ }, FAILOVER_ISGO_DELAY_MS);
501
+
502
+ // Adaya varış + izleme başlatma
503
+ setTimeout(() => {
504
+ recheckHandle = setInterval(() => {
505
+ try {
506
+ const arr = Object.values(failoverBot.entities || {});
507
+ let allRecovered = true;
508
+ let sawAnyWatched = false;
509
+
510
+ for (const e of arr) {
511
+ if (!isWatchedKaziciEntity(e)) continue;
512
+
513
+ sawAnyWatched = true;
514
+ const poseKey = getPoseKey(e);
515
+ const prev = backupPoseTracker.get(e.id);
516
+
517
+ if (!prev) {
518
+ backupPoseTracker.set(e.id, { poseKey, lastChangeAt: Date.now() });
519
+ allRecovered = false; // henüz yeterince veri yok, bir tur daha bekle
520
+ continue;
521
+ }
522
+
523
+ if (prev.poseKey !== poseKey) {
524
+ prev.poseKey = poseKey;
525
+ prev.lastChangeAt = Date.now();
526
+ } else if (Date.now() - prev.lastChangeAt < STALL_TIMEOUT_MS) {
527
+ // son 1 dk içinde bir değişiklik olmuş demek ki hareket ediyor
528
+ } else {
529
+ allRecovered = false;
530
+ }
531
+ }
532
+
533
+ if (sawAnyWatched && allRecovered) {
534
+ logger.info("[Failover - Yedek Hesap] İzlenen minyonlar tekrar hareket ediyor. Yedek hesaptan çıkılıyor.");
535
+ finish();
536
+ }
537
+ } catch (e) {}
538
+ }, FAILOVER_RECHECK_INTERVAL_MS);
539
+ }, FAILOVER_HOME_ARRIVAL_DELAY_MS + FAILOVER_ISGO_DELAY_MS);
540
+ });
541
+
542
+ failoverBot.on("error", (err) => {
543
+ logger.error(`[Failover - Yedek Hesap] Hata: ${err && err.message ? err.message : err}`);
544
+ });
545
+
546
+ failoverBot.on("kicked", (reason) => {
547
+ logger.warn(`[Failover - Yedek Hesap] Sunucudan atıldı: ${util.inspect(reason)}`);
548
+ finish();
549
+ });
550
+
551
+ failoverBot.on("end", () => {
552
+ if (!finished) {
553
+ logger.warn("[Failover - Yedek Hesap] Bağlantı beklenmedik şekilde koptu.");
554
+ finish();
555
+ }
556
+ });
557
+
558
+ // Güvenlik: sonsuza kadar beklemesin
559
+ safetyTimeoutHandle = setTimeout(() => {
560
+ logger.warn(`[Failover - Yedek Hesap] Azami bekleme süresi (${formatDurationMs(FAILOVER_MAX_WAIT_MS)}) doldu, ana hesaba geri dönülüyor.`);
561
+ finish();
562
+ }, FAILOVER_MAX_WAIT_MS);
563
+ });
564
+ }
565
+
566
  async function createBot() {
567
  if (isConnecting) {
568
  logger.warn("[Sistem] Zaten aktif bir bağlantı süreci yürütülüyor. Mükerrer istek engellendi.");
 
1061
  }
1062
  }
1063
 
1064
+ // --- SENDMONEY: /money oku, bakiyeden 1000 eksiğini ilgili nick'e /pay ile gönder ---
1065
+ const MONEY_BALANCE_REGEX = /bakiyen:\s*([\d.,]+)\s*TL/i;
1066
+ const MONEY_RESPONSE_TIMEOUT_MS = 8000;
1067
+
1068
+ function parseBalanceToAdjustedAmount(rawBalanceStr) {
1069
+ // "444,626.89" -> virgülleri kaldır -> "444626.89" -> noktadan öncesini al -> "444626"
1070
+ const noCommas = rawBalanceStr.replace(/,/g, "");
1071
+ const beforeDot = noCommas.split(".")[0];
1072
+ const intValue = parseInt(beforeDot, 10);
1073
+ if (isNaN(intValue)) return null;
1074
+ return intValue - 1000;
1075
+ }
1076
+
1077
+ async function sendMoneyToNick(nick) {
1078
+ if (!bot || !botIsReady) {
1079
+ return { success: false, message: "Bot hazır değil." };
1080
+ }
1081
+ if (!nick || typeof nick !== "string" || nick.trim().length === 0) {
1082
+ return { success: false, message: "nick parametresi zorunlu." };
1083
+ }
1084
+
1085
+ return new Promise((resolve) => {
1086
+ let finished = false;
1087
+ let timeoutHandle = null;
1088
+
1089
+ const cleanup = () => {
1090
+ if (finished) return;
1091
+ finished = true;
1092
+ clearTimeout(timeoutHandle);
1093
+ bot.removeListener('message', onMessage);
1094
+ };
1095
+
1096
+ const onMessage = (jsonMsg) => {
1097
+ try {
1098
+ const chatMessage = bot.chat.ChatMessage ? new bot.chat.ChatMessage(jsonMsg) : jsonMsg;
1099
+ const text = typeof chatMessage.toString === 'function' ? chatMessage.toString() : String(jsonMsg);
1100
+ const plain = stripColorCodes(text);
1101
+
1102
+ const match = plain.match(MONEY_BALANCE_REGEX);
1103
+ if (match) {
1104
+ const adjusted = parseBalanceToAdjustedAmount(match[1]);
1105
+ cleanup();
1106
+
1107
+ if (adjusted == null) {
1108
+ logger.error(`[Sendmoney] Bakiye ayrıştırılamadı: "${plain}"`);
1109
+ resolve({ success: false, message: "Bakiye mesajı ayrıştırılamadı." });
1110
+ return;
1111
+ }
1112
+
1113
+ try {
1114
+ const payCommand = `/pay ${nick} ${adjusted}`;
1115
+ bot.chat(payCommand);
1116
+ logger.info(`[Sendmoney] Bakiye okundu: ${match[1]} -> Gönderilecek: ${adjusted}. Komut: ${payCommand}`);
1117
+ resolve({ success: true, message: "Para gönderildi.", balance_raw: match[1], amount_sent: adjusted, nick });
1118
+ } catch (err) {
1119
+ logger.error(`[Sendmoney] /pay komutu gönderilemedi: ${err && err.message ? err.message : err}`);
1120
+ resolve({ success: false, message: "/pay komutu gönderilirken hata oluştu." });
1121
+ }
1122
+ }
1123
+ } catch (err) {
1124
+ logger.error(`[Sendmoney] Mesaj işlenirken hata: ${err && err.message ? err.message : err}`);
1125
+ }
1126
+ };
1127
+
1128
+ bot.on('message', onMessage);
1129
+
1130
+ timeoutHandle = setTimeout(() => {
1131
+ cleanup();
1132
+ logger.warn(`[Sendmoney] ${formatDurationMs(MONEY_RESPONSE_TIMEOUT_MS)} içinde bakiye mesajı gelmedi.`);
1133
+ resolve({ success: false, message: "Zaman aşımı: bakiye mesajı alınamadı." });
1134
+ }, MONEY_RESPONSE_TIMEOUT_MS);
1135
+
1136
+ try {
1137
+ bot.chat("/money");
1138
+ logger.info(`[Sendmoney] /money komutu gönderildi, bakiye mesajı bekleniyor...`);
1139
+ } catch (err) {
1140
+ cleanup();
1141
+ logger.error(`[Sendmoney] /money komutu gönderilemedi: ${err && err.message ? err.message : err}`);
1142
+ resolve({ success: false, message: "/money komutu gönderilirken hata oluştu." });
1143
+ }
1144
+ });
1145
+ }
1146
+
1147
+ async function antiBotMove() {
1148
+ if (!bot || !botIsReady) return;
1149
+ try {
1150
+ const moves = ['forward', 'left', 'right', 'jump'];
1151
+
1152
+ const moveCount = Math.floor(Math.random() * 4) + 2; // 2-5 hareket
1153
+
1154
+ for (let i = 0; i < moveCount; i++) {
1155
+ const move = moves[Math.floor(Math.random() * moves.length)];
1156
+ const duration = 150 + Math.floor(Math.random() * 500);
1157
+
1158
+ bot.setControlState(move, true);
1159
+ await new Promise(r => setTimeout(r, duration));
1160
+ bot.setControlState(move, false);
1161
+
1162
+ await new Promise(r => setTimeout(r, 100 + Math.random() * 300));
1163
+
1164
+ }
1165
+
1166
+ // Rastgele kafa hareketi
1167
+ await bot.look(
1168
+ bot.entity.yaw + ((Math.random() - 0.5) * 1.5),
1169
+ bot.entity.pitch + ((Math.random() - 0.5) * 0.3),
1170
+ true
1171
+ );
1172
+ } catch (err) {
1173
+ logger.error(`[antiBotMove Hatası] Komut gönderilemedi: ${err && err.message ? err.message : err}`);
1174
+ }
1175
+ };
1176
+
1177
  function scheduleNextCheck(delayMs) {
1178
  if (periodicTimeoutHandle) {
1179
  clearTimeout(periodicTimeoutHandle);
 
1190
  await goToIsland();
1191
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
1192
  await scanAndFeed();
 
 
 
1193
  } catch (err) {
1194
  logger.error(`[Planlayıcı Hatası] Periyodik döngüde beklenmeyen hata: ${err && err.stack ? err.stack : err}`);
1195
  try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {}
 
1211
  });
1212
  }
1213
 
 
1214
  async function scanAndFeedCore() {
1215
  if (!bot || !botIsReady) return;
1216
  if (!guiMutex.isLocked()) {
 
1323
  });
1324
  }
1325
 
 
 
 
1326
  const FEED_LOCK_RETRY_DELAY_MS = 15 * 1000;
1327
+ const FEED_LOCK_MAX_RETRIES = 8;
1328
 
 
 
 
1329
  async function scanAndFeed(retryCount) {
1330
  retryCount = retryCount || 0;
1331
  if (!guiMutex.tryAcquire()) {
 
1348
  }
1349
  }
1350
 
 
 
1351
  async function scanAndFeedManual() {
1352
  logger.info("[Besleyici - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1353
  await guiMutex.acquire();
 
1361
  }
1362
  }
1363
 
 
1364
  async function activateEntityAndAwaitWindow(entity, labelForLogs) {
1365
  if (!bot || !botIsReady || !entity || !entity.position) {
1366
  return { success: false, reason: "Bot veya entity hazır değil" };
 
1458
  return matches;
1459
  }
1460
 
 
 
1461
  async function scanAndCollectMinersCore() {
1462
  if (!bot || !botIsReady) return;
1463
  if (!guiMutex.isLocked()) {
 
1508
  logger.info(`[${currentBotUsername}] Kazıcı minyon depo aktarım turu tamamlandı.`);
1509
  }
1510
 
 
1511
  async function scanAndCollectMiners() {
1512
  if (!guiMutex.tryAcquire()) {
1513
  logger.warn("[Kazıcı Toplama] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor.");
 
1522
  }
1523
  }
1524
 
 
1525
  async function scanAndCollectMinersManual() {
1526
  logger.info("[Kazıcı Toplama - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1527
  await guiMutex.acquire();
 
1536
  }
1537
  }
1538
 
 
 
 
 
 
1539
  async function openCiftciAndSellAllCore() {
1540
  if (!bot || !botIsReady) return;
1541
  if (!guiMutex.isLocked()) {
 
1551
  await new Promise(r => setTimeout(r, 300));
1552
  }
1553
 
 
 
 
 
1554
  let timedOut = false;
1555
 
1556
  await new Promise((resolve) => {
 
1589
  }
1590
  }
1591
 
 
1592
  async function openCiftciAndSellAll() {
1593
  if (!guiMutex.tryAcquire()) {
1594
  logger.warn("[Çiftçi] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor.");
 
1603
  }
1604
  }
1605
 
 
1606
  async function openCiftciAndSellAllManual() {
1607
  logger.info("[Çiftçi - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1608
  await guiMutex.acquire();
 
1617
  }
1618
  }
1619
 
 
 
 
1620
  function startKaziciPeriodic() {
1621
+ if (kaziciIntervalHandle) return;
1622
 
1623
  const runCycle = async () => {
1624
  if (!bot || !botIsReady) return;
 
1649
  logger.info(`[Kazıcı Periyodik] Bağımsız döngü başlatıldı (her ${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)}).`);
1650
  }
1651
 
 
1652
  function startCiftciPeriodic() {
1653
+ if (ciftciIntervalHandle) return;
1654
 
1655
  const runCycle = async () => {
1656
  if (!bot || !botIsReady) return;
 
1690
  botIsReady = true;
1691
  botUptimeStart = Date.now();
1692
 
 
1693
  botActions.scanAndFeedManual = scanAndFeedManual;
1694
  botActions.scanAndCollectMinersManual = scanAndCollectMinersManual;
1695
  botActions.openCiftciAndSellAllManual = openCiftciAndSellAllManual;
1696
  botActions.goToIslandManual = goToIsland;
1697
  botActions.sendSkyblockManual = sendSkyblockCommand;
1698
+ botActions.antiBotMove = antiBotMove;
1699
+ botActions.sendMoneyToNick = sendMoneyToNick;
1700
 
 
 
1701
  startKaziciPeriodic();
1702
  startCiftciPeriodic();
1703
+ startStallWatcher();
1704
 
1705
  if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) {
1706
  const password = config.utils["auto-auth"].password;
 
1727
  }
1728
  }, 7500);
1729
 
 
1730
  setSafeTimeout(async () => {
1731
  if (!bot || !botIsReady) return;
1732
  isMainCycleRunning = true;
 
1734
  await goToIsland();
1735
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
1736
  await scanAndFeed();
 
 
1737
  } catch (err) {
1738
  logger.error(`[Spawn Hatası] İlk kontrol döngüsünde beklenmeyen hata: ${err && err.stack ? err.stack : err}`);
1739
  try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {}
 
1747
  }
1748
  });
1749
 
1750
+ bot.on("entityMoved", (entity) => {
1751
+ try { trackKaziciPoseIfWatched(entity); } catch (e) {}
1752
+ });
1753
+
1754
+ bot.on("entitySpawn", (entity) => {
1755
+ try { trackKaziciPoseIfWatched(entity); } catch (e) {}
1756
+ });
1757
+
1758
  bot.on("windowOpen", async (window) => {
1759
  if (!bot) return;
1760
  try {
 
1784
  try { bot.closeWindow(window); } catch(e){}
1785
 
1786
  } else if (isCiftciWindow) {
 
 
 
 
 
1787
  try {
1788
  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...`);
1789
  await new Promise(r => setTimeout(r, CIFTCI_CLICK_DELAY_AFTER_OPEN_MS));
 
1868
  try {
1869
  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...");
1870
  stopPeriodicHomeChecks();
1871
+ stopStallWatcher();
1872
  lastCheckAtMs = null;
1873
  botIsReady = false;
1874
  baglantiTuru = "Yok";
 
1885
  botActions.openCiftciAndSellAllManual = null;
1886
  botActions.goToIslandManual = null;
1887
  botActions.sendSkyblockManual = null;
1888
+ botActions.antiBotMove = null;
1889
+ botActions.sendMoneyToNick = null;
1890
 
1891
  safeDestroyAllConnections();
1892
 
 
2157
  next_ciftci_sell: nextCiftciTimeStr,
2158
  gui_lock_busy: guiMutex.isLocked(),
2159
  manually_stopped: manualStopRequested,
2160
+ is_connecting: isConnecting,
2161
+ failover_mode_active: failoverModeActive
2162
  });
2163
  });
2164
 
 
2181
  res.send({ success: true, message: "Bot tamamen sıfırdan yeniden başlatılıyor (soket/SSH dahil)." });
2182
  });
2183
 
 
 
 
 
 
 
2184
  app.get("/bot/kazici-kontrol", (req, res) => {
2185
  if (!bot || !botIsReady || !botActions.scanAndCollectMinersManual) {
2186
  return res.status(503).send({ success: false, message: "Bot hazır değil." });
 
2221
  res.send({ success: true, message: `${GO_ISLAND_COMMAND} komutu gönderildi.` });
2222
  });
2223
 
2224
+ app.get("/bot/sendmsg", (req, res) => {
2225
+ if (!bot || !botIsReady) {
2226
+ return res.status(503).send({ success: false, message: "Bot hazır değil." });
2227
+ }
2228
+
2229
+ const message = req.query.message;
2230
+ if (!message || typeof message !== "string" || message.trim().length === 0) {
2231
+ return res.status(400).send({ success: false, message: "message query parametresi zorunlu." });
2232
+ }
2233
+
2234
+ try {
2235
+ bot.chat(message);
2236
+ logger.info(`[${bot.username}] /sendmsg üzerinden chat mesajı gönderildi: ${message}`);
2237
+ res.send({ success: true, message: "Mesaj gönderildi.", sent: message });
2238
+ } catch (err) {
2239
+ logger.error(`[/bot/sendmsg Hatası] ${err && err.message ? err.message : err}`);
2240
+ res.status(500).send({ success: false, message: "Mesaj gönderilirken hata oluştu." });
2241
+ }
2242
+ });
2243
+
2244
+ app.get("/bot/antibotmove", (req, res) => {
2245
+ if (!bot || !botIsReady) {
2246
+ return res.status(503).send({ success: false, message: "Bot hazır değil." });
2247
+ }
2248
+
2249
+ try {
2250
+ botActions.antiBotMove().catch(e => logger.error(`[/bot/antibotmove Hatası] ${e && e.stack ? e.stack : e}`));
2251
+ res.send({ success: true, message: "Anti Bot Yürüyüşü Yapıldı."});
2252
+ } catch (err) {
2253
+ logger.error(`[/bot/antibotmove Hatası] ${err && err.message ? err.message : err}`);
2254
+ res.status(500).send({ success: false, message: "Anti Bot Move sırasında hata oluştu." });
2255
+ }
2256
+ });
2257
+
2258
+ app.get("/bot/sendmoney", (req, res) => {
2259
+ if (!bot || !botIsReady || !botActions.sendMoneyToNick) {
2260
+ return res.status(503).send({ success: false, message: "Bot hazır değil." });
2261
+ }
2262
+
2263
+ const nick = req.query.nick;
2264
+ if (!nick || typeof nick !== "string" || nick.trim().length === 0) {
2265
+ return res.status(400).send({ success: false, message: "nick query parametresi zorunlu." });
2266
+ }
2267
+
2268
+ botActions.sendMoneyToNick(nick)
2269
+ .then((result) => {
2270
+ if (result.success) {
2271
+ res.send(result);
2272
+ } else {
2273
+ res.status(500).send(result);
2274
+ }
2275
+ })
2276
+ .catch((err) => {
2277
+ logger.error(`[/bot/sendmoney Hatası] ${err && err.stack ? err.stack : err}`);
2278
+ res.status(500).send({ success: false, message: "Beklenmeyen bir hata oluştu." });
2279
+ });
2280
+ });
2281
+
2282
  app.get("/", (req, res) => res.send("Bot Aktif"));
2283
 
2284
  app.get("/messages", (req, res) => {
 
2514
  return true;
2515
  }
2516
  return originalStderrWrite.apply(process.stderr, arguments);
2517
+ };