import os, time, threading, zipfile, io, re, json, hashlib, gc from flask import Flask import telebot from telebot import types from playwright.sync_api import sync_playwright from PIL import Image # ════════════════════════════════ # ⚙️ الإعدادات # ════════════════════════════════ BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") ARCHIVE_CHANNEL = os.getenv("ARCHIVE_CHANNEL_ID") # قناة خاصة ADMIN_HANDLE = "@svipfast" bot = telebot.TeleBot(BOT_TOKEN, threaded=False) app = Flask(__name__) archive_index = {} archive_cache = {} processing_now = set() # ════════════════════════════════ # 🔑 مفتاح ذكي # ════════════════════════════════ def make_key(url, chapter): raw = f"{url}|{chapter}" return hashlib.md5(raw.encode()).hexdigest() # ════════════════════════════════ # 📦 تحميل/حفظ الأرشيف # ════════════════════════════════ def load_archive(): global archive_index, archive_cache try: chat = bot.get_chat(ARCHIVE_CHANNEL) if chat.pinned_message: archive_index = json.loads(chat.pinned_message.text) archive_cache = archive_index.copy() print(f"✅ Loaded archive: {len(archive_index)}") except: print("ℹ️ New archive will be created") def save_archive(): try: text = json.dumps(archive_index) chat = bot.get_chat(ARCHIVE_CHANNEL) if chat.pinned_message: bot.edit_message_text(text, ARCHIVE_CHANNEL, chat.pinned_message.message_id) else: msg = bot.send_message(ARCHIVE_CHANNEL, text) bot.pin_chat_message(ARCHIVE_CHANNEL, msg.message_id) except Exception as e: print("❌ Save error:", e) def add_to_archive(key, file_path, caption): try: with open(file_path, 'rb') as f: msg = bot.send_document(ARCHIVE_CHANNEL, f, caption=caption) archive_index[key] = msg.message_id archive_cache[key] = msg.message_id save_archive() return msg.message_id except: return None # ════════════════════════════════ # 🌐 Playwright (مرة واحدة) # ════════════════════════════════ playwright = sync_playwright().start() browser = playwright.chromium.launch(headless=True) def fetch_images(url): context = browser.new_context() page = context.new_page() try: page.goto(url, wait_until="networkidle", timeout=60000) for _ in range(5): page.mouse.wheel(0, 1200) time.sleep(0.5) images = page.query_selector_all("img") img_list = [] for img in images: src = img.get_attribute("src") or img.get_attribute("data-src") if src and any(ext in src.lower() for ext in ['.jpg','.png','.webp']): try: res = page.request.get(src) if res.status == 200 and len(res.body()) > 20000: img_list.append(Image.open(io.BytesIO(res.body())).convert('RGB')) except: continue context.close() return img_list except: context.close() return [] def safe_fetch(url): for _ in range(3): imgs = fetch_images(url) if imgs: return imgs return [] # ════════════════════════════════ # 🤖 البوت # ════════════════════════════════ user_states = {} @app.route('/') def home(): return "✅ Bot Running" @bot.message_handler(commands=['start']) def start(msg): markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("📥 إرسال رابط", callback_data="new")) bot.send_message(msg.chat.id, "🚀 أرسل رابط الفصل", reply_markup=markup) @bot.callback_query_handler(func=lambda c: True) def handle(c): if c.data == "new": m = bot.send_message(c.message.chat.id, "🔗 أرسل الرابط") bot.register_next_step_handler(m, get_url) def get_url(msg): user_states[msg.chat.id] = {"url": msg.text} m = bot.send_message(msg.chat.id, "🔢 اكتب رقم الفصل") bot.register_next_step_handler(m, process) def process(msg): chat_id = msg.chat.id url = user_states[chat_id]["url"] chapter = int(msg.text) key = make_key(url, chapter) # 🧠 cache if key in archive_cache: bot.send_message(chat_id, "⚡ من الأرشيف") bot.forward_message(chat_id, ARCHIVE_CHANNEL, archive_cache[key]) return # 🔒 منع تكرار if key in processing_now: bot.send_message(chat_id, "⏳ يتم معالجته...") return processing_now.add(key) bot.send_message(chat_id, "🔄 جاري السحب...") base = re.sub(r'/\d+$', '', url) target = f"{base}/{chapter}" imgs = safe_fetch(target) if imgs: pdf = f"ch_{chapter}.pdf" imgs[0].save(pdf, save_all=True, append_images=imgs[1:]) zip_name = f"ch_{chapter}.zip" with zipfile.ZipFile(zip_name, 'w', compression=zipfile.ZIP_DEFLATED) as z: z.write(pdf) msg_id = add_to_archive(key, zip_name, f"Chapter {chapter}") if msg_id: bot.forward_message(chat_id, ARCHIVE_CHANNEL, msg_id) os.remove(pdf) os.remove(zip_name) gc.collect() else: bot.send_message(chat_id, "❌ فشل السحب") processing_now.remove(key) # ════════════════════════════════ # 🚀 التشغيل # ════════════════════════════════ if __name__ == "__main__": os.system("playwright install chromium") load_archive() threading.Thread(target=lambda: bot.infinity_polling(), daemon=True).start() app.run(host="0.0.0.0", port=7860)