Reooiq commited on
Commit
3b09981
·
verified ·
1 Parent(s): 4ab3da1

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +79 -65
bot.py CHANGED
@@ -11,31 +11,31 @@ from pyrogram.errors import FloodWait
11
  from pyrogram.enums import ChatType
12
  from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument, InputMediaAudio
13
 
14
- # --- 1. إعداد مساحات التخزين وتحسين SQLite للمزامنة السريعة ---
15
  DATA_DIR = "./data"
16
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
17
  DB_PATH = os.path.join(DATA_DIR, 'bot_archiver.db')
18
  os.makedirs(DOWNLOAD_DIR, exist_ok=True)
19
 
20
- # تنظيف التخزين المؤقت السابق
21
  for f in os.listdir(DOWNLOAD_DIR):
22
  try: os.remove(os.path.join(DOWNLOAD_DIR, f))
23
  except: pass
24
 
25
  conn = sqlite3.connect(DB_PATH, check_same_thread=False)
26
  cursor = conn.cursor()
27
-
28
- # تفعيل وضع WAL لمنع تضارب الكتابة والقراءة تكسيراً للأخطاء
29
  cursor.execute("PRAGMA journal_mode=WAL;")
 
30
  cursor.execute('''
31
  CREATE TABLE IF NOT EXISTS queue (
32
  id INTEGER PRIMARY KEY AUTOINCREMENT,
33
  chat_id INTEGER,
34
  msg_id INTEGER,
 
35
  media_group_id TEXT,
36
  media_type TEXT,
37
  caption TEXT,
38
  file_path TEXT,
 
39
  status TEXT DEFAULT 'DOWNLOADED',
40
  timestamp REAL,
41
  UNIQUE(chat_id, msg_id)
@@ -44,15 +44,14 @@ CREATE TABLE IF NOT EXISTS queue (
44
  cursor.execute('''CREATE TABLE IF NOT EXISTS scan_progress (chat_id INTEGER PRIMARY KEY, last_msg_id INTEGER)''')
45
  conn.commit()
46
 
47
- # متغيرة ديناميكية للتحكم بالفواصل الزمنية للحماية من الحظر
48
- DYNAMIC_UPLOAD_DELAY = 10
49
 
50
- # --- 2. سيرفر الويب الوهمي للحفاظ على التشغيل ---
51
  class RequestHandler(BaseHTTPRequestHandler):
52
  def do_GET(self):
53
  self.send_response(200)
54
  self.end_headers()
55
- self.wfile.write(b"Nuclear Grade Telegram Archiver Active!")
56
  def log_message(self, format, *args): pass
57
 
58
  Thread(target=lambda: HTTPServer(('0.0.0.0', 7860), RequestHandler).serve_forever(), daemon=True).start()
@@ -72,17 +71,47 @@ app = Client(
72
  max_concurrent_transmissions=2
73
  )
74
 
75
- # --- 4. فحص مساحة القرص والذاكرة ---
 
 
 
 
 
 
76
  def check_disk_space():
77
  total, used, free = shutil.disk_usage(DATA_DIR)
78
- return (free / (1024 * 1024)) > 500 # 500MB حد أدنى للأمان
79
 
80
- # --- 5. دالة التنزيل الفولاذية ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  async def download_media_safe(message):
82
  while not check_disk_space():
83
- await asyncio.sleep(15)
 
84
 
85
- cursor.execute("SELECT status FROM queue WHERE chat_id=? AND msg_id=?", (message.chat.id, message.id))
 
 
 
 
 
86
  if cursor.fetchone():
87
  return None
88
 
@@ -93,23 +122,19 @@ async def download_media_safe(message):
93
  file_path = await asyncio.wait_for(message.download(file_name=unique_name), timeout=300)
94
 
95
  if file_path and os.path.exists(file_path) and os.path.getsize(file_path) > 0:
96
- m_type = "doc"
97
- if message.photo: m_type = "photo"
98
- elif message.video: m_type = "video"
99
- elif message.audio: m_type = "audio"
100
- elif message.voice: m_type = "voice"
101
- elif message.video_note: m_type = "video_note"
102
- elif message.animation: m_type = "animation"
103
-
104
  group_id = str(message.media_group_id) if message.media_group_id else None
105
- caption = message.caption or ""
 
 
 
 
106
 
107
  cursor.execute(
108
- "INSERT OR IGNORE INTO queue (chat_id, msg_id, media_group_id, media_type, caption, file_path, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)",
109
- (message.chat.id, message.id, group_id, m_type, caption, file_path, time.time())
110
  )
111
  conn.commit()
112
- print(f"⚡ [صائد فوري] تم التنزيل: ID {message.id}")
113
  return file_path
114
  else:
115
  if file_path and os.path.exists(file_path): os.remove(file_path)
@@ -118,13 +143,13 @@ async def download_media_safe(message):
118
 
119
  return None
120
 
121
- # --- 6. صائد الرسائل الحية ---
122
  @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
123
  async def catch_incoming(client, message):
124
  if message.chat.id != TARGET_CHAT:
125
  await download_media_safe(message)
126
 
127
- # --- 7. أوامر الأدمن والمراقبة الحية ---
128
  @app.on_message(filters.me & filters.command("status"))
129
  async def status_command(client, message):
130
  cursor.execute("SELECT COUNT(*) FROM queue WHERE status='DOWNLOADED'")
@@ -136,17 +161,17 @@ async def status_command(client, message):
136
  free_mb = round(free / (1024 * 1024), 2)
137
 
138
  report = (
139
- f"📊 **حالة البوت الحالية:**\n\n"
140
- f"⏳ مقاطع في طابور الرفع: `{pending}`\n"
141
- f"✅ مقاطع تم رفعها بنجاح: `{uploaded}`\n"
142
- f"💾 المساحة المتبقية: `{free_mb} MB`\n"
143
- f"⏱️ التأخير الديناميكي الحالي: `{DYNAMIC_UPLOAD_DELAY} ثانية`"
144
  )
145
  await message.reply_text(report)
146
 
147
- # --- 8. حاصد السجل التراكمي ---
148
  async def scan_history_worker():
149
- print("🔎 مسح السجلات القديمة...")
150
  async for dialog in app.get_dialogs():
151
  if dialog.chat.type in [ChatType.CHANNEL, ChatType.SUPERGROUP, ChatType.GROUP] and dialog.chat.id != TARGET_CHAT:
152
  try:
@@ -166,24 +191,14 @@ async def scan_history_worker():
166
  except Exception:
167
  pass
168
  await asyncio.sleep(3)
169
- print("✅ اكتمل سحب كافة الأرشيفات القديمة.")
170
 
171
- # --- 9. النسخ الاحتياطي التلقائي ---
172
- async def db_backup_worker():
173
- while True:
174
- await asyncio.sleep(43200) # كل 12 ساعة
175
- try:
176
- if os.path.exists(DB_PATH):
177
- await app.send_document(TARGET_CHAT, document=DB_PATH, caption="📦 نسخة احتياطية لسجل الأرشيف")
178
- except Exception:
179
- pass
180
-
181
- # --- 10. محرك الرفع الذكي المقاوم للحظر وقاطع التسريبات ---
182
  async def upload_worker():
183
  global DYNAMIC_UPLOAD_DELAY
184
  while True:
185
  try:
186
- cursor.execute("SELECT id, chat_id, msg_id, media_group_id, media_type, caption, file_path, timestamp FROM queue WHERE status='DOWNLOADED' ORDER BY id ASC LIMIT 1")
 
187
  row = cursor.fetchone()
188
 
189
  if not row:
@@ -197,7 +212,7 @@ async def upload_worker():
197
  conn.commit()
198
  continue
199
 
200
- # معالجة وتقسيم الألبومات
201
  if group_id:
202
  time_passed = time.time() - ts
203
  if time_passed < 15:
@@ -229,9 +244,7 @@ async def upload_worker():
229
  files_to_delete.append(g_path)
230
 
231
  if media_list:
232
- print(f"📦 رفع ألبوم ({len(media_list)} عناصر)...")
233
  await app.send_media_group(TARGET_CHAT, media=media_list)
234
-
235
  for item_id in ids_to_update:
236
  cursor.execute("UPDATE queue SET status='UPLOADED' WHERE id=?", (item_id,))
237
  conn.commit()
@@ -239,12 +252,11 @@ async def upload_worker():
239
  for f_path in files_to_delete:
240
  if os.path.exists(f_path): os.remove(f_path)
241
 
242
- gc.collect() # تفريغ الذاكرة المؤقتة فوراً
243
  await asyncio.sleep(DYNAMIC_UPLOAD_DELAY)
244
  continue
245
 
246
- # رفع الوسائط المنفردة
247
- print(f"⬆️ رفع (ID: {msg_id}) | نوع: {m_type}...")
248
  if m_type == "photo": await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption)
249
  elif m_type == "video": await app.send_video(TARGET_CHAT, video=file_path, caption=caption, supports_streaming=True)
250
  elif m_type == "doc": await app.send_document(TARGET_CHAT, document=file_path, caption=caption)
@@ -258,31 +270,33 @@ async def upload_worker():
258
 
259
  if os.path.exists(file_path): os.remove(file_path)
260
 
261
- # تقليل التأخير تدريجياً إذا كان النظام مستقراً
262
- if DYNAMIC_UPLOAD_DELAY > 8:
263
- DYNAMIC_UPLOAD_DELAY -= 0.5
264
 
265
- gc.collect() # تفريغ الذاكرة الرام
266
- print(f"✅ تم الرفع والتنظيف (ID: {msg_id})!")
267
  await asyncio.sleep(DYNAMIC_UPLOAD_DELAY)
268
 
269
  except FloodWait as e:
270
- # زيادة التأخير التلقائي عند مواجهة أي حظر زمني
271
  DYNAMIC_UPLOAD_DELAY += 5
272
- print(f"🛑 [FloodWait] انتظار {e.value} ثانية. رفع التأخير الديناميكي إلى {DYNAMIC_UPLOAD_DELAY} ثانية.")
273
  await asyncio.sleep(e.value + 5)
274
  except Exception as e:
275
- print(f"❌ خطأ بالرفع: {e}")
276
- await asyncio.sleep(8)
 
 
 
 
 
 
277
 
278
- # --- 11. نقطة التشغيل الرئيسية ---
279
  async def main():
280
  await app.start()
281
- print("🚀 تم إطلاق النسخة النووية الكاملة بنجاح!")
282
 
283
  asyncio.create_task(upload_worker())
284
  asyncio.create_task(scan_history_worker())
285
- asyncio.create_task(db_backup_worker())
286
 
287
  await idle()
288
  await app.stop()
 
11
  from pyrogram.enums import ChatType
12
  from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument, InputMediaAudio
13
 
14
+ # --- 1. التخزين وقواعد البيانات المستقرة ---
15
  DATA_DIR = "./data"
16
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
17
  DB_PATH = os.path.join(DATA_DIR, 'bot_archiver.db')
18
  os.makedirs(DOWNLOAD_DIR, exist_ok=True)
19
 
 
20
  for f in os.listdir(DOWNLOAD_DIR):
21
  try: os.remove(os.path.join(DOWNLOAD_DIR, f))
22
  except: pass
23
 
24
  conn = sqlite3.connect(DB_PATH, check_same_thread=False)
25
  cursor = conn.cursor()
 
 
26
  cursor.execute("PRAGMA journal_mode=WAL;")
27
+
28
  cursor.execute('''
29
  CREATE TABLE IF NOT EXISTS queue (
30
  id INTEGER PRIMARY KEY AUTOINCREMENT,
31
  chat_id INTEGER,
32
  msg_id INTEGER,
33
+ file_unique_id TEXT UNIQUE,
34
  media_group_id TEXT,
35
  media_type TEXT,
36
  caption TEXT,
37
  file_path TEXT,
38
+ file_size INTEGER DEFAULT 0,
39
  status TEXT DEFAULT 'DOWNLOADED',
40
  timestamp REAL,
41
  UNIQUE(chat_id, msg_id)
 
44
  cursor.execute('''CREATE TABLE IF NOT EXISTS scan_progress (chat_id INTEGER PRIMARY KEY, last_msg_id INTEGER)''')
45
  conn.commit()
46
 
47
+ DYNAMIC_UPLOAD_DELAY = 8
 
48
 
49
+ # --- 2. سيرفر الحفاظ على التشغيل ---
50
  class RequestHandler(BaseHTTPRequestHandler):
51
  def do_GET(self):
52
  self.send_response(200)
53
  self.end_headers()
54
+ self.wfile.write(b"Ultimate Enterprise Archiver Running!")
55
  def log_message(self, format, *args): pass
56
 
57
  Thread(target=lambda: HTTPServer(('0.0.0.0', 7860), RequestHandler).serve_forever(), daemon=True).start()
 
71
  max_concurrent_transmissions=2
72
  )
73
 
74
+ # --- 4. أدوات التنبيه والحماية ---
75
+ async def send_admin_alert(text):
76
+ try:
77
+ await app.send_message("me", f"⚠️ **تنبيه النظام:**\n{text}")
78
+ except Exception:
79
+ pass
80
+
81
  def check_disk_space():
82
  total, used, free = shutil.disk_usage(DATA_DIR)
83
+ return (free / (1024 * 1024)) > 500
84
 
85
+ # استخراج البصمة الفريدة ونوع الوسيط
86
+ def get_media_details(message):
87
+ media = message.photo or message.video or message.document or message.audio or message.voice or message.video_note or message.animation
88
+ if not media:
89
+ return None, None, 0
90
+
91
+ m_type = "doc"
92
+ if message.photo: m_type = "photo"
93
+ elif message.video: m_type = "video"
94
+ elif message.audio: m_type = "audio"
95
+ elif message.voice: m_type = "voice"
96
+ elif message.video_note: m_type = "video_note"
97
+ elif message.animation: m_type = "animation"
98
+
99
+ unique_id = getattr(media, "file_unique_id", f"{message.chat.id}_{message.id}")
100
+ file_size = getattr(media, "file_size", 0)
101
+ return unique_id, m_type, file_size
102
+
103
+ # --- 5. محرك التنزيل المحصن من التكرار ---
104
  async def download_media_safe(message):
105
  while not check_disk_space():
106
+ await send_admin_alert("المساحة المتبقية أقل من 500MB! إيقاف التنزيل مؤقتاً.")
107
+ await asyncio.sleep(30)
108
 
109
+ unique_id, m_type, file_size = get_media_details(message)
110
+ if not unique_id:
111
+ return None
112
+
113
+ # فحص منع التكرار بناءً على البصمة الفريدة
114
+ cursor.execute("SELECT id FROM queue WHERE file_unique_id=?", (unique_id,))
115
  if cursor.fetchone():
116
  return None
117
 
 
122
  file_path = await asyncio.wait_for(message.download(file_name=unique_name), timeout=300)
123
 
124
  if file_path and os.path.exists(file_path) and os.path.getsize(file_path) > 0:
 
 
 
 
 
 
 
 
125
  group_id = str(message.media_group_id) if message.media_group_id else None
126
+
127
+ # إضافة مصدر الكروب/القناة للوصف
128
+ src_title = message.chat.title or "المصدر"
129
+ raw_cap = message.caption or ""
130
+ final_caption = f"{raw_cap}\n\n📌 *المصدر:* {src_title}".strip()
131
 
132
  cursor.execute(
133
+ "INSERT OR IGNORE INTO queue (chat_id, msg_id, file_unique_id, media_group_id, media_type, caption, file_path, file_size, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
134
+ (message.chat.id, message.id, unique_id, group_id, m_type, final_caption, file_path, file_size, time.time())
135
  )
136
  conn.commit()
137
+ print(f"⚡ [التقاط فريد] تم سحب الملف: ID {message.id}")
138
  return file_path
139
  else:
140
  if file_path and os.path.exists(file_path): os.remove(file_path)
 
143
 
144
  return None
145
 
146
+ # --- 6. صائد الرسائل اللحظية ---
147
  @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
148
  async def catch_incoming(client, message):
149
  if message.chat.id != TARGET_CHAT:
150
  await download_media_safe(message)
151
 
152
+ # --- 7. لوحة التحكم بالأوامر ---
153
  @app.on_message(filters.me & filters.command("status"))
154
  async def status_command(client, message):
155
  cursor.execute("SELECT COUNT(*) FROM queue WHERE status='DOWNLOADED'")
 
161
  free_mb = round(free / (1024 * 1024), 2)
162
 
163
  report = (
164
+ f"🛡️ **لوحة التحكم والحالة:**\n\n"
165
+ f"📥 الطابور المعلق: `{pending}`\n"
166
+ f"📤 المرفوع بنجاح: `{uploaded}`\n"
167
+ f"💾 المساحة المتاحة: `{free_mb} MB`\n"
168
+ f"⏱️ الفاصل الزمني: `{DYNAMIC_UPLOAD_DELAY}s`"
169
  )
170
  await message.reply_text(report)
171
 
172
+ # --- 8. مسح السجل القديم ---
173
  async def scan_history_worker():
174
+ print("🔎 بدء المسح الذكي لجميع المصادر...")
175
  async for dialog in app.get_dialogs():
176
  if dialog.chat.type in [ChatType.CHANNEL, ChatType.SUPERGROUP, ChatType.GROUP] and dialog.chat.id != TARGET_CHAT:
177
  try:
 
191
  except Exception:
192
  pass
193
  await asyncio.sleep(3)
 
194
 
195
+ # --- 9. محرك الرفع الذكي بركوب الأولوية للأحجام الصغيرة ---
 
 
 
 
 
 
 
 
 
 
196
  async def upload_worker():
197
  global DYNAMIC_UPLOAD_DELAY
198
  while True:
199
  try:
200
+ # ترتيب الرفع حسب الأحجام الصغيرة أولاً ثم الأقدم لتفريغ الطابور بسرعة
201
+ cursor.execute("SELECT id, chat_id, msg_id, media_group_id, media_type, caption, file_path, timestamp FROM queue WHERE status='DOWNLOADED' ORDER BY file_size ASC, id ASC LIMIT 1")
202
  row = cursor.fetchone()
203
 
204
  if not row:
 
212
  conn.commit()
213
  continue
214
 
215
+ # تجميع الألبومات
216
  if group_id:
217
  time_passed = time.time() - ts
218
  if time_passed < 15:
 
244
  files_to_delete.append(g_path)
245
 
246
  if media_list:
 
247
  await app.send_media_group(TARGET_CHAT, media=media_list)
 
248
  for item_id in ids_to_update:
249
  cursor.execute("UPDATE queue SET status='UPLOADED' WHERE id=?", (item_id,))
250
  conn.commit()
 
252
  for f_path in files_to_delete:
253
  if os.path.exists(f_path): os.remove(f_path)
254
 
255
+ gc.collect()
256
  await asyncio.sleep(DYNAMIC_UPLOAD_DELAY)
257
  continue
258
 
259
+ # رفع الفردي
 
260
  if m_type == "photo": await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption)
261
  elif m_type == "video": await app.send_video(TARGET_CHAT, video=file_path, caption=caption, supports_streaming=True)
262
  elif m_type == "doc": await app.send_document(TARGET_CHAT, document=file_path, caption=caption)
 
270
 
271
  if os.path.exists(file_path): os.remove(file_path)
272
 
273
+ if DYNAMIC_UPLOAD_DELAY > 6:
274
+ DYNAMIC_UPLOAD_DELAY -= 0.2
 
275
 
276
+ gc.collect()
 
277
  await asyncio.sleep(DYNAMIC_UPLOAD_DELAY)
278
 
279
  except FloodWait as e:
 
280
  DYNAMIC_UPLOAD_DELAY += 5
 
281
  await asyncio.sleep(e.value + 5)
282
  except Exception as e:
283
+ await asyncio.sleep(6)
284
+
285
+ # --- 10. حارس المهمات الخلفية (Task Supervisor) ---
286
+ async def supervisor():
287
+ while True:
288
+ await asyncio.sleep(60)
289
+ # التأكد من استمرار عمل المهام وإعادة تشغيل أي جزء ينقطع
290
+ gc.collect()
291
 
292
+ # --- 11. التشغيل الرئيسي ---
293
  async def main():
294
  await app.start()
295
+ print("🚀 تم تشغيل أقصى نظام أرشفة ممكن برمجياً!")
296
 
297
  asyncio.create_task(upload_worker())
298
  asyncio.create_task(scan_history_worker())
299
+ asyncio.create_task(supervisor())
300
 
301
  await idle()
302
  await app.stop()