Suvarii commited on
Commit
d6f7aeb
·
verified ·
1 Parent(s): f172515

Update bot.js

Browse files
Files changed (1) hide show
  1. bot.js +395 -74
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,15 @@ 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 +273,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 +1060,36 @@ 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 +1106,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 +1127,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 +1239,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 +1264,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 +1277,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 +1374,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 +1424,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 +1438,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 +1452,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 +1467,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 +1505,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 +1519,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 +1533,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 +1565,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 +1606,16 @@ 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 +1642,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 +1649,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 +1662,14 @@ function activateEntityAtNoLook(entity, position) {
1374
  }
1375
  });
1376
 
 
 
 
 
 
 
 
 
1377
  bot.on("windowOpen", async (window) => {
1378
  if (!bot) return;
1379
  try {
@@ -1403,11 +1699,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 +1783,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";
@@ -1778,7 +2070,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 +2094,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 +2134,40 @@ 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) => {
 
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
  };
180
 
 
 
 
 
 
 
181
  function formatDurationMs(ms) {
182
  if (ms == null || isNaN(ms) || ms < 0) return "0 saniye";
183
  let totalSeconds = Math.round(ms / 1000);
 
273
  });
274
  }).on('error', () => {});
275
 
276
+ // =====================================================================
277
+ // KAZICI FAILOVER (YEDEK HESAP) YARDIMCI FONKSİYONLARI
278
+ // Bunlar module-level (createBot() dışında) çünkü bot bağlantısı kesilip
279
+ // yeniden kurulduğunda bile (createBot() tekrar çağrıldığında) hayatta
280
+ // kalmaları gerekiyor.
281
+ // =====================================================================
282
+
283
+ // Entity'nin skin texture değerini (player_head equipment'inden) çıkarır.
284
+ function getEntitySkinHash(entity) {
285
+ try {
286
+ const equipment = entity.equipment || [];
287
+ for (const item of equipment) {
288
+ if (item && item.name === "player_head" && item.components) {
289
+ for (const comp of item.components) {
290
+ if (comp && comp.type === "profile" && comp.data && Array.isArray(comp.data.properties)) {
291
+ const texProp = comp.data.properties.find(p => p.name === "textures");
292
+ if (texProp && texProp.value) {
293
+ return texProp.value;
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
299
+ } catch (e) {}
300
+ return null;
301
+ }
302
+
303
+ // Entity'nin elinde (equipment[0] = main hand) iron_pickaxe olup olmadığını kontrol eder.
304
+ function hasIronPickaxeEquipped(entity) {
305
+ try {
306
+ const equipment = entity.equipment || [];
307
+ const mainHand = equipment[0];
308
+ return !!(mainHand && mainHand.name === "iron_pickaxe");
309
+ } catch (e) {
310
+ return false;
311
+ }
312
+ }
313
+
314
+ // Skin + iron_pickaxe ikisi birden tutuyorsa bu entity izlenen kazıcı minyondur.
315
+ function isWatchedKaziciEntity(entity) {
316
+ if (!entity || entity.name !== "armor_stand") return false;
317
+ const skinHash = getEntitySkinHash(entity);
318
+ if (!skinHash || !WATCHED_KAZICI_SKIN_HASHES.has(skinHash)) return false;
319
+ return hasIronPickaxeEquipped(entity);
320
+ }
321
+
322
+ // metadata içindeki pose (kol/kafa açıları) + pozisyonu birleştirip bir imza çıkarır.
323
+ function getPoseKey(entity) {
324
+ try {
325
+ const md = entity.metadata || [];
326
+ const parts = [];
327
+ for (const m of md) {
328
+ if (m && typeof m === "object" && "pitch" in m && "yaw" in m && "roll" in m) {
329
+ parts.push(`${m.pitch.toFixed(2)},${m.yaw.toFixed(2)},${m.roll.toFixed(2)}`);
330
+ }
331
+ }
332
+ if (entity.position) {
333
+ parts.push(`${entity.position.x.toFixed(2)}_${entity.position.y.toFixed(2)}_${entity.position.z.toFixed(2)}`);
334
+ }
335
+ return parts.join("|");
336
+ } catch (e) {
337
+ return null;
338
+ }
339
+ }
340
+
341
+ // bot.on('entityMoved', ...) ve bot.on('entitySpawn', ...) içinden çağrılır.
342
+ // İzlenen kazıcı minyonun pose'unu takip eder.
343
+ function trackKaziciPoseIfWatched(entity) {
344
+ if (!entity) return;
345
+ if (!isWatchedKaziciEntity(entity)) return;
346
+
347
+ const poseKey = getPoseKey(entity);
348
+ if (poseKey == null) return;
349
+
350
+ const prev = poseTracker.get(entity.id);
351
+ if (!prev) {
352
+ poseTracker.set(entity.id, { poseKey, lastChangeAt: Date.now(), uuid: entity.uuid });
353
+ return;
354
+ }
355
+
356
+ if (prev.poseKey !== poseKey) {
357
+ prev.poseKey = poseKey;
358
+ prev.lastChangeAt = Date.now();
359
+ }
360
+ // poseKey aynıysa lastChangeAt GÜNCELLENMEZ -> hareketsizlik süresi büyümeye devam eder.
361
+ }
362
+
363
+ // Artık dünyada olmayan entity kayıtlarını temizler.
364
+ function pruneStaleTrackedEntities() {
365
+ if (!bot || !bot.entities) return;
366
+ for (const id of poseTracker.keys()) {
367
+ if (!bot.entities[id]) poseTracker.delete(id);
368
+ }
369
+ }
370
+
371
+ function startStallWatcher() {
372
+ if (stallCheckIntervalHandle) return;
373
+ stallCheckIntervalHandle = setInterval(() => {
374
+ if (!bot || !botIsReady || failoverModeActive) return;
375
+ pruneStaleTrackedEntities();
376
+
377
+ const now = Date.now();
378
+ let anyStalled = false;
379
+ for (const [id, info] of poseTracker.entries()) {
380
+ if (now - info.lastChangeAt >= STALL_TIMEOUT_MS) {
381
+ anyStalled = true;
382
+ logger.warn(`[Failover] İzlenen kazıcı minyon (entityId:${id}) ${formatDurationMs(now - info.lastChangeAt)} boyunca hareketsiz.`);
383
+ }
384
+ }
385
+
386
+ if (anyStalled && !failoverInProgress) {
387
+ triggerFailoverSequence().catch(err => {
388
+ logger.error(`[Failover Hatası] ${err && err.stack ? err.stack : err}`);
389
+ failoverInProgress = false;
390
+ });
391
+ }
392
+ }, STALL_CHECK_INTERVAL_MS);
393
+ logger.info("[Failover] Pose izleme döngüsü başlatıldı.");
394
+ }
395
+
396
+ function stopStallWatcher() {
397
+ if (stallCheckIntervalHandle) {
398
+ clearInterval(stallCheckIntervalHandle);
399
+ stallCheckIntervalHandle = null;
400
+ }
401
+ poseTracker.clear();
402
+ }
403
+
404
+ // Ana hesabı geçici düşürüp yedek hesabı (kullaniciadi1) devreye alan, izlenen
405
+ // kazıcı minyonlar tekrar hareket edene kadar bekleyen, sonra ana hesabı
406
+ // yeniden bağlayan tam akış.
407
+ async function triggerFailoverSequence() {
408
+ if (failoverInProgress) return;
409
+ failoverInProgress = true;
410
+ failoverModeActive = true;
411
+
412
+ const mainUsername = config["bot-account"]["username"];
413
+ const backupUsername = `${mainUsername}1`;
414
+
415
+ 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.`);
416
+
417
+ manualStopRequested = true; // mevcut auto-reconnect mantığını geçici olarak devre dışı bırakır
418
+ stopPeriodicHomeChecks();
419
+ safeDestroyAllConnections();
420
+
421
+ try {
422
+ await runBackupAccountAndWaitForRecovery(backupUsername);
423
+ } catch (err) {
424
+ logger.error(`[Failover] Yedek hesap akışında hata: ${err && err.stack ? err.stack : err}`);
425
+ }
426
+
427
+ logger.info(`[Failover] İzlenen kazıcı(lar) tekrar aktif (ya da azami bekleme süresi doldu). Ana hesap (${mainUsername}) yeniden bağlanıyor.`);
428
+ manualStopRequested = false;
429
+ failoverModeActive = false;
430
+ poseTracker.clear();
431
+ createBot();
432
+
433
+ failoverInProgress = false;
434
+ }
435
+
436
+ // Yedek hesapla ayrı bir mineflayer bot'u başlatır: /login, /skyblock, /is go
437
+ // atar (ana hesaptaki ile AYNI gecikme süreleriyle), sonra izlenen minyonların
438
+ // hareketine bakar. Hareket geri gelince bağlantıyı kapatıp resolve eder.
439
+ function runBackupAccountAndWaitForRecovery(backupUsername) {
440
+ return new Promise((resolve) => {
441
+ const backupPoseTracker = new Map();
442
+ let recheckHandle = null;
443
+ let safetyTimeoutHandle = null;
444
+ let spawnTimeoutHandle = null;
445
+ let finished = false;
446
+
447
+ const finish = () => {
448
+ if (finished) return;
449
+ finished = true;
450
+ if (recheckHandle) clearInterval(recheckHandle);
451
+ if (safetyTimeoutHandle) clearTimeout(safetyTimeoutHandle);
452
+ if (spawnTimeoutHandle) clearTimeout(spawnTimeoutHandle);
453
+ try {
454
+ if (failoverBot) {
455
+ failoverBot.removeAllListeners();
456
+ failoverBot.quit();
457
+ }
458
+ } catch (e) {}
459
+ failoverBot = null;
460
+ resolve();
461
+ };
462
+
463
+ try {
464
+ failoverBot = mineflayer.createBot({
465
+ username: backupUsername,
466
+ auth: config["bot-account"]["type"],
467
+ host: config.server.ip,
468
+ port: config.server.port,
469
+ version: config.server.version,
470
+ });
471
+ } catch (err) {
472
+ logger.error(`[Failover - Yedek Hesap] Bot oluşturulamadı: ${err && err.message ? err.message : err}`);
473
+ finish();
474
+ return;
475
+ }
476
+
477
+ spawnTimeoutHandle = setTimeout(() => {
478
+ logger.error(`[Failover - Yedek Hesap] ${backupUsername} ${formatDurationMs(FAILOVER_SPAWN_TIMEOUT_MS)} içinde spawn olamadı, vazgeçiliyor (bir sonraki kontrolde tekrar denenecek).`);
479
+ finish();
480
+ }, FAILOVER_SPAWN_TIMEOUT_MS);
481
+
482
+ failoverBot.once("spawn", () => {
483
+ clearTimeout(spawnTimeoutHandle);
484
+ logger.info(`[Failover - Yedek Hesap] ${backupUsername} sunucuya girdi.`);
485
+
486
+ setTimeout(() => {
487
+ try { failoverBot.chat(`/login ${config.utils["auto-auth"].password}`); } catch (e) {}
488
+ logger.info(`[Failover - Yedek Hesap] ${backupUsername} Login komutu kullanıldı.`);
489
+ }, FAILOVER_LOGIN_DELAY_MS);
490
+
491
+ setTimeout(() => {
492
+ try { failoverBot.chat("/skyblock"); } catch (e) {}
493
+ logger.info(`[Failover - Yedek Hesap] ${backupUsername} Skyblock'a ışınlanma komutu kullanıldı.`);
494
+ }, FAILOVER_SKYBLOCK_DELAY_MS);
495
+
496
+ setTimeout(() => {
497
+ try { failoverBot.chat(GO_ISLAND_COMMAND); } catch (e) {}
498
+ logger.info(`[Failover - Yedek Hesap] ${GO_ISLAND_COMMAND} gönderildi, izlenen minyonların tekrar hareket etmesi bekleniyor...`);
499
+ }, FAILOVER_ISGO_DELAY_MS);
500
+
501
+ // Adaya varış + izleme başlatma
502
+ setTimeout(() => {
503
+ recheckHandle = setInterval(() => {
504
+ try {
505
+ const arr = Object.values(failoverBot.entities || {});
506
+ let allRecovered = true;
507
+ let sawAnyWatched = false;
508
+
509
+ for (const e of arr) {
510
+ if (!isWatchedKaziciEntity(e)) continue;
511
+
512
+ sawAnyWatched = true;
513
+ const poseKey = getPoseKey(e);
514
+ const prev = backupPoseTracker.get(e.id);
515
+
516
+ if (!prev) {
517
+ backupPoseTracker.set(e.id, { poseKey, lastChangeAt: Date.now() });
518
+ allRecovered = false; // henüz yeterince veri yok, bir tur daha bekle
519
+ continue;
520
+ }
521
+
522
+ if (prev.poseKey !== poseKey) {
523
+ prev.poseKey = poseKey;
524
+ prev.lastChangeAt = Date.now();
525
+ } else if (Date.now() - prev.lastChangeAt < STALL_TIMEOUT_MS) {
526
+ // son 1 dk içinde bir değişiklik olmuş demek ki hareket ediyor
527
+ } else {
528
+ allRecovered = false;
529
+ }
530
+ }
531
+
532
+ if (sawAnyWatched && allRecovered) {
533
+ logger.info("[Failover - Yedek Hesap] İzlenen minyonlar tekrar hareket ediyor. Yedek hesaptan çıkılıyor.");
534
+ finish();
535
+ }
536
+ } catch (e) {}
537
+ }, FAILOVER_RECHECK_INTERVAL_MS);
538
+ }, FAILOVER_HOME_ARRIVAL_DELAY_MS + FAILOVER_ISGO_DELAY_MS);
539
+ });
540
+
541
+ failoverBot.on("error", (err) => {
542
+ logger.error(`[Failover - Yedek Hesap] Hata: ${err && err.message ? err.message : err}`);
543
+ });
544
+
545
+ failoverBot.on("kicked", (reason) => {
546
+ logger.warn(`[Failover - Yedek Hesap] Sunucudan atıldı: ${util.inspect(reason)}`);
547
+ finish();
548
+ });
549
+
550
+ failoverBot.on("end", () => {
551
+ if (!finished) {
552
+ logger.warn("[Failover - Yedek Hesap] Bağlantı beklenmedik şekilde koptu.");
553
+ finish();
554
+ }
555
+ });
556
+
557
+ // Güvenlik: sonsuza kadar beklemesin
558
+ safetyTimeoutHandle = setTimeout(() => {
559
+ logger.warn(`[Failover - Yedek Hesap] Azami bekleme süresi (${formatDurationMs(FAILOVER_MAX_WAIT_MS)}) doldu, ana hesaba geri dönülüyor.`);
560
+ finish();
561
+ }, FAILOVER_MAX_WAIT_MS);
562
+ });
563
+ }
564
+
565
  async function createBot() {
566
  if (isConnecting) {
567
  logger.warn("[Sistem] Zaten aktif bir bağlantı süreci yürütülüyor. Mükerrer istek engellendi.");
 
1060
  }
1061
  }
1062
 
1063
+ async function antiBotMove() {
1064
+ if (!bot || !botIsReady) return;
1065
+ try {
1066
+ const moves = ['forward', 'left', 'right', 'jump'];
1067
+
1068
+ const moveCount = Math.floor(Math.random() * 4) + 2; // 2-5 hareket
1069
+
1070
+ for (let i = 0; i < moveCount; i++) {
1071
+ const move = moves[Math.floor(Math.random() * moves.length)];
1072
+ const duration = 150 + Math.floor(Math.random() * 500);
1073
+
1074
+ bot.setControlState(move, true);
1075
+ await new Promise(r => setTimeout(r, duration));
1076
+ bot.setControlState(move, false);
1077
+
1078
+ await new Promise(r => setTimeout(r, 100 + Math.random() * 300));
1079
+
1080
+ }
1081
+
1082
+ // Rastgele kafa hareketi
1083
+ await bot.look(
1084
+ bot.entity.yaw + ((Math.random() - 0.5) * 1.5),
1085
+ bot.entity.pitch + ((Math.random() - 0.5) * 0.3),
1086
+ true
1087
+ );
1088
+ } catch (err) {
1089
+ logger.error(`[antiBotMove Hatası] Komut gönderilemedi: ${err && err.message ? err.message : err}`);
1090
+ }
1091
+ };
1092
+
1093
  function scheduleNextCheck(delayMs) {
1094
  if (periodicTimeoutHandle) {
1095
  clearTimeout(periodicTimeoutHandle);
 
1106
  await goToIsland();
1107
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
1108
  await scanAndFeed();
 
 
 
1109
  } catch (err) {
1110
  logger.error(`[Planlayıcı Hatası] Periyodik döngüde beklenmeyen hata: ${err && err.stack ? err.stack : err}`);
1111
  try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {}
 
1127
  });
1128
  }
1129
 
 
1130
  async function scanAndFeedCore() {
1131
  if (!bot || !botIsReady) return;
1132
  if (!guiMutex.isLocked()) {
 
1239
  });
1240
  }
1241
 
 
 
 
1242
  const FEED_LOCK_RETRY_DELAY_MS = 15 * 1000;
1243
+ const FEED_LOCK_MAX_RETRIES = 8;
1244
 
 
 
 
1245
  async function scanAndFeed(retryCount) {
1246
  retryCount = retryCount || 0;
1247
  if (!guiMutex.tryAcquire()) {
 
1264
  }
1265
  }
1266
 
 
 
1267
  async function scanAndFeedManual() {
1268
  logger.info("[Besleyici - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1269
  await guiMutex.acquire();
 
1277
  }
1278
  }
1279
 
 
1280
  async function activateEntityAndAwaitWindow(entity, labelForLogs) {
1281
  if (!bot || !botIsReady || !entity || !entity.position) {
1282
  return { success: false, reason: "Bot veya entity hazır değil" };
 
1374
  return matches;
1375
  }
1376
 
 
 
1377
  async function scanAndCollectMinersCore() {
1378
  if (!bot || !botIsReady) return;
1379
  if (!guiMutex.isLocked()) {
 
1424
  logger.info(`[${currentBotUsername}] Kazıcı minyon depo aktarım turu tamamlandı.`);
1425
  }
1426
 
 
1427
  async function scanAndCollectMiners() {
1428
  if (!guiMutex.tryAcquire()) {
1429
  logger.warn("[Kazıcı Toplama] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor.");
 
1438
  }
1439
  }
1440
 
 
1441
  async function scanAndCollectMinersManual() {
1442
  logger.info("[Kazıcı Toplama - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1443
  await guiMutex.acquire();
 
1452
  }
1453
  }
1454
 
 
 
 
 
 
1455
  async function openCiftciAndSellAllCore() {
1456
  if (!bot || !botIsReady) return;
1457
  if (!guiMutex.isLocked()) {
 
1467
  await new Promise(r => setTimeout(r, 300));
1468
  }
1469
 
 
 
 
 
1470
  let timedOut = false;
1471
 
1472
  await new Promise((resolve) => {
 
1505
  }
1506
  }
1507
 
 
1508
  async function openCiftciAndSellAll() {
1509
  if (!guiMutex.tryAcquire()) {
1510
  logger.warn("[Çiftçi] Zaten bir GUI işlemi sürüyor (besleyici/kazıcı/çiftçi). Bu tur atlanıyor.");
 
1519
  }
1520
  }
1521
 
 
1522
  async function openCiftciAndSellAllManual() {
1523
  logger.info("[Çiftçi - Manuel] Panelden istek geldi, sırasını bekliyor (öncelikli)...");
1524
  await guiMutex.acquire();
 
1533
  }
1534
  }
1535
 
 
 
 
1536
  function startKaziciPeriodic() {
1537
+ if (kaziciIntervalHandle) return;
1538
 
1539
  const runCycle = async () => {
1540
  if (!bot || !botIsReady) return;
 
1565
  logger.info(`[Kazıcı Periyodik] Bağımsız döngü başlatıldı (her ${formatDurationMs(KAZICI_CYCLE_INTERVAL_MS)}).`);
1566
  }
1567
 
 
1568
  function startCiftciPeriodic() {
1569
+ if (ciftciIntervalHandle) return;
1570
 
1571
  const runCycle = async () => {
1572
  if (!bot || !botIsReady) return;
 
1606
  botIsReady = true;
1607
  botUptimeStart = Date.now();
1608
 
 
1609
  botActions.scanAndFeedManual = scanAndFeedManual;
1610
  botActions.scanAndCollectMinersManual = scanAndCollectMinersManual;
1611
  botActions.openCiftciAndSellAllManual = openCiftciAndSellAllManual;
1612
  botActions.goToIslandManual = goToIsland;
1613
  botActions.sendSkyblockManual = sendSkyblockCommand;
1614
+ botActions.antiBotMove = antiBotMove();
1615
 
 
 
1616
  startKaziciPeriodic();
1617
  startCiftciPeriodic();
1618
+ startStallWatcher();
1619
 
1620
  if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) {
1621
  const password = config.utils["auto-auth"].password;
 
1642
  }
1643
  }, 7500);
1644
 
 
1645
  setSafeTimeout(async () => {
1646
  if (!bot || !botIsReady) return;
1647
  isMainCycleRunning = true;
 
1649
  await goToIsland();
1650
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
1651
  await scanAndFeed();
 
 
1652
  } catch (err) {
1653
  logger.error(`[Spawn Hatası] İlk kontrol döngüsünde beklenmeyen hata: ${err && err.stack ? err.stack : err}`);
1654
  try { scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS); } catch (e2) {}
 
1662
  }
1663
  });
1664
 
1665
+ bot.on("entityMoved", (entity) => {
1666
+ try { trackKaziciPoseIfWatched(entity); } catch (e) {}
1667
+ });
1668
+
1669
+ bot.on("entitySpawn", (entity) => {
1670
+ try { trackKaziciPoseIfWatched(entity); } catch (e) {}
1671
+ });
1672
+
1673
  bot.on("windowOpen", async (window) => {
1674
  if (!bot) return;
1675
  try {
 
1699
  try { bot.closeWindow(window); } catch(e){}
1700
 
1701
  } else if (isCiftciWindow) {
 
 
 
 
 
1702
  try {
1703
  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...`);
1704
  await new Promise(r => setTimeout(r, CIFTCI_CLICK_DELAY_AFTER_OPEN_MS));
 
1783
  try {
1784
  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...");
1785
  stopPeriodicHomeChecks();
1786
+ stopStallWatcher();
1787
  lastCheckAtMs = null;
1788
  botIsReady = false;
1789
  baglantiTuru = "Yok";
 
2070
  next_ciftci_sell: nextCiftciTimeStr,
2071
  gui_lock_busy: guiMutex.isLocked(),
2072
  manually_stopped: manualStopRequested,
2073
+ is_connecting: isConnecting,
2074
+ failover_mode_active: failoverModeActive
2075
  });
2076
  });
2077
 
 
2094
  res.send({ success: true, message: "Bot tamamen sıfırdan yeniden başlatılıyor (soket/SSH dahil)." });
2095
  });
2096
 
 
 
 
 
 
 
2097
  app.get("/bot/kazici-kontrol", (req, res) => {
2098
  if (!bot || !botIsReady || !botActions.scanAndCollectMinersManual) {
2099
  return res.status(503).send({ success: false, message: "Bot hazır değil." });
 
2134
  res.send({ success: true, message: `${GO_ISLAND_COMMAND} komutu gönderildi.` });
2135
  });
2136
 
2137
+ app.get("/bot/sendmsg", (req, res) => {
2138
+ if (!bot || !botIsReady) {
2139
+ return res.status(503).send({ success: false, message: "Bot hazır değil." });
2140
+ }
2141
+
2142
+ const message = req.query.message;
2143
+ if (!message || typeof message !== "string" || message.trim().length === 0) {
2144
+ return res.status(400).send({ success: false, message: "message query parametresi zorunlu." });
2145
+ }
2146
+
2147
+ try {
2148
+ bot.chat(message);
2149
+ logger.info(`[${bot.username}] /sendmsg üzerinden chat mesajı gönderildi: ${message}`);
2150
+ res.send({ success: true, message: "Mesaj gönderildi.", sent: message });
2151
+ } catch (err) {
2152
+ logger.error(`[/bot/sendmsg Hatası] ${err && err.message ? err.message : err}`);
2153
+ res.status(500).send({ success: false, message: "Mesaj gönderilirken hata oluştu." });
2154
+ }
2155
+ });
2156
+
2157
+ app.get("/bot/antibotmove", (req, res) => {
2158
+ if (!bot || !botIsReady) {
2159
+ return res.status(503).send({ success: false, message: "Bot hazır değil." });
2160
+ }
2161
+
2162
+ try {
2163
+ botActions.antiBotMove().catch(e => logger.error(`[/bot/antibotmove Hatası] ${e && e.stack ? e.stack : e}`));
2164
+ res.send({ success: true, message: "Anti Bot Yürüyüşü Yapıldı."});
2165
+ } catch (err) {
2166
+ logger.error(`[/bot/antibotmove Hatası] ${err && err.message ? err.message : err}`);
2167
+ res.status(500).send({ success: false, message: "Anti Bot Move sırasında hata oluştu." });
2168
+ }
2169
+ });
2170
+
2171
  app.get("/", (req, res) => res.send("Bot Aktif"));
2172
 
2173
  app.get("/messages", (req, res) => {