Selind commited on
Commit
ba2328d
·
verified ·
1 Parent(s): 3cdf9cc

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +2 -2
  2. bot.js +828 -107
  3. package.json +9 -2
Dockerfile CHANGED
@@ -1,10 +1,10 @@
1
- FROM node:18
2
  ENV TZ=Europe/Istanbul
3
 
4
  WORKDIR /app
5
 
6
  COPY package*.json ./
7
- RUN npm install --production
8
 
9
  COPY . .
10
 
 
1
+ FROM node:22
2
  ENV TZ=Europe/Istanbul
3
 
4
  WORKDIR /app
5
 
6
  COPY package*.json ./
7
+ RUN npm install --omit=dev --prefer-offline --no-audit --no-fund --legacy-peer-deps
8
 
9
  COPY . .
10
 
bot.js CHANGED
@@ -1,77 +1,345 @@
1
  const mineflayer = require("mineflayer");
 
2
  const Movements = require("mineflayer-pathfinder").Movements;
3
  const pathfinder = require("mineflayer-pathfinder").pathfinder;
4
  const { GoalBlock, GoalXZ } = require("mineflayer-pathfinder").goals;
5
  const util = require("util");
6
-
7
- const { Client } = require("ssh2");
8
- const { SocksProxyAgent } = require("socks-proxy-agent");
9
 
10
  const config = require("./settings.json");
11
  const loggers = require("./logging.js");
12
  const logger = loggers.logger;
13
 
 
 
14
  // --- AYARLAR ---
15
  const HOME_ARRIVAL_DELAY_MS = 2000;
16
  const POST_ACTION_DELAY_MS = 4000;
17
- const WINDOW_OPEN_TIMEOUT_MS = 6000;
18
- const PERIODIC_CHECK_INTERVAL_MS = 10 * 60 * 1000;
19
- const FULL_FEED_COOLDOWN_MS = 50 * 60 * 1000;
20
  const GO_HOME_COMMAND = "/home";
21
  const RETURN_ISLAND_COMMAND = "/is home";
22
  const BESLEYICI_NAMES = (config.utils && config.utils["besleyici-names"]) || ["Besleyici", "ʙᴇsʟᴇʏiᴄi"];
 
 
 
 
 
 
 
 
 
23
 
24
- // --- SSH Failover için eklenen kısım ---
25
- const sshServers = [
26
- { host: "tr2.vpnjantit.com", port: 22, username: "Pawrot-vpnjantit.com", password: "Pawrot" },
27
- { host: "tr5.vpnjantit.com", port: 22, username: "Pawrot-vpnjantit.com", password: "Pawrot" }
28
- ];
29
- let currentIndex = 0;
30
- let usingFallback = false;
31
-
32
- function connectWithSSH() {
33
- if (currentIndex >= sshServers.length) {
34
- console.log("Tüm SSH bağlantıları başarısız. Normal IP ile bağlanılıyor...");
35
- usingFallback = true;
36
- createBot(); // fallback: normal bağlantı
37
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
 
40
- const server = sshServers[currentIndex];
41
- const conn = new Client();
42
 
43
- conn.on("ready", () => {
44
- console.log(`SSH bağlantısı başarılı: ${server.host}`);
45
- usingFallback = false;
46
- const agent = new SocksProxyAgent("socks5://127.0.0.1:1080");
47
- createBot(agent);
 
 
 
 
48
  });
 
49
 
50
- conn.on("error", (err) => {
51
- console.log(`SSH bağlantısı başarısız: ${server.host} - ${err.message}`);
52
- currentIndex++;
53
- connectWithSSH(); // sıradaki sunucuyu dene
54
- });
 
 
55
 
56
- conn.connect(server);
57
- }
58
- function createBot(agent = null) {
59
- const bot = mineflayer.createBot({
60
- username: config["bot-account"]["username"],
 
 
 
 
 
61
  auth: config["bot-account"]["type"],
62
- host: config.server.ip,
63
- port: config.server.port,
64
  version: config.server.version,
65
- brand: "vanilla",
66
- agent: agent || undefined
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  });
68
 
69
  bot.loadPlugin(pathfinder);
70
 
71
- // --- SADECE TİMİNG SIFIRLAMA İÇİN EKLENEN KISIM ---
72
  let lastFullFeedAt = 0;
73
- let windowOpenResolvers = [];
74
- let periodicHandle = null;
75
  let activeTimeouts = [];
76
 
77
  function setSafeTimeout(fn, delay) {
@@ -81,14 +349,26 @@ function createBot(agent = null) {
81
  }
82
 
83
  function stopPeriodicHomeChecks() {
84
- if (periodicHandle) clearInterval(periodicHandle);
85
- periodicHandle = null;
 
 
86
  activeTimeouts.forEach(t => clearTimeout(t));
87
  activeTimeouts = [];
88
  }
89
- // ------------------------------------------------
90
 
91
- // --- SENİN ORİJİNAL FONKSİYONLARIN (TAMAMEN AYNI) ---
 
 
 
 
 
 
 
 
 
 
 
92
  function stripColorCodes(s) {
93
  if (!s || typeof s !== "string") return s;
94
  return s.replace(/§[0-9a-fk-or]/gi, "");
@@ -207,18 +487,47 @@ function createBot(agent = null) {
207
  } catch (err) { return null; }
208
  }
209
 
210
- async function goHome() {
211
- logger.info(`[${config["bot-account"]["username"]}] ${GO_HOME_COMMAND} komutu gönderiliyor.`);
 
212
  bot.chat(GO_HOME_COMMAND);
213
  }
214
 
215
  async function returnIsland() {
216
- logger.info(`[${config["bot-account"]["username"]}] ${RETURN_ISLAND_COMMAND} komutu gönderiliyor.`);
 
217
  bot.chat(RETURN_ISLAND_COMMAND);
218
  }
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  async function scanAndFeed() {
221
- if (Date.now() < (lastFullFeedAt + FULL_FEED_COOLDOWN_MS)) return;
 
 
 
 
 
 
 
222
  const arr = Object.values(bot.entities);
223
  let best = null;
224
 
@@ -239,123 +548,535 @@ function createBot(agent = null) {
239
  }
240
 
241
  if (!best) {
242
- logger.info(`[${config["bot-account"]["username"]}] /home sonrası tarama: besleyici minyon bulunamadı.`);
243
  bot.chat("/skyblock");
 
 
244
  return;
245
  }
246
 
247
  const dist = bot.entity.position.distanceTo(best.entity.position);
248
- logger.info(`[${config["bot-account"]["username"]}] '${best.entity.name || 'Armor Stand'}' bulundu id:${best.entity.id} path:${best.path} dist:${dist.toFixed(2)} — aktive ediliyor.`);
249
 
250
  if (dist > 7) {
251
  logger.warn(`safeActivateEntityPreferAt: entity id:${best.entity.id} uzak (${dist.toFixed(2)}). Max 7 blok.`);
252
- logger.warn(`[${config["bot-account"]["username"]}] 'Besleyici' aktive edilemedi (id:${best.entity.id}).`);
 
253
  return;
254
  }
255
 
256
  try {
257
  if (bot.lookAt) await bot.lookAt(best.entity.position.offset(0, 1.0, 0), true);
258
- await new Promise(r => setTimeout(r, 200));
259
-
260
- logger.info(`activateEntityAt çağrıldı id:${best.entity.id}`);
261
- if (typeof bot.activateEntityAt === "function") bot.activateEntityAt(best.entity, best.entity.position);
262
- else if (typeof bot.activateEntity === "function") bot.activateEntity(best.entity);
263
 
 
 
 
 
 
264
  await new Promise((resolve) => {
265
- const timer = setTimeout(() => resolve(), WINDOW_OPEN_TIMEOUT_MS);
266
- windowOpenResolvers.push(() => { clearTimeout(timer); resolve(); });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  });
 
268
  } catch (err) {}
269
  }
270
 
271
  bot.once("spawn", async () => {
 
 
272
  bot.pathfinder.setMovements(new Movements(bot));
273
  logger.info("Bot sunucuya giriş yaptı.");
 
 
274
 
275
  if (config.utils && config.utils["auto-auth"] && config.utils["auto-auth"].enabled) {
276
  const password = config.utils["auto-auth"].password;
277
 
278
  setSafeTimeout(() => {
279
- bot.chat(`/login ${password}`);
280
- logger.info(`[${config["bot-account"]["username"]}] Login komutu kullanıldı.`);
281
- }, 5000);
 
 
282
 
283
  setSafeTimeout(() => {
284
- bot.chat(`/skyblock`);
285
- logger.info(`[${config["bot-account"]["username"]}] Skyblock'a ışınlanma komutu kullanıldı.`);
286
- }, 15000);
 
 
287
 
 
288
  setSafeTimeout(async () => {
 
289
  await goHome();
290
  await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
291
  await scanAndFeed();
292
  await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS));
293
  await returnIsland();
294
-
295
- periodicHandle = setInterval(async () => {
296
- if (Date.now() < (lastFullFeedAt + FULL_FEED_COOLDOWN_MS)) return;
297
- await goHome();
298
- await new Promise(r => setTimeout(r, HOME_ARRIVAL_DELAY_MS));
299
- await scanAndFeed();
300
- await new Promise(r => setTimeout(r, POST_ACTION_DELAY_MS));
301
- await returnIsland();
302
- }, PERIODIC_CHECK_INTERVAL_MS);
303
- }, 21000);
304
  }
305
  });
306
 
307
- bot.on("windowOpen", (window) => {
 
308
  const title = parseWindowTitleToString(window.title).toLowerCase();
309
  if (!title.includes("besleyici") && !parseWindowTitleToString(window.title).includes("ʙᴇsʟᴇʏiᴄi")) {
310
- logger.info(`[${config["bot-account"]["username"]}] Bi chest açıldı ama window title ı uyuşmadığı için kapatıldı. ${parseWindowTitleToString(window.title)}`);
311
- bot.closeWindow(window);
312
  return;
313
  }
314
  const beefSlot = window.slots.find(s => s?.name === "cooked_beef");
315
  const can = extractMinyonCanFromSlot(beefSlot);
 
316
  if (can != null) {
 
 
 
317
  if (can <= 2) {
318
- bot.clickWindow(36, 0, 0);
319
- lastFullFeedAt = Date.now();
320
- logger.info(`[${config["bot-account"]["username"]}] Minyon canı ${can} olduğu için beslendi. 50dk bekleme aktif.`);
321
- } else if (can >= 10) {
322
- lastFullFeedAt = Date.now();
323
- logger.info(`[${config["bot-account"]["username"]}] Minyon canı ${can} (tam). 50dk bekleme aktif.`);
 
 
 
 
 
 
324
  } else {
325
- logger.info(`[${config["bot-account"]["username"]}] Minyon canı ${can} olduğu için yenilenmedi.`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  }
 
 
 
 
 
 
 
 
 
 
 
327
  }
328
- setSafeTimeout(() => { try { bot.closeWindow(window); } catch(e){} }, 500);
329
- while (windowOpenResolvers.length > 0) windowOpenResolvers.shift()();
330
  });
331
 
332
  bot.on("end", () => {
333
- logger.warn("[" + config["bot-account"]["username"] + "]" + " Bağlantı koptu. Tüm zamanlayıcılar sıfırlanıyor...");
334
  stopPeriodicHomeChecks();
335
- if (usingFallback) {
336
- currentIndex = 0; // normal IP’deyken koparsa index sıfırlanır
 
 
 
 
 
 
 
 
 
 
337
  }
338
- connectWithSSH(); // tekrar SSH denemesi
339
  });
340
 
341
- bot.on("kicked", (reason) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  try {
343
- // Eğer reason zaten string değilse JSON.stringify ile aç
344
- const readable = typeof reason === "string"
345
- ? reason
346
- : JSON.stringify(reason, null, 2);
 
 
 
 
 
347
 
348
- logger.warn("[" + config["bot-account"]["username"] + "]" + " Bot sunucudan atıldı: " + readable);
349
- } catch (e) {
350
- logger.error("Bot sunucudan atıldı ama mesaj parse edilemedi: " + e.message);
351
  }
352
- });
353
- bot.on("error", (err) => logger.error(`${err}`));
 
 
 
 
354
  }
355
- // Başlatma
356
- connectWithSSH();
357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  const express = require("express");
359
  const app = express();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  app.get("/", (req, res) => res.send("Bot Aktif"));
361
- app.listen(process.env.PORT || 7860);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  const mineflayer = require("mineflayer");
2
+ const mineflayerViewer = require('prismarine-viewer').mineflayer;
3
  const Movements = require("mineflayer-pathfinder").Movements;
4
  const pathfinder = require("mineflayer-pathfinder").pathfinder;
5
  const { GoalBlock, GoalXZ } = require("mineflayer-pathfinder").goals;
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");
13
  const logger = loggers.logger;
14
 
15
+ const https = require('https');
16
+
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"];
26
+ let viewerInstance = null;
27
+ let viewerTimeout = null;
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"],
126
+ host: targetServerIp,
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,
292
+ targetServerIp,
293
+ parseInt(targetServerPort) || 25565,
294
+ (err, stream) => {
295
+ if (err) {
296
+ connectDirectly(`Tünel yönlendirme hatası: ${err.message}`);
297
+ return;
298
+ }
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 {
332
+ sshClient.connect(sshConfig);
333
+ } catch (err) {
334
+ clearTimeout(connectionTimeout);
335
+ connectDirectly(`Sistem Hatası: ${err.message}`);
336
+ }
337
+ }
338
  });
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, "");
 
487
  } catch (err) { return null; }
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
 
 
548
  }
549
 
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
 
558
  const dist = bot.entity.position.distanceTo(best.entity.position);
559
+ logger.info(`[${currentBotUsername}] '${best.entity.name || 'Armor Stand'}' bulundu id:${best.entity.id} path:${best.path} dist:${dist.toFixed(2)} — aktive ediliyor.`);
560
 
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) * 15 dakika. Maksimum 65 dakika.
688
+ let calculatedMinutes = (can - 2) * 15;
689
+ if (calculatedMinutes > 50) calculatedMinutes = 65;
690
+ if (calculatedMinutes < 15) calculatedMinutes = 15;
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);
802
+ const timeStr = new Date().toLocaleTimeString('tr-TR');
803
+
804
+ activeMessages.push({ time: timeStr, text: htmlMessage });
805
+
806
+ if (activeMessages.length > 50) {
807
+ activeMessages.shift();
808
+ }
809
+ } catch (err) {
810
+ logger.error("Mesaj işlenirken hata oluştu: " + err.message);
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
+
847
  app.get("/", (req, res) => res.send("Bot Aktif"));
848
+
849
+ app.get("/messages", (req, res) => {
850
+ if (messagesTimeout) {
851
+ clearTimeout(messagesTimeout);
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);
936
+ viewerTimeout = null;
937
+ }
938
+
939
+ const isBotReady = bot && botIsReady;
940
+
941
+ if (!viewerInstance && isBotReady) {
942
+ try {
943
+ mineflayerViewer(bot, { port: 3000, firstPerson: true });
944
+ viewerInstance = bot.viewer;
945
+ logger.info("[Viewer] 3D Harita motoru arka planda (3000) başlatıldı.");
946
+ setTimeout(() => next(), 600);
947
+ return;
948
+ } catch (err) {
949
+ logger.error("Görüntüleyici başlatılamadı: " + err);
950
+ }
951
+ }
952
+
953
+ if (!isBotReady && !viewerInstance) {
954
+ return res.status(503).send("Bot henüz sunucuya giriş yapmadığı için ekran görüntülenemiyor.");
955
+ }
956
+
957
+ next();
958
+ };
959
+
960
+ app.use('/3dview', checkAndStartViewer, (req, res) => {
961
+ if (req.originalUrl === '/3dview' && !req.originalUrl.endsWith('/')) {
962
+ return res.redirect(301, '/3dview/');
963
+ }
964
+
965
+ const targetPath = req.url;
966
+ const proxyReq = http.request({
967
+ host: '127.0.0.1',
968
+ port: 3000,
969
+ path: targetPath,
970
+ method: req.method,
971
+ headers: req.headers
972
+ }, (proxyRes) => {
973
+ res.writeHead(proxyRes.statusCode, proxyRes.headers);
974
+ proxyRes.pipe(res, { end: true });
975
+ });
976
+
977
+ proxyReq.on('error', (err) => {
978
+ logger.error("[Asset Hatası] Veri 3000 portundan çekilemedi: " + err.message);
979
+ if (!res.headersSent) res.status(502).send("Harita yükleniyor, lütfen sayfayı yenileyin.");
980
+ });
981
+
982
+ req.pipe(proxyReq, { end: true });
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);
1008
+ }
1009
+ });
1010
+
1011
+ const server = app.listen(process.env.PORT || 7860, () => {
1012
+ logger.info("Ana Express sunucusu 7860 portunda aktif.");
1013
+ });
1014
+
1015
+ server.on('upgrade', (req, socket, head) => {
1016
+ if (req.url.includes('socket.io') || req.url.startsWith('/3dview')) {
1017
+ if (viewerTimeout) {
1018
+ clearTimeout(viewerTimeout);
1019
+ viewerTimeout = null;
1020
+ }
1021
+
1022
+ let targetWsPath = req.url;
1023
+ if (targetWsPath.startsWith('/3dview')) {
1024
+ targetWsPath = targetWsPath.replace('/3dview', '');
1025
+ }
1026
+ if (!targetWsPath.startsWith('/')) {
1027
+ targetWsPath = '/' + targetWsPath;
1028
+ }
1029
+
1030
+ const proxyReq = http.request({
1031
+ host: '127.0.0.1',
1032
+ port: 3000,
1033
+ path: targetWsPath,
1034
+ method: req.method,
1035
+ headers: req.headers
1036
+ });
1037
+
1038
+ proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => {
1039
+ socket.write('HTTP/1.1 101 Switching Protocols\r\n' +
1040
+ Object.keys(proxyRes.headers).map(key => `${key}: ${proxyRes.headers[key]}`).join('\r\n') +
1041
+ '\r\n\r\n');
1042
+
1043
+ proxySocket.pipe(socket);
1044
+ socket.pipe(proxySocket);
1045
+
1046
+ socket.on('close', () => {
1047
+ if (viewerTimeout) clearTimeout(viewerTimeout);
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) {
1059
+ logger.error("Viewer kapatılırken hata: " + e.message);
1060
+ }
1061
+ }
1062
+ }, 10000);
1063
+ });
1064
+ });
1065
+
1066
+ proxyReq.on('error', (err) => {
1067
+ logger.error("[WS Tünel Hatası] Websocket aktarılamadı: " + err.message);
1068
+ });
1069
+
1070
+ proxyReq.end();
1071
+ }
1072
+ });
1073
+
1074
+ const originalStderrWrite = process.stderr.write;
1075
+ process.stderr.write = function (chunk, encoding, callback) {
1076
+ const message = chunk.toString();
1077
+ if (message.includes("Ignoring block entities as chunk failed to load")) {
1078
+ if (typeof callback === 'function') callback();
1079
+ return true;
1080
+ }
1081
+ return originalStderrWrite.apply(process.stderr, arguments);
1082
+ };
package.json CHANGED
@@ -9,12 +9,19 @@
9
  "author": "Suvari",
10
  "license": "MIT",
11
  "dependencies": {
 
12
  "express": "^4.21.0",
13
  "log4js": "^6.7.1",
14
  "minecraft-data": "^3.69.0",
15
- "mineflayer": "github:PrismarineJS/mineflayer#89686596c8de1091bf45104ba3230a5e87707a20",
 
16
  "mineflayer-pathfinder": "github:PrismarineJS/mineflayer-pathfinder#874949196ac15a7bca23ac54b403b7a8c8bc71be",
 
 
 
17
  "ssh2": "^1.17.0",
18
- "socks-proxy-agent": "^8.0.1"
 
 
19
  }
20
  }
 
9
  "author": "Suvari",
10
  "license": "MIT",
11
  "dependencies": {
12
+ "ansi-to-html": "^0.7.2",
13
  "express": "^4.21.0",
14
  "log4js": "^6.7.1",
15
  "minecraft-data": "^3.69.0",
16
+ "mineflayer": "^4.37.1",
17
+ "minecraft-protocol": "^1.66.2",
18
  "mineflayer-pathfinder": "github:PrismarineJS/mineflayer-pathfinder#874949196ac15a7bca23ac54b403b7a8c8bc71be",
19
+ "prismarine-viewer": "^1.33.0",
20
+ "http-proxy-middleware": "^2.0.6",
21
+ "canvas": "^3.2.3",
22
  "ssh2": "^1.17.0",
23
+ "socks-proxy-agent": "^8.0.1",
24
+ "proxy-chain": "^3.0.0",
25
+ "socks": "^2.8.9"
26
  }
27
  }