Spaces:
iq7se2
/
5
Sleeping

iq7se2 commited on
Commit
d7e681e
·
verified ·
1 Parent(s): 89438fd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -162
app.py CHANGED
@@ -1,187 +1,105 @@
1
  import os, time, threading, zipfile, io, re, telebot, requests
2
- from flask import Flask, render_template_string
3
  from playwright.sync_api import sync_playwright
4
  from PIL import Image
5
- import img2pdf
6
  from telebot import types
7
 
8
  # --- الإعدادات ---
9
  BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
 
 
10
  ADMIN_HANDLE = "@svipfast"
 
11
  bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
12
  app = Flask(__name__)
13
 
14
- # مخزن مؤقت لحالات المستخدمين
15
- user_states = {}
 
16
 
17
  @app.route('/')
18
- def home(): return "<h1>Manga Engine Professional is Running</h1>"
19
 
20
- # --- محرك السحب (Playwright) ---
21
- def fetch_images_engine(url):
22
  with sync_playwright() as p:
23
  browser = p.chromium.launch(headless=True)
24
- context = browser.new_context(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
25
- page = context.new_page()
26
- try:
27
- page.goto(url, wait_until="networkidle", timeout=60000)
28
- # التمرير لضمان تحميل الصور بالكامل
29
- for _ in range(5):
30
- page.mouse.wheel(0, 1200)
31
- time.sleep(0.8)
32
-
33
- images = page.query_selector_all("img")
34
- img_list = []
35
- for img in images:
36
- src = img.get_attribute("src") or img.get_attribute("data-src") or img.get_attribute("data-lazy-src")
37
- if src and any(ext in src.lower() for ext in ['.jpg', '.jpeg', '.png', '.webp', '.avif']):
38
- if "logo" in src.lower() or "banner" in src.lower(): continue
39
- try:
40
- res = page.request.get(src)
41
- if res.status == 200 and len(res.body()) > 25000:
42
- img_list.append(Image.open(io.BytesIO(res.body())).convert('RGB'))
43
- except: continue
44
- browser.close()
45
- return img_list
46
- except:
47
- browser.close()
48
- return []
49
-
50
- # --- لوحة التحكم والترحيب ---
51
- @bot.message_handler(commands=['start'])
52
- def welcome(message):
53
- user_states[message.chat.id] = {} # تصفير حالة المستخدم
54
- markup = types.InlineKeyboardMarkup(row_width=1)
55
- btn_start = types.InlineKeyboardButton("📥 إرسال رابط جديد", callback_data="new_order")
56
- btn_help = types.InlineKeyboardButton("💡 كيف أحصل على الرابط؟", callback_data="help_link")
57
- btn_admin = types.InlineKeyboardButton("👨‍💻 المطور", url=f"https://t.me/{ADMIN_HANDLE.replace('@','')}")
58
- markup.add(btn_start, btn_help, btn_admin)
59
-
60
- bot.send_message(message.chat.id,
61
- f"<b>مرحباً بك في بوت سحب المانجا الإحترافي 🚀</b>\n\n"
62
- f"النظام يعمل بمحرك محاكاة لتجاوز الحمايات.\n"
63
- f"المطور المسؤول: {ADMIN_HANDLE}",
64
- parse_mode="HTML", reply_markup=markup)
65
-
66
- # --- معالجة الضغط على الأزرار ---
67
- @bot.callback_query_handler(func=lambda call: True)
68
- def handle_query(call):
69
- chat_id = call.message.chat.id
70
-
71
- if call.data == "new_order":
72
- msg = bot.send_message(chat_id, "<b>الآن، أرسل رابط المانجا (رابط أي فصل):</b>", parse_mode="HTML")
73
- bot.register_next_step_handler(msg, process_url_step)
74
-
75
- elif call.data == "help_link":
76
- help_txt = (
77
- "<b>طريقة إرسال الرابط الصحيحة:</b>\n\n"
78
- "انسخ رابط الفصل من الموقع مباشرة، مثال:\n"
79
- "<code>https://olympustaff.com/series/manga-name/1</code>"
80
- )
81
- bot.send_message(chat_id, help_txt, parse_mode="HTML")
82
 
83
- # أزرار خيارات التحميل
84
- elif call.data.startswith("mode_"):
85
- mode = call.data.split("_")[1]
86
- user_states[chat_id]['mode'] = mode
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
- if mode == "single":
89
- msg = bot.send_message(chat_id, "🔢 أرسل <b>رقم الفصل</b> فقط:")
90
- elif mode == "range":
91
- msg = bot.send_message(chat_id, "🔢 أرسل <b>المدى</b> (مثال: 1-10):")
92
- elif mode == "auto10":
93
- msg = bot.send_message(chat_id, "🔢 أرسل <b>رقم الفصل</b> الذي سنبدأ التحميل منه:")
94
-
95
- bot.register_next_step_handler(msg, final_execution_step)
96
-
97
- bot.answer_callback_query(call.id)
98
 
99
- # --- الخطوة الثانية: استلام الرابط ---
100
- def process_url_step(message):
101
- url = message.text.strip()
102
- if not url.startswith("http"):
103
- bot.send_message(message.chat.id, "❌ الرابط غير صحيح، أرسل رابط يبدأ بـ http")
104
- return
105
-
106
- user_states[message.chat.id] = {'url': url}
107
 
108
- # إظهار خيارات التحميل كأزرار
109
- markup = types.InlineKeyboardMarkup(row_width=2)
110
- markup.add(
111
- types.InlineKeyboardButton("🎯 فصل واحد", callback_data="mode_single"),
112
- types.InlineKeyboardButton("📦 حزمة فصول", callback_data="mode_range"),
113
- types.InlineKeyboardButton("⏩ تحميل 10 فصول", callback_data="mode_auto10")
114
- )
115
- bot.send_message(message.chat.id, "<b>ممتاز! اختر الآن طريقة التحميل:</b>", parse_mode="HTML", reply_markup=markup)
116
 
117
- # --- الخطوة الأخيرة: التنفيذ ---
118
- def final_execution_step(message):
119
- chat_id = message.chat.id
120
- state = user_states.get(chat_id)
121
- if not state or 'url' not in state:
122
- bot.send_message(chat_id, "⚠️ حدث خطأ، يرجى البدء من جديد عبر /start")
123
- return
124
-
125
- text = message.text.strip()
126
- mode = state['mode']
127
- url = state['url']
128
-
129
- try:
130
- if mode == "single":
131
- start, end = int(text), int(text)
132
- elif mode == "range":
133
- start, end = map(int, text.split('-'))
134
- elif mode == "auto10":
135
- start = int(text)
136
- end = start + 9
137
-
138
- status_msg = bot.send_message(chat_id, f"<b>🔄 جاري تشغيل المحرك الذكي...</b>\n📦 سحب الفصول: {start} ⬅️ {end}", parse_mode="HTML")
139
-
140
- # تشغيل السحب
141
- zip_path = run_manga_engine(url, start, end)
142
-
143
- if zip_path:
144
- with open(zip_path, 'rb') as f:
145
- caption = (
146
- f"<b>✅ اكتملت عملية السحب بنجاح</b>\n\n"
147
- f"📂 <b>النطاق:</b> {start} إلى {end}\n"
148
- f"👨‍💻 <b>بواسطة:</b> {ADMIN_HANDLE}"
149
- )
150
- bot.send_document(chat_id, f, caption=caption, parse_mode="HTML")
151
- os.remove(zip_path)
152
- bot.delete_message(chat_id, status_msg.message_id)
153
  else:
154
- bot.edit_message_text(f"❌ لم ينجح المحرك في سحب الصور.\nتواصل مع الإدارة: {ADMIN_HANDLE}", chat_id, status_msg.message_id)
155
-
156
- except Exception as e:
157
- bot.send_message(chat_id, "⚠️ خطأ في إدخال الأرقام. يرجى التأكد من كتابتها بشكل صحيح (مثال: 1-5).")
158
 
159
- # --- منطق بناء الروابط وضغط الملفات ---
160
- def run_manga_engine(sample_url, start, end):
161
- base_part = re.sub(r'/(?:chapter-)?\d+$', '', sample_url.strip().rstrip('/'))
162
- is_azora = "azoramoon" in sample_url
163
- pdf_files = []
 
 
 
 
 
 
 
 
 
164
 
165
- for i in range(start, end + 1):
166
- target_url = f"{base_part}/chapter-{i}" if is_azora else f"{base_part}/{i}"
167
- imgs = fetch_images_engine(target_url)
168
- if imgs:
169
- fname = f"Chapter_{i}.pdf"
170
- imgs[0].save(fname, save_all=True, append_images=imgs[1:], format='PDF')
171
- pdf_files.append(fname)
172
-
173
- if not pdf_files: return None
174
-
175
- zip_name = f"Manga_Pack_{int(time.time())}.zip"
176
- with zipfile.ZipFile(zip_name, 'w') as zipf:
177
- for f in pdf_files:
178
- zipf.write(f)
179
- os.remove(f)
180
- return zip_name
181
 
182
- # --- التشغيل ---
183
- if __name__ == "__main__":
184
- # تنصيب متصفح Playwright
185
- os.system("playwright install chromium")
186
- threading.Thread(target=lambda: bot.infinity_polling(), daemon=True).start()
187
- app.run(host="0.0.0.0", port=7860)
 
1
  import os, time, threading, zipfile, io, re, telebot, requests
2
+ from flask import Flask
3
  from playwright.sync_api import sync_playwright
4
  from PIL import Image
 
5
  from telebot import types
6
 
7
  # --- الإعدادات ---
8
  BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
9
+ # معرف القناة التي سيتم تخزين الملفات فيها (الأرشيف)
10
+ ARCHIVE_CH = os.getenv("ARCHIVE_CHANNEL_ID")
11
  ADMIN_HANDLE = "@svipfast"
12
+
13
  bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
14
  app = Flask(__name__)
15
 
16
+ # قاموس لتخزين "معرفات الملفات" (File IDs) لتجنب إعادة الرفع
17
+ # في الإنتاج الحقيقي، يفضل استخدام قاعدة بيانات بسيطة مثل SQLite
18
+ archive_db = {}
19
 
20
  @app.route('/')
21
+ def home(): return "<h1>Archive Engine Pro is Running</h1>"
22
 
23
+ # --- وظيفة السحب (نفس المحرك السابق لكن محسن) ---
24
+ def fetch_and_pack(url, start, end):
25
  with sync_playwright() as p:
26
  browser = p.chromium.launch(headless=True)
27
+ page = browser.new_page()
28
+ downloaded_files = []
29
+
30
+ base_part = re.sub(r'/(?:chapter-)?\d+$', '', url.strip().rstrip('/'))
31
+ is_azora = "azoramoon" in url
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ for i in range(start, end + 1):
34
+ target_url = f"{base_part}/chapter-{i}" if is_azora else f"{base_part}/{i}"
35
+ try:
36
+ page.goto(target_url, wait_until="networkidle", timeout=60000)
37
+ # تمرير ذكي
38
+ page.evaluate("window.scrollTo(0, document.body.scrollHeight/2)")
39
+ time.sleep(1)
40
+ page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
41
+
42
+ images = page.query_selector_all("img")
43
+ img_data = []
44
+ for img in images:
45
+ src = img.get_attribute("src") or img.get_attribute("data-src")
46
+ if src and any(x in src.lower() for x in ['.jpg', '.jpeg', '.png', '.webp']):
47
+ if "logo" in src.lower() or "banner" in src.lower(): continue
48
+ res = requests.get(src, timeout=10)
49
+ if res.status_code == 200 and len(res.content) > 25000:
50
+ img_data.append(res.content)
51
+
52
+ if img_data:
53
+ # ضغط الصور في ZIP للفصل الواحد
54
+ ch_zip = f"CH_{i}.zip"
55
+ with zipfile.ZipFile(ch_zip, 'w') as z:
56
+ for idx, data in enumerate(img_data):
57
+ z.writestr(f"{idx:03d}.jpg", data)
58
+ downloaded_files.append((i, ch_zip))
59
+ except: continue
60
 
61
+ browser.close()
62
+ return downloaded_files
 
 
 
 
 
 
 
 
63
 
64
+ # --- معالجة الطلب بنظام الأرشيف ---
65
+ def smart_process(chat_id, url, start, end, status_msg):
66
+ # توليد مفتاح فريد للمانجا بناءً على الرابط
67
+ manga_id = re.sub(r'\W+', '', url.split('/')[-2])
 
 
 
 
68
 
69
+ needed_chapters = []
70
+ found_files = []
 
 
 
 
 
 
71
 
72
+ # 1. البحث في الأرشيف أولاً
73
+ for i in range(start, end + 1):
74
+ key = f"{manga_id}_{i}"
75
+ if key in archive_db:
76
+ found_files.append(archive_db[key])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  else:
78
+ needed_chapters.append(i)
 
 
 
79
 
80
+ # 2. إذا كانت هناك فصول ناقصة، نسحبها
81
+ if needed_chapters:
82
+ bot.edit_message_text(f"🔍 الفصل/الفصول {needed_chapters} غير موجودة في الأرشيف.. جاري السحب الآن.", chat_id, status_msg.message_id)
83
+ new_files = fetch_and_pack(url, min(needed_chapters), max(needed_chapters))
84
+
85
+ for ch_num, file_path in new_files:
86
+ # رفع الملف للقناة الخاصة (الأرشيف) للحفظ الدائم
87
+ with open(file_path, 'rb') as f:
88
+ archived_msg = bot.send_document(ARCHIVE_CH, f, caption=f"Archive: {manga_id} | Ch: {ch_num}")
89
+ # حفظ الـ File ID لاستخدامه لاحقاً دون إعادة رفع
90
+ file_id = archived_msg.document.file_id
91
+ archive_db[f"{manga_id}_{ch_num}"] = file_id
92
+ found_files.append(file_id)
93
+ os.remove(file_path)
94
 
95
+ # 3. إرسال جميع الملفات للمستخدم (بسرعة البرق من سيرفرات تليجرام)
96
+ if found_files:
97
+ bot.send_message(chat_id, f"✅ تم تجهيز الحزمة لك من الأرشيف الذكي:")
98
+ for fid in found_files:
99
+ bot.send_document(chat_id, fid)
100
+ bot.delete_message(chat_id, status_msg.message_id)
101
+ else:
102
+ bot.edit_message_text("❌ فشل السحب بالكامل.", chat_id, status_msg.message_id)
 
 
 
 
 
 
 
 
103
 
104
+ # --- استكمال الأزرار والـ Start كما في النسخ السابقة ---
105
+ # ملاحظة: في execute() استدعي smart_process() بدلاً من المحرك القديم.