'use strict'; try { require('dotenv').config(); } catch (_) {} /** * ╔══════════════════════════════════════════════════════════════════════════╗ * Sayed Bot Team — v17.0 | النسخة الذكية الكاملة * ╠══════════════════════════════════════════════════════════════════════════╣ * v17.0 — تحسينات الاستقرار الكاملة: * * SETUP-01: معالج إعداد ويب — يطلب IP, Port, الإصدار, عدد البوتات * PLUG-01: كشف البلاجن الكاملة عند منح OP + تحليل كل بلاجن * PERF-01: مراقبة أداء السيرفر + تحسين تلقائي عند الـ lag * DASH-01: لوحة تحكم كاملة RTL عربي — live update بدون reload * VER-01: دعم كل الإصدارات + fallback تلقائي عند version mismatch * ANTI-01: حماية ضد قطع Aternos — 9 طبقات حماية * ANTI-02: تشغيل Aternos تلقائياً عند DeepSleep + healthz check * MEM-01: إدارة ذاكرة محسّنة + حفظ البيانات تلقائياً * POS-01: إحداثيات البوتات في الداشبورد * CHAT-01: إرسال رسائل في السيرفر من الداشبورد * ╚══════════════════════════════════════════════════════════════════════════╝ */ process.setMaxListeners(0); process.on('uncaughtException', err => { const c = err.code || err.message || ''; if (/EPIPE|ETIMEDOUT|ECONNRESET|ECONNREFUSED|EHOSTUNREACH/.test(c)) return; console.error('[SYS] ❌ uncaughtException:', err.message); }); process.on('unhandledRejection', reason => { const m = String(reason?.message || reason || ''); if (/EPIPE|ETIMEDOUT|ECONNRESET|ECONNREFUSED|EHOSTUNREACH/.test(m)) return; console.error('[SYS] ❌ unhandledRejection:', m); }); const fs = require('fs'); const path = require('path'); const http = require('http'); const net = require('net'); const dns = require('dns'); const { spawn } = require('child_process'); const express = require('express'); const mineflayer = require('mineflayer'); const { pathfinder, Movements, goals } = require('mineflayer-pathfinder'); // ════════════════════════════════════════════════════════════════════════════ // CONFIG: يُحمَّل من ملف config.json أو متغيرات البيئة // ════════════════════════════════════════════════════════════════════════════ const CONFIG_PATH = path.join(__dirname, 'config.json'); let CFG = loadConfig(); function loadConfig() { let file = {}; try { if (fs.existsSync(CONFIG_PATH)) file = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); } catch (_) {} return { host: file.host || process.env.MC_HOST || 'A3BO3D-XlvV.aternos.me' port: parseInt(file.port || process.env.MC_PORT || '12476', 10), version: file.version || process.env.MC_VERSION || 'auto', botCount: parseInt(file.botCount || process.env.BOT_COUNT || '4', 10), bot1Name: file.bot1Name || process.env.BOT1_NAME || 'Alex_miner26', bot2Name: file.bot2Name || process.env.BOT2_NAME || 'Steve_builder', bot3Name: file.bot3Name || process.env.BOT3_NAME || 'NightExplorer', bot4Name: file.bot4Name || process.env.BOT4_NAME || 'RedstoneKing', staggerMin: parseInt(file.staggerMin || process.env.BOT_STAGGER_MIN || '120', 10) * 1000, staggerMax: parseInt(file.staggerMax || process.env.BOT_STAGGER_MAX || '240', 10) * 1000, dashPort: parseInt(file.dashPort || process.env.PORT || '7860', 10), isConfigured: !!(file.host || process.env.MC_HOST), }; } function saveConfig(data) { const updated = { ...CFG, ...data, isConfigured: true }; fs.writeFileSync(CONFIG_PATH, JSON.stringify(updated, null, 2)); CFG = updated; return CFG; } // ════════════════════════════════════════════════════════════════════════════ // DATA PERSISTENCE — MEM-01 + حفظ البيانات بين عمليات التشغيل // ════════════════════════════════════════════════════════════════════════════ const DATA_PATH = path.join(__dirname, 'data.json'); function saveData() { try { fs.writeFileSync(DATA_PATH, JSON.stringify({ plugins: detectedPlugins, perfHistory: performanceData.history.slice(-20), players: [...playerRegistry.entries()], savedAt: tsNow(), }, null, 2)); } catch (_) {} } function loadData() { try { if (!fs.existsSync(DATA_PATH)) return; const d = JSON.parse(fs.readFileSync(DATA_PATH, 'utf8')); if (Array.isArray(d.plugins) && d.plugins.length) { detectedPlugins.push(...d.plugins); sysLog('💾', `محُمِّل ${d.plugins.length} بلاجن من الحفظ السابق`); } if (Array.isArray(d.perfHistory) && d.perfHistory.length) { performanceData.history.push(...d.perfHistory); } if (Array.isArray(d.players) && d.players.length) { d.players.forEach(([name, data]) => { playerRegistry.set(name, { ...data, joinedAt: null, lastChat: null }); }); sysLog('💾', `محُمِّل ${d.players.length} لاعب من السجل السابق`); } } catch (_) {} } // ════════════════════════════════════════════════════════════════════════════ // ATERNOS AUTO-TRIGGER — ANTI-02 // ════════════════════════════════════════════════════════════════════════════ let aternosRunning = false; function triggerAternos() { if (aternosRunning) return; aternosRunning = true; sysLog('🔔', 'ANTI-02: السيرفر لا يرد — تنبيه بتشغيل Aternos...'); addEvent('info', '🔔 السيرفر متوقف — افتح aternos.org وشغّل السيرفر يدوياً'); addEvent('info', '🔗 aternos.org → My Servers → Start'); // لا نستخدم Puppeteer (لا يعمل على HuggingFace — لا Chrome) // بدلاً منه: نبث رابط مباشر في الداشبورد ونعيد المحاولة تلقائياً setTimeout(() => { aternosRunning = false; sysLog('🔄', 'ANTI-02: إعادة محاولة الاتصال...'); }, 3 * 60000); // بعد 3 دقائق نعيد المحاولة تلقائياً } // ════════════════════════════════════════════════════════════════════════════ // VERSIONS — الإصدارات المدعومة بالكامل // ════════════════════════════════════════════════════════════════════════════ const SUPPORTED_VERSIONS = [ '1.21.6','1.21.5','1.21.4','1.21.3','1.21.2','1.21.0', '1.20.0','1.19.0','1.18.0','1.17.0','1.16.0', '1.15.0','1.14.0','1.13.0','1.12.0','1.11.0','1.10.0', '1.9.0','1.8.0','1.7.2','1.6.1','1.5.0','1.4.2','1.3.1', '1.2.1','1.1.0','1.0.0', '26.2.0','26.1.0', ]; const PROTOCOL_MAP = { 770:'1.21.5', 769:'1.21.4', 768:'1.21.3', 767:'1.21.2', 766:'1.21.0', 765:'1.20.4', 764:'1.20.2', 763:'1.20.1', 762:'1.20.0', 761:'1.19.4', 760:'1.19.3', 759:'1.19.2', 758:'1.19.1', 757:'1.19.0', 756:'1.18.2', 755:'1.18.1', 748:'1.18.0', 753:'1.17.0', 754:'1.16.5', 736:'1.16.0', 578:'1.15.0', 498:'1.14.0', 393:'1.13.0', 340:'1.12.0', 316:'1.11.0', 210:'1.10.0', 110:'1.9.0', 47:'1.8.0', 4:'1.7.2', 39:'1.6.1', 61:'1.5.0', }; const VERSION_VERSION_MAP = { '26.1.0': '1.21.4', '26.2.0': '1.21.5', '1.21.6': '1.21.5', '1.21.0': '1.21', '1.20.0': '1.20', '1.19.0': '1.19', '1.18.0': '1.18', '1.17.0': '1.17', '1.16.0': '1.16.5', '1.15.0': '1.15.2', '1.14.0': '1.14.4', '1.13.0': '1.13.2', '1.12.0': '1.12.2', '1.11.0': '1.11.2', '1.10.0': '1.10.2', '1.9.0': '1.9.4', '1.8.0': '1.8.9', }; function resolveVersion(v) { if (!v || v === 'auto') return null; if (VERSION_VERSION_MAP[v]) return VERSION_VERSION_MAP[v]; return v; } let autoVersion = null; let fallbackVersionList = buildVersionFallbacks(); function buildVersionFallbacks() { try { const mcProto = require('minecraft-protocol'); const raw = mcProto.supportedVersions || (mcProto.versions && (mcProto.versions.pc || mcProto.versions)) || []; const list = Array.isArray(raw) ? raw : Object.values(raw); const versions = list .map(v => typeof v === 'string' ? v : v.minecraftVersion || v.version || '') .filter(v => { const p = v.split('.'); return parseInt(p[0]) === 1 && parseInt(p[1]) >= 7; }); if (versions.length > 0) { versions.sort((a, b) => { const pa = a.split('.').map(Number), pb = b.split('.').map(Number); for (let i = 0; i < 3; i++) { const d = (pb[i]||0)-(pa[i]||0); if (d) return d; } return 0; }); return [...new Set(versions)]; } } catch (_) {} return ['1.21.4','1.21.3','1.21.2','1.21.1','1.21','1.20.6','1.20.4','1.20.2', '1.20.1','1.20','1.19.4','1.19.2','1.19','1.18.2','1.18','1.17.1', '1.16.5','1.15.2','1.14.4','1.12.2','1.9.4','1.8.9']; } async function detectServerVersion(host, port) { return new Promise(resolve => { try { const mcProto = require('minecraft-protocol'); const timer = setTimeout(() => resolve(null), 8000); mcProto.ping({ host, port, closeTimeout: 6000 }, (err, data) => { clearTimeout(timer); if (err || !data) { resolve(null); return; } try { const raw = String(data.version?.name || data.version || ''); const protocol = data.version?.protocol || null; const m = raw.match(/(\d+\.\d+(?:\.\d+)?)/); let ver = m ? m[1] : null; if (!ver && protocol) { ver = PROTOCOL_MAP[protocol] || null; } if (ver && !fallbackVersionList.includes(ver)) { ver = fallbackVersionList[0]; } resolve(ver); } catch (_) { resolve(null); } }); } catch (_) { resolve(null); } }); } // ════════════════════════════════════════════════════════════════════════════ // PLUGIN DATABASE — قاعدة بيانات البلاجن المعروفة // ════════════════════════════════════════════════════════════════════════════ const PLUGIN_DB = { // أداء 'clearlag': { cat:'performance', desc:'يحذف الـ entities الزائدة لتقليل الـ lag', good:true }, 'spark': { cat:'performance', desc:'أداة مراقبة أداء متقدمة — تحليل TPS وRAM', good:true }, 'laggremover': { cat:'performance', desc:'يزيل entities ونواتج البناء لتحسين الأداء', good:true }, 'farmcontrol': { cat:'performance', desc:'يتحكم في حجم المزارع لمنع الـ lag', good:true }, 'entitytracker': { cat:'performance', desc:'يتتبع ويحذف الـ entities الزائدة', good:true }, 'tpsbenchmark': { cat:'performance', desc:'يقيس وعرض معدل TPS الحالي', good:true }, 'chunky': { cat:'performance', desc:'يولّد الـ chunks مسبقاً لمنع الـ lag', good:true }, 'fastchunkpregenerator': { cat:'performance', desc:'توليد chunks سريع', good:true }, 'entityclearer': { cat:'performance', desc:'ينظف الـ entities تلقائياً', good:true }, 'performanceplus': { cat:'performance', desc:'تحسين عام للأداء', good:true }, // حماية 'worldguard': { cat:'protection', desc:'حماية المناطق من التخريب والتعديل', good:true }, 'essentialsx': { cat:'utility', desc:'أوامر أساسية: /tpa, /spawn, /home, AFK', good:true }, 'essentials': { cat:'utility', desc:'أوامر أساسية للسيرفر', good:true }, 'anticheat': { cat:'security', desc:'كشف الغش والـ hacks', good:true }, 'coreprotect': { cat:'protection', desc:'تسجيل التعديلات وإمكانية التراجع', good:true }, 'griefprevention': { cat:'protection', desc:'منع الـ grief وحماية الأراضي', good:true }, // إدارة 'luckperms': { cat:'admin', desc:'إدارة الصلاحيات المتقدمة', good:true }, 'vaultapi': { cat:'admin', desc:'API للاقتصاد والصلاحيات', good:true }, 'vault': { cat:'admin', desc:'API للاقتصاد والصلاحيات', good:true }, 'multiverse-core': { cat:'worlds', desc:'إدارة عوالم متعددة', good:true }, 'multiverse': { cat:'worlds', desc:'إدارة عوالم متعددة', good:true }, 'dynmap': { cat:'map', desc:'خريطة ديناميكية للسيرفر على المتصفح', good:true }, 'bluemap': { cat:'map', desc:'خريطة ثلاثية الأبعاد على المتصفح', good:true }, // لاعبين 'skinsrestorer': { cat:'cosmetic', desc:'استعادة الـ skins في السيرفرات offline', good:true }, 'tab': { cat:'ui', desc:'تخصيص قائمة اللاعبين (TAB)', good:true }, 'placeholderapi': { cat:'utility', desc:'API للـ placeholders للبلاجن الأخرى', good:true }, 'papi': { cat:'utility', desc:'PlaceholderAPI', good:true }, 'chatmanager': { cat:'chat', desc:'إدارة وتنسيق الشات', good:true }, 'discordsrv': { cat:'integration', desc:'ربط السيرفر بـ Discord', good:true }, // AFK 'antialfkplus': { cat:'afk', desc:'منع طرد AFK في Aternos', good:true }, 'antialfk': { cat:'afk', desc:'منع طرد AFK', good:true }, 'noafkkick': { cat:'afk', desc:'منع طرد AFK', good:true }, 'afkplus': { cat:'afk', desc:'نظام AFK محسّن', good:true }, 'antiafc': { cat:'afk', desc:'منع kick AFK', good:true }, // اقتصاد 'essentialsxeco': { cat:'economy', desc:'نظام اقتصاد مدمج مع EssentialsX', good:true }, 'shopguiplus': { cat:'economy', desc:'متجر GUI سهل الاستخدام', good:true }, 'aureliumskills': { cat:'skills', desc:'نظام مهارات للاعبين', good:true }, 'mmocore': { cat:'rpg', desc:'نظام RPG متقدم', good:true }, }; function analyzePlugin(name) { const key = name.toLowerCase().replace(/[\s\-_]/g, ''); for (const [k, v] of Object.entries(PLUGIN_DB)) { if (key.includes(k.replace(/[\s\-_]/g, ''))) return { name, ...v }; } const lk = key; if (/anticheat|nocheat|aac|matrix|spartan|grim/.test(lk)) return { name, cat:'security', desc:'مكافحة الغش', good:true }; if (/economy|econ|money|coin|token/.test(lk)) return { name, cat:'economy', desc:'نظام اقتصاد', good:true }; if (/chat|message|msg|talk/.test(lk)) return { name, cat:'chat', desc:'إدارة الشات', good:true }; if (/protect|guard|safe|region/.test(lk)) return { name, cat:'protection', desc:'حماية وأمان', good:true }; if (/shop|market|sell|buy|trade/.test(lk)) return { name, cat:'economy', desc:'نظام تجاري', good:true }; if (/skill|level|xp|exp|rpg/.test(lk)) return { name, cat:'rpg', desc:'نظام مهارات/مستويات', good:true }; if (/clear|clean|remov|delet/.test(lk)) return { name, cat:'performance', desc:'تنظيف الـ entities/drops', good:true }; if (/world|dimension|teleport|warp/.test(lk)) return { name, cat:'worlds', desc:'إدارة عوالم أو نقاط teleport', good:true }; return { name, cat:'misc', desc:'بلاجن متنوعة', good:false }; } // ════════════════════════════════════════════════════════════════════════════ // GLOBAL STATE // ════════════════════════════════════════════════════════════════════════════ const STATE = Object.freeze({ IDLE:'IDLE', WALKING:'WALKING', DIGGING:'DIGGING', SLEEPING:'SLEEPING', EATING:'EATING', FOLLOWING:'FOLLOWING', FARMING:'FARMING', COMMAND:'COMMAND', SCANNING:'SCANNING', }); const sleep = ms => new Promise(r => setTimeout(r, Math.max(0, ms))); const randInt = (a, b) => Math.floor(Math.random() * (b - a + 1)) + a; const randFloat = (a, b) => Math.random() * (b - a) + a; const pick = arr => arr[Math.floor(Math.random() * arr.length)]; const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v)); const tsNow = () => new Date().toISOString().replace('T',' ').slice(0,19); const sysLog = (e, m) => console.log(`[${tsNow()}] [SYS] ${e} ${m}`); const botLog = (id, e, m) => console.log(`[${tsNow()}] [B${id+1}] ${e} ${m}`); // سجل الأحداث للـ dashboard const EVENT_LOG = []; function addEvent(type, msg) { EVENT_LOG.unshift({ time: tsNow(), type, msg }); if (EVENT_LOG.length > 100) EVENT_LOG.length = 100; } // بيانات البلاجن let detectedPlugins = []; let performanceData = { tps: null, lag: false, lastCheck: null, history: [], ecoMode: false, ecoCount: 0 }; let serverInfo = { motd: '', players: 0, maxPlayers: 0, version: '' }; // تحميل البيانات المحفوظة من التشغيل السابق (بعد تهيئة المتغيرات) setTimeout(loadData, 0); // ════════════════════════════════════════════════════════════════════════════ // BOT ROLES — نظام أدوار الفريق المتكامل // ════════════════════════════════════════════════════════════════════════════ const BOT_ROLES = [ { id:'commander', label:'🎖️ القائد', color:'#f59e0b', desc:'يوجّه الفريق ويدير السيرفر تلقائياً' }, { id:'guardian', label:'🛡️ الحارس', color:'#3b82f6', desc:'يراقب اللاعبين ويكشف AFK والغش' }, { id:'farmer', label:'🌾 المزارع', color:'#10b981', desc:'يدير الموارد والأكل للجميع' }, { id:'builder', label:'🔨 البنّاء', color:'#8b5cf6', desc:'يعمر ويحسّن السيرفر' }, { id:'diplomat', label:'💬 الدبلوماسي', color:'#06b6d4', desc:'يرحّب باللاعبين ويتواصل معهم' }, ]; // ════════════════════════════════════════════════════════════════════════════ // TASK MAP — توزيع المهام ديناميكياً حسب عدد البوتات المتصلة // المهام: guardian = مراقبة AFK + طرد الخاملين // diplomat = ترحيب + تشجيع اللاعبين // commander = تقارير دورية + إعلانات // farmer = تذكير بالأكل // optimizer = تحسين أداء السيرفر // ════════════════════════════════════════════════════════════════════════════ const TASK_MAP = { 1: [['guardian','diplomat','commander','farmer','optimizer']], 2: [['guardian','commander'], ['diplomat','farmer','optimizer']], 3: [['guardian','optimizer'], ['commander','farmer'], ['diplomat']], 4: [['commander'], ['guardian'], ['farmer'], ['diplomat']], }; // ════════════════════════════════════════════════════════════════════════════ // PLAYER REGISTRY — سجل اللاعبين (ROLE-01) // ════════════════════════════════════════════════════════════════════════════ const playerRegistry = new Map(); // username → { firstSeen, lastSeen, visits, joinedAt, lastChat } const _chatRateMap = new Map(); // slotId → lastSendTime (rate limit للـ /api/send-chat) // Shared team state — حالة الفريق المشتركة const teamState = { lastReport: null, eventPending: false }; // ════════════════════════════════════════════════════════════════════════════ // DANGER BLOCKS & FOOD // ════════════════════════════════════════════════════════════════════════════ const DANGER_BLOCKS = new Set([ 'lava','flowing_lava','fire','soul_fire','cactus','magma_block', 'sweet_berry_bush','wither_rose','powder_snow', ]); const FOOD_NAMES = [ 'bread','cooked_beef','cooked_chicken','cooked_pork_chop','cooked_salmon', 'cooked_mutton','cooked_rabbit','apple','golden_apple','carrot','baked_potato', 'melon_slice','cookie','dried_kelp','beef','pork_chop','chicken','salmon', 'rabbit','mutton','pumpkin_pie','sweet_berries', ]; const DIGGABLE = [ 'stone','cobblestone','dirt','grass_block','sand','gravel','netherrack', 'oak_log','spruce_log','birch_log','jungle_log','acacia_log','dark_oak_log', 'coal_ore','iron_ore','gold_ore','diamond_ore','deepslate','clay','soul_sand', 'andesite','diorite','granite', ]; // ════════════════════════════════════════════════════════════════════════════ // CHAT DATA // ════════════════════════════════════════════════════════════════════════════ const GENERAL_CHAT = [ 'hey','yo','lol','gg','bruh','fr?','nice','wow','يلا','ماشي','اوكي','هههه', 'back','lmao','rip','bro fr','سبحان الله','omg','real??','اه والله', 'anyone here?','مين اونلاين؟','fr fr','bro 😭','lag rn tbh', 'night time already??','morning guys!','يلا نلعب','inventory full 😭', 'صباح الـ grind!!','gn everyone','who else online?', ]; const SMART_REPLIES = { greet: ['hey!','yo!','هاي!','ايه الأخبار','wassup bro','مرحبا!','ahlan!'], how: ['good lol','fine والله','alright','chilling','not bad tbh','تمام بحمد الله'], bot: ['no lol im human','أنا مش bot 😂','bruh im not a bot','what?? no lol','لأ يعني 😭'], afk: ['nah im here','مش afk','just busy bro','im good','still here tbh'], bye: ['bye!','later bro','cya','مع السلامة','yala bye','peace ✌️'], lag: ['yeah bad rn','same lol','الـ lag terrible','fr ping rough'], diamond: ['y-58 يا جماعة','cave mining works','strip mine الطريقة'], food: ['same lol جعان','need food too','farming rn','wheat soon yalla'], night: ['scary night lol','الليل bad','mobs everywhere careful'], death: ['F 💀','rip lol 😭','oof bro','كيف مت؟؟'], yes: ['اه','yep','sure','facts','true'], no: ['nah','لأ','idk','prob not'], }; const B2B_GENERIC = [ 'fr??','nice lol','ماشي','اوكي bro','هههه','seriously??','lmaooo', 'real??','bro 😭','lol yep','همممم','wait what','اه والله','fr fr', 'اه كده؟','omg','هههه اه','يسطا!','dang bro','no way!!', ]; // ════════════════════════════════════════════════════════════════════════════ // TEAM CONFIG — يُبنى من CFG عند بدء البوتات // ════════════════════════════════════════════════════════════════════════════ function buildTeamCfg() { const names = [CFG.bot1Name, CFG.bot2Name, CFG.bot3Name, CFG.bot4Name]; const count = Math.min(CFG.botCount, 4); // تقسيم ذكي: لو بوتان فقط → بوت نشط + بوت مراقبة خفيف const lightMode = count <= 2; // وضع خفيف للبوت الثاني عند 1-2 بوتات const all = [ { id:0, label:'حفّار', emoji:'⛏️', personality:'miner', username: names[0], sessionMinH:4, sessionMaxH:8, breakMinM:20, breakMaxM:60, chatStyle:[ 'بحفر deep rn','found iron!!','y-58 يا جماعة','الكهف ده ضخم جداً', 'diamonds?? maybe!!','lava everywhere يا رب 😭','coal كتير هنا', 'out of torches rip','creeper blew up my stuff 💀','cave sound scary lol', 'ancient debris maybe??','full inventory already 😭','found spawner 👀', 'skeleton almost got me لول','بحفر branch mine dude', ], oreTargets:['coal_ore','iron_ore','copper_ore','gold_ore','diamond_ore', 'deepslate_iron_ore','deepslate_coal_ore','deepslate_copper_ore', 'deepslate_gold_ore','deepslate_diamond_ore','redstone_ore','lapis_ore'], topicWords:['mine','dig','iron','coal','diamond','cave','ore','y-58','deepslate'], }, { id:1, label:'بنّاء', emoji:'🏗️', personality:'builder', username: names[1], sessionMinH:5, sessionMaxH:9, breakMinM:25, breakMaxM:70, chatStyle:[ 'بباني بيتي شوية شوية 🏗️','need more wood lol','anyone got stone؟', 'ran out of glass 😭','working on my farm','almost done with the roof!!', 'adding a second floor يلا','this design coming together 🔥','بعمل storage room', 'صممت حاجة حلوة النهارده','building underwater base 😱', ], oreTargets:['oak_log','birch_log','spruce_log','jungle_log','acacia_log', 'dark_oak_log','stone','cobblestone','sand','gravel'], topicWords:['build','wood','stone','house','base','farm','glass','craft','design'], }, { id:2, label:'مستكشف', emoji:'🗺️', personality:'explorer', username: names[2], sessionMinH:5, sessionMaxH:10, breakMinM:30, breakMaxM:90, chatStyle:[ 'بفتش على الخريطة','found a village!! 👀','this biome is SICK bro', 'found desert pyramid!!','cherry blossom biome gorgeous', 'ancient city down below scary 😱','warden almost got me omg!!', 'exploring the nether rn 🔥','this mesa biome is wild bro', 'beautiful sunset من هنا 🌅','found swamp village!! rare', ], oreTargets:['chest','barrel','crafting_table','furnace','lectern','bookshelf'], topicWords:['explore','village','biome','temple','coords','far','map','nether','end'], }, { id:3, label:'مهندس ريدستون', emoji:'🔴', personality:'redstone', username: names[3], sessionMinH:4, sessionMaxH:9, breakMinM:20, breakMaxM:60, chatStyle:[ 'redstone circuit almost done 🔴','making auto farm rn!!','this piston door is tricky', 'bro my flying machine broke 💀','comparator logic is wild','TNT duper working!!', 'zero-tick farm patched ugh','need more redstone ore','my sorter is finally sorting', 'observer chain going brr','made an auto smelter 🔥','slime block shenanigans', 'flush piston door complete!!','lag machine? nah just redstone 😂', ], oreTargets:['redstone_ore','deepslate_redstone_ore','observer','piston', 'sticky_piston','redstone_torch','lever','comparator'], topicWords:['redstone','piston','farm','auto','circuit','comparator','observer','machine'], }, ]; const result = all.slice(0, count); // تقسيم ذكي للمهام: بوت واحد أو اثنان → الثاني يصبح مراقب خفيف if (lightMode && result.length >= 2) { result[1] = { ...result[1], lightBot: true, sessionMinH: 10, sessionMaxH: 14, breakMinM: 5, breakMaxM: 10, chatStyle: ['مراقب السيرفر 👀','كل شيء تمام 👍','السيرفر مستقر','هنا للمراقبة 🛡️'] }; } return result; } // ════════════════════════════════════════════════════════════════════════════ // TASK MANAGER // ════════════════════════════════════════════════════════════════════════════ class TaskManager { constructor() { this._task=null; this._pri=0; this._state=STATE.IDLE; this._timer=null; } tryAcquire(name, pri=0, tms=20000) { if (this._task && pri<=this._pri) return false; if (this._timer) { clearTimeout(this._timer); this._timer=null; } this._task=name; this._pri=pri; const n=name.toLowerCase(); if (n.includes('dig')) this._state=STATE.DIGGING; else if (n.includes('walk')) this._state=STATE.WALKING; else if (n.includes('sleep')) this._state=STATE.SLEEPING; else if (n.includes('follow')) this._state=STATE.FOLLOWING; else if (n.includes('farm')) this._state=STATE.FARMING; else if (n.includes('cmd')) this._state=STATE.COMMAND; else if (n.includes('eat')) this._state=STATE.EATING; else if (n.includes('scan')) this._state=STATE.SCANNING; else this._state=STATE.WALKING; this._timer=setTimeout(()=>{ if(this._task===name){ this._task=null; this._pri=0; this._state=STATE.IDLE; this._timer=null; }}, tms); return true; } release(name) { if(this._task===name){ if(this._timer){ clearTimeout(this._timer); this._timer=null; } this._task=null; this._pri=0; this._state=STATE.IDLE; } } reset() { if(this._timer){ clearTimeout(this._timer); this._timer=null; } this._task=null; this._pri=0; this._state=STATE.IDLE; } get busy() { return !!this._task; } get state() { return this._state; } } // ════════════════════════════════════════════════════════════════════════════ // HELPERS // ════════════════════════════════════════════════════════════════════════════ async function humanSleep(mn, mx) { if (mn>=mx) { await sleep(mn); return; } const r=Math.random(), range=mx-mn; let ms = r<0.70 ? mn+Math.random()*range*0.50 : r<0.95 ? mn+range*0.50+Math.random()*range*0.375 : mn+range*0.875+Math.random()*range*0.125; ms=Math.round(ms); const chunks=Math.floor(ms/2000); let elapsed=0; for (let i=0; iMath.PI) yd-=2*Math.PI; while(yd<-Math.PI) yd+=2*Math.PI; await bot.look(sy+yd*e, clamp(sp+(tp-sp)*e,-Math.PI/2,Math.PI/2), false); await sleep(Math.max(20,delay+randInt(-12,12))); } } catch(_){} } function dangerNear(bot) { if (!bot?.entity) return false; const pos=bot.entity.position; for (let dx=-2; dx<=2; dx++) for (let dy=-1; dy<=2; dy++) for (let dz=-2; dz<=2; dz++) { try { const b=bot.blockAt(pos.offset(dx,dy,dz)); if (b&&DANGER_BLOCKS.has(b.name)) return true; } catch(_){} } return false; } function scanEnv(bot) { const r={hasOre:false,hasWood:false,hasCrops:false,hasDanger:false, hasWater:false,hasPlayers:false,playerCount:0,isUnderground:false,isNight:false}; if (!bot?.entity) return r; try { const pos=bot.entity.position; r.isNight=(bot.time?.timeOfDay??0)>12000; r.isUnderground=pos.y<60; const nearby=Object.values(bot.players||{}).filter(p=>p.entity&&p.username!==bot.username); r.hasPlayers=nearby.length>0; r.playerCount=nearby.length; for (const n of ['coal_ore','iron_ore','gold_ore','diamond_ore','deepslate_iron_ore']) { try { const b=bot.findBlock({matching:bl=>bl.name===n,maxDistance:8}); if(b){r.hasOre=true;break;} } catch(_){} } for (const n of ['oak_log','birch_log','spruce_log','jungle_log']) { try { const b=bot.findBlock({matching:bl=>bl.name===n,maxDistance:6}); if(b){r.hasWood=true;break;} } catch(_){} } r.hasDanger=dangerNear(bot); } catch(_){} return r; } // ════════════════════════════════════════════════════════════════════════════ // TEAM CHAT LOCK // ════════════════════════════════════════════════════════════════════════════ const teamChat = { locked:false, speaker:null, lastMsgTime:0, lock(id) { this.locked=true; this.speaker=id; this.lastMsgTime=Date.now(); setTimeout(()=>{ this.locked=false; },2500); }, tryLock(id) { if (this.locked&&this.speaker!==id) return false; this.lock(id); return true; }, canChat() { return Date.now()-this.lastMsgTime>1500; }, }; // ════════════════════════════════════════════════════════════════════════════ // BOT SLOT — كل بوت له حالته المعزولة // ════════════════════════════════════════════════════════════════════════════ class BotSlot { constructor(cfg) { this.cfg = cfg; this.bot = null; this.connecting = false; this.spawnTime = null; this.lastAct = null; this.reconnDelay = 15000; this.gen = 0; this.sessCount = 0; this.totalUptime = 0; this.avgPing = null; this.chatOff = false; this.deepSleep = false; this.connRefused = 0; this.tasks = new TaskManager(); this.cmdQueue = []; this.followTarget = null; this.envCache = {}; this.kickHistory = []; this.pingHist = []; this.isOp = false; this.eating = false; this.sleeping = false; this._reconnTimer = null; this._sessTimer = null; this._keepTimer = null; this._watchTimer = null; this._lastVer = null; this._deepSleepStart = null; this.assignedTasks = []; // يُحدَّث ديناميكياً من TeamCoordinator.redistributeTasks() this.role = BOT_ROLES[cfg.id % BOT_ROLES.length]; this.opActionsRun = false; this._stealthNames = [ `${cfg.username}_x`,`Player${randInt(1000,9999)}`,`Craft${randInt(100,999)}`, ]; this._stealthIdx = 0; } log(e, m) { botLog(this.cfg.id, e, m); } mark() { this.lastAct=Date.now(); } alive(b,g) { return this.bot===b && this.gen===g && !!b?.entity; } clearCtrl() { for (const s of ['forward','back','left','right','jump','sprint','sneak']) try { this.bot?.setControlState(s,false); } catch(_){} this.tasks.reset(); } getUser() { return this.cfg.username; } // ── Plugin Detection (PLUG-01) — يعمل مع OP وبدونه ──────────────────────── async scanPlugins(bot, force=false) { if (force) this.isOp = true; this.log('🔍','فحص البلاجن...'); addEvent('plugin','🔍 بدأ فحص البلاجن على السيرفر'); if (this.isOp) { // مع OP: نستخدم الأوامر المباشرة const commands = ['/plugins','/pl','/pluginlist']; for (const cmd of commands) { try { bot.chat(cmd); await sleep(3000); break; } catch(_) {} } } else { // بدون OP: نطلب المعلومات من الـ Server Brand + version command try { bot.chat('/version'); await sleep(2000); } catch(_){} try { bot.chat('/help'); await sleep(2000); } catch(_){} // نستمع للردود في parsePluginsFromChat تلقائياً this.log('🔍','فحص سلبي بدون OP (نستمع لردود الشات)'); } } parsePluginsFromChat(msg) { const lo = msg.toLowerCase(); const cleaned = msg.replace(/\u00a7./g,'').replace(/[§❌✅]/g,''); // كشف مباشر من قائمة البلاجن const isPluginList = lo.includes('plugin') || lo.includes('pl:') || lo.includes('pl ') || lo.includes('/version') || lo.includes('running'); if (isPluginList) { const parts = cleaned.split(/[\,\s]+/); const found = []; for (const part of parts) { const clean = part.trim().replace(/[\[\]()]/g,''); if (clean.length < 2 || clean.length > 40) continue; if (/^\d+$/.test(clean)) continue; if (['plugins','plugin','list','pl','enabled','disabled','total','server','running','version','bukkit','spigot','paper'].includes(clean.toLowerCase())) continue; found.push(clean); } if (found.length > 0) { const analyzed = found.map(n => analyzePlugin(n)); const newPlugins = analyzed.filter(p => !detectedPlugins.some(e => e.name.toLowerCase()===p.name.toLowerCase())); if (newPlugins.length > 0) { detectedPlugins.push(...newPlugins); addEvent('plugin',`✅ اكتُشف ${newPlugins.length} بلاجن جديدة`); this.log('🔌',`اكتُشف ${detectedPlugins.length} بلاجن إجمالاً`); this.analyzePerformancePlugins(); } } } // كشف سلبي: بلاجن معروفة تظهر في رسائل الشات العادية const PASSIVE_PLUGINS = [ ['essentials','EssentialsX'],['worldguard','WorldGuard'],['worldedit','WorldEdit'], ['luckperms','LuckPerms'],['vault','Vault'],['clearlag','ClearLag'], ['coreprotect','CoreProtect'],['dynmap','Dynmap'],['griefprevention','GriefPrevention'], ['shopguiplus','ShopGUI+'],['mcmmo','mcMMO'],['aureliumskills','AureliumSkills'], ['discordsrv','DiscordSRV'],['geyser','Geyser'],['floodgate','Floodgate'], ['spark','spark'],['lagg','NoLagg'],['anticheat','AntiCheat'], ]; for (const [key, name] of PASSIVE_PLUGINS) { if (lo.includes(key) && !detectedPlugins.some(p=>p.name.toLowerCase()===name.toLowerCase())) { const analyzed = analyzePlugin(name); detectedPlugins.push(analyzed); addEvent('plugin',`🔎 كُشف سلبياً: ${name}`); } } } analyzePerformancePlugins() { const perf = detectedPlugins.filter(p => p.cat==='performance' || p.cat==='afk'); if (perf.length > 0) { addEvent('perf',`🚀 بلاجن أداء مكتشفة: ${perf.map(p=>p.name).join(', ')}`); this.log('🚀',`بلاجن أداء: ${perf.map(p=>p.name).join(', ')}`); } const hasAntiAfk = detectedPlugins.some(p=>p.cat==='afk'); if (hasAntiAfk) { addEvent('info','✅ بلاجن anti-AFK موجودة — البوتات محمية من الـ kick'); } } // ── Performance Monitor (PERF-01) ──────────────────────────────────────── async monitorPerformance(bot, gen) { while (this.alive(bot, gen)) { try { await sleep(60000); if (!this.alive(bot, gen)) continue; performanceData.lastCheck = tsNow(); // اقرأ TPS — مع OP بأمر /tps، وبدون OP نستمع للشات if (this.isOp) { try { bot.chat('/tps'); } catch(_){} await sleep(2000); } // مراقبة أداء البوت نفسه (ping) — تعمل مع/بدون OP const ping = this.avgPing || 0; if (ping > 500 || performanceData.lag) { this.log('⚠️','اكتُشف lag عالي (ping: ' + ping + 'ms)'); addEvent('perf',`⚠️ Ping عالي: ${ping}ms — السيرفر يعاني lag`); // لو عندنا OP: نطبق التحسينات التلقائية if (this.isOp) await this.optimizePerformance(bot); // بدون OP: البوت ينتظر ويخفف نشاطه (Eco Mode مفعّل من parseTpsFromChat) } } catch(_) { await sleep(30000); } } } async optimizePerformance(bot) { if (!this.isOp) return; const cmds = []; // بناءً على البلاجن المكتشفة const hasClearLag = detectedPlugins.some(p=>p.name.toLowerCase().includes('clearlag')); const hasSpark = detectedPlugins.some(p=>p.name.toLowerCase().includes('spark')); const hasLagg = detectedPlugins.some(p=>p.name.toLowerCase().includes('lagg')); if (hasClearLag) cmds.push('/lagg clear'); if (hasLagg) cmds.push('/lagg clear'); if (hasSpark) cmds.push('/spark gc'); // أوامر عامة cmds.push('/minecraft:kill @e[type=item]'); for (const cmd of cmds) { try { bot.chat(cmd); await sleep(1500); addEvent('perf',`⚡ تم تنفيذ: ${cmd}`); } catch(_) {} } } parseTpsFromChat(msg) { const m = msg.match(/tps[:\s]+([0-9.]+)/i) || msg.match(/([0-9.]+)\s*tps/i) || msg.match(/TPS[^0-9]*([0-9.]+)/); if (m) { const tps = parseFloat(m[1]); if (!isNaN(tps) && tps >= 0 && tps <= 22) { performanceData.tps = tps; performanceData.lag = tps < 15; performanceData.history.push({ time: tsNow(), tps }); if (performanceData.history.length > 30) performanceData.history.shift(); // ── ECO MODE: تخفيف ذكي عند انخفاض TPS ────────────────────────────── if (tps < 14) { performanceData.ecoCount = Math.min(5, (performanceData.ecoCount || 0) + 1); if (!performanceData.ecoMode && performanceData.ecoCount >= 2) { performanceData.ecoMode = true; sysLog('🌿','ECO MODE مفعّل — تخفيف نشاط البوتات لتقليل الضغط على السيرفر'); addEvent('perf',`🌿 ECO MODE: TPS=${tps.toFixed(1)} — البوتات تعمل بحد أدنى`); } addEvent('perf',`⚠️ TPS منخفض: ${tps.toFixed(1)} — السيرفر يعاني من lag`); } else if (tps >= 18) { performanceData.ecoCount = Math.max(0, (performanceData.ecoCount || 0) - 1); if (performanceData.ecoMode && performanceData.ecoCount === 0) { performanceData.ecoMode = false; sysLog('✅','ECO MODE منتهي — TPS عاد لطبيعي، البوتات تعمل بكامل طاقتها'); addEvent('perf',`✅ ECO MODE منتهي: TPS=${tps.toFixed(1)} — عودة للنشاط الكامل`); } } } } } // ── Kick Analyzer ────────────────────────────────────────────────────── analyzeKick(raw) { let text=raw||''; try { const p=JSON.parse(raw); text=p.text||p.translate||raw; } catch(_){} const lo=text.toLowerCase(); const isBot = /\bbot\b|automated|captcha|verification|suspicious|\bdetected\b|\bhuman\b/.test(lo); const isBan = /\bbanned\b|ip.?ban|suspended/.test(lo); const isStop = /server.*stop|shutdown|restart|closing/.test(lo); const isFull = /server is full|no slot/.test(lo); const isSess = /logged in from|already connected|duplicate/.test(lo); const isVer = /outdated|version|protocol|incompatible/.test(lo); let delay = null; if (isBan) delay = 8*60000+randInt(0,120000); if (isStop) delay = randInt(25000,45000); if (isFull) delay = randInt(90000,180000); if (isSess) delay = randInt(5*60000,8*60000); return { text, isBot, isBan, isStop, isFull, isSess, isVer, delay }; } // ── Reconnect ────────────────────────────────────────────────────────── scheduleReconnect(delay) { if (this.deepSleep) return; // FIX-2: لا إعادة اتصال في السبات if (this._reconnTimer) { clearTimeout(this._reconnTimer); this._reconnTimer=null; } const d = delay ?? Math.min(this.reconnDelay, 120000); this.reconnDelay = Math.min(this.reconnDelay * 1.5, 120000); this.log('⏳',`إعادة الاتصال خلال ${Math.round(d/1000)}ث`); this._reconnTimer = setTimeout(() => { this._reconnTimer=null; if (!this.deepSleep && !this.bot && !this.connecting) this.connect(); }, d); } // ── CONNECT ──────────────────────────────────────────────────────────── async connect() { if (this.deepSleep) return; if (this.bot || this.connecting) return; // FIX-WATCH: تنظيف watchTimer القديم قبل اتصال جديد if (this._watchTimer) { clearInterval(this._watchTimer); this._watchTimer = null; } this.connecting=true; this.log('🔗',`اتصال → ${CFG.host}:${CFG.port}`); addEvent('connect',`🔗 ${this.cfg.username} يحاول الاتصال`); let ver = resolveVersion(CFG.version) || autoVersion || fallbackVersionList[0]; if (!ver || ver==='auto') { ver = await detectServerVersion(CFG.host, CFG.port) || fallbackVersionList[0]; } if (!autoVersion) autoVersion=ver; this._lastVer = ver; this.log('🎮',`الإصدار: ${ver}`); this.gen++; const g=this.gen; let bot; try { bot = mineflayer.createBot({ host: CFG.host, port: CFG.port, username: this.getUser(), version: ver, disableChatSigning: true, checkTimeoutInterval: 60000, hideErrors: true, auth: 'offline', }); } catch (e) { this.connecting=false; this.log('❌',`فشل الإنشاء: ${e.message}`); this.scheduleReconnect(30000); return; } this.bot=bot; this.connecting=false; // ANTI-01: حماية من الـ timeout const connTimer = setTimeout(() => { if (!bot.entity) { this.log('⏱️','timeout اتصال'); try { bot.quit(); } catch(_){} } }, 45000); bot.on('login', () => { clearTimeout(connTimer); this.reconnDelay=15000; this.connRefused=0; this.spawnTime=Date.now(); this.sessCount++; this.isOp=false; this.log('✅',`متصل | جلسة #${this.sessCount} | إصدار: ${ver}`); addEvent('connect',`✅ ${this.cfg.username} اتصل بنجاح (إصدار ${ver})`); serverInfo.version=ver; try { bot.loadPlugin(pathfinder); bot.pathfinder.setMovements(new Movements(bot)); } catch(_){} // بدء الحلقات this._startLoops(bot, g); // جلسة تلقائية const sessLen = randInt( this.cfg.sessionMinH * 3600000, this.cfg.sessionMaxH * 3600000 ); this._sessTimer = setTimeout(() => { this.log('💤',`انتهت الجلسة — استراحة`); addEvent('session',`💤 ${this.cfg.username} يأخذ استراحة`); this.totalUptime += Math.round((Date.now()-(this.spawnTime||Date.now()))/1000); this.spawnTime=null; try { bot.quit(); } catch(_){} const breakLen = randInt(this.cfg.breakMinM, this.cfg.breakMaxM)*60000; this._reconnTimer = setTimeout(()=>{ this._reconnTimer=null; this.connect(); }, breakLen); }, sessLen); }); bot.on('spawn', () => { this.log('🌍','spawn'); this.mark(); this.startKeepAlive(bot, g); // بعد spawn بـ 10 ثواني: فحص البلاجن لو OP setTimeout(() => this.scanPlugins(bot), 10000); }); bot.on('chat', (username, message) => { if (username === bot.username) return; this.handleChat(username, message, bot, g); this.parsePluginsFromChat(message); this.parseTpsFromChat(message); // معلومات السيرفر const lo = message.toLowerCase(); const isOpMsg = lo.includes('you are now an operator') || lo.includes('granted op') || lo.includes('op level') || lo.includes('made') && lo.includes('server operator') || lo.includes('you have been granted op') || lo.includes('granted operator') || (username==='' && /\bop\b/.test(lo) && !/\bshop\b|\bdrop\b|\btop\b|\bcrop\b/.test(lo)); if (isOpMsg) { this.isOp=true; this.log('👑','حصل على OP!'); addEvent('op',`👑 ${this.cfg.username} حصل على OP — بدء فحص البلاجن`); setTimeout(() => this.scanPlugins(bot), 2000); setTimeout(() => this.doOPActions(bot), 6000); // OP-OPT: تحسينات السيرفر التلقائية } // رسائل النظام — كشف TPS if (username==='') { this.parseTpsFromChat(message); } }); // FIX-3: كشف OP من كل مصادر الرسائل الممكنة const detectOpText = (raw) => { if (this.isOp) return; // بالفعل عنده OP const tlo = raw.toLowerCase().replace(/[§\u00a7]./g,'').replace(/[""]/g,'"'); const isOp = tlo.includes('you are now an operator') || tlo.includes('granted op') || tlo.includes('you have been granted op') || tlo.includes('made') && tlo.includes('server operator') || tlo.includes('granted operator') || (tlo.includes('op') && tlo.includes('now') && (tlo.includes('you') || tlo.includes('are'))); if (isOp) { this.isOp=true; this.log('👑','كُشف OP!'); addEvent('op',`👑 ${this.cfg.username} حصل على OP — جاري فحص البلاجن والإعداد`); setTimeout(()=>this.scanPlugins(bot),2000); setTimeout(()=>this.doOPActions(bot),5000); // FIX: أضف doOPActions هنا أيضاً } }; bot.on('message', (jsonMsg) => { try { // طريقة 1: toString() العادية const t1 = jsonMsg.toString(); detectOpText(t1); this.parsePluginsFromChat(t1.toLowerCase()); this.parseTpsFromChat(t1); // طريقة 2: text مباشر من الـ JSON try { const j = jsonMsg.json || jsonMsg; if (j && j.text) detectOpText(j.text); if (j && j.translate) detectOpText(j.translate); // استخراج extra if (j && Array.isArray(j.extra)) { j.extra.forEach(ex => { if(ex && ex.text) detectOpText(ex.text); }); } } catch(_){} } catch(_){} }); // FIX-3: Raw packet listener — يمسك أي صيغة من Aternos try { bot._client.on('chat', (packet) => { try { const raw = typeof packet.message==='string' ? packet.message : JSON.stringify(packet.message||''); detectOpText(raw); // حاول extract نص من JSON مباشرة try { const obj = JSON.parse(raw); if (obj.text) detectOpText(obj.text); if (obj.translate) detectOpText(obj.translate); } catch(_){} } catch(_){} }); } catch(_){} bot.on('kicked', (reason) => { clearTimeout(connTimer); const k = this.analyzeKick(reason); this.log('🚫',`طُرد: ${k.text.slice(0,80)}`); addEvent('kick',`🚫 ${this.cfg.username} طُرد: ${k.text.slice(0,60)}`); this.kickHistory.unshift({ time: tsNow(), reason: k.text.slice(0,80) }); if (this.kickHistory.length>5) this.kickHistory.length=5; // VER-02: إذا كان السبب version mismatch جرّب الإصدار التالي في القائمة if (k.isVer) { const curIdx = fallbackVersionList.indexOf(this._lastVer || ''); const nextVer = curIdx >= 0 ? fallbackVersionList[curIdx + 1] : null; if (nextVer) { this.log('🔄',`إصدار غير متوافق — محاولة: ${nextVer}`); addEvent('connect',`🔄 إصدار بديل: ${nextVer}`); autoVersion = nextVer; } } this._cleanup(g); const delay = k.delay ?? this.reconnDelay; this.scheduleReconnect(delay); }); bot.on('end', (reason) => { clearTimeout(connTimer); this.log('🔌',`انقطع: ${reason||'unknown'}`); addEvent('disconnect',`🔌 ${this.cfg.username} انقطع: ${reason||'unknown'}`); this._cleanup(g); if (!this._reconnTimer) this.scheduleReconnect(); }); // ROLE-01: سجل اللاعبين — تتبع الدخول والخروج bot.on('playerJoined', (player) => { if (player.username === bot.username) return; if (coord?.slots.some(s => s.cfg.username === player.username)) return; const now = tsNow(); const existing = playerRegistry.get(player.username); if (!existing) { playerRegistry.set(player.username, { firstSeen: now, lastSeen: now, visits: 1, joinedAt: Date.now(), lastChat: null, }); addEvent('info', `👋 لاعب جديد دخل: ${player.username}`); // الدبلوماسي يرحّب // SEC: تصفية اسم اللاعب قبل وضعه في رسالة الشات const safeUser = player.username.replace(/[^\x20-\x7E\u0600-\u06FF]/g,'').slice(0,16); const dip = coord?.slots.find(s => s.role?.id === 'diplomat' && s.bot); const welcomeBot = dip || (this.role?.id === 'diplomat' ? this : null); if (welcomeBot?.bot) { setTimeout(() => { try { welcomeBot.bot?.chat(`أهلاً يا ${safeUser}! 👋 مرحباً بك في السيرفر!`); } catch(_){} }, randInt(3000, 8000)); } } else { existing.lastSeen = now; existing.visits++; existing.joinedAt = Date.now(); existing.lastChat = null; const safeUser = player.username.replace(/[^\x20-\x7E\u0600-\u06FF]/g,'').slice(0,16); if (existing.visits > 1) addEvent('info', `🎮 ${player.username} عاد للسيرفر (زيارة #${existing.visits})`); const dip = coord?.slots.find(s => s.role?.id === 'diplomat' && s.bot); if (dip?.bot && existing.visits > 1) { setTimeout(() => { try { dip.bot?.chat(`يا هلا يا ${safeUser}! 🎮 زيارة رقم ${existing.visits}`); } catch(_){} }, randInt(4000, 9000)); } } serverInfo.players = Object.keys(bot.players || {}).length; }); bot.on('playerLeft', (player) => { if (player.username === bot.username) return; const reg = playerRegistry.get(player.username); if (reg) { reg.lastSeen = tsNow(); reg.joinedAt = null; } serverInfo.players = Math.max(0, (serverInfo.players || 1) - 1); }); bot.on('error', err => { const c = err.code||err.message||''; if (/ECONNREFUSED|EHOSTUNREACH|ETIMEDOUT/.test(c)) { this.connRefused++; if (this.connRefused >= 3) { this.deepSleep=true; this._deepSleepStart = Date.now(); const d=randInt(5*60000,12*60000); this.log('😴',`DeepSleep ${Math.round(d/60000)} دقيقة`); addEvent('sleep',`😴 ${this.cfg.username} DeepSleep — السيرفر غير متاح`); // ANTI-02: تشغيل Aternos تلقائياً عند السبات العميق if (!aternosRunning) triggerAternos(); setTimeout(()=>{ this.deepSleep=false; this._deepSleepStart=null; this.connRefused=0; this.scheduleReconnect(5000); }, d); return; } } this.log('❌',`خطأ: ${err.message}`); }); // Ping monitor — ANTI-01 bot.on('physicsTick', () => { const p = bot.player?.ping; if (p!=null) { this.pingHist.push(p); if (this.pingHist.length>10) this.pingHist.shift(); this.avgPing = Math.round(this.pingHist.reduce((a,b)=>a+b,0)/this.pingHist.length); } this.mark(); }); // ANTI-01: مراقبة الـ ticks — إذا توقف الـ bot نعيد الاتصال this._watchTimer = setInterval(() => { if (!this.bot || this.deepSleep) return; const idle = Date.now() - (this.lastAct || Date.now()); if (idle > 5*60000 && this.bot?.entity) { this.log('🔄','watchdog: لا نشاط → إعادة تشغيل keep-alive'); this.mark(); try { bot.swingArm(); } catch(_){} } }, 60000); } _cleanup(gen) { if (this._sessTimer) { clearTimeout(this._sessTimer); this._sessTimer=null; } if (this._keepTimer) { clearInterval(this._keepTimer); this._keepTimer=null; } if (this._watchTimer) { clearInterval(this._watchTimer); this._watchTimer=null; } if (this.gen===gen) { this.totalUptime += Math.round((Date.now()-(this.spawnTime||Date.now()))/1000); this.spawnTime=null; this.bot=null; this.isOp=false; this.eating=false; // FIX: reset eating lock on disconnect this.opActionsRun=false; // FIX: allow doOPActions on next OP grant this.tasks.reset(); this.cmdQueue=[]; } } _startLoops(bot, g) { this._runLoop('personality', ()=>this.personalityLoop(bot,g)); this._runLoop('chat', ()=>this.chatLoop(bot,g)); this._runLoop('hunger', ()=>this.hungerLoop(bot,g)); this._runLoop('sleep', ()=>this.sleepLoop(bot,g)); this._runLoop('cmd', ()=>this.cmdLoop(bot,g)); this._runLoop('follow', ()=>this.followLoop(bot,g)); this._runLoop('perf', ()=>this.monitorPerformance(bot,g)); this._runLoop('role', ()=>this.roleLoop(bot,g)); } _runLoop(name, fn) { fn().catch(e => { if (!/EPIPE|ETIMEDOUT|ECONNRESET/.test(e.message||'')) this.log('⚠️',`Loop ${name} crashed: ${e.message}`); }); } // ── Real Player Activity Loop — FIX-1 ──────────────────────────────── // بدل الـ AFK micro-movements، البوت بيعمل أنشطة حقيقية زي لاعب فعلي startKeepAlive(bot, gen) { if (this._keepTimer) { clearInterval(this._keepTimer); this._keepTimer=null; } const self = this; async function realPlayerActivity() { if (!self.bot || self.gen!==gen || !bot.entity || self.tasks.busy) return; self.mark(); const r = Math.random(); // 1) مشي حقيقي لمسافة معقولة (8-25 block) ثم إكمال طريق if (r < 0.30 && bot.pathfinder) { if (!self.tasks.tryAcquire('keep_walk',0,25000)) return; try { const pos = bot.entity.position; const angle = Math.random() * Math.PI * 2; const dist = randInt(20, 60); const tx = pos.x + Math.cos(angle)*dist; const tz = pos.z + Math.sin(angle)*dist; bot.pathfinder.setGoal(new goals.GoalNear(tx, pos.y, tz, 2), true); // امشي 5-12 ثانية ثم وقّف await sleep(randInt(5000, 12000)); try { bot.pathfinder.setGoal(null); } catch(_){} // انتظر 2-5 ثانية في المكان (لاعب حقيقي بيوقف ويبص) await sleep(randInt(2000, 5000)); self.clearCtrl(); // ارجع لنقطة قريبة من البداية if (self.alive(bot,gen) && !self.tasks.busy) { bot.pathfinder.setGoal(new goals.GoalNear(pos.x, pos.y, pos.z, 3), true); await sleep(randInt(5000, 10000)); try { bot.pathfinder.setGoal(null); } catch(_){} self.clearCtrl(); } } catch(_){ self.clearCtrl(); } finally { self.tasks.release('keep_walk'); } } // 2) حفر وجمع — يضرب التراب/الحجر أمامه else if (r < 0.52 && bot.entity) { if (!self.tasks.tryAcquire('keep_mine',0,18000)) return; try { const yaw = bot.entity.yaw; // اتجه لمكان ثم "يحفر" (swing arm متواصل) await bot.look(yaw + randFloat(-0.5,0.5), randFloat(-0.6,-0.1), false); await sleep(randInt(500,1200)); const swings = randInt(5, 15); for (let i=0; i { // في ECO MODE أو lightBot: نتجنب المشي والحفر (أثقل الأنشطة) if (performanceData.ecoMode || this.cfg.lightBot) { if (!self.bot || !self.bot.entity) return; self.mark(); // فقط نظرة عشوائية لإثبات الحياة const yaw = self.bot.entity?.yaw || 0; self.bot.look(yaw + randFloat(-1, 1), randFloat(-0.3, 0.2), false).catch(_=>{}); if (Math.random() < 0.3) { try { self.bot.swingArm(); } catch(_){} } } else { realPlayerActivity().catch(_=>{}); } }, interval); } // ── Personality Loop ────────────────────────────────────────────────── async personalityLoop(bot, gen) { await humanSleep(randInt(5000,20000), randInt(20000,40000)); while (this.alive(bot, gen)) { try { // ECO MODE أو lightBot: فترات أطول بكثير const minW = (performanceData.ecoMode || this.cfg.lightBot) ? 300000 : 90000; const maxW = (performanceData.ecoMode || this.cfg.lightBot) ? 600000 : 360000; await humanSleep(minW, maxW); if (!this.alive(bot,gen)||!bot.entity) continue; if (this.tasks.busy) { await sleep(15000); continue; } // ECO MODE: نتخطى الحركات الثقيلة (pathfinder) كلياً if (performanceData.ecoMode || this.cfg.lightBot) { try { await bot.look(bot.entity.yaw+randFloat(-0.5,0.5), randFloat(-0.3,0.2), false); } catch(_){} await sleep(randInt(2000,5000)); continue; } if (!this.tasks.tryAcquire('personality',0,60000)) continue; try { this.mark(); const cfg=this.cfg; const env=scanEnv(bot); this.envCache=env; // حركة عشوائية if (!dangerNear(bot)&&bot.pathfinder) { const pos=bot.entity.position; const tx=pos.x+randFloat(-60,60), ty=pos.y, tz=pos.z+randFloat(-60,60); try { bot.pathfinder.setGoal(new goals.GoalNear(tx,ty,tz,3),true); await sleep(randInt(3000,8000)); try { bot.pathfinder.setGoal(null); } catch(_){} } catch(_){} } // نظرة عشوائية const sy=bot.entity?.yaw||0; await bot.look(sy+randFloat(-2,2),randFloat(-0.5,0.5),false); await sleep(randInt(500,1500)); // حفر (للحفّار) if (cfg.personality==='miner'&&env.hasOre&&!dangerNear(bot)) { for (const name of cfg.oreTargets) { try { const b=bot.findBlock({matching:bl=>bl.name===name,maxDistance:6}); if (b&&isChunkLoaded(bot,b.position)) { await smoothLook(bot,b.position.offset(0.5,0.5,0.5),7,55); await sleep(randInt(500,1000)); await bot.dig(b,true); await sleep(randInt(300,700)); break; } } catch(_){} } } } finally { this.clearCtrl(); this.tasks.release('personality'); } } catch(_) { this.clearCtrl(); this.tasks.release('personality'); await sleep(5*60000); } } } // ── Chat Loop ───────────────────────────────────────────────────────── async chatLoop(bot, gen) { await humanSleep(randInt(60000,180000), randInt(180000,360000)); while (this.alive(bot,gen)) { try { // ECO MODE: شات أقل كثيراً (البوت يصمت معظم الوقت) const minW = performanceData.ecoMode ? 15*60000 : this.cfg.lightBot ? 10*60000 : 4*60000; const maxW = performanceData.ecoMode ? 30*60000 : this.cfg.lightBot ? 20*60000 : 18*60000; await humanSleep(minW, maxW); if (!this.alive(bot,gen)||this.chatOff||this.deepSleep) continue; if (!teamChat.tryLock(this.cfg.id)) continue; this.mark(); const msg = pick([...this.cfg.chatStyle,...GENERAL_CHAT.slice(0,15)]); await typeMsg(bot, msg); addEvent('chat',`💬 ${bot.username}: ${msg}`); } catch(_) { await sleep(3*60000); } } } // ── Hunger Loop ─────────────────────────────────────────────────────── async hungerLoop(bot, gen) { await sleep(randInt(15000, 25000)); while (this.alive(bot, gen)) { try { await sleep(randInt(8000, 15000)); if (!this.alive(bot, gen) || !bot.entity) continue; const food = typeof bot.food === 'number' ? bot.food : 20; // ── حالة طوارئ: جوع حاد + OP → أعطِ نفسك طعاماً ────────────────── if (food < 8 && this.isOp && !this.eating) { try { bot.chat('/give @s cooked_beef 32'); addEvent('info', `🥩 ${this.cfg.username} أعطى نفسه أكل (جوع حاد)`); await sleep(2500); } catch(_) {} } // ── أكل عادي من الـ inventory ─────────────────────────────────────── if (food < 14 && !this.eating) { this.eating = true; try { const item = bot.inventory?.items().find(i => FOOD_NAMES.some(n => i.name.includes(n)) ); if (item) { await bot.equip(item, 'hand'); await sleep(300); await bot.consume(); this.mark(); } else if (this.isOp) { // لا أكل في الـ inventory + عنده OP → أعطِ نفسه bot.chat('/give @s bread 32'); await sleep(2500); // حاول أكل بعد الاستلام const fresh = bot.inventory?.items().find(i => FOOD_NAMES.some(n => i.name.includes(n)) ); if (fresh) { await bot.equip(fresh, 'hand'); await sleep(300); await bot.consume(); this.mark(); } } } catch(_) { // فشل الأكل — reset وحاول بعدين } finally { this.eating = false; } } } catch(_) { await sleep(15000); } } } // ── Sleep Loop ──────────────────────────────────────────────────────── async sleepLoop(bot, gen) { while (this.alive(bot,gen)) { try { await sleep(randInt(25000,40000)); if (!this.alive(bot,gen)||!bot.entity) continue; let tod=0; try { tod=bot.time?.timeOfDay??0; } catch(_){} if (tod>12600&&tod<23000&&!this.sleeping) { this.mark(); let bed=null; try { bed=bot.findBlock({matching:bl=>bl.name.includes('_bed'),maxDistance:10}); } catch(_){} if (bed&&isChunkLoaded(bot,bed.position)) { this.sleeping=true; if (this.tasks.tryAcquire('sleep',1,65000)) { try { if (bot.pathfinder&&!dangerNear(bot)) { bot.pathfinder.setGoal(new goals.GoalNear(bed.position.x,bed.position.y,bed.position.z,1),true); await sleep(3000); try{bot.pathfinder.setGoal(null);}catch(_){} } await smoothLook(bot,bed.position.offset(0.5,0.5,0.5),6,60); await sleep(randInt(300,600)); await bot.sleep(bed); await sleep(randInt(15000,45000)); try{await bot.wake();}catch(_){} this.sleeping=false; } catch(_){this.sleeping=false;} finally{this.clearCtrl();this.tasks.release('sleep');} } } } } catch(_){await sleep(30000);} } } // ── Command Loop ────────────────────────────────────────────────────── async cmdLoop(bot, gen) { while (this.alive(bot,gen)) { try { await sleep(400); if (this.cmdQueue.length===0) continue; const cmd=this.cmdQueue.shift(); if (!this.alive(bot,gen)) continue; const player=Object.values(bot.players||{}).find(p=>p.username.toLowerCase()===cmd.sender.toLowerCase()&&p.entity); this.log('⚙️',`أمر [${cmd.type}] من ${cmd.sender}`); switch(cmd.type) { case 'come': { if (!player?.entity) { if(teamChat.tryLock(this.cfg.id))await typeMsg(bot,'where are you? cant see u'); break; } if (!this.tasks.tryAcquire('cmd_come',2,25000)) break; try { const pp=player.entity.position; if (teamChat.tryLock(this.cfg.id)) await typeMsg(bot,pick(['coming!','on my way','omw'])); if (!dangerNear(bot)&&bot.pathfinder) { bot.pathfinder.setGoal(new goals.GoalNear(pp.x,pp.y,pp.z,2),true); await sleep(15000); try{bot.pathfinder.setGoal(null);}catch(_){} } } finally{this.clearCtrl();this.tasks.release('cmd_come');} break; } case 'stop': { this.tasks.reset(); try{bot.pathfinder.setGoal(null);}catch(_){} this.clearCtrl(); this.followTarget=null; if(teamChat.tryLock(this.cfg.id))await typeMsg(bot,pick(['ok stopped','sure','ok'])); break; } case 'follow': { this.followTarget=cmd.sender; if(teamChat.tryLock(this.cfg.id))await typeMsg(bot,pick(['following you!','ok following'])); break; } case 'plugins': { await this.scanPlugins(bot); if(teamChat.tryLock(this.cfg.id))await typeMsg(bot,`Scanning plugins... found ${detectedPlugins.length} so far`); break; } case 'optimize': { await this.optimizePerformance(bot); if(teamChat.tryLock(this.cfg.id))await typeMsg(bot,'Optimization commands sent!'); break; } } } catch(_){await sleep(2000);} } } // ── Follow Loop ─────────────────────────────────────────────────────── async followLoop(bot, gen) { while (this.alive(bot,gen)) { try { await sleep(1500); if (!this.followTarget||!bot.entity) continue; if (this.tasks.busy&&this.tasks.state!==STATE.FOLLOWING){await sleep(3000);continue;} const player=Object.values(bot.players||{}).find(p=>p.username===this.followTarget&&p.entity); if (!player?.entity) continue; const dist=bot.entity.position.distanceTo(player.entity.position); if (dist>5) { if (!this.tasks.tryAcquire('follow',0,6000)){await sleep(2000);continue;} try { if (!dangerNear(bot)&&bot.pathfinder) { const pp=player.entity.position; bot.pathfinder.setGoal(new goals.GoalNear(pp.x,pp.y,pp.z,3),true); await sleep(2500); try{bot.pathfinder.setGoal(null);}catch(_){} } } finally{this.clearCtrl();this.tasks.release('follow');} } } catch(_){await sleep(3000);} } } // ── OP Optimizer (OP-OPT) ───────────────────────────────────────────── async doOPActions(bot) { if (this.opActionsRun) return; this.opActionsRun = true; this.log('⚡', 'تطبيق تحسينات السيرفر + إعداد الأكل التلقائي...'); addEvent('op', `⚡ ${this.cfg.username} يطبق تحسينات السيرفر التلقائية`); // ── أولاً: أعطِ البوت أكلاً وفيراً (أهم خطوة) ─────────────────────── if (this.alive(bot, this.gen)) { try { bot.chat('/give @s cooked_beef 64'); await sleep(1200); bot.chat('/give @s bread 32'); await sleep(1200); this.log('🥩', 'تم منح الأكل تلقائياً بعد الحصول على OP'); addEvent('info', `🥩 ${this.cfg.username} أعطى نفسه أكلاً بعد OP`); // reset eating lock ليأكل فوراً في الـ hungerLoop this.eating = false; } catch(_) {} await sleep(1500); } // ── ثانياً: تحسينات السيرفر ───────────────────────────────────────── const cmds = [ '/gamerule doPatrolSpawning false', '/gamerule doTraderSpawning false', '/gamerule doInsomnia false', '/gamerule announceAdvancements false', '/gamerule maxEntityCramming 8', '/gamerule spawnRadius 8', ]; for (const cmd of cmds) { if (!this.alive(bot, this.gen)) break; await sleep(700 + randInt(200, 400)); try { bot.chat(cmd); } catch(_) {} } if (this.alive(bot, this.gen)) { await sleep(1500); try { bot.chat('✅ تم تحسين أداء السيرفر تلقائياً — أداء أفضل للجميع!'); } catch(_) {} } } // ── Role Loop (ROLE-01) — سلوك خاص لكل دور ─────────────────────────── async roleLoop(bot, g) { await sleep(randInt(20000, 40000)); while (this.alive(bot, g)) { try { await sleep(randInt(50000, 100000)); if (!this.alive(bot, g)) continue; // ── قراءة المهام المخصصة ديناميكياً ────────────────────────────── // assignedTasks يُحدَّث من TeamCoordinator.redistributeTasks() عند // كل connect/disconnect، فلا حاجة لإعادة تشغيل البوت. const tasks = this.assignedTasks.length > 0 ? this.assignedTasks : (this.role ? [this.role.id] : []); // fallback لو لم يُوزَّع بعد const realPlayers = () => Object.keys(bot.players || {}).filter(n => coord?.slots.every(s => s.cfg.username !== n) ); // ────────────────────────────────────────────────────────────────── // 🛡️ guardian — مراقبة AFK وطرد الخاملين // ────────────────────────────────────────────────────────────────── if (tasks.includes('guardian')) { const now = Date.now(); for (const [name] of playerRegistry) { if (coord?.slots.some(s => s.cfg.username === name)) continue; const reg = playerRegistry.get(name); if (!reg?.joinedAt) continue; if (now - reg.joinedAt > 35 * 60000) { const lastAct = reg.lastChat || reg.joinedAt; if (now - lastAct > 35 * 60000) { this.log('⏰', `AFK مشتبه: ${name}`); // SEC: تصفية اسم اللاعب قبل إرساله في أمر /kick (منع command injection) const safeName = name.replace(/[^a-zA-Z0-9_]/g,'').slice(0,16); if (!safeName) { reg.joinedAt = now; continue; } if (this.isOp) { try { bot.chat(`/kick ${safeName} AFK لأكثر من 35 دقيقة`); } catch(_) {} addEvent('info', `⏰ Guardian: طرد ${safeName} لعدم النشاط`); } else { try { bot.chat(`${safeName} يبدو AFK منذ فترة طويلة 😴`); } catch(_) {} } reg.joinedAt = now; } } } } // ────────────────────────────────────────────────────────────────── // 💬 diplomat — رسائل ترحيب + تشجيع دورية // ────────────────────────────────────────────────────────────────── if (tasks.includes('diplomat')) { const rp = realPlayers(); if (rp.length > 0 && Math.random() < 0.3) { const msgs = [ 'استمتعوا باللعب! 🎮', 'السيرفر شغّال 24/7 على طول 💪', 'لو في مشكلة قولولنا 💬', 'خليكوا حلوين مع بعض 😄', 'السيرفر محمي ومراقب دايماً 🛡️', `${rp.length} لاعب متصل الآن — نشاط حلو! 🔥`, ]; try { bot.chat(msgs[Math.floor(Math.random() * msgs.length)]); } catch(_) {} } } // ────────────────────────────────────────────────────────────────── // 🎖️ commander — تقرير دوري + إعلان عند نشاط عالي // ────────────────────────────────────────────────────────────────── if (tasks.includes('commander')) { const rp = realPlayers(); teamState.lastReport = { time: Date.now(), botsOnline: coord?.count() || 0, realPlayers: rp.length, tps: performanceData.tps, }; if (rp.length >= 5 && !teamState.eventPending) { teamState.eventPending = true; addEvent('info', `🎖️ Commander: ${rp.length} لاعبين متصلين!`); try { bot.chat(`/title @a title {"text":"🔥 السيرفر نشط!","color":"gold"}`); } catch(_) {} setTimeout(() => { teamState.eventPending = false; }, 10 * 60000); } } // ────────────────────────────────────────────────────────────────── // 🌾 farmer — تذكير بالأكل للاعبين // ────────────────────────────────────────────────────────────────── if (tasks.includes('farmer') && Math.random() < 0.2) { const rp = realPlayers(); if (rp.length > 0) { const foodMsgs = [ '🌾 المزرعة التلقائية شغّالة — أكل متاح في الـ spawn!', '🍞 لو جعان فيه أكل في الـ chest الرئيسي عند spawn 😄', '🥕 تذكّر تاخد أكل معاك قبل ما تنزل في الـ cave!', ]; try { bot.chat(foodMsgs[Math.floor(Math.random() * foodMsgs.length)]); } catch(_) {} } } // ────────────────────────────────────────────────────────────────── // ⚡ optimizer — تحسين أداء السيرفر عند TPS منخفض // ────────────────────────────────────────────────────────────────── if (tasks.includes('optimizer')) { const tps = performanceData.tps; if (tps != null && tps < 14) { this.log('⚡', `optimizer: TPS=${tps} — محاولة تخفيف الضغط`); addEvent('perf', `⚡ Optimizer (${this.cfg.username}): TPS ${tps.toFixed(1)} — تفعيل تخفيف`); if (this.isOp) { try { bot.chat('/gamerule doMobSpawning false'); await sleep(2000); bot.chat('/gamerule doMobSpawning true'); } catch(_) {} } else { // بدون OP: فقط أبلغ اللاعبين try { bot.chat(`⚠️ TPS=${tps.toFixed(1)} — السيرفر شايل شوية، خليكوا light!`); } catch(_) {} } } else if (tps != null && tps >= 18 && performanceData.ecoMode) { // TPS جيد — ارفع eco mode addEvent('perf', `✅ Optimizer: TPS عادل (${tps.toFixed(1)}) — ECO mode سيُخفَّف`); } } } catch(_) { await sleep(5000); } } } // ── Chat Handler ────────────────────────────────────────────────────── handleChat(sender, message, bot, gen) { // تحديث آخر نشاط للاعب (لكشف AFK) const pReg = playerRegistry.get(sender); if (pReg) pReg.lastChat = Date.now(); // كشف سلبي للبلاجن من أي رسالة شات (بدون OP) this.parsePluginsFromChat(message); // كشف TPS من أي رسالة (بعض البلاجن تبث TPS في الشات) this.parseTpsFromChat(message); const lo=message.toLowerCase(); const myName=bot.username.toLowerCase(); const addressed=lo.includes(myName)||lo.includes(myName.split('_')[0]); if (addressed) { if (/\bcome\b|تعال|هنا|اجي/.test(lo)) { this.cmdQueue.push({type:'come',sender}); return; } if (/\bstop\b|وقف|كفي/.test(lo)) { this.cmdQueue.push({type:'stop',sender}); return; } if (/\bfollow\b|اتبع|تابعني/.test(lo)) { this.cmdQueue.push({type:'follow',sender}); return; } if (/plugins|بلاجن/.test(lo)) { this.cmdQueue.push({type:'plugins',sender}); return; } if (/optimize|أداء|تحسين/.test(lo)) { this.cmdQueue.push({type:'optimize',sender}); return; } } let pool=null; if (/hello|hi|hey|yo|مرحبا|هلا|السلام/.test(lo)) pool=SMART_REPLIES.greet; else if (/how are|ازيك|عامل/.test(lo)) pool=SMART_REPLIES.how; else if (/\bbot\b|are you.*bot|u a bot|بوت/.test(lo)) pool=SMART_REPLIES.bot; else if (/\bafk\b|sleeping|idle/.test(lo)) pool=SMART_REPLIES.afk; else if (/bye|cya|later|goodbye|مع السلامة/.test(lo)) pool=SMART_REPLIES.bye; else if (/\blag\b|ping/.test(lo)) pool=SMART_REPLIES.lag; else if (/food|hungry|جوعان/.test(lo)) pool=SMART_REPLIES.food; else if (/die|died|rip|مات/.test(lo)) pool=SMART_REPLIES.death; if (pool&&Math.random()<0.42) { const reply=pick(pool); setTimeout(async()=>{ if (!this.alive(bot,gen)||this.chatOff) return; if (!teamChat.tryLock(this.cfg.id)) return; await typeMsg(bot,reply); }, randInt(3500,14000)); } } snapshot() { const up=this.spawnTime?Math.round((Date.now()-this.spawnTime)/1000):0; const pos=this.bot?.entity?.position; return { id:this.cfg.id, label:this.cfg.label, emoji:this.cfg.emoji, username:this.bot?.username||this.cfg.username, personality:this.cfg.personality, connected:!!this.bot, connecting:this.connecting, uptime:up, totalUptime:this.totalUptime+up, sessions:this.sessCount, avgPing:this.avgPing, chatOff:this.chatOff, deepSleep:this.deepSleep, state:this.tasks.state, isOp:this.isOp, kicks:this.kickHistory, connRefused:this.connRefused, env:this.envCache, position: pos ? { x:Math.round(pos.x), y:Math.round(pos.y), z:Math.round(pos.z) } : null, role: this.role ? { id:this.role.id, label:this.role.label, color:this.role.color, desc:this.role.desc } : null, assignedTasks: this.assignedTasks.slice(), performanceMode: this.assignedTasks.includes('optimizer'), lightBot: this.cfg.lightBot || false, }; } } // ════════════════════════════════════════════════════════════════════════════ // TEAM COORDINATOR // ════════════════════════════════════════════════════════════════════════════ class TeamCoordinator { constructor() { this.slots=[]; this.live=new Set(); this.started=false; } add(s) { this.slots.push(s); } // ─── إعادة توزيع المهام ديناميكياً ────────────────────────────────────── redistributeTasks() { const n = Math.min(Math.max(this.live.size, 1), 4); const map = TASK_MAP[n] || TASK_MAP[4]; this.slots.forEach((s, idx) => { // كل بوت يأخذ الصف المقابل له، أو آخر صف لو كان idx > map.length s.assignedTasks = map[Math.min(idx, map.length - 1)].slice(); const taskLabel = s.assignedTasks.join(', '); if (typeof s.log === 'function') s.log('📋', `مهام جديدة (${n} بوت): [${taskLabel}]`); addEvent('info', `📋 ${s.cfg.username}: [${taskLabel}]`); }); } onConnect(id) { this.live.add(id); this.redistributeTasks(); } onDisconnect(id) { this.live.delete(id); if (this.dead) return; this.redistributeTasks(); if (this.live.size === 0) { const b1 = this.slots[0]; setTimeout(() => { if (!b1.deepSleep && !b1.bot && !b1.connecting && !b1._reconnTimer) b1.scheduleReconnect(); }, 2500); } } destroy() { this.dead = true; this.stopAll(); } count() { return this.live.size; } status() { return this.slots.map(s=>s.snapshot()); } async startAll() { this.started=true; for (let i=0; i0) await sleep(randInt(CFG.staggerMin, CFG.staggerMax)); s.connect(); } } stopAll() { for (const s of this.slots) { s.deepSleep=true; if (s._reconnTimer) { clearTimeout(s._reconnTimer); s._reconnTimer=null; } if (s.bot) { try { s.bot.quit(); } catch(_){} } } } restartAll() { this.stopAll(); setTimeout(()=>{ this.slots.forEach(s=>{s.deepSleep=false;s.connect();}); }, 3000); } } // ════════════════════════════════════════════════════════════════════════════ // MEMORY CLEANUP — MEM-01 // ════════════════════════════════════════════════════════════════════════════ setInterval(() => { if (EVENT_LOG.length > 100) EVENT_LOG.length = 100; if (performanceData.history.length > 20) performanceData.history.length = 20; // FIX: منع تسرب الذاكرة — حد playerRegistry بـ 200 مدخل (offline players first) if (playerRegistry.size > 200) { const toDelete = [...playerRegistry.entries()] .filter(([,p]) => p.joinedAt === null) // offline أولاً .slice(0, playerRegistry.size - 200); toDelete.forEach(([k]) => playerRegistry.delete(k)); } // حذف معدلات الـ rate limit القديمة (أقدم من 5 دقائق) const rateCutoff = Date.now() - 5 * 60000; for (const [k, v] of _chatRateMap) { if (v < rateCutoff) _chatRateMap.delete(k); } try { if (global.gc) global.gc(); } catch(_){} saveData(); }, 30*60000); // ════════════════════════════════════════════════════════════════════════════ // TPS SPARKLINE — رسم بياني SVG للأداء // ════════════════════════════════════════════════════════════════════════════ function tpsSparkline() { const h = performanceData.history; if (h.length < 2) return '
لا بيانات كافية بعد — في انتظار قراءة TPS...
'; const W=340, H=70, PAD=6; const vals = h.map(p => p.tps); const mn = Math.max(0, Math.min(...vals) - 1); const mx = Math.min(22, Math.max(...vals) + 1); const range = mx - mn || 1; const pts = vals.map((v,i) => { const x = PAD + (i / (vals.length-1)) * (W - PAD*2); const y = H - PAD - ((v-mn)/range) * (H - PAD*2); return `${x.toFixed(1)},${y.toFixed(1)}`; }).join(' '); // خط الـ 15 TPS (حد lag) const lagY = (H - PAD - ((15-mn)/range) * (H - PAD*2)).toFixed(1); const lastTps = vals[vals.length-1]; const lineColor = lastTps >= 18 ? '#00e676' : lastTps >= 14 ? '#ffca28' : '#ff5252'; const fillPts = `${PAD},${H} ` + pts + ` ${W-PAD},${H}`; return ` ${lastTps.toFixed(1)} TPS 15 TPS `; } // ════════════════════════════════════════════════════════════════════════════ // DASHBOARD HTML — لوحة التحكم الكاملة // ════════════════════════════════════════════════════════════════════════════ function getDashboardHTML(coord) { const team = coord.status(); const online = coord.count(); const totSess = team.reduce((a,s)=>a+s.sessions,0); const totUp = Math.floor(team.reduce((a,s)=>a+s.totalUptime,0)/3600); const uptime = process.uptime(); const procUp = `${Math.floor(uptime/3600)}h ${Math.floor((uptime%3600)/60)}m`; const plugCount = detectedPlugins.length; const perfPlugs = detectedPlugins.filter(p=>p.cat==='performance'); const tps = performanceData.tps; const tpsColor = !tps ? '#64748b' : tps>=18 ? '#22c55e' : tps>=14 ? '#f59e0b' : '#ef4444'; const tpsLabel = !tps ? 'N/A' : `${tps.toFixed(1)} TPS`; const ecoMode = performanceData.ecoMode; const sparkSVG = tpsSparkline(); const botCards = team.map(s => { const h=Math.floor(s.uptime/3600), m=Math.floor((s.uptime%3600)/60), sc=s.uptime%60; const pingColor = !s.avgPing?'var(--muted)':s.avgPing<200?'#34d399':s.avgPing<500?'#fcd34d':'#f87171'; let statusDot, statusText, statusColor; if (s.deepSleep) { statusDot='💤'; statusText='سبات عميق'; statusColor='#c4b5fd'; } else if (s.connected) { statusDot='🟢'; statusText=`متصل — ${h}h ${m}m ${sc}s`; statusColor='#34d399'; } else if (s.connecting){ statusDot='🟡'; statusText='جاري الاتصال...'; statusColor='#fcd34d'; } else { statusDot='🔴'; statusText='في الانتظار'; statusColor='var(--muted)'; } const envChips = []; if (s.env?.hasOre) envChips.push('⛏️ خام'); if (s.env?.hasWood) envChips.push('🪵 أشجار'); if (s.env?.hasDanger) envChips.push('⚠️ خطر'); if (s.env?.isNight) envChips.push('🌙 ليل'); if (s.env?.hasPlayers)envChips.push(`👤 ${s.env.playerCount}`); const statusClass = s.connected ? 'online' : s.deepSleep ? 'sleeping' : s.connecting ? 'connecting' : 'offline'; const envChipsNew = []; if (s.env?.hasOre) envChipsNew.push('⛏️ خام'); if (s.env?.hasWood) envChipsNew.push('🪵 أشجار'); if (s.env?.hasDanger) envChipsNew.push('⚠️ خطر'); if (s.env?.isNight) envChipsNew.push('🌙 ليل'); if (s.env?.hasPlayers) envChipsNew.push(`👤 ${s.env.playerCount}`); return `
${s.emoji}
${s.username}
${s.label}
${s.role ? `${s.role.label}` : ''} ${s.isOp ? '👑 OP' : ''} ${s.lightBot ? '💡 مساعد' : ''} ${s.performanceMode ? '🌿 ECO' : ''}
${statusDot} ${statusText}
⏱️ الجلسة
${h}h ${m}m ${sc}s
📊 إجمالي
${Math.floor(s.totalUptime/3600)}h ${Math.floor((s.totalUptime%3600)/60)}m
📶 Ping
${s.avgPing!=null?s.avgPing+'ms':'N/A'}
🔄 جلسات
${s.sessions}${s.kicks.length?` · ${s.kicks.length}x طُرد`:''}
⚙️ الحالة
${s.state}
📍 الموقع
${s.position?`${s.position.x}, ${s.position.y}, ${s.position.z}`:'—'}
${envChipsNew.length?`
${envChipsNew.join('')}
`:''}
${s.connected?``:''} ${s.isOp?``:''}
`; }).join(''); const pluginRows = detectedPlugins.slice(0,30).map(p => { const catColor = {performance:'#22c55e',protection:'#3b82f6',utility:'#8b5cf6', security:'#f59e0b',admin:'#ec4899',economy:'#06b6d4',afk:'#10b981',misc:'#64748b'}[p.cat]||'#64748b'; return ` ${p.name} ${p.cat} ${p.desc} `; }).join('') || 'لم يُكتشف أي بلاجن بعد — أعطِ البوت OP'; const eventRows = EVENT_LOG.slice(0,20).map(e => { const typeColor={connect:'#34d399',disconnect:'#5a7fa8',kick:'#f87171', plugin:'#c4b5fd',perf:'#fcd34d',op:'#fbbf24',chat:'#67e8f9', sleep:'#a78bfa',info:'#93c5fd',session:'#7f9cc0'}[e.type]||'#7f9cc0'; return `
${e.msg}${e.time.slice(11,19)}
`; }).join('') || '
لا أحداث بعد...
'; return ` ⛏️ Sayed Bot Team v18
${ecoMode ? `
🌿
ECO MODE مفعّل — تخفيف ذكي للضغط
TPS منخفض — البوتات أوقفت المشي والحفر تلقائياً للحفاظ على أداء السيرفر
${tpsLabel}
` : ''}
🤖
${online}/${team.length}
بوتات متصلة
${tpsLabel}
TPS السيرفر
🔌
${plugCount}
بلاجن مكتشفة
👥
${playerRegistry.size}
لاعبين مسجّلين
🌐 العنوان
${CFG.host}
🔌 البورت
${CFG.port}
🎮 الإصدار
${CFG.version==='auto'?`تلقائي (${autoVersion||'?'})`:CFG.version}
⚡ TPS حالي
${tpsLabel}
🚀 أداء
${perfPlugs.length} بلاجن
🕐 تشغيل
${procUp}
📈 سجل أداء السيرفر — TPS History
TPS حد 15 ${performanceData.history.length} قراءة
${sparkSVG}

🎭 أدوار الفريق

كل بوت له مهمة محددة
${team.map(s => s.role ? `
${s.role.label}
${s.role.desc}
${s.emoji} ${s.username} ${s.connected?`● متصل`:`● غير متصل`}
` : '').join('')}

🤖 البوتات

تحديث كل 15 ثانية
${botCards}

🔌 البلاجن المكتشفة

${plugCount} بلاجن
${plugCount===0?`
ℹ️ لم يُكتشف أي بلاجن بعد — البوتات تكشف تلقائياً من الشات أو أعطِها OP للكشف الكامل
`:''}
${pluginRows}
الاسمالتصنيفالوصف

👥 سجل اللاعبين

${playerRegistry.size} لاعب
${playerRegistry.size>0?` ${[...playerRegistry.entries()].slice(0,25).map(([name,p])=>{ const isOnline=p.joinedAt!==null; return ``; }).join('')}
اللاعبأول ظهورآخر ظهورالزياراتالحالة
${name}${p.visits===1?'جديد ✨':''} ${p.firstSeen?p.firstSeen.slice(0,16):'—'} ${p.lastSeen?p.lastSeen.slice(0,16):'—'} ${p.visits} ${isOnline?'● متصل':'○ غير متصل'}
`:`
لم يُرصد أي لاعع بعد — البوتات تراقب وتسجّل تلقائياً عند دخول أي لاعب 👀
`}

📋 سجل الأحداث

${eventRows}

💬 إرسال رسالة في السيرفر

عبر أي بوت متصل
⛏️ Sayed Bot Team v18 — تحديث تلقائي كل 15 ثانية
`; } // ════════════════════════════════════════════════════════════════════════════ // SETUP WIZARD HTML — Gaming UI // ════════════════════════════════════════════════════════════════════════════ function getSetupHTML() { const verGroups = { 'الإصدارات القديمة (1.0 – 1.7)': ['1.0.0','1.1.0','1.2.1','1.3.1','1.4.2','1.5.0','1.6.1','1.7.2'], 'الإصدارات الكلاسيكية (1.8 – 1.12)': ['1.8.0','1.9.0','1.10.0','1.11.0','1.12.0'], 'الإصدارات الحديثة (1.13 – 1.19)': ['1.13.0','1.14.0','1.15.0','1.16.0','1.17.0','1.18.0','1.19.0'], 'الإصدارات الجديدة (1.20 – 1.21)': ['1.20.0','1.21.0','1.21.2','1.21.3','1.21.4','1.21.5','1.21.6'], 'إصدارات Bedrock': ['26.1.0','26.2.0'], }; const verOptions = Object.entries(verGroups).map(([grp, vers]) => `${vers.map(v=>``).join('')}` ).join(''); return ` ⛏️ Sayed Bot Team v16 — إعداد السيرفر
🌐
السيرفر
🎮
الإصدار
🤖
البوتات
🚀
تشغيل

🌐 خطوة 1 من 4 — بيانات السيرفر

أدخل عنوان سيرفر Aternos والبورت

📋 من أين تحصل على IP السيرفر؟
روح Aternos → My Servers → اختار السيرفر → Connect
هتلاقي عنوان زي: myserver.aternos.me:25565
🏠
🔢
لم يتم الاختبار بعد
💡 لا تعرف الإصدار؟ اختر "كشف تلقائي" — البوت هيكتشف الإصدار لوحده عند الاتصال.
لو عارف الإصدار حدده يدوياً للاتصال أسرع.
📦
الإصدارات المدعومة:
Java: 1.0.0 ← 1.21.6  |  Bedrock: 26.1.0 و 26.2.0
1
⛏️
بوت واحد
2
⛏️🏗️
اثنان
3
⛏️🏗️🗺️
ثلاثة
MAX
4
⛏️🏗️🗺️🔴
أربعة
⛏️
حفّار — يحفر ويجمع خامات
🏗️
بنّاء — يبني ويجمع خشب وحجارة
🗺️
مستكشف — يجوب الخريطة ويكتشف
⚠️ مهم: اختار أسماء تبدو طبيعية مثل لاعب حقيقي (حروف + أرقام).
تجنب كلمات مثل "bot" أو "robot" في الاسم — السيرفر قد يكشفها.
🚀 كل شيء جاهز! راجع الإعدادات وابدأ تشغيل البوتات.
🌐 السيرفر 🔌 البورت 🎮 الإصدار 🤖 البوتات 👥 الأسماء
🔒 بعد التشغيل:
• افتح لوحة السيرفر في Aternos واعطِ OP للبوتات
• البوتات ستكتشف كل البلاجن تلقائياً عند الاتصال
• أضف UptimeRobot لإبقاء الـ Space شغّال 24/7
جاري تشغيل البوتات...
يرجى الانتظار، سيتم التحويل تلقائياً
`; } // ════════════════════════════════════════════════════════════════════════════ // EXPRESS SERVER // ════════════════════════════════════════════════════════════════════════════ const app = express(); let coord = new TeamCoordinator(); app.use(express.json()); app.use(express.urlencoded({ extended:true })); // ANTI-01: منع HuggingFace من إيقاف الـ Space app.use((req, res, next) => { res.setHeader('Cache-Control','no-store, no-cache, must-revalidate'); res.setHeader('X-Bot-Status','running'); next(); }); // Health Check — ANTI-01 + ANTI-02 app.get('/healthz', (req,res) => { const online = coord.count(); // ANTI-02: لو كل البوتات في سبات عميق أكتر من 20 دقيقة → شغّل Aternos const allSleep = coord.slots.length > 0 && coord.slots.every(s => s.deepSleep); if (allSleep && !aternosRunning) { const oldest = Math.min(...coord.slots.map(s => s._deepSleepStart || Date.now())); if (Date.now() - oldest > 20 * 60000) { sysLog('🔄', 'healthz: كل البوتات في سبات عميق — تشغيل Aternos'); triggerAternos(); } } res.json({ status:'ok', botsOnline:online, botsTotal:coord.slots.length, uptime:Math.floor(process.uptime()), configured:CFG.isConfigured, host:CFG.host, version:autoVersion||CFG.version, plugins:detectedPlugins.length, tps:performanceData.tps, lag:performanceData.lag, allSleep, aternosRunning, }); }); // ANTI-01: Ping endpoint app.get('/ping', (req,res) => res.send('pong')); // Setup: Test server connection from browser app.get('/api/ping-server', async (req, res) => { const host = String(req.query.host||'').trim().slice(0,200); const port = Math.max(1, Math.min(65535, parseInt(req.query.port)||25565)); if (!host) { res.json({online:false, error:'no host'}); return; } try { const mcProto = require('minecraft-protocol'); const t = Date.now(); mcProto.ping({ host, port, closeTimeout:5000 }, (err, data) => { if (err) { res.json({online:false, error:err.message}); return; } const ping = Date.now()-t; const version = data && (data.version?.name || data.version); res.json({ online:true, ping, version: version||'unknown', players: data && data.players ? { online: data.players.online, max: data.players.max } : null }); }); } catch(e) { res.json({online:false, error:e.message}); } }); // Dashboard app.get('/', (req,res) => { if (!CFG.isConfigured) { res.redirect('/setup'); return; } res.send(getDashboardHTML(coord)); }); // Setup Wizard app.get('/setup', (req,res) => res.send(getSetupHTML())); // Save Config & Start Bots app.post('/api/setup', async (req,res) => { try { const { host, port, version, botCount, bot1Name, bot2Name, bot3Name, bot4Name } = req.body; if (!host || host.trim()==='') { res.json({ok:false,error:'عنوان السيرفر مطلوب'}); return; } saveConfig({ host:host.trim(), port:parseInt(port)||25565, version:version||'auto', botCount:parseInt(botCount)||4, bot1Name:bot1Name||'Alex_miner26', bot2Name:bot2Name||'Steve_builder', bot3Name:bot3Name||'NightExplorer', bot4Name:bot4Name||'RedstoneKing' }); sysLog('⚙️',`إعداد جديد: ${CFG.host}:${CFG.port} | إصدار: ${CFG.version}`); addEvent('info',`⚙️ إعداد جديد: ${CFG.host}:${CFG.port}`); // FIX-2: دمّر الـ coord القديم تماماً حتى لا يتصل البوتات القديمة من جديد try { coord.destroy(); } catch(_){} detectedPlugins.length = 0; // صفّر البلاجن للسيرفر الجديد autoVersion = null; await sleep(3000); coord = new TeamCoordinator(); const teamCfg = buildTeamCfg(); teamCfg.forEach(c => coord.add(new BotSlot(c))); coord.startAll(); res.json({ok:true}); } catch(e) { res.json({ok:false,error:e.message}); } }); // Bot Status API app.get('/api/status', (req,res) => { res.json({ ok:true, bots:coord.status(), online:coord.count(), plugins:detectedPlugins, performance:{ ...performanceData, ecoMode:performanceData.ecoMode }, config:{ host:CFG.host, port:CFG.port, version:CFG.version, autoVersion }, events:EVENT_LOG.slice(0,20), aternosRunning, hfSpaceUrl: HF_SPACE_URL || null, }); }); // SEC: قائمة الأوامر المسموحة فقط من الداشبورد const ALLOWED_BOT_CMDS = new Set(['stop','restart','plugins','optimize','come','follow','sleep','wake']); // Bot Command API app.post('/api/cmd', (req,res) => { try { const { botId, cmd } = req.body; // SEC: تحقق من صحة الأمر قبل تنفيذه if (!cmd || !ALLOWED_BOT_CMDS.has(String(cmd))) { res.json({ok:false, error:`أمر غير مسموح: ${String(cmd).slice(0,20)}`}); return; } const slot = coord.slots[parseInt(botId)]; if (!slot) { res.json({ok:false,error:'بوت غير موجود'}); return; } if (cmd==='stop') { if (slot._reconnTimer) { clearTimeout(slot._reconnTimer); slot._reconnTimer=null; } if (slot.bot) { try{slot.bot.quit();}catch(_){} } res.json({ok:true,msg:'تم إيقاف البوت'}); } else if (cmd==='restart') { if (slot.bot) { try{slot.bot.quit();}catch(_){} } slot.deepSleep=false; setTimeout(()=>slot.connect(),3000); res.json({ok:true,msg:'جاري إعادة التشغيل'}); } else if (cmd==='plugins' && slot.bot) { slot.scanPlugins(slot.bot); res.json({ok:true,msg:'جاري فحص البلاجن'}); } else if (cmd==='optimize' && slot.bot && slot.isOp) { slot.optimizePerformance(slot.bot); res.json({ok:true,msg:'جاري تحسين الأداء'}); } else { slot.cmdQueue.push({type:cmd, sender:'dashboard'}); res.json({ok:true,msg:`أمر [${cmd}] أُضيف للقائمة`}); } } catch(e) { res.json({ok:false,error:e.message}); } }); // Control All app.post('/api/control', (req,res) => { const { action } = req.body; if (action==='stop') { coord.stopAll(); addEvent('info','⏹ تم إيقاف جميع البوتات'); } if (action==='restart') { coord.restartAll(); addEvent('info','🔄 جاري إعادة تشغيل جميع البوتات'); } res.json({ok:true}); }); // Grant OP يدوياً لبوت محدد وفحص البلاجن app.post('/api/grant-op', (req,res) => { try { const { botId } = req.body; const slot = coord.slots[botId]; if (!slot) { res.json({ok:false,error:'بوت غير موجود'}); return; } if (!slot.bot) { res.json({ok:false,error:'البوت غير متصل حالياً'}); return; } slot.isOp = true; slot.scanPlugins(slot.bot, true); addEvent('op',`👑 ${slot.cfg.username} مُنح OP يدوياً — جاري فحص البلاجن`); res.json({ok:true, msg:`${slot.cfg.username} الآن OP — جاري فحص البلاجن (5 ثواني)`}); } catch(e){ res.json({ok:false,error:e.message}); } }); // Force Scan — يفحص البلاجن بدون شرط OP (المستخدم منح OP يدوياً) app.post('/api/force-scan', (req,res) => { let scanned = 0; for (const slot of coord.slots) { if (slot.bot) { slot.isOp = true; slot.scanPlugins(slot.bot, true); scanned++; } } if (scanned === 0) { res.json({ok:false, error:'لا يوجد بوت متصل حالياً'}); } else { addEvent('plugin', `🔍 بدأ فحص البلاجن على ${scanned} بوت (force)`); res.json({ok:true, msg:`جاري فحص البلاجن على ${scanned} بوت...`}); } }); // Plugins API app.get('/api/plugins', (req,res) => res.json({ok:true,plugins:detectedPlugins})); // Performance API app.get('/api/perf', (req,res) => res.json({ok:true,...performanceData})); // CHAT-01: إرسال رسالة في السيرفر من الداشبورد app.post('/api/send-chat', (req,res) => { try { const { botId, msg } = req.body; if (!msg || !String(msg).trim()) { res.json({ok:false,error:'الرسالة فارغة'}); return; } const slot = coord.slots[parseInt(botId) || 0]; if (!slot) { res.json({ok:false,error:'بوت غير موجود'}); return; } if (!slot.bot) { res.json({ok:false,error:'البوت غير متصل حالياً'}); return; } // SEC: rate limit — منع الـ spam const now = Date.now(); const last = _chatRateMap.get(slot.cfg.id) || 0; if (now - last < 2000) { res.json({ok:false,error:'انتظر ثانيتين بين الرسائل'}); return; } _chatRateMap.set(slot.cfg.id, now); // SEC: إزالة الـ control chars ومنع أوامر / المباشرة (إلا لو OP) let cleanMsg = String(msg).trim() .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g,'') // control chars .slice(0, 200); if (cleanMsg.startsWith('/') && !slot.isOp) { res.json({ok:false, error:'أوامر / غير مسموحة بدون OP'}); return; } if (!cleanMsg) { res.json({ok:false,error:'رسالة فارغة بعد التنظيف'}); return; } slot.bot.chat(cleanMsg); addEvent('chat', `💬 [داشبورد→${slot.cfg.username}]: ${cleanMsg}`); res.json({ok:true, msg:`تم الإرسال عبر ${slot.cfg.username}`}); } catch(e) { res.json({ok:false,error:e.message}); } }); // ROLE-01: سجل اللاعبين API app.get('/api/players', (req,res) => { const list = [...playerRegistry.entries()].map(([name, p]) => ({ name, ...p, online: p.joinedAt !== null, })); res.json({ ok:true, total: playerRegistry.size, players: list }); }); // ANTI-02: تشغيل Aternos يدوياً من الداشبورد app.post('/api/aternos-start', (req,res) => { if (aternosRunning) { res.json({ok:false, error:'Aternos يعمل بالفعل، انتظر حتى ينتهي'}); return; } triggerAternos(); res.json({ok:true, msg:'جاري تشغيل Aternos... تحقق من سجل الأحداث'}); }); // HF-ALIVE: إبقاء HuggingFace Space حي — طبقتين // طبقة 1: ping داخلي يمنع Docker من اعتبار الـ container idle // طبقة 2: ping خارجي على URL الـ Space (يمنع HF من إيقافه) // HF_SPACE_URL: يُكتشف تلقائياً من SPACE_HOST (HuggingFace يضبطه تلقائياً) const HF_SPACE_URL = process.env.HF_SPACE_URL || (process.env.SPACE_HOST ? `https://${process.env.SPACE_HOST}` : '') || (process.env.SPACE_ID ? `https://${process.env.SPACE_ID.replace('/','-')}.hf.space` : ''); function keepAlive() { // — طبقة 1: self-ping داخلي ─────────────────────────────────────────── try { http.get(`http://localhost:${CFG.dashPort}/healthz`, r => r.resume()).on('error',()=>{}); } catch(_){} // — طبقة 2: ping خارجي على HF Space URL ─────────────────────────────── if (HF_SPACE_URL) { try { const https = require('https'); const target = HF_SPACE_URL.replace(/\/$/, '') + '/healthz'; const mod = target.startsWith('https') ? https : http; const req = mod.get(target, { headers:{'User-Agent':'SayedBotTeam-KeepAlive/1.0'}, timeout:15000 }, r => { r.resume(); sysLog('🏓', `HF ping → ${r.statusCode}`); }); req.on('error', () => {}); req.on('timeout', () => req.destroy()); } catch(_){} } } // HF-WAKE: endpoint مخصص للـ external monitors (cron-job.org / UptimeRobot) app.get('/wake', (_req, res) => { res.json({ ok:true, ts: Date.now(), bots: coord?.count() || 0 }); }); setInterval(keepAlive, 4*60000); // كل 4 دقائق — يمنع HF pause // ════════════════════════════════════════════════════════════════════════════ // START // ════════════════════════════════════════════════════════════════════════════ const server = app.listen(CFG.dashPort, '0.0.0.0', () => { sysLog('🌐',`Dashboard على http://0.0.0.0:${CFG.dashPort}`); if (!CFG.isConfigured) { sysLog('⚙️','لا يوجد إعداد — افتح لوحة التحكم للإعداد'); } else { sysLog('🚀',`بدء تشغيل ${CFG.botCount} بوت على ${CFG.host}:${CFG.port}`); const teamCfg = buildTeamCfg(); teamCfg.forEach(c => coord.add(new BotSlot(c))); coord.startAll().catch(() => {}); addEvent('info',`🚀 تشغيل ${CFG.botCount} بوتات على ${CFG.host}:${CFG.port}`); } }); // ANTI-01: keep server alive on socket errors server.on('error', err => { if (err.code==='EADDRINUSE') { sysLog('❌',`البورت ${CFG.dashPort} مشغول!`); process.exit(1); } sysLog('⚠️',`خطأ server: ${err.message}`); }); // ANTI-01: graceful shutdown ['SIGTERM','SIGINT'].forEach(sig => { process.on(sig, () => { sysLog('👋','إيقاف...'); coord.stopAll(); server.close(() => process.exit(0)); }); }); sysLog('✅','Sayed Bot Team v18.0 — جاهز! ECO MODE + TPS Graph + Smart Tasks + HF Auto-URL');