SlnDmry commited on
Commit
aef8be5
·
verified ·
1 Parent(s): e9b7ff1

Update bot.js

Browse files
Files changed (1) hide show
  1. bot.js +527 -183
bot.js CHANGED
@@ -6,6 +6,7 @@ const { GoalBlock, GoalXZ } = require("mineflayer-pathfinder").goals;
6
  const util = require("util");
7
  const { SocksClient } = require('socks');
8
  const { Client } = require('ssh2');
 
9
 
10
  const config = require("./settings.json");
11
  const loggers = require("./logging.js");
@@ -16,9 +17,9 @@ const https = require('https');
16
  // --- AYARLAR ---
17
  const HOME_ARRIVAL_DELAY_MS = 2000;
18
  const POST_ACTION_DELAY_MS = 4000;
19
- const WINDOW_OPEN_TIMEOUT_MS = 6000;
20
- const PERIODIC_CHECK_INTERVAL_MS = 10 * 60 * 1000;
21
- const FULL_FEED_COOLDOWN_MS = 50 * 60 * 1000;
22
  const GO_HOME_COMMAND = "/home";
23
  const RETURN_ISLAND_COMMAND = "/is home";
24
  const BESLEYICI_NAMES = (config.utils && config.utils["besleyici-names"]) || ["Besleyici", "ʙᴇsʟᴇʏiᴄi"];
@@ -27,97 +28,98 @@ let viewerTimeout = null;
27
  let bot = null;
28
  let botIsReady = false;
29
  let baglantiTuru = "Yok";
30
- let activeMessages = []; // Canlı mesajların tutulacağı dizi
31
- let messagesTimeout = null; // 1 dakikalık kapatma sayacı
 
32
  const ansiToHtmlConvert = new (require('ansi-to-html'))({ newline: true });
33
 
34
- // Çift tetiklenmeyi ve kopma durumlarını kontrol eden küresel değişken
35
- let isFinished = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- // Dış IP tespiti
38
  https.get('https://api.ipify.org?format=json', (res) => {
39
  let data = '';
40
  res.on('data', chunk => { data += chunk; });
41
  res.on('end', () => {
42
  try {
43
  const ipInfo = JSON.parse(data);
44
- console.log(`Dış IP adresin: ${ipInfo.ip}`);
45
- } catch (err) {
46
- console.error('JSON parse hatası:', err);
47
- }
48
  });
49
- }).on('error', (err) => {
50
- console.error('İstek hatası:', err);
51
- });
52
 
53
  async function createBot() {
54
- const net = require('net');
55
- const sshClient = new Client();
56
- isFinished = false; // Yeni bot döngüsünde durumu sıfırla
57
-
58
- // --- GİTHUB CONFIG ÇEKME AŞAMASI (DİNAMİK SSH) ---
59
- const githubRawUrl = 'https://raw.githubusercontent.com/suvarileader/che/refs/heads/main/doc/ower/doc.json';
60
- logger.info("[GitHub Config] Güncel SSH tünel bilgileri GitHub'dan çekiliyor...");
61
-
62
- let remoteData = null;
63
- try {
64
- remoteData = await new Promise((resolve, reject) => {
65
- https.get(githubRawUrl, (res) => {
66
- let body = '';
67
- res.on('data', chunk => body += chunk);
68
- res.on('end', () => {
69
- try {
70
- resolve(JSON.parse(body));
71
- } catch (e) {
72
- reject(new Error("JSON Ayrıştırma Hatası: " + e.message));
73
- }
74
- });
75
- }).on('error', (err) => reject(err));
76
- });
77
- logger.info("[GitHub Config] SSH dosyası başarıyla indirildi.");
78
- } catch (err) {
79
- logger.error(`[GitHub Config Hatası] SSH verisi alınamadı: ${err.message}. Sabit SSH ayarlarıyla devam ediliyor...`);
80
  }
 
 
81
 
82
- // Varsayılan / Sabit SSH Ayarları (Yedek)
83
- let sshConfig = {
84
- host: 'tr1.vpnjantit.com',
85
- port: 22,
86
- username: 'suvaa-vpnjantit.com',
87
- password: 'suvaa'
88
- };
89
-
90
- // Yerel config'den bot kullanıcı adı
91
- const currentBotUsername = config["bot-account"]["username"];
92
-
93
- // GitHub'dan gelen verilerle SSH konfigürasyonunu eşleştirme
94
- if (remoteData && Array.isArray(remoteData)) {
95
- const myConfig = remoteData.find(item => item.account === currentBotUsername);
96
- if (myConfig) {
97
- logger.info(`[GitHub Config] '${currentBotUsername}' botu için dinamik SSH parametreleri uygulandı.`);
98
- if (myConfig.serverName && myConfig.serverName !== "empty") {
99
- sshConfig.host = myConfig.serverName;
100
- }
101
- if (myConfig.username && myConfig.username !== "empty") {
102
- sshConfig.username = myConfig.username;
103
- }
104
-
105
- // SSH Şifre ataması (ssh2 paketi 'password' anahtarını kabul eder)
106
- if (myConfig.pass && myConfig.pass !== "empty") {
107
- sshConfig.password = myConfig.pass;
108
- } else {
109
- sshConfig.password = config.utils["auto-auth"].password;
110
- }
111
- } else {
112
- logger.warn(`[GitHub Config] Listede '${currentBotUsername}' bulunamadı. Sabit/Yedek SSH kullanılacak.`);
113
- }
114
- }
115
 
116
- // Minecraft Sunucu Hedef Bilgileri (Yerel config'den)
117
  const targetServerIp = config.server.ip;
118
  const targetServerPort = config.server.port;
 
119
 
120
- // Mineflayer Başlatılıyor
121
  bot = mineflayer.createBot({
122
  username: currentBotUsername,
123
  auth: config["bot-account"]["type"],
@@ -125,85 +127,165 @@ async function createBot() {
125
  port: targetServerPort,
126
  version: config.server.version,
127
 
128
- connect: (client) => {
129
  let fallbackTriggered = false;
130
  let isFinalFailureHandled = false;
131
 
132
- // Kritik Çökme Yönetim Merkezi
133
  const handleFinalFailure = (reason) => {
134
  if (isFinalFailureHandled) return;
135
  isFinalFailureHandled = true;
136
 
137
- logger.error(`[Kritik Hata] ${reason}. 2 alternatif de başarısız oldu! Sistem en başa döndürülüyor...`);
138
 
139
  baglantiTuru = "Yok";
140
  botIsReady = false;
 
 
 
141
 
142
  stopPeriodicHomeChecks();
143
- if (bot) {
144
- try { bot.removeAllListeners(); } catch(e){}
145
- bot = null;
146
- }
147
 
148
  if (config.utils && config.utils["auto-reconnect"]) {
149
- logger.info(`[Sistem] ${config.utils["auto-reconnect-delay"]} ms sonra her şey sıfırdan tekrar denenecek...`);
150
- setTimeout(() => {
151
- createBot();
152
- }, config.utils["auto-reconnect-delay"]);
153
  }
154
  };
155
 
156
- // Yedek Plan: Doğrudan Minecraft Sunucusuna Bağlanma
157
- const connectDirectly = (reason) => {
158
  if (fallbackTriggered) return;
159
  fallbackTriggered = true;
160
 
161
- logger.warn(`[Bağlantı Yedekleme] SSH Tüneli başarısız oldu (${reason}). Normal IP üzerinden GERÇEK TCP bağlantısı kuruluyor...`);
162
 
163
- try { sshClient.end(); } catch (e) {}
 
 
 
 
 
 
 
 
 
164
 
165
  const socket = net.connect({
166
  host: targetServerIp,
167
  port: parseInt(targetServerPort) || 25565
168
  });
 
 
 
169
 
170
  const directTimeout = setTimeout(() => {
 
 
 
171
  socket.destroy();
172
- handleFinalFailure("Normal IP bağlantısı zaman aşımına uğradı (Sunucu yanıt vermiyor)");
173
- }, 10000);
174
 
175
  socket.on('connect', () => {
 
 
 
176
  clearTimeout(directTimeout);
177
- logger.info("[Bağlantı Yedekleme] Normal IP üzerinden GERÇEK TCP bağlantısı başarıyla kuruldu!");
178
  baglantiTuru = "Normal";
179
- client.setSocket(socket);
180
- client.emit('connect');
 
 
181
  });
182
 
183
  socket.on('error', (err) => {
 
 
 
184
  clearTimeout(directTimeout);
185
- logger.error(`[Bağlantı Yedekleme Hatası] Normal IP ile de sunucuya bağlanılamadı: ${err.message}`);
186
- handleFinalFailure(`Normal IP hatası: ${err.message}`);
187
  });
188
 
189
  socket.on('close', () => {
 
 
 
190
  clearTimeout(directTimeout);
191
  handleFinalFailure("Normal TCP soketi kapandı");
192
  });
193
  };
194
 
195
- // Dinamik SSH Sunucusuna Bağlanılıyor
196
- logger.info(`[SSH Tünel] SSH üzerinden ${sshConfig.host} sunucusuna (${sshConfig.username}) bağlanılıyor...`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
  const connectionTimeout = setTimeout(() => {
199
  connectDirectly("SSH Bağlantı Zaman Aşımı (10 saniye yanıt alınamadı)");
200
  }, 10000);
201
 
202
- sshClient.on('ready', () => {
203
  clearTimeout(connectionTimeout);
204
  logger.info('[SSH Tünel] SSH bağlantısı başarılı. Minecraft sunucusuna tünel açılıyor...');
205
 
206
- // Tünelin ucu gerçek Minecraft IP'sine yönlendiriliyor
 
 
 
 
 
 
 
207
  sshClient.forwardOut(
208
  '127.0.0.1',
209
  12345,
@@ -217,24 +299,33 @@ async function createBot() {
217
 
218
  logger.info("[SSH Tünel] Tünel başarıyla kuruldu! Mineflayer sokete bağlanıyor.");
219
  baglantiTuru = "SSH";
 
220
 
221
- stream.on('close', () => {
222
- connectDirectly("Tünel stream kapandı");
 
 
223
  });
224
 
225
- stream.on('error', (e) => {
226
- connectDirectly(`Stream hatası: ${e.message}`);
 
 
227
  });
228
 
229
- client.setSocket(stream);
230
- client.emit('connect');
 
 
231
  }
232
  );
233
  });
234
 
235
  sshClient.on('error', (err) => {
236
  clearTimeout(connectionTimeout);
237
- connectDirectly(`SSH Sunucu Hatası: ${err.message}`);
 
 
238
  });
239
 
240
  try {
@@ -248,10 +339,7 @@ async function createBot() {
248
 
249
  bot.loadPlugin(pathfinder);
250
 
251
- // --- TİMİNG SIFIRLAMA İÇİN YARDIMCI PARAMETRELER ---
252
  let lastFullFeedAt = 0;
253
- let windowOpenResolvers = [];
254
- let periodicHandle = null;
255
  let activeTimeouts = [];
256
 
257
  function setSafeTimeout(fn, delay) {
@@ -261,13 +349,26 @@ async function createBot() {
261
  }
262
 
263
  function stopPeriodicHomeChecks() {
264
- if (periodicHandle) clearInterval(periodicHandle);
265
- periodicHandle = null;
 
 
266
  activeTimeouts.forEach(t => clearTimeout(t));
267
  activeTimeouts = [];
268
  }
269
 
270
- // --- YARDIMCI FONKSİYONLAR ---
 
 
 
 
 
 
 
 
 
 
 
271
  function stripColorCodes(s) {
272
  if (!s || typeof s !== "string") return s;
273
  return s.replace(/§[0-9a-fk-or]/gi, "");
@@ -387,17 +488,46 @@ async function createBot() {
387
  }
388
 
389
  async function goHome() {
 
390
  logger.info(`[${currentBotUsername}] ${GO_HOME_COMMAND} komutu gönderiliyor.`);
391
  bot.chat(GO_HOME_COMMAND);
392
  }
393
 
394
  async function returnIsland() {
 
395
  logger.info(`[${currentBotUsername}] ${RETURN_ISLAND_COMMAND} komutu gönderiliyor.`);
396
  bot.chat(RETURN_ISLAND_COMMAND);
397
  }
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  async function scanAndFeed() {
400
- if (Date.now() < (lastFullFeedAt + FULL_FEED_COOLDOWN_MS)) return;
 
 
 
 
 
 
 
401
  const arr = Object.values(bot.entities);
402
  let best = null;
403
 
@@ -420,6 +550,8 @@ async function createBot() {
420
  if (!best) {
421
  logger.info(`[${currentBotUsername}] /home sonrası tarama: besleyici minyon bulunamadı.`);
422
  bot.chat("/skyblock");
 
 
423
  return;
424
  }
425
 
@@ -429,118 +561,241 @@ async function createBot() {
429
  if (dist > 7) {
430
  logger.warn(`safeActivateEntityPreferAt: entity id:${best.entity.id} uzak (${dist.toFixed(2)}). Max 7 blok.`);
431
  logger.warn(`[${currentBotUsername}] 'Besleyici' aktive edilemedi (id:${best.entity.id}).`);
 
432
  return;
433
  }
434
 
435
  try {
436
  if (bot.lookAt) await bot.lookAt(best.entity.position.offset(0, 1.0, 0), true);
437
- await new Promise(r => setTimeout(r, 200));
438
-
439
- logger.info(`activateEntityAt çağrıldı id:${best.entity.id}`);
440
- if (typeof bot.activateEntityAt === "function") bot.activateEntityAt(best.entity, best.entity.position);
441
- else if (typeof bot.activateEntity === "function") bot.activateEntity(best.entity);
442
 
 
 
 
 
 
443
  await new Promise((resolve) => {
444
- const timer = setSafeTimeout(() => resolve(), WINDOW_OPEN_TIMEOUT_MS);
445
- windowOpenResolvers.push(() => { clearTimeout(timer); resolve(); });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
  });
 
447
  } catch (err) {}
448
  }
449
 
450
- // --- BOT SPAWN / EVENT DİNLEYİCİLERİ ---
451
  bot.once("spawn", async () => {
452
- // BOT GİRDİ: Tünel kopma tetikleyicilerini sustur, normal akışa bırak.
453
- isFinished = true;
454
-
455
  bot.pathfinder.setMovements(new Movements(bot));
456
  logger.info("Bot sunucuya giriş yaptı.");
457
  botIsReady = true;
 
458
 
459
  if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) {
460
  const password = config.utils["auto-auth"].password;
461
 
462
  setSafeTimeout(() => {
463
- bot.chat(`/login ${password}`);
464
- logger.info(`[${currentBotUsername}] Login komutu kullanıldı.`);
 
 
465
  }, 2500);
466
 
467
  setSafeTimeout(() => {
468
- bot.chat(`/skyblock`);
469
- logger.info(`[${currentBotUsername}] Skyblock'a ışınlanma komutu kullanıldı.`);
 
 
470
  }, 7500);
471
 
 
472
  setSafeTimeout(async () => {
 
473
  await goHome();
474
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
475
  await scanAndFeed();
476
  await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS));
477
  await returnIsland();
478
-
479
- periodicHandle = setInterval(async () => {
480
- if (Date.now() < (lastFullFeedAt + FULL_FEED_COOLDOWN_MS)) return;
481
- await goHome();
482
- await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
483
- await scanAndFeed();
484
- await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS));
485
- await returnIsland();
486
- }, PERIODIC_CHECK_INTERVAL_MS);
487
  }, 12500);
488
  }
489
  });
490
 
491
- bot.on("windowOpen", (window) => {
 
492
  const title = parseWindowTitleToString(window.title).toLowerCase();
493
  if (!title.includes("besleyici") && !parseWindowTitleToString(window.title).includes("ʙᴇsʟᴇʏiᴄi")) {
494
  logger.info(`[${currentBotUsername}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${parseWindowTitleToString(window.title)}`);
495
- bot.closeWindow(window);
496
  return;
497
  }
498
  const beefSlot = window.slots.find(s => s?.name === "cooked_beef");
499
  const can = extractMinyonCanFromSlot(beefSlot);
 
500
  if (can != null) {
 
 
 
501
  if (can <= 2) {
502
- bot.clickWindow(36, 0, 0);
503
- lastFullFeedAt = Date.now();
504
- logger.info(`[${currentBotUsername}] Minyon canı ${can} olduğu için beslendi. 50dk bekleme aktif.`);
505
- } else if (can >= 10) {
506
- lastFullFeedAt = Date.now();
507
- logger.info(`[${currentBotUsername}] Minyon canı ${can} (tam). 50dk bekleme aktif.`);
 
 
 
 
 
 
508
  } else {
509
- logger.info(`[${currentBotUsername}] Minyon canı ${can} olduğu için yenilenmedi.`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  }
 
 
 
 
 
 
 
 
 
 
 
511
  }
512
- setSafeTimeout(() => { try { bot.closeWindow(window); } catch(e){} }, 500);
513
- while (windowOpenResolvers.length > 0) windowOpenResolvers.shift()();
514
  });
515
 
516
  bot.on("end", () => {
517
- if (isFinished === "reconnected") return; // Çoklu tetiklenmeyi önleme
518
- isFinished = "reconnected";
519
-
520
- logger.warn("Bağlantı koptu. Tüm zamanlayıcılar sıfırlanıyor...");
521
  stopPeriodicHomeChecks();
522
  botIsReady = false;
523
  baglantiTuru = "Yok";
524
- bot = null;
 
 
 
 
525
 
 
 
526
  if (config.utils && config.utils["auto-reconnect"]) {
527
- setTimeout(() => createBot(), config.utils["auto-reconnect-delay"]);
528
  }
529
  });
530
 
531
  bot.on("kicked", (reason) => logger.warn(`Bot sunucudan atıldı: ${util.inspect(reason)}`));
532
- bot.on("error", (err) => logger.error(`${err}`));
533
  }
534
 
535
- createBot();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
537
- // --- EXPRESS SUNUCUSU VE PANEL ALTYAPISI ---
538
- const express = require("express");
539
- const app = express();
540
- const http = require('http');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
 
542
- function minecraftMessageListener(jsonMsg) {
 
 
 
 
 
 
 
 
 
 
 
 
543
  try {
 
544
  const chatMessage = bot.chat.ChatMessage ? new bot.chat.ChatMessage(jsonMsg) : jsonMsg;
545
  const coloredAnsi = typeof chatMessage.toAnsi === 'function' ? chatMessage.toAnsi() : chatMessage.toString();
546
  const htmlMessage = ansiToHtmlConvert.toHtml(coloredAnsi);
@@ -556,11 +811,36 @@ function minecraftMessageListener(jsonMsg) {
556
  }
557
  }
558
 
 
 
 
 
 
 
 
559
  app.get("/status", (req, res) => {
 
 
 
 
 
 
 
 
 
 
 
 
560
  res.send({
561
  ready: botIsReady,
562
  username: bot ? bot.username : null,
563
- connection_type: baglantiTuru
 
 
 
 
 
 
564
  });
565
  });
566
 
@@ -572,30 +852,84 @@ app.get("/messages", (req, res) => {
572
  messagesTimeout = null;
573
  }
574
 
575
- if (bot && botIsReady) {
576
- if (!bot.listeners('message').includes(minecraftMessageListener)) {
577
- bot.on('message', minecraftMessageListener);
578
- logger.info("[Sohbet Tasarrufu] Sohbet izlenmeye başlandı. 'message' eventi aktif.");
579
- }
580
- } else {
581
  return res.status(503).send("Bot hazır olmadığı için mesajlar dinlenemiyor.");
582
  }
583
 
 
 
 
 
 
 
584
  res.send({
585
  server_time: new Date().toLocaleTimeString('tr-TR'),
586
  logs: activeMessages
587
  });
588
 
589
  messagesTimeout = setTimeout(() => {
590
- if (bot) {
591
- bot.off('message', minecraftMessageListener);
592
- activeMessages = [];
593
- messagesTimeout = null;
594
- logger.info("[Sohbet Tasarrufu] 1 dakikadır izlenmiyor. 'message' eventi kapatıldı ve RAM temizlendi.");
 
 
595
  }
596
  }, 60000);
597
  });
598
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  const checkAndStartViewer = (req, res, next) => {
600
  if (viewerTimeout) {
601
  clearTimeout(viewerTimeout);
@@ -649,18 +983,25 @@ app.use('/3dview', checkAndStartViewer, (req, res) => {
649
  });
650
 
651
  app.get("/restart", (req, res) => {
 
 
 
 
 
 
 
652
  if (bot && botIsReady) {
653
- logger.info("[Sistem] Web paneli üzerinden tam restart isteği geldi. Proje kapatılıyor...");
654
  try { bot.chat("Bot web paneli uzerinden tum kodlariyla yeniden baslatiliyor..."); } catch(e){}
655
- res.send({ success: true, message: "Tüm proje kapatıldı. Hugging Face sistemi birkaç saniye içinde kodu sıfırdan başlatacak." });
 
656
  setTimeout(() => {
657
- if (bot) bot.quit();
658
- logger.info("[Sistem] process.exit(0) çağrıldı. Konteyner yeniden başlatılacak.");
 
659
  process.exit(0);
660
  }, 1000);
661
  } else {
662
- logger.info("[Sistem] Bot bağlı değilken web panelinden tam restart istendi.");
663
- res.send({ success: true, message: "Bot bağlı değildi ancak tüm proje kökten kapatıldı, yeniden başlatılıyor." });
664
  setTimeout(() => {
665
  process.exit(0);
666
  }, 1000);
@@ -707,8 +1048,11 @@ server.on('upgrade', (req, socket, head) => {
707
  viewerTimeout = setTimeout(() => {
708
  if (viewerInstance && typeof viewerInstance.close === 'function') {
709
  try {
 
 
 
710
  viewerInstance.close();
711
- bot.viewer = null;
712
  viewerInstance = null;
713
  logger.info("[RAM Tasarrufu] 10 saniyedir kimse izlemiyor. Harita kapatıldı, RAM serbest!");
714
  } catch (e) {
 
6
  const util = require("util");
7
  const { SocksClient } = require('socks');
8
  const { Client } = require('ssh2');
9
+ const dns = require('dns').promises;
10
 
11
  const config = require("./settings.json");
12
  const loggers = require("./logging.js");
 
17
  // --- AYARLAR ---
18
  const HOME_ARRIVAL_DELAY_MS = 2000;
19
  const POST_ACTION_DELAY_MS = 4000;
20
+ const WINDOW_OPEN_TIMEOUT_MS = 6000;
21
+ const DEFAULT_CHECK_INTERVAL_MS = 10 * 60 * 1000; // Varsayılan 10 dk
22
+ const FULL_FEED_COOLDOWN_MS = 50 * 60 * 1000; // Besleme sonrası sabit 50 dk
23
  const GO_HOME_COMMAND = "/home";
24
  const RETURN_ISLAND_COMMAND = "/is home";
25
  const BESLEYICI_NAMES = (config.utils && config.utils["besleyici-names"]) || ["Besleyici", "ʙᴇsʟᴇʏiᴄi"];
 
28
  let bot = null;
29
  let botIsReady = false;
30
  let baglantiTuru = "Yok";
31
+ let activeMessages = [];
32
+ let messagesTimeout = null;
33
+ let isListeningChat = false;
34
  const ansiToHtmlConvert = new (require('ansi-to-html'))({ newline: true });
35
 
36
+ // --- GÜVENLİ BAĞLANTI DEĞİŞKENLERİ ---
37
+ let isConnecting = false;
38
+ let isDestroying = false;
39
+ let reconnectTimeout = null;
40
+ let currentSshClient = null;
41
+ let currentStream = null;
42
+ let currentSocket = null;
43
+ let windowOpenResolvers = [];
44
+
45
+ // --- PANEL METRİKLERİ ---
46
+ let botUptimeStart = null;
47
+ let lastFullFeedTimeStr = "Henüz besleme yapılmadı";
48
+ let lastCheckTimeStr = "Henüz kontrol yapılmadı";
49
+ let nextScheduledCheckTimeStr = "Botun spawn olması bekleniyor...";
50
+
51
+ // --- DİNAMİK IP VE KONUM BİLGİLERİ ---
52
+ let activeConnectionIp = "Bilinmiyor";
53
+ let activeConnectionLocation = "Bilinmiyor";
54
+ let localExternalIp = "Bilinmiyor";
55
+
56
+ // --- DİNAMİK ZAMANLAYICI KONTROLÜ ---
57
+ let periodicTimeoutHandle = null;
58
+
59
+ async function getIpLocation(ip) {
60
+ if (!ip || ip === "Bilinmiyor" || ip.startsWith("127.")) return "Bilinmiyor";
61
+
62
+ // Denenecek servisler listesi
63
+ const services = [
64
+ `https://ip-api.com/json/${ip}?fields=status,country,city`,
65
+ `https://ipapi.co/${ip}/json/`
66
+ ];
67
+
68
+ for (const url of services) {
69
+ try {
70
+ const location = await new Promise((resolve) => {
71
+ https.get(url, (res) => {
72
+ let data = '';
73
+ res.on('data', chunk => data += chunk);
74
+ res.on('end', () => {
75
+ try {
76
+ const geo = JSON.parse(data);
77
+ // Servise göre cevap yapısı değişebilir, burada basit bir kontrol yapıyoruz
78
+ if (geo.country && geo.status !== "fail") {
79
+ resolve(`${geo.country} / ${geo.city || geo.region || ""}`);
80
+ } else {
81
+ resolve(null); // Bir sonraki servise geç
82
+ }
83
+ } catch (e) { resolve(null); }
84
+ });
85
+ }).on('error', () => resolve(null));
86
+ });
87
+
88
+ if (location) return location;
89
+ } catch (e) { continue; }
90
+ }
91
+
92
+ return "Konum Alınamadı";
93
+ }
94
 
 
95
  https.get('https://api.ipify.org?format=json', (res) => {
96
  let data = '';
97
  res.on('data', chunk => { data += chunk; });
98
  res.on('end', () => {
99
  try {
100
  const ipInfo = JSON.parse(data);
101
+ localExternalIp = ipInfo.ip;
102
+ console.log(`Dış IP adresin: ${localExternalIp}`);
103
+ } catch (err) {}
 
104
  });
105
+ }).on('error', () => {});
 
 
106
 
107
  async function createBot() {
108
+ if (isConnecting) {
109
+ logger.warn("[Sistem] Zaten aktif bir bağlantı süreci yürütülüyor. Mükerrer istek engellendi.");
110
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
+ isConnecting = true;
113
+ isDestroying = false;
114
 
115
+ const net = require('net');
116
+ const sshClient = new Client();
117
+ currentSshClient = sshClient;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
 
119
  const targetServerIp = config.server.ip;
120
  const targetServerPort = config.server.port;
121
+ const currentBotUsername = config["bot-account"]["username"];
122
 
 
123
  bot = mineflayer.createBot({
124
  username: currentBotUsername,
125
  auth: config["bot-account"]["type"],
 
127
  port: targetServerPort,
128
  version: config.server.version,
129
 
130
+ connect: async (client) => {
131
  let fallbackTriggered = false;
132
  let isFinalFailureHandled = false;
133
 
 
134
  const handleFinalFailure = (reason) => {
135
  if (isFinalFailureHandled) return;
136
  isFinalFailureHandled = true;
137
 
138
+ logger.error(`[Kritik Hata] ${reason}. Bağlantı sağlanamadı! Sistem en başa döndürülüyor...`);
139
 
140
  baglantiTuru = "Yok";
141
  botIsReady = false;
142
+ botUptimeStart = null;
143
+ activeConnectionIp = "Bilinmiyor";
144
+ activeConnectionLocation = "Bilinmiyor";
145
 
146
  stopPeriodicHomeChecks();
147
+ safeDestroyAllConnections();
 
 
 
148
 
149
  if (config.utils && config.utils["auto-reconnect"]) {
150
+ triggerSafeReconnect();
 
 
 
151
  }
152
  };
153
 
154
+ const connectDirectly = async (reason) => {
 
155
  if (fallbackTriggered) return;
156
  fallbackTriggered = true;
157
 
158
+ logger.warn(`[Bağlantı Yedekleme] Doğrudan IP moduna geçiliyor (Sebep: ${reason}). Kendi (Yurt dışı) IP'miz üzerinden GERÇEK TCP bağlantısı kuruluyor...`);
159
 
160
+ if (sshClient) {
161
+ try {
162
+ sshClient.removeAllListeners('ready');
163
+ sshClient.removeAllListeners('error');
164
+ sshClient.destroy();
165
+ } catch (e) {}
166
+ }
167
+
168
+ activeConnectionIp = localExternalIp !== "Bilinmiyor" ? localExternalIp : "Kendi Dış IP'niz";
169
+ activeConnectionLocation = await getIpLocation(localExternalIp);
170
 
171
  const socket = net.connect({
172
  host: targetServerIp,
173
  port: parseInt(targetServerPort) || 25565
174
  });
175
+ currentSocket = socket;
176
+
177
+ let isDirectHandled = false;
178
 
179
  const directTimeout = setTimeout(() => {
180
+ if (isDirectHandled) return;
181
+ isDirectHandled = true;
182
+
183
  socket.destroy();
184
+ handleFinalFailure("Kendi IP bağlantısı 1 dakikalık süre içinde zaman aşımına uğradı (Sunucu yanıt vermedi)");
185
+ }, 60000);
186
 
187
  socket.on('connect', () => {
188
+ if (isDirectHandled) return;
189
+ isDirectHandled = true;
190
+
191
  clearTimeout(directTimeout);
192
+ logger.info("[Bağlantı Yedekleme] Kendi IP'miz üzerinden GERÇEK TCP bağlantısı başarıyla kuruldu!");
193
  baglantiTuru = "Normal";
194
+ if (client && typeof client.setSocket === 'function') {
195
+ client.setSocket(socket);
196
+ client.emit('connect');
197
+ }
198
  });
199
 
200
  socket.on('error', (err) => {
201
+ if (isDirectHandled) return;
202
+ isDirectHandled = true;
203
+
204
  clearTimeout(directTimeout);
205
+ logger.error(`[Bağlantı Yedekleme Hatası] Sunucu bağlantıyı reddetti veya olumsuz yanıt verdi: ${err.message}`);
206
+ handleFinalFailure(`Kendi IP hatası: ${err.message}`);
207
  });
208
 
209
  socket.on('close', () => {
210
+ if (isDirectHandled) return;
211
+ isDirectHandled = true;
212
+
213
  clearTimeout(directTimeout);
214
  handleFinalFailure("Normal TCP soketi kapandı");
215
  });
216
  };
217
 
218
+ const githubRawUrl = 'https://raw.githubusercontent.com/suvarileader/che/refs/heads/main/doc/ower/doc.json';
219
+ logger.info("[GitHub Config] Güncel SSH tünel bilgileri GitHub'dan çekiliyor...");
220
+
221
+ let remoteData = null;
222
+ try {
223
+ remoteData = await new Promise((resolve, reject) => {
224
+ const reqTimeout = setTimeout(() => {
225
+ reject(new Error("GitHub bağlantısı 6 saniye içinde yanıt vermedi (Timeout)"));
226
+ }, 6000);
227
+
228
+ https.get(githubRawUrl, (res) => {
229
+ clearTimeout(reqTimeout);
230
+ let body = '';
231
+ res.on('data', chunk => body += chunk);
232
+ res.on('end', () => {
233
+ try {
234
+ resolve(JSON.parse(body));
235
+ } catch (e) {
236
+ reject(new Error("JSON Ayrıştırma Hatası"));
237
+ }
238
+ });
239
+ }).on('error', (err) => {
240
+ clearTimeout(reqTimeout);
241
+ reject(err);
242
+ });
243
+ });
244
+ } catch (err) {
245
+ connectDirectly(`GitHub verisi alınamadı veya sayfa çöktü (${err.message})`);
246
+ return;
247
+ }
248
+
249
+ let sshConfig = null;
250
+ if (remoteData && Array.isArray(remoteData)) {
251
+ const myConfig = remoteData.find(item => item && item.account === currentBotUsername);
252
+ if (myConfig) {
253
+ sshConfig = {
254
+ host: (myConfig.serverName && myConfig.serverName !== "empty") ? myConfig.serverName : null,
255
+ port: 22,
256
+ username: (myConfig.username && myConfig.username !== "empty") ? myConfig.username : null,
257
+ password: (myConfig.pass && myConfig.pass !== "empty") ? myConfig.pass : config.utils["auto-auth"].password
258
+ };
259
+
260
+ if (!sshConfig.host || !sshConfig.username) {
261
+ sshConfig = null;
262
+ }
263
+ }
264
+ }
265
+
266
+ if (!sshConfig) {
267
+ connectDirectly(`GitHub listesinde '${currentBotUsername}' botu ile uyumlu geçerli SSH verisi bulunamadı`);
268
+ return;
269
+ }
270
+
271
+ logger.info(`[SSH Tünel] GitHub'dan alınan ${sshConfig.host} sunucusuna (${sshConfig.username}) bağlanılıyor...`);
272
 
273
  const connectionTimeout = setTimeout(() => {
274
  connectDirectly("SSH Bağlantı Zaman Aşımı (10 saniye yanıt alınamadı)");
275
  }, 10000);
276
 
277
+ sshClient.on('ready', async () => {
278
  clearTimeout(connectionTimeout);
279
  logger.info('[SSH Tünel] SSH bağlantısı başarılı. Minecraft sunucusuna tünel açılıyor...');
280
 
281
+ try {
282
+ const lookup = await dns.lookup(sshConfig.host);
283
+ activeConnectionIp = lookup.address;
284
+ } catch (e) {
285
+ activeConnectionIp = sshConfig.host;
286
+ }
287
+ activeConnectionLocation = await getIpLocation(activeConnectionIp);
288
+
289
  sshClient.forwardOut(
290
  '127.0.0.1',
291
  12345,
 
299
 
300
  logger.info("[SSH Tünel] Tünel başarıyla kuruldu! Mineflayer sokete bağlanıyor.");
301
  baglantiTuru = "SSH";
302
+ currentStream = stream;
303
 
304
+ stream.once('close', () => {
305
+ if (!fallbackTriggered && !isDestroying) {
306
+ connectDirectly("Tünel stream kapandı");
307
+ }
308
  });
309
 
310
+ stream.once('error', (e) => {
311
+ if (!fallbackTriggered && !isDestroying) {
312
+ connectDirectly(`Stream hatası: ${e.message}`);
313
+ }
314
  });
315
 
316
+ if (client && typeof client.setSocket === 'function') {
317
+ client.setSocket(stream);
318
+ client.emit('connect');
319
+ }
320
  }
321
  );
322
  });
323
 
324
  sshClient.on('error', (err) => {
325
  clearTimeout(connectionTimeout);
326
+ if (!fallbackTriggered && !isDestroying) {
327
+ connectDirectly(`SSH Sunucu Hatası: ${err.message}`);
328
+ }
329
  });
330
 
331
  try {
 
339
 
340
  bot.loadPlugin(pathfinder);
341
 
 
342
  let lastFullFeedAt = 0;
 
 
343
  let activeTimeouts = [];
344
 
345
  function setSafeTimeout(fn, delay) {
 
349
  }
350
 
351
  function stopPeriodicHomeChecks() {
352
+ if (periodicTimeoutHandle) {
353
+ clearTimeout(periodicTimeoutHandle);
354
+ periodicTimeoutHandle = null;
355
+ }
356
  activeTimeouts.forEach(t => clearTimeout(t));
357
  activeTimeouts = [];
358
  }
359
 
360
+ function getExtendedDateTimeString() {
361
+ const now = new Date();
362
+ const dateStr = now.toLocaleDateString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric' });
363
+ const timeStr = now.toLocaleTimeString('tr-TR');
364
+ return `${dateStr} - ${timeStr}`;
365
+ }
366
+
367
+ function getFutureDateTimeString(delayMs) {
368
+ const future = new Date(Date.now() + delayMs);
369
+ return future.toLocaleTimeString('tr-TR');
370
+ }
371
+
372
  function stripColorCodes(s) {
373
  if (!s || typeof s !== "string") return s;
374
  return s.replace(/§[0-9a-fk-or]/gi, "");
 
488
  }
489
 
490
  async function goHome() {
491
+ if (!bot || !botIsReady) return;
492
  logger.info(`[${currentBotUsername}] ${GO_HOME_COMMAND} komutu gönderiliyor.`);
493
  bot.chat(GO_HOME_COMMAND);
494
  }
495
 
496
  async function returnIsland() {
497
+ if (!bot || !botIsReady) return;
498
  logger.info(`[${currentBotUsername}] ${RETURN_ISLAND_COMMAND} komutu gönderiliyor.`);
499
  bot.chat(RETURN_ISLAND_COMMAND);
500
  }
501
 
502
+ // --- DİNAMİK PLANLAMA MOTORU ---
503
+ function scheduleNextCheck(delayMs) {
504
+ if (periodicTimeoutHandle) {
505
+ clearTimeout(periodicTimeoutHandle);
506
+ }
507
+
508
+ nextScheduledCheckTimeStr = `${getFutureDateTimeString(delayMs)} (${Math.round(delayMs / 60 / 1000)} dk sonra)`;
509
+ logger.info(`[Planlayıcı] Bir sonraki döngü planlandı. Zaman: ${nextScheduledCheckTimeStr}`);
510
+
511
+ periodicTimeoutHandle = setTimeout(async () => {
512
+ if (!bot || !botIsReady) return;
513
+
514
+ await goHome();
515
+ await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
516
+ await scanAndFeed();
517
+ await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS));
518
+ await returnIsland();
519
+ }, delayMs);
520
+ }
521
+
522
  async function scanAndFeed() {
523
+ if (!bot || !botIsReady) return;
524
+
525
+ // Sunucu lagı yüzünden döngü üst üste tetiklenirse koruma
526
+ if (Date.now() < (lastFullFeedAt + 30000)) {
527
+ logger.warn("[Planlayıcı] Çok kısa süre önce işlem yapıldı. Güvenlik kilidi devrede.");
528
+ return;
529
+ }
530
+
531
  const arr = Object.values(bot.entities);
532
  let best = null;
533
 
 
550
  if (!best) {
551
  logger.info(`[${currentBotUsername}] /home sonrası tarama: besleyici minyon bulunamadı.`);
552
  bot.chat("/skyblock");
553
+ // Okuyamadığımız için 10 dakika sonra tekrar deneyecek şekilde planla
554
+ scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS);
555
  return;
556
  }
557
 
 
561
  if (dist > 7) {
562
  logger.warn(`safeActivateEntityPreferAt: entity id:${best.entity.id} uzak (${dist.toFixed(2)}). Max 7 blok.`);
563
  logger.warn(`[${currentBotUsername}] 'Besleyici' aktive edilemedi (id:${best.entity.id}).`);
564
+ scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS);
565
  return;
566
  }
567
 
568
  try {
569
  if (bot.lookAt) await bot.lookAt(best.entity.position.offset(0, 1.0, 0), true);
570
+ await new Promise(r => setTimeout(r, 400));
 
 
 
 
571
 
572
+ const startTime = Date.now();
573
+ const endTime = startTime + WINDOW_OPEN_TIMEOUT_MS;
574
+ let clickCount = 0;
575
+ let isLoopActive = true;
576
+
577
  await new Promise((resolve) => {
578
+ const cleanFinish = () => {
579
+ isLoopActive = false;
580
+ clearInterval(clickInterval);
581
+ windowOpenResolvers = [];
582
+ resolve();
583
+ };
584
+
585
+ windowOpenResolvers.push(() => {
586
+ cleanFinish();
587
+ });
588
+
589
+ const sendClickPacket = () => {
590
+ if (!isLoopActive) return;
591
+
592
+ if (bot.currentWindow != null) {
593
+ cleanFinish();
594
+ return;
595
+ }
596
+
597
+ if (Date.now() > endTime) {
598
+ logger.warn(`[Tıklama Sistemi] ${WINDOW_OPEN_TIMEOUT_MS} ms doldu ancak menü sunucudan gelmedi. Pes ediliyor.`);
599
+ cleanFinish();
600
+ scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS);
601
+ return;
602
+ }
603
+
604
+ clickCount++;
605
+ logger.info(`[Tıklama Sistemi] Menü tespiti başarısız. Deneme #${clickCount} gönderiliyor... (id:${best.entity.id})`);
606
+
607
+ try {
608
+ if (typeof bot.activateEntityAt === "function") bot.activateEntityAt(best.entity, best.entity.position);
609
+ else if (typeof bot.activateEntity === "function") bot.activateEntity(best.entity);
610
+ } catch (err) {}
611
+ };
612
+
613
+ sendClickPacket();
614
+ const clickInterval = setInterval(sendClickPacket, 600);
615
  });
616
+
617
  } catch (err) {}
618
  }
619
 
 
620
  bot.once("spawn", async () => {
621
+ isConnecting = false;
622
+ if (!bot) return;
 
623
  bot.pathfinder.setMovements(new Movements(bot));
624
  logger.info("Bot sunucuya giriş yaptı.");
625
  botIsReady = true;
626
+ botUptimeStart = Date.now();
627
 
628
  if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) {
629
  const password = config.utils["auto-auth"].password;
630
 
631
  setSafeTimeout(() => {
632
+ if (bot && botIsReady) {
633
+ bot.chat(`/login ${password}`);
634
+ logger.info(`[${currentBotUsername}] Login komutu kullanıldı.`);
635
+ }
636
  }, 2500);
637
 
638
  setSafeTimeout(() => {
639
+ if (bot && botIsReady) {
640
+ bot.chat(`/skyblock`);
641
+ logger.info(`[${currentBotUsername}] Skyblock'a ışınlanma komutu kullanıldı.`);
642
+ }
643
  }, 7500);
644
 
645
+ // İlk çalıştırma: Girişten 12.5 saniye sonra ilk kontrolü yap
646
  setSafeTimeout(async () => {
647
+ if (!bot || !botIsReady) return;
648
  await goHome();
649
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
650
  await scanAndFeed();
651
  await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS));
652
  await returnIsland();
 
 
 
 
 
 
 
 
 
653
  }, 12500);
654
  }
655
  });
656
 
657
+ bot.on("windowOpen", async (window) => {
658
+ if (!bot) return;
659
  const title = parseWindowTitleToString(window.title).toLowerCase();
660
  if (!title.includes("besleyici") && !parseWindowTitleToString(window.title).includes("ʙᴇsʟᴇʏiᴄi")) {
661
  logger.info(`[${currentBotUsername}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${parseWindowTitleToString(window.title)}`);
662
+ try { bot.closeWindow(window); } catch(e){}
663
  return;
664
  }
665
  const beefSlot = window.slots.find(s => s?.name === "cooked_beef");
666
  const can = extractMinyonCanFromSlot(beefSlot);
667
+
668
  if (can != null) {
669
+ const currentStamp = getExtendedDateTimeString();
670
+ lastCheckTimeStr = `${currentStamp} (Okunan Can: ${can})`;
671
+
672
  if (can <= 2) {
673
+ // CAN 2 VEYA ALTINDA: BESLE VE SABİT 50 DK PLANLA
674
+ try {
675
+ logger.info(`[${currentBotUsername}] Minyon canı ${can} olduğu için besleniyor...`);
676
+ await bot.clickWindow(36, 0, 0);
677
+ lastFullFeedAt = Date.now();
678
+ lastFullFeedTimeStr = `${currentStamp} (Minyon canı ${can} iken başarıyla beslendi)`;
679
+ logger.info(`[${currentBotUsername}] Tıklama onaylandı. Fixed 50dk bekleme aktif.`);
680
+ } catch(e) {
681
+ logger.error(`[Arayüz Hatası] Tıklama işlemi sunucu tarafından onaylanmadı: ${e.message}`);
682
+ }
683
+ scheduleNextCheck(FULL_FEED_COOLDOWN_MS);
684
+
685
  } else {
686
+ // CAN 3 VEYA DAHA YÜKSEK: DİNAMİK PLANLAMA YAP
687
+ // Formül: (Can - 2) * 5 dakika. Maksimum 50 dakika.
688
+ let calculatedMinutes = (can - 2) * 5;
689
+ if (calculatedMinutes > 50) calculatedMinutes = 50;
690
+ if (calculatedMinutes < 10) calculatedMinutes = 10;
691
+
692
+ const dynamicDelayMs = calculatedMinutes * 60 * 1000;
693
+
694
+ logger.info(`[${currentBotUsername}] Minyon canı ${can} olarak okundu. Canın 2'ye düşmesi için ${calculatedMinutes} dakika beklenecek.`);
695
+
696
+ if (can >= 10) {
697
+ lastFullFeedTimeStr = `${currentStamp} (Canı ${can} olduğu için besleme gerekmedi)`;
698
+ }
699
+
700
+ scheduleNextCheck(dynamicDelayMs);
701
  }
702
+ } else {
703
+ // Can okunamadıysa güvenlik amacıyla 10 dk sonra tekrar kontrol et
704
+ logger.warn(`[${currentBotUsername}] Menü açıldı ama can okunamadı. 10 dk sonra tekrar denenecek.`);
705
+ scheduleNextCheck(DEFAULT_CHECK_INTERVAL_MS);
706
+ }
707
+
708
+ try { bot.closeWindow(window); } catch(e){}
709
+
710
+ while (windowOpenResolvers.length > 0) {
711
+ const fn = windowOpenResolvers.shift();
712
+ if (typeof fn === 'function') fn();
713
  }
 
 
714
  });
715
 
716
  bot.on("end", () => {
717
+ logger.warn("Bağlantı koptu. Tüm zamanlayıcılar sıfırlanıyor ve YENİDEN BAĞLANMA SÜRECİ BAŞLIYOR...");
 
 
 
718
  stopPeriodicHomeChecks();
719
  botIsReady = false;
720
  baglantiTuru = "Yok";
721
+ botUptimeStart = null;
722
+ isListeningChat = false;
723
+ activeConnectionIp = "Bilinmiyor";
724
+ activeConnectionLocation = "Bilinmiyor";
725
+ nextScheduledCheckTimeStr = "Bağlantı koptuğu için planlama iptal edildi.";
726
 
727
+ safeDestroyAllConnections();
728
+
729
  if (config.utils && config.utils["auto-reconnect"]) {
730
+ triggerSafeReconnect();
731
  }
732
  });
733
 
734
  bot.on("kicked", (reason) => logger.warn(`Bot sunucudan atıldı: ${util.inspect(reason)}`));
735
+ bot.on("error", (err) => logger.error(`Mineflayer Hatası: ${err}`));
736
  }
737
 
738
+ function safeDestroyAllConnections() {
739
+ if (isDestroying) return;
740
+ isDestroying = true;
741
+ isConnecting = false;
742
+
743
+ windowOpenResolvers = [];
744
+
745
+ if (viewerInstance && typeof viewerInstance.close === 'function') {
746
+ try {
747
+ if (viewerInstance.ws && typeof viewerInstance.ws.close === 'function') {
748
+ viewerInstance.ws.close();
749
+ }
750
+ viewerInstance.close();
751
+ } catch(e){}
752
+ }
753
+ viewerInstance = null;
754
+ if (bot) { bot.viewer = null; }
755
 
756
+ if (currentStream && typeof currentStream.destroy === 'function') {
757
+ try { currentStream.destroy(); } catch(e){}
758
+ }
759
+
760
+ if (currentSshClient && typeof currentSshClient.destroy === 'function') {
761
+ try { currentSshClient.destroy(); } catch(e){}
762
+ } else {
763
+ if (currentSocket && typeof currentSocket.destroy === 'function') {
764
+ try { currentSocket.destroy(); } catch(e){}
765
+ }
766
+ }
767
+
768
+ currentStream = null;
769
+ currentSocket = null;
770
+ currentSshClient = null;
771
+
772
+ if (bot) {
773
+ try {
774
+ bot.removeAllListeners('message');
775
+ bot.removeAllListeners('spawn');
776
+ bot.removeAllListeners('windowOpen');
777
+ bot.removeAllListeners('end');
778
+ bot.quit();
779
+ } catch(e){}
780
+ bot = null;
781
+ }
782
+ }
783
 
784
+ function triggerSafeReconnect() {
785
+ if (reconnectTimeout) {
786
+ clearTimeout(reconnectTimeout);
787
+ }
788
+ const delay = (config.utils && config.utils["auto-reconnect-delay"]) || 5000;
789
+ logger.info(`[Sistem] ${delay} ms sonra tekil bir hat üzerinden sistem sıfırdan başlatılacak...`);
790
+
791
+ reconnectTimeout = setTimeout(() => {
792
+ createBot();
793
+ }, delay);
794
+ }
795
+
796
+ function globalMinecraftMessageListener(jsonMsg) {
797
  try {
798
+ if (!bot) return;
799
  const chatMessage = bot.chat.ChatMessage ? new bot.chat.ChatMessage(jsonMsg) : jsonMsg;
800
  const coloredAnsi = typeof chatMessage.toAnsi === 'function' ? chatMessage.toAnsi() : chatMessage.toString();
801
  const htmlMessage = ansiToHtmlConvert.toHtml(coloredAnsi);
 
811
  }
812
  }
813
 
814
+ createBot();
815
+
816
+ // --- EXPRESS SUNUCUSU VE PANEL ALTYAPISI ---
817
+ const express = require("express");
818
+ const app = express();
819
+ const http = require('http');
820
+
821
  app.get("/status", (req, res) => {
822
+ let uptimeString = "Bağlı Değil";
823
+ if (botIsReady && botUptimeStart) {
824
+ const diffMs = Date.now() - botUptimeStart;
825
+ const diffSecs = Math.floor(diffMs / 1000);
826
+ const days = Math.floor(diffSecs / 86400);
827
+ const hours = Math.floor((diffSecs % 86400) / 3600);
828
+ const mins = Math.floor((diffSecs % 3600) / 60);
829
+ const secs = diffSecs % 60;
830
+
831
+ uptimeString = `${days} Gün, ${hours} Saat, ${mins} Dakika, ${secs} Saniye`;
832
+ }
833
+
834
  res.send({
835
  ready: botIsReady,
836
  username: bot ? bot.username : null,
837
+ connection_type: baglantiTuru,
838
+ connection_ip: activeConnectionIp,
839
+ connection_geo: activeConnectionLocation,
840
+ bot_uptime: uptimeString,
841
+ last_minyon_check: lastCheckTimeStr,
842
+ last_minyon_feed: lastFullFeedTimeStr,
843
+ next_scheduled_check: nextScheduledCheckTimeStr // Panel için sonraki kontrol saati
844
  });
845
  });
846
 
 
852
  messagesTimeout = null;
853
  }
854
 
855
+ if (!bot || !botIsReady) {
 
 
 
 
 
856
  return res.status(503).send("Bot hazır olmadığı için mesajlar dinlenemiyor.");
857
  }
858
 
859
+ if (!isListeningChat) {
860
+ isListeningChat = true;
861
+ bot.on('message', globalMinecraftMessageListener);
862
+ logger.info("[Sohbet Tasarrufu] Panel aktif edildi. Minecraft mesaj dinleyicisi BAŞLATILDI.");
863
+ }
864
+
865
  res.send({
866
  server_time: new Date().toLocaleTimeString('tr-TR'),
867
  logs: activeMessages
868
  });
869
 
870
  messagesTimeout = setTimeout(() => {
871
+ activeMessages = [];
872
+ messagesTimeout = null;
873
+
874
+ if (bot && isListeningChat) {
875
+ bot.removeListener('message', globalMinecraftMessageListener);
876
+ isListeningChat = false;
877
+ logger.info("[Sohbet Tasarrufu] 1 dakikadır izlenmiyor. Minecraft mesaj dinleyicisi DURDURULDU ve havuz temizlendi.");
878
  }
879
  }, 60000);
880
  });
881
 
882
+ app.get("/chat", (req, res) => {
883
+ const html = `
884
+ <!DOCTYPE html>
885
+ <html>
886
+ <head>
887
+ <meta charset="UTF-8">
888
+ <title>Canlı Minecraft Sohbeti</title>
889
+ <style>
890
+ body { background-color: #111; color: #eee; font-family: 'Courier New', Courier, monospace; padding: 20px; margin: 0; }
891
+ .container { max-width: 900px; margin: 0 auto; background: #1a1a1a; padding: 15px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.5); }
892
+ h2 { margin-top: 0; border-bottom: 1px solid #333; padding-bottom: 10px; color: #fff; }
893
+ #chat-box { height: 500px; overflow-y: auto; background: #000; border: 1px solid #333; padding: 10px; border-radius: 4px; display: flex; flex-direction: column; }
894
+ .msg-line { margin-bottom: 6px; line-height: 1.4; font-size: 14px; word-break: break-all; }
895
+ .time-tag { color: #555; margin-right: 8px; font-size: 12px; }
896
+ </style>
897
+ </head>
898
+ <body>
899
+ <div class="container">
900
+ <h2>Canlı Oyun Sohbeti</h2>
901
+ <div id="chat-box"></div>
902
+ </div>
903
+ <script>
904
+ async function updateChat() {
905
+ try {
906
+ const res = await fetch('/messages');
907
+ if (!res.ok) return;
908
+ const data = await res.json();
909
+ const chatBox = document.getElementById('chat-box');
910
+ chatBox.innerHTML = '';
911
+ if (data.logs && data.logs.length > 0) {
912
+ data.logs.forEach(msg => {
913
+ const div = document.createElement('div');
914
+ div.className = 'msg-line';
915
+ div.innerHTML = '<span class="time-tag">[' + msg.time + ']</span>' + msg.text;
916
+ chatBox.appendChild(div);
917
+ });
918
+ chatBox.scrollTop = chatBox.scrollHeight;
919
+ } else {
920
+ chatBox.innerHTML = '<div style="color:#555;">Henüz yeni mesaj yok veya havuz temizlendi...</div>';
921
+ }
922
+ } catch(e) {}
923
+ }
924
+ setInterval(updateChat, 2000);
925
+ updateChat();
926
+ </script>
927
+ </body>
928
+ </html>
929
+ `;
930
+ res.send(html);
931
+ });
932
+
933
  const checkAndStartViewer = (req, res, next) => {
934
  if (viewerTimeout) {
935
  clearTimeout(viewerTimeout);
 
983
  });
984
 
985
  app.get("/restart", (req, res) => {
986
+ logger.info("[Sistem] Web paneli üzerinden tam restart isteği geldi. Sistem felç ediliyor...");
987
+
988
+ if (config.utils) {
989
+ config.utils["auto-reconnect"] = false;
990
+ }
991
+ isDestroying = true;
992
+
993
  if (bot && botIsReady) {
 
994
  try { bot.chat("Bot web paneli uzerinden tum kodlariyla yeniden baslatiliyor..."); } catch(e){}
995
+ res.send({ success: true, message: "Tüm proje kapatıldı. Konteyner birkaç saniye içinde sıfırdan başlayacak." });
996
+
997
  setTimeout(() => {
998
+ try { bot.quit(); } catch(e){}
999
+ safeDestroyAllConnections();
1000
+ logger.info("[Sistem] process.exit(0) çağrılıyor.");
1001
  process.exit(0);
1002
  }, 1000);
1003
  } else {
1004
+ res.send({ success: true, message: "Bot bağlı değildi ancak konteyner kökten kapatılıyor." });
 
1005
  setTimeout(() => {
1006
  process.exit(0);
1007
  }, 1000);
 
1048
  viewerTimeout = setTimeout(() => {
1049
  if (viewerInstance && typeof viewerInstance.close === 'function') {
1050
  try {
1051
+ if (viewerInstance.ws && typeof viewerInstance.ws.close === 'function') {
1052
+ viewerInstance.ws.close();
1053
+ }
1054
  viewerInstance.close();
1055
+ if (bot) bot.viewer = null;
1056
  viewerInstance = null;
1057
  logger.info("[RAM Tasarrufu] 10 saniyedir kimse izlemiyor. Harita kapatıldı, RAM serbest!");
1058
  } catch (e) {