Suvarii commited on
Commit
d197078
·
verified ·
1 Parent(s): bf1a70b

Update bot.js

Browse files
Files changed (1) hide show
  1. bot.js +177 -202
bot.js CHANGED
@@ -44,19 +44,13 @@ const CIFTCI_SELL_ALL_SLOT = 45;
44
  const CIFTCI_COMMAND = "/çiftçi menü";
45
  const CIFTCI_WINDOW_TIMEOUT_MS = 6000;
46
  const CIFTCI_LORE_FALLBACK_KEYWORDS = ["hepsini sat"];
47
- // Menü açıldıktan sonra sunucunun içeriği (lore/slot) tam yerleştirmesi için
48
- // beklenecek süre, tıklamadan önce. Ardından art arda birkaç kez tıklanır.
49
  const CIFTCI_CLICK_DELAY_AFTER_OPEN_MS = 1000;
50
  const CIFTCI_CLICK_REPEAT_COUNT = 3;
51
  const CIFTCI_CLICK_REPEAT_GAP_MS = 350;
52
 
53
- // --- KAZICI PERİYODİK DÖNGÜ AYARLARI (bağımsız, 5 dakikada bir) ---
54
- const KAZICI_CYCLE_INTERVAL_MS = 5 * 60 * 1000; // 5 dakika
55
 
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
- // --- ÇİFTÇİ FİLTRE / STORAGE INFO TARAMA AYARLARI ---
60
  const CIFTCI_FILTER_MODES = [
61
  "Standart",
62
  "Favoriler",
@@ -65,14 +59,14 @@ const CIFTCI_FILTER_MODES = [
65
  "Satışı açık olanlar",
66
  "Toplaması açık olanlar"
67
  ];
68
- const CIFTCI_TARGET_FILTER_INDEX = 3; // "Jeneratör materyalleri"
69
  const CIFTCI_ACTIVE_FILTER_COLOR = "#FCF988";
70
  const CIFTCI_FILTER_ITEM_NAME_KEYWORD = "Filtre";
71
- const CIFTCI_FILTER_STEP_WAIT_MS = 1000; // her tık sonrası ilk bekleme
72
- const CIFTCI_FILTER_RETRY_WAIT_MS = 1000; // item gözükmezse ek bekleme
73
- const CIFTCI_FILTER_MAX_RETRIES = 10; // güvenlik limiti
74
- const STORAGE_SCAN_INTERVAL_MS = 15 * 60 * 1000; // 15 dakika
75
- const STORAGE_TRACKED_ITEMS = ["diamond", "emerald", "iron_block"]; // internal name eşleşmesi
76
 
77
  // --- SİPARİŞ (ORDER) SİSTEMİ AYARLARI ---
78
  const ORDER_COMMAND_PREFIX = "/order";
@@ -81,8 +75,11 @@ const ORDER_WINDOW_SETTLE_DELAY_MS = 500;
81
  const ORDER_AFTER_SELECT_DELAY_MS = 500;
82
  const ORDER_IS_GO_SECOND_DELAY_MS = 5000;
83
  const ORDER_ALLOWED_ITEMS = ["diamond", "emerald"];
84
- const ORDER_LIST_TITLE_KEYWORD = "ѕiᴘᴀʀişʟᴇʀ"; // nick seçim penceresi
85
- const ORDER_DELIVERY_TITLE_KEYWORD = "ᴛᴇѕʟiᴍ ᴇᴛᴍᴇ"; // item bırakma kutusu
 
 
 
86
 
87
  let storageInfoCache = {
88
  diamond: null,
@@ -92,23 +89,18 @@ let storageInfoCache = {
92
  };
93
  let storageScanIntervalHandle = null;
94
 
95
- // Çiftçi penceresinin şu an hangi amaçla açık olduğunu tutan bayrak.
96
- // "sell" -> hepsini-sat akışı, "scan" -> storage bilgisi tarama akışı, "order" -> sipariş akışı.
97
  let ciftciWindowPurpose = "sell";
98
 
99
- // --- KAZICI FAILOVER (YEDEK HESAP) AYARLARI ---
100
- const STALL_CHECK_INTERVAL_MS = 15 * 1000; // pose kontrolü ne sıklıkla yapılsın
101
- const STALL_TIMEOUT_MS = 60 * 1000; // bu süre hareketsiz kalırsa "durdu" say
102
- const FAILOVER_HOME_ARRIVAL_DELAY_MS = 4000; // yedek hesapta /is go sonrası varış beklemesi
103
  const FAILOVER_LOGIN_DELAY_MS = 2500;
104
  const FAILOVER_SKYBLOCK_DELAY_MS = 7500;
105
  const FAILOVER_ISGO_DELAY_MS = 12500;
106
- const FAILOVER_RECHECK_INTERVAL_MS = 20 * 1000; // yedek hesapta minyonları tekrar kontrol aralığı
107
- const FAILOVER_MAX_WAIT_MS = 60 * 60 * 1000; // güvenlik: 1 saatte hâlâ aktifleşmediyse pes et, ana hesaba dön
108
- const FAILOVER_SPAWN_TIMEOUT_MS = 30 * 1000; // yedek hesap 30 sn içinde spawn olmazsa vazgeç
109
 
110
- // İzlenen (KAZICI_NAMES ile eşleşmeyen, başka hesaptan konan) kazıcı minyonların
111
- // skin texture hash'i. Bu skin + elde iron_pickaxe olan armor stand'ler izlenir.
112
  const WATCHED_KAZICI_SKIN_HASHES = new Set([
113
  "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDQ4ODcwY2E3N2FhNjk5ZDAxNzA5M2I4Mjc3OWQwYTU5OGRjNGYzZjQ5ZDM1MTUzMmFlNGQ5NjMzZGNjMmE1YSJ9fX0=",
114
  ]);
@@ -123,7 +115,6 @@ let messagesTimeout = null;
123
  let isListeningChat = false;
124
  const ansiToHtmlConvert = new (require('ansi-to-html'))({ newline: true });
125
 
126
- // --- GÜVENLİ BAĞLANTI DEĞİŞKENLERİ ---
127
  let isConnecting = false;
128
  let isDestroying = false;
129
  let reconnectTimeout = null;
@@ -132,45 +123,37 @@ let currentStream = null;
132
  let currentSocket = null;
133
  let windowOpenResolvers = [];
134
 
135
- // --- PANEL METRİKLERİ ---
136
  let botUptimeStart = null;
137
  let lastFullFeedTimeStr = "Henüz besleme yapılmadı";
138
  let lastCheckTimeStr = "Henüz kontrol yapılmadı";
139
  let nextScheduledCheckTimeStr = "Botun spawn olması bekleniyor...";
140
 
141
- // --- KAZICI PANEL METRİKLERİ (bağımsız) ---
142
  let lastKaziciCheckTimeStr = "Henüz kazıcı kontrolü yapılmadı";
143
  let nextKaziciTimeStr = "Botun spawn olması bekleniyor...";
144
  let nextKaziciAtMs = null;
145
 
146
- // --- ÇİFTÇİ PANEL METRİKLERİ (bağımsız) ---
147
  let lastCiftciSellTimeStr = "Henüz çiftçi satışı yapılmadı";
148
  let nextCiftciTimeStr = "Botun spawn olması bekleniyor...";
149
  let nextCiftciAtMs = null;
150
 
151
- // --- SİPARİŞ (ORDER) PANEL METRİKLERİ ---
152
  let lastOrderTimeStr = "Henüz sipariş işlemi yapılmadı";
153
 
154
- // --- ÇİFTÇİ/KAZICI PERİYODİK DÖNGÜ KONTROLÜ ---
155
- let lastCheckAtMs = null; // Besleyici için (panel bilgisi amaçlı, artık kazıcı/çiftçiyi etkilemiyor)
156
- let nextScheduledCheckAtMs = null; // Besleyici için
157
- let kaziciIntervalHandle = null; // Kazıcı bağımsız döngü zamanlayıcısı (5 dk)
158
- let ciftciIntervalHandle = null; // Çiftçi bağımsız döngü zamanlayıcısı (1 dk)
159
- let isMainCycleRunning = false; // Besleyici (goToIsland->besle) ana döngüsü sürüyor mu
160
 
161
- // --- KAZICI FAILOVER (YEDEK HESAP) DURUM DEĞİŞKENLERİ ---
162
- let poseTracker = new Map(); // entityId -> { poseKey, lastChangeAt, uuid }
163
  let stallCheckIntervalHandle = null;
164
- let failoverModeActive = false; // true iken ana botun periyodik işlemleri fiilen durur (bot=null olduğu için)
165
- let failoverBot = null; // yedek hesabın mineflayer instance'ı
166
- let failoverInProgress = false; // aynı anda 2 kez tetiklenmesin diye kilit
167
 
168
- // --- DİNAMİK IP VE KONUM BİLGİLERİ ---
169
  let activeConnectionIp = "Bilinmiyor";
170
  let activeConnectionLocation = "Bilinmiyor";
171
  let localExternalIp = "Bilinmiyor";
172
 
173
- // --- DİNAMİK ZAMANLAYICI KONTROLÜ ---
174
  let periodicTimeoutHandle = null;
175
  let activeTimeouts = [];
176
 
@@ -180,7 +163,6 @@ function setSafeTimeout(fn, delay) {
180
  return t;
181
  }
182
 
183
- // --- ÖNCELİKLİ GUI KİLİDİ (MUTEX) ---
184
  class PriorityGuiMutex {
185
  constructor() {
186
  this._locked = false;
@@ -275,7 +257,6 @@ function stopPeriodicHomeChecks() {
275
  nextCiftciTimeStr = "Zamanlama durduruldu.";
276
  }
277
 
278
- // --- MANUEL DURDURMA/BAŞLATMA KONTROLÜ ---
279
  let manualStopRequested = false;
280
 
281
  async function getIpLocation(ip) {
@@ -324,14 +305,6 @@ https.get('https://api.ipify.org?format=json', (res) => {
324
  });
325
  }).on('error', () => {});
326
 
327
- // =====================================================================
328
- // KAZICI FAILOVER (YEDEK HESAP) YARDIMCI FONKSİYONLARI
329
- // Bunlar module-level (createBot() dışında) çünkü bot bağlantısı kesilip
330
- // yeniden kurulduğunda bile (createBot() tekrar çağrıldığında) hayatta
331
- // kalmaları gerekiyor.
332
- // =====================================================================
333
-
334
- // Entity'nin skin texture değerini (player_head equipment'inden) çıkarır.
335
  function getEntitySkinHash(entity) {
336
  try {
337
  const equipment = entity.equipment || [];
@@ -351,7 +324,6 @@ function getEntitySkinHash(entity) {
351
  return null;
352
  }
353
 
354
- // Entity'nin elinde (equipment[0] = main hand) iron_pickaxe olup olmadığını kontrol eder.
355
  function hasIronPickaxeEquipped(entity) {
356
  try {
357
  const equipment = entity.equipment || [];
@@ -362,7 +334,6 @@ function hasIronPickaxeEquipped(entity) {
362
  }
363
  }
364
 
365
- // Skin + iron_pickaxe ikisi birden tutuyorsa bu entity izlenen kazıcı minyondur.
366
  function isWatchedKaziciEntity(entity) {
367
  if (!entity || entity.name !== "armor_stand") return false;
368
  const skinHash = getEntitySkinHash(entity);
@@ -370,7 +341,6 @@ function isWatchedKaziciEntity(entity) {
370
  return hasIronPickaxeEquipped(entity);
371
  }
372
 
373
- // metadata içindeki pose (kol/kafa açıları) + pozisyonu birleştirip bir imza çıkarır.
374
  function getPoseKey(entity) {
375
  try {
376
  const md = entity.metadata || [];
@@ -389,8 +359,6 @@ function getPoseKey(entity) {
389
  }
390
  }
391
 
392
- // bot.on('entityMoved', ...) ve bot.on('entitySpawn', ...) içinden çağrılır.
393
- // İzlenen kazıcı minyonun pose'unu takip eder.
394
  function trackKaziciPoseIfWatched(entity) {
395
  if (!entity) return;
396
  if (!isWatchedKaziciEntity(entity)) return;
@@ -408,10 +376,8 @@ function trackKaziciPoseIfWatched(entity) {
408
  prev.poseKey = poseKey;
409
  prev.lastChangeAt = Date.now();
410
  }
411
- // poseKey aynıysa lastChangeAt GÜNCELLENMEZ -> hareketsizlik süresi büyümeye devam eder.
412
  }
413
 
414
- // Artık dünyada olmayan entity kayıtlarını temizler.
415
  function pruneStaleTrackedEntities() {
416
  if (!bot || !bot.entities) return;
417
  for (const id of poseTracker.keys()) {
@@ -452,9 +418,6 @@ function stopStallWatcher() {
452
  poseTracker.clear();
453
  }
454
 
455
- // Ana hesabı geçici düşürüp yedek hesabı (kullaniciadi1) devreye alan, izlenen
456
- // kazıcı minyonlar tekrar hareket edene kadar bekleyen, sonra ana hesabı
457
- // yeniden bağlayan tam akış.
458
  async function triggerFailoverSequence() {
459
  if (failoverInProgress) return;
460
  failoverInProgress = true;
@@ -465,7 +428,7 @@ async function triggerFailoverSequence() {
465
 
466
  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.`);
467
 
468
- manualStopRequested = true; // mevcut auto-reconnect mantığını geçici olarak devre dışı bırakır
469
  stopPeriodicHomeChecks();
470
  safeDestroyAllConnections();
471
 
@@ -484,9 +447,6 @@ async function triggerFailoverSequence() {
484
  failoverInProgress = false;
485
  }
486
 
487
- // Yedek hesapla ayrı bir mineflayer bot'u başlatır: /login, /skyblock, /is go
488
- // atar (ana hesaptaki ile AYNI gecikme süreleriyle), sonra izlenen minyonların
489
- // hareketine bakar. Hareket geri gelince bağlantıyı kapatıp resolve eder.
490
  function runBackupAccountAndWaitForRecovery(backupUsername) {
491
  return new Promise((resolve) => {
492
  const backupPoseTracker = new Map();
@@ -549,7 +509,6 @@ function runBackupAccountAndWaitForRecovery(backupUsername) {
549
  logger.info(`[Failover - Yedek Hesap] ${GO_ISLAND_COMMAND} gönderildi, izlenen minyonların tekrar hareket etmesi bekleniyor...`);
550
  }, FAILOVER_ISGO_DELAY_MS);
551
 
552
- // Adaya varış + izleme başlatma
553
  setTimeout(() => {
554
  recheckHandle = setInterval(() => {
555
  try {
@@ -566,7 +525,7 @@ function runBackupAccountAndWaitForRecovery(backupUsername) {
566
 
567
  if (!prev) {
568
  backupPoseTracker.set(e.id, { poseKey, lastChangeAt: Date.now() });
569
- allRecovered = false; // henüz yeterince veri yok, bir tur daha bekle
570
  continue;
571
  }
572
 
@@ -574,7 +533,7 @@ function runBackupAccountAndWaitForRecovery(backupUsername) {
574
  prev.poseKey = poseKey;
575
  prev.lastChangeAt = Date.now();
576
  } else if (Date.now() - prev.lastChangeAt < STALL_TIMEOUT_MS) {
577
- // son 1 dk içinde bir değişiklik olmuş demek ki hareket ediyor
578
  } else {
579
  allRecovered = false;
580
  }
@@ -605,7 +564,6 @@ function runBackupAccountAndWaitForRecovery(backupUsername) {
605
  }
606
  });
607
 
608
- // Güvenlik: sonsuza kadar beklemesin
609
  safetyTimeoutHandle = setTimeout(() => {
610
  logger.warn(`[Failover - Yedek Hesap] Azami bekleme süresi (${formatDurationMs(FAILOVER_MAX_WAIT_MS)}) doldu, ana hesaba geri dönülüyor.`);
611
  finish();
@@ -967,72 +925,68 @@ async function createBot() {
967
  } catch (e) { return undefined; }
968
  };
969
 
970
- // components dizisinden belirli tipteki component'in data'sını bulur
971
- function getComponentData(slot, type) {
972
- try {
973
- if (!slot || !Array.isArray(slot.components)) return undefined;
974
- const comp = slot.components.find(c => c && c.type === type);
975
- return comp ? comp.data : undefined;
976
- } catch (e) { return undefined; }
977
- }
978
-
979
- // {type,value} tarzı NBT-benzeri sarmalayıcıyı açar
980
- function nbtUnwrap(node) {
981
- if (node && typeof node === "object" && "type" in node && "value" in node) {
982
- return node.value;
983
  }
984
- return node;
985
- };
986
 
987
- // Yeni "components" formatındaki text-component ağacını düz metne çevirir
988
- function flattenTextComponent(node) {
989
- if (node == null) return "";
990
- if (typeof node === "string") return node;
991
- if (Array.isArray(node)) return node.map(flattenTextComponent).join("");
992
-
993
- if (typeof node === "object") {
994
- if ("type" in node && "value" in node) {
995
- const t = node.type, v = node.value;
996
- if (t === "string") return typeof v === "string" ? v : "";
997
- if (t === "list" || t === "compound") return flattenTextComponent(v);
998
- return ""; // byte/int vs. metin değil
999
- }
1000
- let out = "";
1001
- if ("text" in node) out += flattenTextComponent(node.text);
1002
- if ("extra" in node) out += flattenTextComponent(node.extra);
1003
- return out;
1004
- }
1005
- return "";
1006
- };
1007
 
1008
- // Tek bir lore satırı node'unu {text, color} olarak parse eder (yeni format)
1009
- function parseComponentLoreLine(lineNode) {
1010
- let text = "";
1011
- let color = null;
1012
 
1013
- function walk(node) {
1014
- if (node == null) return;
1015
- if (typeof node === "string") { text += node; return; }
1016
- if (Array.isArray(node)) { node.forEach(walk); return; }
1017
  if (typeof node === "object") {
1018
  if ("type" in node && "value" in node) {
1019
  const t = node.type, v = node.value;
1020
- if (t === "string") { text += (typeof v === "string" ? v : ""); return; }
1021
- if (t === "list" || t === "compound") { walk(v); return; }
1022
- return;
1023
  }
1024
- if ("color" in node) {
1025
- const colVal = nbtUnwrap(node.color);
1026
- if (color == null && typeof colVal === "string") color = colVal;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1027
  }
1028
- if ("text" in node) walk(node.text);
1029
- if ("extra" in node) walk(node.extra);
1030
  }
1031
- }
1032
 
1033
- walk(lineNode);
1034
- return { text: text.trim(), color };
1035
- };
1036
 
1037
  function deepSearchForString(root, targetVariantsList) {
1038
  const results = [];
@@ -1106,8 +1060,7 @@ function parseComponentLoreLine(lineNode) {
1106
  const texts = [];
1107
  try {
1108
  if (!slot) return texts;
1109
-
1110
- // Yeni format: components
1111
  const customNameData = getComponentData(slot, "custom_name");
1112
  if (customNameData !== undefined) {
1113
  const nameTxt = flattenTextComponent(customNameData);
@@ -1121,8 +1074,7 @@ function parseComponentLoreLine(lineNode) {
1121
  }
1122
  }
1123
  if (texts.length > 0) return texts;
1124
-
1125
- // Eski format: nbt (fallback)
1126
  function extractPlain(raw) {
1127
  if (!raw || typeof raw !== "string") return null;
1128
  try {
@@ -1137,12 +1089,12 @@ function parseComponentLoreLine(lineNode) {
1137
  } catch (e) {}
1138
  return raw;
1139
  }
1140
-
1141
  if (slot.nbt) {
1142
  const name = safeGet(slot.nbt, ["value", "display", "value", "Name", "value"]);
1143
  const plainName = extractPlain(name);
1144
  if (plainName) texts.push(plainName);
1145
-
1146
  const lore = safeGet(slot.nbt, ["value", "display", "value", "Lore", "value"]);
1147
  if (Array.isArray(lore)) {
1148
  for (const line of lore) {
@@ -1151,7 +1103,7 @@ function parseComponentLoreLine(lineNode) {
1151
  }
1152
  }
1153
  }
1154
-
1155
  if (texts.length === 0) {
1156
  texts.push(util.inspect(slot, { depth: null }));
1157
  }
@@ -1194,12 +1146,10 @@ function parseComponentLoreLine(lineNode) {
1194
  }
1195
  }
1196
 
1197
- // --- SENDMONEY: /money oku, bakiyeden 1000 eksiğini ilgili nick'e /pay ile gönder ---
1198
  const MONEY_BALANCE_REGEX = /bakiyen:\s*([\d.,]+)\s*TL/i;
1199
  const MONEY_RESPONSE_TIMEOUT_MS = 8000;
1200
 
1201
  function parseBalanceToAdjustedAmount(rawBalanceStr) {
1202
- // "444,626.89" -> virgülleri kaldır -> "444626.89" -> noktadan öncesini al -> "444626"
1203
  const noCommas = rawBalanceStr.replace(/,/g, "");
1204
  const beforeDot = noCommas.split(".")[0];
1205
  const intValue = parseInt(beforeDot, 10);
@@ -1281,22 +1231,21 @@ function parseComponentLoreLine(lineNode) {
1281
  if (!bot || !botIsReady) return;
1282
  try {
1283
  const moves = ['forward', 'left', 'right', 'jump'];
1284
-
1285
- const moveCount = Math.floor(Math.random() * 4) + 2; // 2-5 hareket
1286
-
1287
  for (let i = 0; i < moveCount; i++) {
1288
  const move = moves[Math.floor(Math.random() * moves.length)];
1289
  const duration = 150 + Math.floor(Math.random() * 500);
1290
-
1291
  bot.setControlState(move, true);
1292
  await new Promise(r => setTimeout(r, duration));
1293
  bot.setControlState(move, false);
1294
-
1295
  await new Promise(r => setTimeout(r, 100 + Math.random() * 300));
1296
-
1297
  }
1298
-
1299
- // Rastgele kafa hareketi
1300
  await bot.look(
1301
  bot.entity.yaw + ((Math.random() - 0.5) * 1.5),
1302
  bot.entity.pitch + ((Math.random() - 0.5) * 0.3),
@@ -1752,24 +1701,18 @@ function activateEntityAtNoLook(entity, position) {
1752
  }
1753
  }
1754
 
1755
- // =====================================================================
1756
- // ÇİFTÇİ FİLTRE NAVİGASYONU + STORAGE INFO TARAMA
1757
- // =====================================================================
1758
-
1759
  function stripColorAndGetLoreLines(window) {
1760
  if (!window || !Array.isArray(window.slots)) return null;
1761
  for (const slot of window.slots) {
1762
  if (!slot) continue;
1763
-
1764
  let nameTxt = "";
1765
-
1766
- // Yeni format: components
1767
  const customNameData = getComponentData(slot, "custom_name");
1768
  if (customNameData !== undefined) {
1769
  nameTxt = flattenTextComponent(customNameData);
1770
  }
1771
-
1772
- // Eski format: nbt (fallback)
1773
  if (!nameTxt && slot.nbt) {
1774
  const nameRaw = safeGet(slot.nbt, ["value", "display", "value", "Name", "value"]);
1775
  try {
@@ -1778,7 +1721,7 @@ function activateEntityAtNoLook(entity, position) {
1778
  else if (maybe && maybe.text) nameTxt = maybe.text;
1779
  } catch (e) { nameTxt = nameRaw || ""; }
1780
  }
1781
-
1782
  if (nameTxt.includes(CIFTCI_FILTER_ITEM_NAME_KEYWORD)) {
1783
  return { slot, index: window.slots.indexOf(slot) };
1784
  }
@@ -1788,8 +1731,7 @@ function activateEntityAtNoLook(entity, position) {
1788
 
1789
  function getFilterItemLoreEntries(filterSlot) {
1790
  const entries = [];
1791
-
1792
- // Yeni format: components
1793
  const loreData = getComponentData(filterSlot, "lore");
1794
  if (Array.isArray(loreData)) {
1795
  for (const line of loreData) {
@@ -1797,8 +1739,7 @@ function activateEntityAtNoLook(entity, position) {
1797
  }
1798
  if (entries.length > 0) return entries;
1799
  }
1800
-
1801
- // Eski format: nbt (fallback)
1802
  try {
1803
  const lore = safeGet(filterSlot.nbt, ["value", "display", "value", "Lore", "value"]);
1804
  if (!Array.isArray(lore)) return entries;
@@ -1834,7 +1775,6 @@ function activateEntityAtNoLook(entity, position) {
1834
  );
1835
  if (match) return i;
1836
  }
1837
- // fallback: sadece renkle bulmayı dene
1838
  const colorMatch = loreEntries.find(e => e.color === CIFTCI_ACTIVE_FILTER_COLOR);
1839
  if (colorMatch) {
1840
  for (let i = 0; i < CIFTCI_FILTER_MODES.length; i++) {
@@ -1845,7 +1785,6 @@ function activateEntityAtNoLook(entity, position) {
1845
  }
1846
 
1847
  async function waitForFilterItem(maxRetries) {
1848
- // window açıldıktan sonra Filtre item'ı gözükene kadar bekler
1849
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1850
  await new Promise(r => setTimeout(r, attempt === 0 ? CIFTCI_FILTER_STEP_WAIT_MS : CIFTCI_FILTER_RETRY_WAIT_MS));
1851
  if (!bot || !bot.currentWindow) return null;
@@ -1917,11 +1856,11 @@ function activateEntityAtNoLook(entity, position) {
1917
  }
1918
 
1919
  const total = CIFTCI_FILTER_MODES.length;
1920
- const forwardDist = (CIFTCI_TARGET_FILTER_INDEX - currentIndex + total) % total; // sol tık (aşağı)
1921
- const backwardDist = (currentIndex - CIFTCI_TARGET_FILTER_INDEX + total) % total; // sağ tık (yukarı)
1922
 
1923
  const useLeftClick = forwardDist <= backwardDist;
1924
- const mouseButton = useLeftClick ? 0 : 1; // 0 = left, 1 = right
1925
 
1926
  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.`);
1927
 
@@ -1931,8 +1870,6 @@ function activateEntityAtNoLook(entity, position) {
1931
  logger.error(`[Çiftçi Filtre] Tıklama hatası: ${e && e.message ? e.message : e}`);
1932
  return false;
1933
  }
1934
-
1935
- // bir sonraki turda waitForFilterItem zaten bekleyecek
1936
  }
1937
 
1938
  logger.warn("[Çiftçi Filtre] Azami deneme sayısına ulaşıldı, Jeneratör materyallerine geçilemedi.");
@@ -2033,16 +1970,6 @@ function activateEntityAtNoLook(entity, position) {
2033
 
2034
  // =====================================================================
2035
  // SİPARİŞ (ORDER) SİSTEMİ
2036
- // Akış:
2037
- // 1) Çiftçi menüsü -> Jeneratör materyalleri filtresine geç (varsa zaten oradadır)
2038
- // 2) İstenen item'ın (diamond/emerald) slotuna shift+sağ tık -> envanter dolana kadar item verir
2039
- // 3) Menüyü kapat
2040
- // 4) antiBotMove -> /is go -> 5sn bekle -> /is go
2041
- // 5) /order <nick> gönder
2042
- // 6) windowOpen bekle, +500ms sonra güncel pencereyi oku
2043
- // 7) Lore'da nick geçen slotu bul -> sol tık ile seç
2044
- // 8) +500ms sonra pencere boşsa envanterdeki item'ları shift+sol tık ile order'a aktar
2045
- // 9) Pencereyi kapat
2046
  // =====================================================================
2047
 
2048
  async function waitForNextWindowOpen(timeoutMs) {
@@ -2075,7 +2002,7 @@ function activateEntityAtNoLook(entity, position) {
2075
  return -1;
2076
  }
2077
 
2078
- async function processOrderCore(itemInternalName, nick) {
2079
  if (!bot || !botIsReady) return { success: false, message: "Bot hazır değil." };
2080
  if (!guiMutex.isLocked()) {
2081
  logger.error("[GÜVENLİK İHLALİ] processOrderCore kilitsiz çağrıldı! İşlem iptal ediliyor.");
@@ -2137,6 +2064,22 @@ async function processOrderCore(itemInternalName, nick) {
2137
  logger.error(`[Sipariş] Shift+sağ tık hatası: ${e && e.message ? e.message : e}`);
2138
  }
2139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2140
  // --- 3) Menüyü kapat ---
2141
  try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
2142
  // ÖNEMLİ: purpose burada "sell"e ÇEVRİLMEZ! /order akışı (Siparişler +
@@ -2194,25 +2137,54 @@ async function processOrderCore(itemInternalName, nick) {
2194
  }
2195
 
2196
  logger.info(`[Sipariş] '${nick}' içeren item bulundu (idx:${targetSlotIndex}), sol tık gönderiliyor...`);
 
 
 
 
 
 
 
2197
  try {
2198
  await bot.clickWindow(targetSlotIndex, 0, 0);
2199
  } catch (e) {
2200
  logger.error(`[Sipariş] Slot seçim tıklaması hatası: ${e && e.message ? e.message : e}`);
2201
  }
2202
- // --- 8) +500ms sonra pencere tekrar okunur (Teslim Etme penceresi bu olmalı) ---
2203
- await new Promise(r => setTimeout(r, ORDER_AFTER_SELECT_DELAY_MS));
2204
 
2205
- if (!bot.currentWindow) {
2206
- logger.warn("[Sipariş] Slot seçiminden sonra pencere kapandı, işlem yarım kalmış olabilir.");
 
 
 
2207
  ciftciWindowPurpose = "sell";
2208
- return { success: false, message: "Slot seçiminden sonra pencere kapandı." };
 
 
 
 
 
 
 
2209
  }
2210
 
2211
- // Teslim Etme penceresi her zaman boş açılıyor, bu yüzden dolu/boş kontrolü
2212
- // yapmadan direkt envanterdeki item'ları shift+sol tık ile aktarıyoruz.
2213
  const currentTitle = parseWindowTitleToString(bot.currentWindow.title);
2214
- logger.info(`[Sipariş] Güncel pencere: ${currentTitle}. Envanterdeki '${itemInternalName}' item'ları Teslim Etme'ye aktarılıyor...`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2215
 
 
2216
  let transferredCount = 0;
2217
  let keepGoing = true;
2218
  let safetyCounter = 0;
@@ -2221,12 +2193,17 @@ async function processOrderCore(itemInternalName, nick) {
2221
 
2222
  while (keepGoing && safetyCounter < 40) {
2223
  safetyCounter++;
2224
- if (!bot.currentWindow) break;
 
 
 
 
 
 
2225
 
2226
  const invItem = bot.inventory.items().find(it => it && it.name === itemInternalName);
2227
  if (!invItem) { keepGoing = false; break; }
2228
 
2229
- // Aynı slot art arda takılıp kalıyorsa (item bir türlü taşınmıyorsa) döngüyü durdur.
2230
  if (stuckSlot === invItem.slot) {
2231
  stuckSlotRepeatCount++;
2232
  if (stuckSlotRepeatCount >= 3) {
@@ -2243,10 +2220,9 @@ async function processOrderCore(itemInternalName, nick) {
2243
  await bot.clickWindow(invItem.slot, 0, 1); // mouseButton=0 (sol), mode=1 (shift)
2244
  await new Promise(r => setTimeout(r, 250));
2245
 
2246
- // Gerçekten taşınmış mı doğrula: aynı slotta hâlâ aynı item duruyor mu?
2247
  const stillThere = bot.inventory.items().find(it => it && it.slot === invItem.slot && it.name === itemInternalName);
2248
  if (!stillThere) {
2249
- transferredCount++; // slot boşaldı = gerçekten taşındı
2250
  } else {
2251
  logger.warn(`[Sipariş] Slot ${invItem.slot} tıklandı ama item hâlâ envanterde, taşınmamış olabilir.`);
2252
  }
@@ -2259,18 +2235,25 @@ async function processOrderCore(itemInternalName, nick) {
2259
  if (safetyCounter >= 40) {
2260
  logger.warn(`[Sipariş] Güvenlik limitine (40 deneme) ulaşıldı. Teslim Etme kutusu dolu/limitli olabilir, transfer yarım kalmış olabilir.`);
2261
  }
2262
- logger.info(`[Sipariş] Envanterden Teslim Etme'ye aktarım turu tamamlandı (${transferredCount} yığın gönderildi).`);
 
 
 
 
 
2263
 
2264
  // --- 9) Pencereyi kapat ---
2265
  try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
2266
  ciftciWindowPurpose = "sell"; // akış tamamen bitti, artık normal davranışa dön
2267
 
2268
  return {
2269
- success: true,
2270
- message: "Sipariş işlemi tamamlandı.",
2271
  item: itemInternalName,
2272
  nick,
2273
- inventory_transferred_batches: transferredCount
 
 
2274
  };
2275
  }
2276
 
@@ -2298,7 +2281,6 @@ async function processOrderCore(itemInternalName, nick) {
2298
  scanStorageInfo();
2299
  }, STORAGE_SCAN_INTERVAL_MS);
2300
  logger.info(`[Storage Tarama] Bağımsız döngü başlatıldı (her ${formatDurationMs(STORAGE_SCAN_INTERVAL_MS)}).`);
2301
- // ilk taramayı biraz gecikmeli başlat ki spawn sonrası diğer işler önce otursun
2302
  setSafeTimeout(() => { scanStorageInfo(); }, 30 * 1000);
2303
  }
2304
 
@@ -2453,14 +2435,9 @@ async function processOrderCore(itemInternalName, nick) {
2453
  const isCiftciWindow = !isBesleyiciWindow && !isKaziciWindow &&
2454
  (matchesAnyName(titleRaw, TARGET_CIFTCI_NORMS) || windowMatchesLoreKeyword(window, CIFTCI_LORE_FALLBACK_KEYWORDS));
2455
 
2456
- // Sipariş (/order) penceresi çiftçi/kazıcı/besleyici isimleriyle eşleşmez,
2457
- // bu yüzden ayrıca ele alınır: ciftciWindowPurpose "order" ise otomatik
2458
- // tıklama/kapatma yapılmaz, sadece resolver tetiklenir ve processOrderCore
2459
- // kendi akışını (500ms bekleme + güncel pencere okuma) yönetir.
2460
  if (!isBesleyiciWindow && !isKaziciWindow && !isCiftciWindow) {
2461
  if (ciftciWindowPurpose === "order") {
2462
  logger.info(`[${currentBotUsername}] Sipariş (/order) penceresi açıldı: ${titleRaw}`);
2463
- // otomatik kapatma yapma, processOrderCore yönetecek
2464
  } else {
2465
  logger.info(`[${currentBotUsername}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${titleRaw}`);
2466
  try { bot.closeWindow(window); } catch(e){}
@@ -2670,7 +2647,6 @@ function triggerSafeReconnect() {
2670
  }, delay);
2671
  }
2672
 
2673
- // --- MANUEL BOT KONTROLÜ (stop/start/restart) ---
2674
  let pendingHardRestartTimeout = null;
2675
 
2676
  function stopBotHard(reason) {
@@ -2874,7 +2850,6 @@ app.get("/storageInfo", (req, res) => {
2874
  });
2875
  });
2876
 
2877
- // --- MANUEL BOT KONTROLÜ ENDPOINT'LERİ ---
2878
  app.get("/bot/stop", (req, res) => {
2879
  stopBotHard("Web paneli üzerinden /bot/stop isteği");
2880
  res.send({ success: true, message: "Bot durduruldu. Tekrar başlatmak için /bot/start kullan." });
@@ -3254,4 +3229,4 @@ process.stderr.write = function (chunk, encoding, callback) {
3254
  return true;
3255
  }
3256
  return originalStderrWrite.apply(process.stderr, arguments);
3257
- };
 
44
  const CIFTCI_COMMAND = "/çiftçi menü";
45
  const CIFTCI_WINDOW_TIMEOUT_MS = 6000;
46
  const CIFTCI_LORE_FALLBACK_KEYWORDS = ["hepsini sat"];
 
 
47
  const CIFTCI_CLICK_DELAY_AFTER_OPEN_MS = 1000;
48
  const CIFTCI_CLICK_REPEAT_COUNT = 3;
49
  const CIFTCI_CLICK_REPEAT_GAP_MS = 350;
50
 
51
+ const KAZICI_CYCLE_INTERVAL_MS = 5 * 60 * 1000;
52
+ const CIFTCI_CYCLE_INTERVAL_MS = 60 * 1000;
53
 
 
 
 
 
54
  const CIFTCI_FILTER_MODES = [
55
  "Standart",
56
  "Favoriler",
 
59
  "Satışı açık olanlar",
60
  "Toplaması açık olanlar"
61
  ];
62
+ const CIFTCI_TARGET_FILTER_INDEX = 3;
63
  const CIFTCI_ACTIVE_FILTER_COLOR = "#FCF988";
64
  const CIFTCI_FILTER_ITEM_NAME_KEYWORD = "Filtre";
65
+ const CIFTCI_FILTER_STEP_WAIT_MS = 1000;
66
+ const CIFTCI_FILTER_RETRY_WAIT_MS = 1000;
67
+ const CIFTCI_FILTER_MAX_RETRIES = 10;
68
+ const STORAGE_SCAN_INTERVAL_MS = 15 * 60 * 1000;
69
+ const STORAGE_TRACKED_ITEMS = ["diamond", "emerald", "iron_block"];
70
 
71
  // --- SİPARİŞ (ORDER) SİSTEMİ AYARLARI ---
72
  const ORDER_COMMAND_PREFIX = "/order";
 
75
  const ORDER_AFTER_SELECT_DELAY_MS = 500;
76
  const ORDER_IS_GO_SECOND_DELAY_MS = 5000;
77
  const ORDER_ALLOWED_ITEMS = ["diamond", "emerald"];
78
+ const ORDER_LIST_TITLE_KEYWORD = "ѕiᴘᴀʀişʟᴇʀ";
79
+ const ORDER_DELIVERY_TITLE_KEYWORD = "ᴛᴇѕʟiᴍ ᴇᴛᴍᴇ";
80
+ // Nick seçildikten sonra Teslim Etme penceresinin gerçekten açılmasını beklemek için
81
+ // kullanılan zaman aşımı (sabit sleep yerine).
82
+ const ORDER_DELIVERY_WINDOW_WAIT_MS = 8000;
83
 
84
  let storageInfoCache = {
85
  diamond: null,
 
89
  };
90
  let storageScanIntervalHandle = null;
91
 
 
 
92
  let ciftciWindowPurpose = "sell";
93
 
94
+ const STALL_CHECK_INTERVAL_MS = 15 * 1000;
95
+ const STALL_TIMEOUT_MS = 60 * 1000;
96
+ const FAILOVER_HOME_ARRIVAL_DELAY_MS = 4000;
 
97
  const FAILOVER_LOGIN_DELAY_MS = 2500;
98
  const FAILOVER_SKYBLOCK_DELAY_MS = 7500;
99
  const FAILOVER_ISGO_DELAY_MS = 12500;
100
+ const FAILOVER_RECHECK_INTERVAL_MS = 20 * 1000;
101
+ const FAILOVER_MAX_WAIT_MS = 60 * 60 * 1000;
102
+ const FAILOVER_SPAWN_TIMEOUT_MS = 30 * 1000;
103
 
 
 
104
  const WATCHED_KAZICI_SKIN_HASHES = new Set([
105
  "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDQ4ODcwY2E3N2FhNjk5ZDAxNzA5M2I4Mjc3OWQwYTU5OGRjNGYzZjQ5ZDM1MTUzMmFlNGQ5NjMzZGNjMmE1YSJ9fX0=",
106
  ]);
 
115
  let isListeningChat = false;
116
  const ansiToHtmlConvert = new (require('ansi-to-html'))({ newline: true });
117
 
 
118
  let isConnecting = false;
119
  let isDestroying = false;
120
  let reconnectTimeout = null;
 
123
  let currentSocket = null;
124
  let windowOpenResolvers = [];
125
 
 
126
  let botUptimeStart = null;
127
  let lastFullFeedTimeStr = "Henüz besleme yapılmadı";
128
  let lastCheckTimeStr = "Henüz kontrol yapılmadı";
129
  let nextScheduledCheckTimeStr = "Botun spawn olması bekleniyor...";
130
 
 
131
  let lastKaziciCheckTimeStr = "Henüz kazıcı kontrolü yapılmadı";
132
  let nextKaziciTimeStr = "Botun spawn olması bekleniyor...";
133
  let nextKaziciAtMs = null;
134
 
 
135
  let lastCiftciSellTimeStr = "Henüz çiftçi satışı yapılmadı";
136
  let nextCiftciTimeStr = "Botun spawn olması bekleniyor...";
137
  let nextCiftciAtMs = null;
138
 
 
139
  let lastOrderTimeStr = "Henüz sipariş işlemi yapılmadı";
140
 
141
+ let lastCheckAtMs = null;
142
+ let nextScheduledCheckAtMs = null;
143
+ let kaziciIntervalHandle = null;
144
+ let ciftciIntervalHandle = null;
145
+ let isMainCycleRunning = false;
 
146
 
147
+ let poseTracker = new Map();
 
148
  let stallCheckIntervalHandle = null;
149
+ let failoverModeActive = false;
150
+ let failoverBot = null;
151
+ let failoverInProgress = false;
152
 
 
153
  let activeConnectionIp = "Bilinmiyor";
154
  let activeConnectionLocation = "Bilinmiyor";
155
  let localExternalIp = "Bilinmiyor";
156
 
 
157
  let periodicTimeoutHandle = null;
158
  let activeTimeouts = [];
159
 
 
163
  return t;
164
  }
165
 
 
166
  class PriorityGuiMutex {
167
  constructor() {
168
  this._locked = false;
 
257
  nextCiftciTimeStr = "Zamanlama durduruldu.";
258
  }
259
 
 
260
  let manualStopRequested = false;
261
 
262
  async function getIpLocation(ip) {
 
305
  });
306
  }).on('error', () => {});
307
 
 
 
 
 
 
 
 
 
308
  function getEntitySkinHash(entity) {
309
  try {
310
  const equipment = entity.equipment || [];
 
324
  return null;
325
  }
326
 
 
327
  function hasIronPickaxeEquipped(entity) {
328
  try {
329
  const equipment = entity.equipment || [];
 
334
  }
335
  }
336
 
 
337
  function isWatchedKaziciEntity(entity) {
338
  if (!entity || entity.name !== "armor_stand") return false;
339
  const skinHash = getEntitySkinHash(entity);
 
341
  return hasIronPickaxeEquipped(entity);
342
  }
343
 
 
344
  function getPoseKey(entity) {
345
  try {
346
  const md = entity.metadata || [];
 
359
  }
360
  }
361
 
 
 
362
  function trackKaziciPoseIfWatched(entity) {
363
  if (!entity) return;
364
  if (!isWatchedKaziciEntity(entity)) return;
 
376
  prev.poseKey = poseKey;
377
  prev.lastChangeAt = Date.now();
378
  }
 
379
  }
380
 
 
381
  function pruneStaleTrackedEntities() {
382
  if (!bot || !bot.entities) return;
383
  for (const id of poseTracker.keys()) {
 
418
  poseTracker.clear();
419
  }
420
 
 
 
 
421
  async function triggerFailoverSequence() {
422
  if (failoverInProgress) return;
423
  failoverInProgress = true;
 
428
 
429
  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.`);
430
 
431
+ manualStopRequested = true;
432
  stopPeriodicHomeChecks();
433
  safeDestroyAllConnections();
434
 
 
447
  failoverInProgress = false;
448
  }
449
 
 
 
 
450
  function runBackupAccountAndWaitForRecovery(backupUsername) {
451
  return new Promise((resolve) => {
452
  const backupPoseTracker = new Map();
 
509
  logger.info(`[Failover - Yedek Hesap] ${GO_ISLAND_COMMAND} gönderildi, izlenen minyonların tekrar hareket etmesi bekleniyor...`);
510
  }, FAILOVER_ISGO_DELAY_MS);
511
 
 
512
  setTimeout(() => {
513
  recheckHandle = setInterval(() => {
514
  try {
 
525
 
526
  if (!prev) {
527
  backupPoseTracker.set(e.id, { poseKey, lastChangeAt: Date.now() });
528
+ allRecovered = false;
529
  continue;
530
  }
531
 
 
533
  prev.poseKey = poseKey;
534
  prev.lastChangeAt = Date.now();
535
  } else if (Date.now() - prev.lastChangeAt < STALL_TIMEOUT_MS) {
536
+ // ok
537
  } else {
538
  allRecovered = false;
539
  }
 
564
  }
565
  });
566
 
 
567
  safetyTimeoutHandle = setTimeout(() => {
568
  logger.warn(`[Failover - Yedek Hesap] Azami bekleme süresi (${formatDurationMs(FAILOVER_MAX_WAIT_MS)}) doldu, ana hesaba geri dönülüyor.`);
569
  finish();
 
925
  } catch (e) { return undefined; }
926
  };
927
 
928
+ function getComponentData(slot, type) {
929
+ try {
930
+ if (!slot || !Array.isArray(slot.components)) return undefined;
931
+ const comp = slot.components.find(c => c && c.type === type);
932
+ return comp ? comp.data : undefined;
933
+ } catch (e) { return undefined; }
 
 
 
 
 
 
 
934
  }
 
 
935
 
936
+ function nbtUnwrap(node) {
937
+ if (node && typeof node === "object" && "type" in node && "value" in node) {
938
+ return node.value;
939
+ }
940
+ return node;
941
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
942
 
943
+ function flattenTextComponent(node) {
944
+ if (node == null) return "";
945
+ if (typeof node === "string") return node;
946
+ if (Array.isArray(node)) return node.map(flattenTextComponent).join("");
947
 
 
 
 
 
948
  if (typeof node === "object") {
949
  if ("type" in node && "value" in node) {
950
  const t = node.type, v = node.value;
951
+ if (t === "string") return typeof v === "string" ? v : "";
952
+ if (t === "list" || t === "compound") return flattenTextComponent(v);
953
+ return "";
954
  }
955
+ let out = "";
956
+ if ("text" in node) out += flattenTextComponent(node.text);
957
+ if ("extra" in node) out += flattenTextComponent(node.extra);
958
+ return out;
959
+ }
960
+ return "";
961
+ };
962
+
963
+ function parseComponentLoreLine(lineNode) {
964
+ let text = "";
965
+ let color = null;
966
+
967
+ function walk(node) {
968
+ if (node == null) return;
969
+ if (typeof node === "string") { text += node; return; }
970
+ if (Array.isArray(node)) { node.forEach(walk); return; }
971
+ if (typeof node === "object") {
972
+ if ("type" in node && "value" in node) {
973
+ const t = node.type, v = node.value;
974
+ if (t === "string") { text += (typeof v === "string" ? v : ""); return; }
975
+ if (t === "list" || t === "compound") { walk(v); return; }
976
+ return;
977
+ }
978
+ if ("color" in node) {
979
+ const colVal = nbtUnwrap(node.color);
980
+ if (color == null && typeof colVal === "string") color = colVal;
981
+ }
982
+ if ("text" in node) walk(node.text);
983
+ if ("extra" in node) walk(node.extra);
984
  }
 
 
985
  }
 
986
 
987
+ walk(lineNode);
988
+ return { text: text.trim(), color };
989
+ };
990
 
991
  function deepSearchForString(root, targetVariantsList) {
992
  const results = [];
 
1060
  const texts = [];
1061
  try {
1062
  if (!slot) return texts;
1063
+
 
1064
  const customNameData = getComponentData(slot, "custom_name");
1065
  if (customNameData !== undefined) {
1066
  const nameTxt = flattenTextComponent(customNameData);
 
1074
  }
1075
  }
1076
  if (texts.length > 0) return texts;
1077
+
 
1078
  function extractPlain(raw) {
1079
  if (!raw || typeof raw !== "string") return null;
1080
  try {
 
1089
  } catch (e) {}
1090
  return raw;
1091
  }
1092
+
1093
  if (slot.nbt) {
1094
  const name = safeGet(slot.nbt, ["value", "display", "value", "Name", "value"]);
1095
  const plainName = extractPlain(name);
1096
  if (plainName) texts.push(plainName);
1097
+
1098
  const lore = safeGet(slot.nbt, ["value", "display", "value", "Lore", "value"]);
1099
  if (Array.isArray(lore)) {
1100
  for (const line of lore) {
 
1103
  }
1104
  }
1105
  }
1106
+
1107
  if (texts.length === 0) {
1108
  texts.push(util.inspect(slot, { depth: null }));
1109
  }
 
1146
  }
1147
  }
1148
 
 
1149
  const MONEY_BALANCE_REGEX = /bakiyen:\s*([\d.,]+)\s*TL/i;
1150
  const MONEY_RESPONSE_TIMEOUT_MS = 8000;
1151
 
1152
  function parseBalanceToAdjustedAmount(rawBalanceStr) {
 
1153
  const noCommas = rawBalanceStr.replace(/,/g, "");
1154
  const beforeDot = noCommas.split(".")[0];
1155
  const intValue = parseInt(beforeDot, 10);
 
1231
  if (!bot || !botIsReady) return;
1232
  try {
1233
  const moves = ['forward', 'left', 'right', 'jump'];
1234
+
1235
+ const moveCount = Math.floor(Math.random() * 4) + 2;
1236
+
1237
  for (let i = 0; i < moveCount; i++) {
1238
  const move = moves[Math.floor(Math.random() * moves.length)];
1239
  const duration = 150 + Math.floor(Math.random() * 500);
1240
+
1241
  bot.setControlState(move, true);
1242
  await new Promise(r => setTimeout(r, duration));
1243
  bot.setControlState(move, false);
1244
+
1245
  await new Promise(r => setTimeout(r, 100 + Math.random() * 300));
1246
+
1247
  }
1248
+
 
1249
  await bot.look(
1250
  bot.entity.yaw + ((Math.random() - 0.5) * 1.5),
1251
  bot.entity.pitch + ((Math.random() - 0.5) * 0.3),
 
1701
  }
1702
  }
1703
 
 
 
 
 
1704
  function stripColorAndGetLoreLines(window) {
1705
  if (!window || !Array.isArray(window.slots)) return null;
1706
  for (const slot of window.slots) {
1707
  if (!slot) continue;
1708
+
1709
  let nameTxt = "";
1710
+
 
1711
  const customNameData = getComponentData(slot, "custom_name");
1712
  if (customNameData !== undefined) {
1713
  nameTxt = flattenTextComponent(customNameData);
1714
  }
1715
+
 
1716
  if (!nameTxt && slot.nbt) {
1717
  const nameRaw = safeGet(slot.nbt, ["value", "display", "value", "Name", "value"]);
1718
  try {
 
1721
  else if (maybe && maybe.text) nameTxt = maybe.text;
1722
  } catch (e) { nameTxt = nameRaw || ""; }
1723
  }
1724
+
1725
  if (nameTxt.includes(CIFTCI_FILTER_ITEM_NAME_KEYWORD)) {
1726
  return { slot, index: window.slots.indexOf(slot) };
1727
  }
 
1731
 
1732
  function getFilterItemLoreEntries(filterSlot) {
1733
  const entries = [];
1734
+
 
1735
  const loreData = getComponentData(filterSlot, "lore");
1736
  if (Array.isArray(loreData)) {
1737
  for (const line of loreData) {
 
1739
  }
1740
  if (entries.length > 0) return entries;
1741
  }
1742
+
 
1743
  try {
1744
  const lore = safeGet(filterSlot.nbt, ["value", "display", "value", "Lore", "value"]);
1745
  if (!Array.isArray(lore)) return entries;
 
1775
  );
1776
  if (match) return i;
1777
  }
 
1778
  const colorMatch = loreEntries.find(e => e.color === CIFTCI_ACTIVE_FILTER_COLOR);
1779
  if (colorMatch) {
1780
  for (let i = 0; i < CIFTCI_FILTER_MODES.length; i++) {
 
1785
  }
1786
 
1787
  async function waitForFilterItem(maxRetries) {
 
1788
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1789
  await new Promise(r => setTimeout(r, attempt === 0 ? CIFTCI_FILTER_STEP_WAIT_MS : CIFTCI_FILTER_RETRY_WAIT_MS));
1790
  if (!bot || !bot.currentWindow) return null;
 
1856
  }
1857
 
1858
  const total = CIFTCI_FILTER_MODES.length;
1859
+ const forwardDist = (CIFTCI_TARGET_FILTER_INDEX - currentIndex + total) % total;
1860
+ const backwardDist = (currentIndex - CIFTCI_TARGET_FILTER_INDEX + total) % total;
1861
 
1862
  const useLeftClick = forwardDist <= backwardDist;
1863
+ const mouseButton = useLeftClick ? 0 : 1;
1864
 
1865
  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.`);
1866
 
 
1870
  logger.error(`[Çiftçi Filtre] Tıklama hatası: ${e && e.message ? e.message : e}`);
1871
  return false;
1872
  }
 
 
1873
  }
1874
 
1875
  logger.warn("[Çiftçi Filtre] Azami deneme sayısına ulaşıldı, Jeneratör materyallerine geçilemedi.");
 
1970
 
1971
  // =====================================================================
1972
  // SİPARİŞ (ORDER) SİSTEMİ
 
 
 
 
 
 
 
 
 
 
1973
  // =====================================================================
1974
 
1975
  async function waitForNextWindowOpen(timeoutMs) {
 
2002
  return -1;
2003
  }
2004
 
2005
+ async function processOrderCore(itemInternalName, nick) {
2006
  if (!bot || !botIsReady) return { success: false, message: "Bot hazır değil." };
2007
  if (!guiMutex.isLocked()) {
2008
  logger.error("[GÜVENLİK İHLALİ] processOrderCore kilitsiz çağrıldı! İşlem iptal ediliyor.");
 
2064
  logger.error(`[Sipariş] Shift+sağ tık hatası: ${e && e.message ? e.message : e}`);
2065
  }
2066
 
2067
+ // --- YENİ: Çiftçiden gerçekten item alınmış mı doğrula ---
2068
+ // clickWindow hata fırlatmasa bile sunucu isteği görmezden gelmiş olabilir.
2069
+ // Envanterde ilgili item yoksa devam etmenin anlamı yok (Teslim Etme'ye zaten
2070
+ // aktarılacak bir şey olmaz), bu yüzden burada erkenden kontrol edip
2071
+ // anlamlı bir hata dönüyoruz.
2072
+ const itemsAfterWithdraw = bot.inventory.items().filter(it => it && it.name === itemInternalName);
2073
+ const withdrawnCount = itemsAfterWithdraw.reduce((sum, it) => sum + (it.count || 0), 0);
2074
+ logger.info(`[Sipariş] Çiftçiden çekildikten sonra envanterde '${itemInternalName}': ${withdrawnCount} adet.`);
2075
+
2076
+ if (withdrawnCount === 0) {
2077
+ logger.warn(`[Sipariş] Çiftçi menüsünden '${itemInternalName}' envantere aktarılamadı (0 adet). İşlem iptal ediliyor.`);
2078
+ try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
2079
+ ciftciWindowPurpose = "sell";
2080
+ 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.` };
2081
+ }
2082
+
2083
  // --- 3) Menüyü kapat ---
2084
  try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
2085
  // ÖNEMLİ: purpose burada "sell"e ÇEVRİLMEZ! /order akışı (Siparişler +
 
2137
  }
2138
 
2139
  logger.info(`[Sipariş] '${nick}' içeren item bulundu (idx:${targetSlotIndex}), sol tık gönderiliyor...`);
2140
+
2141
+ // --- YENİ: nick'e tıkladıktan sonra sabit 500ms yerine GERÇEK yeni
2142
+ // pencere açılışını (Teslim Etme) bekliyoruz. Bu, mevcut buğun ana
2143
+ // sebebiydi: sunucu 500ms'den geç yanıt verirse eski (Siparişler)
2144
+ // penceresine göre hesaplanmış slotlara tıklanıyor ve hiçbir şey
2145
+ // taşınmıyordu (sunucu paketi reddetmediği için hata da görünmüyordu).
2146
+ const windowBeforeSelect = bot.currentWindow;
2147
  try {
2148
  await bot.clickWindow(targetSlotIndex, 0, 0);
2149
  } catch (e) {
2150
  logger.error(`[Sipariş] Slot seçim tıklaması hatası: ${e && e.message ? e.message : e}`);
2151
  }
 
 
2152
 
2153
+ // --- 8) Teslim Etme penceresinin gerçekten açılmasını bekle ---
2154
+ const deliveryOpened = await waitForNextWindowOpen(ORDER_DELIVERY_WINDOW_WAIT_MS);
2155
+
2156
+ if (!deliveryOpened || !bot.currentWindow) {
2157
+ logger.warn("[Sipariş] Teslim Etme penceresi zaman aşımına uğradı ya da açılmadı, işlem yarım kalmış olabilir.");
2158
  ciftciWindowPurpose = "sell";
2159
+ return { success: false, message: "Teslim Etme penceresi açılmadı (zaman aşımı)." };
2160
+ }
2161
+
2162
+ // Pencere objesinin gerçekten DEĞİŞTİĞİNİ doğrula (bazı sunucular aynı id'yi
2163
+ // update edip windowOpen yerine sadece slot update gönderebilir; bu durumda
2164
+ // en azından title kontrolüyle devam ediyoruz).
2165
+ if (bot.currentWindow === windowBeforeSelect) {
2166
+ logger.warn("[Sipariş] windowOpen tetiklendi ama pencere referansı değişmedi, dikkatli devam ediliyor.");
2167
  }
2168
 
 
 
2169
  const currentTitle = parseWindowTitleToString(bot.currentWindow.title);
2170
+ const looksLikeDelivery = matchesAnyName(currentTitle, normalizeVariants(ORDER_DELIVERY_TITLE_KEYWORD));
2171
+ logger.info(`[Sipariş] Yeni pencere açıldı: "${currentTitle}" (Teslim Etme eşleşmesi: ${looksLikeDelivery}). Envanterdeki '${itemInternalName}' item'ları aktarılıyor...`);
2172
+
2173
+ if (!looksLikeDelivery) {
2174
+ logger.warn(`[Sipariş] Açılan pencere Teslim Etme'ye benzemiyor, yine de mevcut pencereye aktarım denenecek: "${currentTitle}"`);
2175
+ }
2176
+
2177
+ // Ek güvenlik: envanterde gerçekten aktarılacak item var mı, tekrar kontrol et
2178
+ // (nick seçme / /is go arasında item'lar herhangi bir sebeple kaybolmuş olabilir).
2179
+ const invCheckBeforeTransfer = bot.inventory.items().filter(it => it && it.name === itemInternalName);
2180
+ if (invCheckBeforeTransfer.length === 0) {
2181
+ logger.warn(`[Sipariş] Aktarım öncesi envanterde '${itemInternalName}' bulunamadı, aktarım atlanıyor.`);
2182
+ try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
2183
+ ciftciWindowPurpose = "sell";
2184
+ return { success: false, message: `Envanterde '${itemInternalName}' kalmamış, Teslim Etme'ye aktarılacak item yok.` };
2185
+ }
2186
 
2187
+ const windowAtTransferStart = bot.currentWindow;
2188
  let transferredCount = 0;
2189
  let keepGoing = true;
2190
  let safetyCounter = 0;
 
2193
 
2194
  while (keepGoing && safetyCounter < 40) {
2195
  safetyCounter++;
2196
+
2197
+ // Pencere döngü sırasında değişti/kapandıysa (ör. sunucu Teslim Etme'yi
2198
+ // kapattıysa) devam etmenin bir anlamı yok, güvenle çık.
2199
+ if (!bot.currentWindow || bot.currentWindow !== windowAtTransferStart) {
2200
+ logger.warn("[Sipariş] Aktarım sırasında pencere değişti/kapandı, döngü durduruluyor.");
2201
+ break;
2202
+ }
2203
 
2204
  const invItem = bot.inventory.items().find(it => it && it.name === itemInternalName);
2205
  if (!invItem) { keepGoing = false; break; }
2206
 
 
2207
  if (stuckSlot === invItem.slot) {
2208
  stuckSlotRepeatCount++;
2209
  if (stuckSlotRepeatCount >= 3) {
 
2220
  await bot.clickWindow(invItem.slot, 0, 1); // mouseButton=0 (sol), mode=1 (shift)
2221
  await new Promise(r => setTimeout(r, 250));
2222
 
 
2223
  const stillThere = bot.inventory.items().find(it => it && it.slot === invItem.slot && it.name === itemInternalName);
2224
  if (!stillThere) {
2225
+ transferredCount++;
2226
  } else {
2227
  logger.warn(`[Sipariş] Slot ${invItem.slot} tıklandı ama item hâlâ envanterde, taşınmamış olabilir.`);
2228
  }
 
2235
  if (safetyCounter >= 40) {
2236
  logger.warn(`[Sipariş] Güvenlik limitine (40 deneme) ulaşıldı. Teslim Etme kutusu dolu/limitli olabilir, transfer yarım kalmış olabilir.`);
2237
  }
2238
+
2239
+ const remainingAfterTransfer = bot.inventory.items()
2240
+ .filter(it => it && it.name === itemInternalName)
2241
+ .reduce((sum, it) => sum + (it.count || 0), 0);
2242
+
2243
+ logger.info(`[Sipariş] Envanterden Teslim Etme'ye aktarım turu tamamlandı (${transferredCount} yığın gönderildi, envanterde kalan: ${remainingAfterTransfer}).`);
2244
 
2245
  // --- 9) Pencereyi kapat ---
2246
  try { if (bot.currentWindow) bot.closeWindow(bot.currentWindow); } catch (e) {}
2247
  ciftciWindowPurpose = "sell"; // akış tamamen bitti, artık normal davranışa dön
2248
 
2249
  return {
2250
+ success: transferredCount > 0,
2251
+ message: transferredCount > 0 ? "Sipariş işlemi tamamlandı." : "Hiçbir yığın Teslim Etme'ye aktarılamadı.",
2252
  item: itemInternalName,
2253
  nick,
2254
+ withdrawn_from_ciftci: withdrawnCount,
2255
+ inventory_transferred_batches: transferredCount,
2256
+ remaining_in_inventory: remainingAfterTransfer
2257
  };
2258
  }
2259
 
 
2281
  scanStorageInfo();
2282
  }, STORAGE_SCAN_INTERVAL_MS);
2283
  logger.info(`[Storage Tarama] Bağımsız döngü başlatıldı (her ${formatDurationMs(STORAGE_SCAN_INTERVAL_MS)}).`);
 
2284
  setSafeTimeout(() => { scanStorageInfo(); }, 30 * 1000);
2285
  }
2286
 
 
2435
  const isCiftciWindow = !isBesleyiciWindow && !isKaziciWindow &&
2436
  (matchesAnyName(titleRaw, TARGET_CIFTCI_NORMS) || windowMatchesLoreKeyword(window, CIFTCI_LORE_FALLBACK_KEYWORDS));
2437
 
 
 
 
 
2438
  if (!isBesleyiciWindow && !isKaziciWindow && !isCiftciWindow) {
2439
  if (ciftciWindowPurpose === "order") {
2440
  logger.info(`[${currentBotUsername}] Sipariş (/order) penceresi açıldı: ${titleRaw}`);
 
2441
  } else {
2442
  logger.info(`[${currentBotUsername}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${titleRaw}`);
2443
  try { bot.closeWindow(window); } catch(e){}
 
2647
  }, delay);
2648
  }
2649
 
 
2650
  let pendingHardRestartTimeout = null;
2651
 
2652
  function stopBotHard(reason) {
 
2850
  });
2851
  });
2852
 
 
2853
  app.get("/bot/stop", (req, res) => {
2854
  stopBotHard("Web paneli üzerinden /bot/stop isteği");
2855
  res.send({ success: true, message: "Bot durduruldu. Tekrar başlatmak için /bot/start kullan." });
 
3229
  return true;
3230
  }
3231
  return originalStderrWrite.apply(process.stderr, arguments);
3232
+ };