import telebot from telebot import types import sqlite3 import threading import os import sys import time import uuid import json import psutil import zipfile import shutil import ast import random import string import logging import subprocess import signal from datetime import datetime, timedelta from apscheduler.schedulers.background import BackgroundScheduler # ========================================== # ⚙️ الإعدادات الأساسية # ========================================== TOKEN = '8611700781:AAFWN-i1FZJUgHHfon7jZB6euH-ReRb_31E' ADMIN_ID = 6812997550 YOUR_USERNAME = '@EQJ_1' CHANNELS = ['@EQJ_1'] HOSTING_DIR = 'wolf_projects' BACKUP_DIR = 'wolf_backups' DB_NAME = 'whitewolf_cloud.db' VERSION = '1.1.3' for d in [HOSTING_DIR, BACKUP_DIR]: os.makedirs(d, exist_ok=True) logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger("WhiteWolf-Cloud") bot = telebot.TeleBot(TOKEN, parse_mode="Markdown") # ========================================== # 📊 الخطط والأسعار (مثل السابق) # ========================================== PLAN_LIMITS = { 'free': {'projects': 1, 'label': '🆓 مجاني', 'price': 0, 'ram': '50m'}, 'silver': {'projects': 3, 'label': '🥈 فضي', 'price': 10, 'ram': '100m'}, 'gold': {'projects': 5, 'label': '🥇 ذهبي', 'price': 25, 'ram': '250m'}, 'diamond': {'projects': 10, 'label': '💎 ماسي', 'price': 50, 'ram': '512m'}, } INVITE_REWARD = 2 # ========================================== # 🗄️ قاعدة البيانات (نفس الكود القديم) # ========================================== class DatabaseEngine: def __init__(self): self.lock = threading.Lock() self.init_db() def get_conn(self): conn = sqlite3.connect(DB_NAME, check_same_thread=False) conn.row_factory = sqlite3.Row return conn def init_db(self): with self.lock, self.get_conn() as conn: c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS users ( user_id INTEGER PRIMARY KEY, username TEXT, points INTEGER DEFAULT 0, plan TEXT DEFAULT 'free', plan_expires TIMESTAMP, is_vip INTEGER DEFAULT 0, total_invites INTEGER DEFAULT 0, is_banned INTEGER DEFAULT 0, joined_at TIMESTAMP )''') c.execute('''CREATE TABLE IF NOT EXISTS projects ( project_id TEXT PRIMARY KEY, user_id INTEGER, file_name TEXT, path TEXT, container_id TEXT, status TEXT DEFAULT 'pending', auto_restart INTEGER DEFAULT 1, env_vars TEXT DEFAULT '{}', created_at TIMESTAMP )''') c.execute('''CREATE TABLE IF NOT EXISTS coupons ( code TEXT PRIMARY KEY, value INTEGER, max_uses INTEGER, used_count INTEGER DEFAULT 0 )''') c.execute('''CREATE TABLE IF NOT EXISTS templates ( id TEXT PRIMARY KEY, name TEXT, desc TEXT, path TEXT, downloads INTEGER DEFAULT 0 )''') conn.commit() def add_user(self, user_id, username): with self.lock, self.get_conn() as conn: conn.execute('INSERT OR IGNORE INTO users (user_id, username, joined_at) VALUES (?, ?, ?)', (user_id, username, datetime.now())) conn.commit() def get_user(self, user_id): with self.get_conn() as conn: return conn.execute('SELECT * FROM users WHERE user_id = ?', (user_id,)).fetchone() def add_points(self, user_id, amount): with self.lock, self.get_conn() as conn: conn.execute('UPDATE users SET points = points + ? WHERE user_id = ?', (amount, user_id)) conn.commit() def deduct_points(self, user_id, amount): with self.lock, self.get_conn() as conn: conn.execute('UPDATE users SET points = points - ? WHERE user_id = ?', (amount, user_id)) conn.commit() def set_plan(self, user_id, plan, days=30): expires = datetime.now() + timedelta(days=days) with self.lock, self.get_conn() as conn: conn.execute('UPDATE users SET plan = ?, plan_expires = ? WHERE user_id = ?', (plan, expires, user_id)) conn.commit() def get_projects(self, user_id=None): with self.get_conn() as conn: if user_id: return conn.execute('SELECT * FROM projects WHERE user_id = ?', (user_id,)).fetchall() return conn.execute('SELECT * FROM projects').fetchall() def update_project_status(self, project_id, pid, status): with self.lock, self.get_conn() as conn: conn.execute('UPDATE projects SET container_id = ?, status = ? WHERE project_id = ?', (pid, status, project_id)) conn.commit() def delete_project(self, project_id): with self.lock, self.get_conn() as conn: conn.execute('DELETE FROM projects WHERE project_id = ?', (project_id,)) conn.commit() def update_env(self, project_id, env_vars): with self.lock, self.get_conn() as conn: conn.execute('UPDATE projects SET env_vars = ? WHERE project_id = ?', (env_vars, project_id)) conn.commit() db = DatabaseEngine() # ========================================== # 🛡️ فحص الأمان (نفس الكود) # ========================================== class SecurityScanner: @staticmethod def is_safe_code(file_path, user_id): if user_id == ADMIN_ID: return True, "Admin Bypass" try: with open(file_path, 'r', encoding='utf-8') as f: tree = ast.parse(f.read()) banned_imports = ['os', 'subprocess', 'pty', 'shutil', 'socket', 'sys'] banned_funcs = ['eval', 'exec', 'open', 'system', 'popen'] for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: if alias.name in banned_imports: return False, f"مكتبة ممنوعة: {alias.name}" elif isinstance(node, ast.Call) and getattr(node.func, 'id', '') in banned_funcs: return False, f"دالة خطيرة: {node.func.id}" return True, "Safe" except Exception as e: return False, f"خطأ في الكود: {str(e)[:50]}" # ========================================== # 🐳 محرك **محلي** بديل عن Docker (يدير العمليات) # ========================================== class LocalProcessEngine: """يدير المشاريع كعمليات Python فرعية مع logs و stats""" processes = {} # project_id -> subprocess.Popen log_files = {} # project_id -> path to log file @staticmethod def deploy(project_id, file_path, ram_limit, env_vars_str, auto_restart): # ram_limit مثلاً '100m' - نحوله إلى بايت (نهمله حالياً) # نقرأ متغيرات البيئة env_dict = json.loads(env_vars_str) if env_vars_str else {} env = os.environ.copy() env.update(env_dict) log_path = os.path.join(HOSTING_DIR, f"{project_id}.log") log_file = open(log_path, 'a') # إيقاف العملية القديمة إن وجدت LocalProcessEngine.stop_and_remove(project_id) # تشغيل العملية try: proc = subprocess.Popen( [sys.executable, file_path], stdout=log_file, stderr=log_file, env=env, cwd=os.path.dirname(file_path), preexec_fn=os.setsid # لتسهيل قتل المجموعة ) LocalProcessEngine.processes[project_id] = proc LocalProcessEngine.log_files[project_id] = log_path return True, str(proc.pid) except Exception as e: log_file.close() return False, str(e) @staticmethod def stop_and_remove(project_id): if project_id in LocalProcessEngine.processes: proc = LocalProcessEngine.processes[project_id] try: # قتل العملية ومجموعتها os.killpg(os.getpgid(proc.pid), signal.SIGTERM) proc.terminate() proc.wait(timeout=5) except: pass finally: del LocalProcessEngine.processes[project_id] if project_id in LocalProcessEngine.log_files: # لا نحذف اللوج pass @staticmethod def get_logs(project_id): log_path = LocalProcessEngine.log_files.get(project_id) if not log_path or not os.path.exists(log_path): return "لا توجد سجلات بعد." try: with open(log_path, 'r', encoding='utf-8') as f: lines = f.readlines() return ''.join(lines[-25:]) # آخر 25 سطر except: return "خطأ في قراءة السجلات." @staticmethod def get_stats(project_id): proc = LocalProcessEngine.processes.get(project_id) if not proc or proc.poll() is not None: return "متوقف 🔴" try: p = psutil.Process(proc.pid) mem = p.memory_info().rss / (1024 * 1024) # MB cpu = p.cpu_percent(interval=0.1) return f"🟢 يعمل | RAM: {mem:.1f}MB | CPU: {cpu:.1f}%" except: return "يعمل (لا توجد إحصاءات)" # نستخدم المحرك المحلي بدل Docker ProcessEngine = LocalProcessEngine # ========================================== # 🎨 واجهة المستخدم (نفس الكود القديم) # ========================================== class UI: @staticmethod def main_menu(user_id): markup = types.InlineKeyboardMarkup(row_width=2) markup.add( types.InlineKeyboardButton("📤 رفع مشروع", callback_data="ui_upload"), types.InlineKeyboardButton("📋 إدارة مشاريعي", callback_data="ui_projects"), types.InlineKeyboardButton("👤 حسابي", callback_data="ui_profile"), types.InlineKeyboardButton("🛒 المتجر", callback_data="ui_store"), types.InlineKeyboardButton("🔗 كسب نقاط", callback_data="ui_earn"), types.InlineKeyboardButton("🎟️ استرداد كوبون", callback_data="ui_coupon"), types.InlineKeyboardButton("📚 القوالب الجاهزة", callback_data="ui_templates"), types.InlineKeyboardButton("👨‍💻 الدعم", url=f"https://t.me/{YOUR_USERNAME[1:]}") ) if user_id == ADMIN_ID: markup.add(types.InlineKeyboardButton("🛡️ لوحة المطور", callback_data="admin_root")) return markup @staticmethod def project_ctrl(project_id, status, auto_restart): markup = types.InlineKeyboardMarkup(row_width=2) if status == 'running': markup.add(types.InlineKeyboardButton("🛑 إيقاف", callback_data=f"p_stop_{project_id}")) else: markup.add(types.InlineKeyboardButton("▶️ تشغيل", callback_data=f"p_start_{project_id}")) markup.add( types.InlineKeyboardButton("📋 السجلات", callback_data=f"p_logs_{project_id}"), types.InlineKeyboardButton("🔄 ريستارت", callback_data=f"p_restart_{project_id}"), types.InlineKeyboardButton("🌍 ENV vars", callback_data=f"p_env_{project_id}"), types.InlineKeyboardButton("📊 الإحصائيات", callback_data=f"p_stats_{project_id}") ) ar_text = "🔁 إيقاف الريستارت التلقائي" if auto_restart else "🔁 تفعيل الريستارت التلقائي" markup.add( types.InlineKeyboardButton(ar_text, callback_data=f"p_ar_{project_id}"), types.InlineKeyboardButton("🗑️ حذف", callback_data=f"p_del_{project_id}") ) markup.add(types.InlineKeyboardButton("🔙 رجوع", callback_data="ui_projects")) return markup @staticmethod def back(callback_data="ui_main"): return types.InlineKeyboardMarkup().add(types.InlineKeyboardButton("🔙 رجوع", callback_data=callback_data)) # ========================================== # دوال المساعدة والاشتراك الإجباري # ========================================== user_states = {} def check_sub(user_id): if user_id == ADMIN_ID: return True user = db.get_user(user_id) if user and user['is_vip']: return True for ch in CHANNELS: try: status = bot.get_chat_member(ch, user_id).status if status not in ['member', 'administrator', 'creator']: return False except: return False return True def safe_edit(call, text, markup=None): try: bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=markup, parse_mode="Markdown") except: pass # ========================================== # أوامر البوت الأساسية # ========================================== @bot.message_handler(commands=['start']) def start_cmd(message): user_id = message.from_user.id username = message.from_user.username or f"User{user_id}" db.add_user(user_id, username) user = db.get_user(user_id) if user['is_banned']: return bot.reply_to(message, "🚫 حسابك محظور.") if not check_sub(user_id): markup = types.InlineKeyboardMarkup() for ch in CHANNELS: markup.add(types.InlineKeyboardButton("📡 اشترك هنا أولاً", url=f"https://t.me/{ch[1:]}")) markup.add(types.InlineKeyboardButton("✅ تم الاشتراك", callback_data="ui_main")) return bot.send_message(user_id, "⚠️ يجب الاشتراك في القناة:", reply_markup=markup) # نظام الإحالة args = message.text.split() if len(args) > 1 and args[1].isdigit(): inviter = int(args[1]) if inviter != user_id and not user['total_invites']: db.add_points(inviter, INVITE_REWARD) with db.get_conn() as c: c.execute('UPDATE users SET total_invites = total_invites + 1 WHERE user_id = ?', (inviter,)) c.execute('UPDATE users SET total_invites = -1 WHERE user_id = ?', (user_id,)) c.commit() bot.send_message(inviter, f"🎉 ربحت {INVITE_REWARD} نقطة لدعوتك مستخدم جديد!") plan_name = PLAN_LIMITS.get(user['plan'], PLAN_LIMITS['free'])['label'] text = f"🐺 **مرحباً في White Wolf Cloud**\n\nأقوى نظام استضافة سحابي (نسخة HuggingFace)\n📦 خطتك: {plan_name}" bot.send_message(user_id, text, reply_markup=UI.main_menu(user_id)) @bot.callback_query_handler(func=lambda call: call.data.startswith("ui_")) def ui_handler(call): user_id = call.from_user.id action = call.data[3:] user = db.get_user(user_id) if not check_sub(user_id): return bot.answer_callback_query(call.id, "❌ اشترك بالقناة أولاً", show_alert=True) if action == "main": plan_name = PLAN_LIMITS.get(user['plan'], PLAN_LIMITS['free'])['label'] safe_edit(call, f"🐺 **لوحة التحكم**\n📦 خطتك: {plan_name}", UI.main_menu(user_id)) elif action == "profile": projs = db.get_projects(user_id) plan_info = PLAN_LIMITS.get(user['plan'], PLAN_LIMITS['free']) text = f"👤 **الملف الشخصي**\n🆔 `{user_id}`\n💰 النقاط: `{user['points']}`\n📦 الخطة: {plan_info['label']}\n📂 المشاريع: `{len(projs)}/{plan_info['projects']}`\n🚀 الرام المسموح: `{plan_info['ram']}`" safe_edit(call, text, UI.back()) elif action == "earn": link = f"https://t.me/{bot.get_me().username}?start={user_id}" safe_edit(call, f"🔗 **رابط الدعوة:**\n`{link}`\n\n🎁 {INVITE_REWARD} نقطة لكل مدعو.", UI.back()) elif action == "store": markup = types.InlineKeyboardMarkup(row_width=1) for k, v in PLAN_LIMITS.items(): if k == 'free': continue markup.add(types.InlineKeyboardButton(f"{v['label']} | {v['projects']} مشاريع | {v['price']} نقطة", callback_data=f"buy_{k}")) markup.add(types.InlineKeyboardButton("🔙 رجوع", callback_data="ui_main")) safe_edit(call, f"🛒 **المتجر**\n💰 رصيدك: `{user['points']}` نقطة\nالمدة: 30 يوم.", markup) elif action == "projects": projs = db.get_projects(user_id) if not projs: return bot.answer_callback_query(call.id, "📭 لا توجد مشاريع.", show_alert=True) markup = types.InlineKeyboardMarkup() for p in projs: icon = "🟢" if p['status'] == 'running' else "🔴" markup.add(types.InlineKeyboardButton(f"{icon} {p['file_name']}", callback_data=f"p_view_{p['project_id']}")) markup.add(types.InlineKeyboardButton("🔙 رجوع", callback_data="ui_main")) safe_edit(call, "📋 **قائمة مشاريعك:**", markup) elif action == "upload": limit = PLAN_LIMITS.get(user['plan'], PLAN_LIMITS['free'])['projects'] if len(db.get_projects(user_id)) >= limit and not user['is_vip'] and user_id != ADMIN_ID: return bot.answer_callback_query(call.id, f"⚠️ وصلت للحد ({limit}). قم بالترقية!", show_alert=True) user_states[user_id] = 'upload' safe_edit(call, "📤 **أرسل ملف المشروع الآن** (.py أو .zip)\nأرسل /cancel للإلغاء", UI.back()) @bot.callback_query_handler(func=lambda call: call.data.startswith("buy_")) def buy_plan(call): plan_key = call.data[4:] user = db.get_user(call.from_user.id) price = PLAN_LIMITS[plan_key]['price'] if user['points'] < price: return bot.answer_callback_query(call.id, f"❌ تحتاج {price} نقطة.", show_alert=True) db.deduct_points(user['user_id'], price) db.set_plan(user['user_id'], plan_key) bot.answer_callback_query(call.id, "✅ تم تفعيل الخطة!", show_alert=True) ui_handler(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data="ui_profile")) # ========================================== # إدارة المشاريع باستخدام المحرك المحلي # ========================================== @bot.callback_query_handler(func=lambda call: call.data.startswith("p_")) def project_control(call): user_id = call.from_user.id parts = call.data.split('_') action = parts[1] project_id = parts[2] with db.get_conn() as conn: p = conn.execute('SELECT * FROM projects WHERE project_id = ?', (project_id,)).fetchone() if not p or (p['user_id'] != user_id and user_id != ADMIN_ID): return bot.answer_callback_query(call.id, "❌ مشروع غير موجود.", show_alert=True) if action == "view": stats = ProcessEngine.get_stats(project_id) if p['status'] == 'running' else "متوقف 🔴" text = f"📄 **معلومات المشروع:**\n📁 `{p['file_name']}`\n🆔 `{project_id}`\n📊 الحالة: {stats}" safe_edit(call, text, UI.project_ctrl(project_id, p['status'], p['auto_restart'])) elif action == "start": user = db.get_user(user_id) ram_limit = PLAN_LIMITS.get(user['plan'], PLAN_LIMITS['free'])['ram'] success, pid = ProcessEngine.deploy(project_id, p['path'], ram_limit, p['env_vars'], p['auto_restart']) if success: db.update_project_status(project_id, pid, 'running') bot.answer_callback_query(call.id, "▶️ تم التشغيل!") else: bot.answer_callback_query(call.id, f"❌ فشل: {pid}", show_alert=True) project_control(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data=f"p_view_{project_id}")) elif action == "stop": ProcessEngine.stop_and_remove(project_id) db.update_project_status(project_id, None, 'stopped') bot.answer_callback_query(call.id, "🛑 تم الإيقاف.") project_control(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data=f"p_view_{project_id}")) elif action == "restart": bot.answer_callback_query(call.id, "🔄 جاري إعادة التشغيل...") ProcessEngine.stop_and_remove(project_id) user = db.get_user(user_id) ram_limit = PLAN_LIMITS.get(user['plan'], PLAN_LIMITS['free'])['ram'] success, pid = ProcessEngine.deploy(project_id, p['path'], ram_limit, p['env_vars'], p['auto_restart']) if success: db.update_project_status(project_id, pid, 'running') project_control(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data=f"p_view_{project_id}")) elif action == "logs": logs = ProcessEngine.get_logs(project_id) safe_edit(call, f"📋 **السجلات (آخر 25 سطر):**\n```\n{logs[-3500:]}\n```", UI.back(f"p_view_{project_id}")) elif action == "stats": stats = ProcessEngine.get_stats(project_id) safe_edit(call, f"📊 **الموارد الحية:**\n{stats}", UI.back(f"p_view_{project_id}")) elif action == "ar": new_val = 0 if p['auto_restart'] else 1 with db.lock, db.get_conn() as conn: conn.execute('UPDATE projects SET auto_restart = ? WHERE project_id = ?', (new_val, project_id)) conn.commit() bot.answer_callback_query(call.id, "✅ تم التعديل.") project_control(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data=f"p_view_{project_id}")) elif action == "del": ProcessEngine.stop_and_remove(project_id) try: shutil.rmtree(os.path.dirname(p['path'])) if os.path.isdir(os.path.dirname(p['path'])) else os.remove(p['path']) except: pass db.delete_project(project_id) bot.answer_callback_query(call.id, "🗑️ تم الحذف.", show_alert=True) ui_handler(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data="ui_projects")) elif action == "env": # عرض نافذة لتعديل env vars (يمكن إرسالها في رسالة جديدة) markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("✏️ تعديل المتغيرات", callback_data=f"p_edit_env_{project_id}")) markup.add(types.InlineKeyboardButton("🔙 رجوع", callback_data=f"p_view_{project_id}")) safe_edit(call, f"🌍 **متغيرات البيئة الحالية:**\n```json\n{json.dumps(json.loads(p['env_vars'] or '{}'), indent=2)}\n```\nيمكنك تعديلها عبر إرسال JSON جديد.", markup) elif action == "edit_env": user_states[user_id] = f"env_{project_id}" bot.send_message(user_id, "أرسل المتغيرات الجديدة بصيغة JSON:\nمثال: {\"TOKEN\":\"123\", \"API\":\"abc\"}\nأو /cancel للإلغاء.") bot.answer_callback_query(call.id) @bot.message_handler(func=lambda m: user_states.get(m.from_user.id, '').startswith('env_')) def handle_env_edit(message): user_id = message.from_user.id state = user_states.pop(user_id, '') project_id = state.split('_')[1] try: new_vars = json.loads(message.text) if not isinstance(new_vars, dict): raise ValueError db.update_env(project_id, json.dumps(new_vars)) bot.reply_to(message, "✅ تم تحديث متغيرات البيئة. أعد تشغيل المشروع ليتم تطبيقها.") except: bot.reply_to(message, "❌ صيغة JSON غير صالحة. أرسل قيمة مثل: {\"key\":\"value\"}") # ========================================== # رفع الملفات (نفس الكود القديم مع تعديل بسيط) # ========================================== @bot.message_handler(content_types=['document']) def handle_docs(message): user_id = message.from_user.id if user_states.get(user_id) != 'upload': return user_states.pop(user_id, None) file_name = message.document.file_name if not (file_name.endswith('.py') or file_name.endswith('.zip')): return bot.reply_to(message, "❌ مسموح فقط .py أو .zip") if message.document.file_size > 5 * 1024 * 1024: return bot.reply_to(message, "❌ الحد الأقصى 5MB") msg = bot.reply_to(message, "⏳ جاري الفحص...") project_id = str(uuid.uuid4())[:8] proj_dir = os.path.join(HOSTING_DIR, f"{user_id}_{project_id}") os.makedirs(proj_dir, exist_ok=True) save_path = os.path.join(proj_dir, file_name) downloaded = bot.download_file(bot.get_file(message.document.file_id).file_path) with open(save_path, 'wb') as f: f.write(downloaded) main_file = save_path if file_name.endswith('.zip'): try: with zipfile.ZipFile(save_path, 'r') as zf: for member in zf.namelist(): if not os.path.realpath(os.path.join(proj_dir, member)).startswith(os.path.realpath(proj_dir)): raise Exception("Zip Slip attack") zf.extractall(proj_dir) os.remove(save_path) py_files = [f for f in os.listdir(proj_dir) if f.endswith('.py')] if not py_files: raise Exception("لا يوجد ملف بايثون") main_file = os.path.join(proj_dir, 'main.py' if 'main.py' in py_files else py_files[0]) file_name = os.path.basename(main_file) except Exception as e: shutil.rmtree(proj_dir) return bot.edit_message_text(f"❌ خطأ: {e}", message.chat.id, msg.message_id) is_safe, reason = SecurityScanner.is_safe_code(main_file, user_id) if not is_safe: shutil.rmtree(proj_dir) return bot.edit_message_text(f"🚨 **مرفوض أمنياً:**\n`{reason}`", message.chat.id, msg.message_id) with db.lock, db.get_conn() as conn: conn.execute('INSERT INTO projects (project_id, user_id, file_name, path, created_at) VALUES (?, ?, ?, ?, ?)', (project_id, user_id, file_name, main_file, datetime.now())) conn.commit() bot.edit_message_text("✅ **تم حفظ المشروع!**\nاذهب إلى `مشاريعي` لتشغيله.", message.chat.id, msg.message_id, reply_markup=UI.main_menu(user_id)) @bot.message_handler(commands=['cancel']) def cancel_cmd(message): user_states.pop(message.from_user.id, None) bot.reply_to(message, "❌ تم الإلغاء.") # ========================================== # لوحة المطور (Admin Panel) مختصرة # ========================================== @bot.callback_query_handler(func=lambda call: call.data.startswith("admin_")) def admin_panel(call): if call.from_user.id != ADMIN_ID: return action = call.data[6:] if action == "root": markup = types.InlineKeyboardMarkup(row_width=2) markup.add( types.InlineKeyboardButton("📊 إحصائيات", callback_data="admin_stats"), types.InlineKeyboardButton("📢 إذاعة", callback_data="admin_bc"), types.InlineKeyboardButton("🎁 كوبون", callback_data="admin_add_coupon"), types.InlineKeyboardButton("🛑 إيقاف كل العمليات", callback_data="admin_killall") ) markup.add(types.InlineKeyboardButton("🔙 خروج", callback_data="ui_main")) safe_edit(call, "🛡️ **لوحة المطور**", markup) elif action == "stats": with db.get_conn() as conn: users = conn.execute('SELECT COUNT(*) FROM users').fetchone()[0] projs = conn.execute('SELECT COUNT(*) FROM projects').fetchone()[0] cpu = psutil.cpu_percent() ram = psutil.virtual_memory().percent disk = psutil.disk_usage('/').percent text = f"📊 **إحصائيات**\n👥 مستخدمين: `{users}`\n📂 مشاريع: `{projs}`\n🧠 CPU: `{cpu}%`\n💾 RAM: `{ram}%`\n💿 تخزين: `{disk}%`" safe_edit(call, text, UI.back("admin_root")) elif action == "killall": # إيقاف كل العمليات for pid, proc in list(ProcessEngine.processes.items()): try: os.killpg(os.getpgid(proc.pid), signal.SIGTERM) except: pass ProcessEngine.processes.clear() with db.lock, db.get_conn() as conn: conn.execute('UPDATE projects SET status = "stopped", container_id = NULL') conn.commit() bot.answer_callback_query(call.id, "⚡ تم إيقاف جميع المشاريع!", show_alert=True) admin_panel(types.CallbackQuery(call.id, call.from_user, call.data, call.chat_instance, call.message, data="admin_root")) # ========================================== # الصيانة الدورية (نفس الكود) # ========================================== def system_maintenance(): with db.lock, db.get_conn() as conn: expired = conn.execute("SELECT user_id FROM users WHERE plan != 'free' AND plan_expires < ?", (datetime.now(),)).fetchall() for u in expired: conn.execute("UPDATE users SET plan = 'free' WHERE user_id = ?", (u[0],)) try: bot.send_message(u[0], "⚠️ انتهت صلاحية خطتك المدفوعة.") except: pass conn.commit() backup_path = os.path.join(BACKUP_DIR, f"db_backup_{datetime.now().strftime('%A')}.db") shutil.copy2(DB_NAME, backup_path) logger.info("✅ تمت الصيانة الدورية.") scheduler = BackgroundScheduler() scheduler.add_job(system_maintenance, 'interval', hours=12) scheduler.start() # ========================================== # التشغيل # ========================================== if __name__ == '__main__': logger.info(f"🐺 إقلاع نظام White Wolf Cloud v{VERSION}...") try: bot.infinity_polling(timeout=60, long_polling_timeout=30) except Exception as e: logger.error(f"خطأ عام: {e}")