Reooiq commited on
Commit
ece3133
·
verified ·
1 Parent(s): c0cc677

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +85 -66
bot.py CHANGED
@@ -5,13 +5,11 @@ from threading import Thread
5
  from http.server import BaseHTTPRequestHandler, HTTPServer
6
  from pyrogram import Client, filters, idle
7
  from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument, InputMediaAudio
8
- from pyrogram.errors import FloodWait
9
 
10
- # --- 1. إعداد مساحات التخزين (تم العودة للقرص الدائم لحماية الملفات وقت الحظر) ---
11
  DATA_DIR = "/data"
12
  DB_PATH = os.path.join(DATA_DIR, 'bot_memory.db')
13
-
14
- # استخدام التخزين الدائم لضمان عدم ضياع أي مقطع إذا انطفأ السيرفر أثناء حظر تيليجرام
15
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
16
  os.makedirs(DOWNLOAD_DIR, exist_ok=True)
17
 
@@ -38,7 +36,7 @@ def mark_album_processed(album_id):
38
  cursor.execute("INSERT OR IGNORE INTO processed_albums VALUES (?)", (album_id,))
39
  conn.commit()
40
 
41
- # --- 3. سيرفر الويب الوهمي (متوافق 100% مع UptimeRobot) ---
42
  class RequestHandler(BaseHTTPRequestHandler):
43
  def do_GET(self):
44
  body = b"Ultimate Speed Archiver is Running 24/7!"
@@ -65,7 +63,7 @@ def start_web_server():
65
 
66
  Thread(target=start_web_server, daemon=True).start()
67
 
68
- # --- 4. إعداد البوت مع خيارات السرعة المتوازية ---
69
  API_ID = int(os.environ.get("API_ID", 0))
70
  API_HASH = os.environ.get("API_HASH", "")
71
  SESSION_STRING = os.environ.get("SESSION_STRING", "")
@@ -73,6 +71,9 @@ SESSION_STRING = os.environ.get("SESSION_STRING", "")
73
  target_env = os.environ.get("TARGET_CHAT", "")
74
  TARGET_CHAT = int(target_env) if target_env.lstrip('-').isdigit() else target_env
75
 
 
 
 
76
  app = Client(
77
  "my_userbot",
78
  api_id=API_ID,
@@ -86,26 +87,48 @@ app = Client(
86
  download_queue = asyncio.Queue()
87
  upload_queue = asyncio.Queue()
88
 
89
- # --- 5. الرادار الذكي: التقاط الملفات والألبومات بدون تفويت ---
90
  @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
91
  async def catch_new_messages(client, message):
92
  if message.media_group_id:
93
  if not is_album_processed(str(message.media_group_id)):
94
  mark_album_processed(str(message.media_group_id))
95
- print(f"🚨 [رصد ألبوم] تجميع الأجزاء... (ننتظر 3 ثوانٍ لضمان وصول كل المقاطع)")
96
- # تشغيل مهمة جانبية للانتظار حتى لا يتعطل الرادار عن باقي الرسائل
97
  asyncio.create_task(queue_album_after_delay(message))
98
  return
99
 
100
  if not is_processed(message.chat.id, message.id):
101
- print(f"🚨 [رصد مفرد] تم التقاط ملف من: {message.chat.title}")
102
  await download_queue.put({'type': 'single', 'message': message})
103
 
104
  async def queue_album_after_delay(message):
105
- await asyncio.sleep(3) # التحديث الأهم: تأخير مقصود لتكتمل حزمة الألبوم في سيرفر تيليجرام
106
  await download_queue.put({'type': 'album', 'message': message})
107
 
108
- # --- 6. كتيبة التحميل (حفظ بالقرص الدائم) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  async def downloader_worker(worker_id):
110
  while True:
111
  task = await download_queue.get()
@@ -121,7 +144,6 @@ async def downloader_worker(worker_id):
121
  file_path = await msg.download(file_name=f"{DOWNLOAD_DIR}/")
122
  if file_path:
123
  downloaded_media.append({'path': file_path, 'msg': msg})
124
- mark_processed(msg.chat.id, msg.id)
125
 
126
  if downloaded_media:
127
  await upload_queue.put({'type': 'album', 'items': downloaded_media})
@@ -131,7 +153,7 @@ async def downloader_worker(worker_id):
131
  download_queue.task_done()
132
  continue
133
 
134
- print(f"⚡ [محمل {worker_id}] جارٍ تحميل الملف الدائم (ID: {message.id})...")
135
  file_path = await message.download(file_name=f"{DOWNLOAD_DIR}/")
136
 
137
  if file_path:
@@ -142,7 +164,23 @@ async def downloader_worker(worker_id):
142
  finally:
143
  download_queue.task_done()
144
 
145
- # --- 7. عامل الرفع الصبور (يحترم الحظر ويحفظ الملفات) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  async def uploader_worker():
147
  while True:
148
  task = await upload_queue.get()
@@ -171,103 +209,84 @@ async def uploader_worker():
171
  media_group.append(InputMediaDocument(media=file_path, caption=caption, caption_entities=entities))
172
 
173
  if media_group:
174
- print("🚀 رفع ألبوم متكامل...")
175
- # رفع الألبوم (تيليجرام قد يفصل المستندات عن الصور، Pyrogram يتعامل معها)
176
- await app.send_media_group(TARGET_CHAT, media=media_group)
177
- print("✅ الألبوم ارتفع بنجاح!")
 
 
 
 
 
178
 
179
- # لا نحذف الملفات إلا بعد نجاح الرفع 100%
180
- for fp in files_to_remove:
181
- if os.path.exists(fp): os.remove(fp)
182
 
183
  elif task_type == 'single':
184
  file_path = task['file_path']
185
  message = task['message']
186
- caption = message.caption or ""
187
- entities = message.caption_entities
188
 
189
- print(f"🚀 رفع ملف مفرد...")
190
- if message.photo:
191
- await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption, caption_entities=entities)
192
- elif message.video:
193
- await app.send_video(
194
- TARGET_CHAT,
195
- video=file_path,
196
- caption=caption,
197
- caption_entities=entities,
198
- duration=message.video.duration,
199
- width=message.video.width,
200
- height=message.video.height,
201
- supports_streaming=True
202
- )
203
- elif message.animation:
204
- await app.send_animation(TARGET_CHAT, animation=file_path, caption=caption, caption_entities=entities)
205
- elif message.audio:
206
- await app.send_audio(TARGET_CHAT, audio=file_path, caption=caption, caption_entities=entities)
207
- elif message.voice:
208
- await app.send_voice(TARGET_CHAT, voice=file_path, caption=caption, caption_entities=entities)
209
- else:
210
- await app.send_document(TARGET_CHAT, document=file_path, caption=caption, caption_entities=entities)
211
 
212
  mark_processed(message.chat.id, message.id)
213
  print(f"✅ تم الرفع بنجاح!")
214
  if os.path.exists(file_path): os.remove(file_path)
215
 
216
  except FloodWait as e:
217
- # الخدعة الذهبية: نعيد المهمة للطابور، وننام، والملف بأمان في الهارد دسك!
218
- print(f"⏳ [حظر إرسال] تيليجرام مقفل مؤقتاً. سننتظر {e.value} ثانية. الملفات مخزنة بأمان ولن تضيع!")
219
  await asyncio.sleep(e.value + 5)
220
- await upload_queue.put(task)
221
 
222
  except Exception as e:
223
- print(f"❌ خطأ غير متوقع بالرفع: {e}")
224
- # في حال وجود خطأ آخر غير الحظر، نحتفظ بالملف ليتم استرجاعه عند إعادة التشغيل
225
 
226
  finally:
227
  upload_queue.task_done()
228
- await asyncio.sleep(2)
229
 
230
- # --- 8. المنقذ: استرجاع ملفات ما قبل الانطفاء/الحظر ---
231
  async def recover_orphaned_files():
232
  if not os.path.exists(DOWNLOAD_DIR): return
233
  files = [f for f in os.listdir(DOWNLOAD_DIR) if os.path.isfile(os.path.join(DOWNLOAD_DIR, f))]
234
  if files:
235
- print(f"🔍 [عملية إنقاذ] تم العثور على {len(files)} ملفات مخزنة من الجلسة السابقة بسبب حظر أو انطفاء!")
236
  for filename in files:
237
  file_path = os.path.join(DOWNLOAD_DIR, filename)
238
  try:
239
- # نرفع الملفات المعلقة كمستندات لضمان عدم ضياعها
240
- await app.send_document(TARGET_CHAT, document=file_path, caption="♻️ ملف مسترجع من الذاكرة الاحتياطية (كان معلقاً)")
241
  os.remove(file_path)
242
- await asyncio.sleep(3) # تجنب الحظر أثناء الاسترجاع
243
  except FloodWait as e:
244
- print(f"⏳ لا يزال الحظر مستمراً، ننتظر {e.value} ثانية...")
245
  await asyncio.sleep(e.value + 5)
246
  except Exception as e:
247
- print(f"❌ فشل إنقاذ الملف {filename}: {e}")
248
 
249
- # --- 9. التشغيل المركزي ---
250
  async def main():
251
  await app.start()
252
 
253
- print("🔄 تنشيط ذاكرة الحساب لجلب الأكسس هاش...")
254
  try:
255
  async for dialog in app.get_dialogs():
256
  if dialog.chat.id == TARGET_CHAT:
257
- print(f"✅ تم التعرف على القناة الهدف: {dialog.chat.title}")
258
  break
259
- except Exception as e:
260
  pass
261
 
262
- # فور التشغيل، ابحث عن أي ملفات كانت معلقة بسبب حظر وارفعها
263
  await recover_orphaned_files()
264
 
 
265
  for i in range(1, 4):
266
  asyncio.create_task(downloader_worker(i))
267
-
268
  asyncio.create_task(uploader_worker())
 
 
 
269
 
270
- print("🤖 جي��ي: صائد الملفات المدرّع يعمل الآن! لا ضياع بعد اليوم...")
271
  await idle()
272
  await app.stop()
273
 
 
5
  from http.server import BaseHTTPRequestHandler, HTTPServer
6
  from pyrogram import Client, filters, idle
7
  from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument, InputMediaAudio
8
+ from pyrogram.errors import FloodWait, RPCError
9
 
10
+ # --- 1. إعداد مساحات التخزين الدائمة ---
11
  DATA_DIR = "/data"
12
  DB_PATH = os.path.join(DATA_DIR, 'bot_memory.db')
 
 
13
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
14
  os.makedirs(DOWNLOAD_DIR, exist_ok=True)
15
 
 
36
  cursor.execute("INSERT OR IGNORE INTO processed_albums VALUES (?)", (album_id,))
37
  conn.commit()
38
 
39
+ # --- 3. سيرفر الويب الوهمي للحفاظ على العمل 24/7 ---
40
  class RequestHandler(BaseHTTPRequestHandler):
41
  def do_GET(self):
42
  body = b"Ultimate Speed Archiver is Running 24/7!"
 
63
 
64
  Thread(target=start_web_server, daemon=True).start()
65
 
66
+ # --- 4. إعدادات المتغيرات وتجهيز العميل ---
67
  API_ID = int(os.environ.get("API_ID", 0))
68
  API_HASH = os.environ.get("API_HASH", "")
69
  SESSION_STRING = os.environ.get("SESSION_STRING", "")
 
71
  target_env = os.environ.get("TARGET_CHAT", "")
72
  TARGET_CHAT = int(target_env) if target_env.lstrip('-').isdigit() else target_env
73
 
74
+ source_env = os.environ.get("SOURCE_CHAT", "")
75
+ SOURCE_CHAT = int(source_env) if source_env.lstrip('-').isdigit() else source_env
76
+
77
  app = Client(
78
  "my_userbot",
79
  api_id=API_ID,
 
87
  download_queue = asyncio.Queue()
88
  upload_queue = asyncio.Queue()
89
 
90
+ # --- 5. الرادار المباشر + تأخير الألبومات ---
91
  @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
92
  async def catch_new_messages(client, message):
93
  if message.media_group_id:
94
  if not is_album_processed(str(message.media_group_id)):
95
  mark_album_processed(str(message.media_group_id))
 
 
96
  asyncio.create_task(queue_album_after_delay(message))
97
  return
98
 
99
  if not is_processed(message.chat.id, message.id):
 
100
  await download_queue.put({'type': 'single', 'message': message})
101
 
102
  async def queue_album_after_delay(message):
103
+ await asyncio.sleep(4) # مهلة لتجميع كافة أجزاء الألبوم من سيرفرات تيليجرام
104
  await download_queue.put({'type': 'album', 'message': message})
105
 
106
+ # --- 6. حاصد الأرشيف المفقود (Backfill History Scanner) ---
107
+ async def scan_old_history():
108
+ if not SOURCE_CHAT:
109
+ print("ℹ️ لم يتم تحديد SOURCE_CHAT في المتغيرات، سيتم الاعتماد على الرصد المباشر فقط.")
110
+ return
111
+
112
+ print(f"🔎 [جيمي] بدء مسح الأرشيف القديم للقناة المصدر: {SOURCE_CHAT}...")
113
+ count = 0
114
+ try:
115
+ # جلب تاريخ القناة من الأقدم إلى الأحدث
116
+ async for message in app.get_chat_history(SOURCE_CHAT):
117
+ if message.media:
118
+ if message.media_group_id:
119
+ if not is_album_processed(str(message.media_group_id)):
120
+ mark_album_processed(str(message.media_group_id))
121
+ await download_queue.put({'type': 'album', 'message': message})
122
+ count += 1
123
+ else:
124
+ if not is_processed(message.chat.id, message.id):
125
+ await download_queue.put({'type': 'single', 'message': message})
126
+ count += 1
127
+ print(f"✅ اكتمل مسح الأرشيف! تم إدراج {count} عنصر قديم في طابور التحميل.")
128
+ except Exception as e:
129
+ print(f"�� خطأ أثناء مسح الأرشيف: {e}")
130
+
131
+ # --- 7. كتيبة التحميل إلى التخزين الدائم ---
132
  async def downloader_worker(worker_id):
133
  while True:
134
  task = await download_queue.get()
 
144
  file_path = await msg.download(file_name=f"{DOWNLOAD_DIR}/")
145
  if file_path:
146
  downloaded_media.append({'path': file_path, 'msg': msg})
 
147
 
148
  if downloaded_media:
149
  await upload_queue.put({'type': 'album', 'items': downloaded_media})
 
153
  download_queue.task_done()
154
  continue
155
 
156
+ print(f"⚡ [محمل {worker_id}] جارٍ تحميل الملف (ID: {message.id})...")
157
  file_path = await message.download(file_name=f"{DOWNLOAD_DIR}/")
158
 
159
  if file_path:
 
164
  finally:
165
  download_queue.task_done()
166
 
167
+ # --- 8. عامل الرفع مع آلية التفكيك الاحتياطية عند الفشل ---
168
+ async def send_single_item(file_path, message):
169
+ caption = message.caption or ""
170
+ entities = message.caption_entities
171
+ if message.photo:
172
+ await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption, caption_entities=entities)
173
+ elif message.video:
174
+ await app.send_video(TARGET_CHAT, video=file_path, caption=caption, caption_entities=entities, supports_streaming=True)
175
+ elif message.animation:
176
+ await app.send_animation(TARGET_CHAT, animation=file_path, caption=caption, caption_entities=entities)
177
+ elif message.audio:
178
+ await app.send_audio(TARGET_CHAT, audio=file_path, caption=caption, caption_entities=entities)
179
+ elif message.voice:
180
+ await app.send_voice(TARGET_CHAT, voice=file_path, caption=caption, caption_entities=entities)
181
+ else:
182
+ await app.send_document(TARGET_CHAT, document=file_path, caption=caption, caption_entities=entities)
183
+
184
  async def uploader_worker():
185
  while True:
186
  task = await upload_queue.get()
 
209
  media_group.append(InputMediaDocument(media=file_path, caption=caption, caption_entities=entities))
210
 
211
  if media_group:
212
+ try:
213
+ print("🚀 جارٍ رفع الألبوم كدفعة واحدة...")
214
+ await app.send_media_group(TARGET_CHAT, media=media_group)
215
+ print("✅ تم رفع الألبوم بنجاح!")
216
+ except RPCError as e:
217
+ print(f"⚠️ فشل رفع الألبوم كمجموعة واحدة ({e}). جارٍ التفكيك والرفع منفصلاً لمنع الفقدان...")
218
+ for item in task['items']:
219
+ await send_single_item(item['path'], item['msg'])
220
+ await asyncio.sleep(2)
221
 
222
+ for item in task['items']:
223
+ mark_processed(item['msg'].chat.id, item['msg'].id)
224
+ if os.path.exists(item['path']): os.remove(item['path'])
225
 
226
  elif task_type == 'single':
227
  file_path = task['file_path']
228
  message = task['message']
 
 
229
 
230
+ print(f"🚀 رفع ملف منفرد (ID: {message.id})...")
231
+ await send_single_item(file_path, message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
  mark_processed(message.chat.id, message.id)
234
  print(f"✅ تم الرفع بنجاح!")
235
  if os.path.exists(file_path): os.remove(file_path)
236
 
237
  except FloodWait as e:
238
+ print(f"⏳ [حظر إرسال] انتظار {e.value} ثانية... الملفات محفوظة ولن تضيع.")
 
239
  await asyncio.sleep(e.value + 5)
240
+ await upload_queue.put(task)
241
 
242
  except Exception as e:
243
+ print(f"❌ خطأ غير متوقع أثناء الرفع: {e}")
 
244
 
245
  finally:
246
  upload_queue.task_done()
247
+ await asyncio.sleep(2.5)
248
 
249
+ # --- 9. استرجاع الملفات المعلقة عند إعادة التشغيل ---
250
  async def recover_orphaned_files():
251
  if not os.path.exists(DOWNLOAD_DIR): return
252
  files = [f for f in os.listdir(DOWNLOAD_DIR) if os.path.isfile(os.path.join(DOWNLOAD_DIR, f))]
253
  if files:
254
+ print(f"🔍 [استرجاع] العثور على {len(files)} ملف معلق في القرص، جاري إنقاذها...")
255
  for filename in files:
256
  file_path = os.path.join(DOWNLOAD_DIR, filename)
257
  try:
258
+ await app.send_document(TARGET_CHAT, document=file_path, caption="♻️ ملف مسترجع تلقائياً")
 
259
  os.remove(file_path)
260
+ await asyncio.sleep(3)
261
  except FloodWait as e:
 
262
  await asyncio.sleep(e.value + 5)
263
  except Exception as e:
264
+ print(f"❌ تعذر إنقاذ {filename}: {e}")
265
 
266
+ # --- 10. التشغيل الرئيسي ---
267
  async def main():
268
  await app.start()
269
 
270
+ print("🔄 جلب بيانات القنوات والاتصال...")
271
  try:
272
  async for dialog in app.get_dialogs():
273
  if dialog.chat.id == TARGET_CHAT:
274
+ print(f"✅ القناة الهدف جاهزة: {dialog.chat.title}")
275
  break
276
+ except Exception:
277
  pass
278
 
 
279
  await recover_orphaned_files()
280
 
281
+ # تشغيل العمال
282
  for i in range(1, 4):
283
  asyncio.create_task(downloader_worker(i))
 
284
  asyncio.create_task(uploader_worker())
285
+
286
+ # تشغيل مسح الأرشيف القديم لضمان جلب الفيديوهات الـ 116 السابقة
287
+ asyncio.create_task(scan_old_history())
288
 
289
+ print("🤖 [جيمي] البوت بآلية الفحص الشاملة يعمل الآن بكفاءة!")
290
  await idle()
291
  await app.stop()
292