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

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +57 -70
bot.py CHANGED
@@ -1,5 +1,6 @@
1
  import os
2
  import gc
 
3
  import asyncio
4
  import sqlite3
5
  import time
@@ -11,7 +12,7 @@ from pyrogram.errors import FloodWait
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')
@@ -24,6 +25,7 @@ for f in os.listdir(DOWNLOAD_DIR):
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 (
@@ -44,49 +46,46 @@ 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
- 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()
58
 
59
- # --- 3. إعدادات الحساب ---
60
  API_ID = int(os.environ.get("API_ID", 0))
61
  API_HASH = os.environ.get("API_HASH", "")
62
  SESSION_STRING = os.environ.get("SESSION_STRING", "")
63
  TARGET_CHAT = int(os.environ.get("TARGET_CHAT", 0))
64
 
 
65
  app = Client(
66
  "memory_session",
67
  api_id=API_ID,
68
  api_hash=API_HASH,
69
  session_string=SESSION_STRING,
70
  in_memory=True,
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"
@@ -100,20 +99,16 @@ def get_media_details(message):
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
 
118
  unique_name = f"{DOWNLOAD_DIR}/{message.chat.id}_{message.id}"
119
 
@@ -123,8 +118,6 @@ async def download_media_safe(message):
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()
@@ -134,22 +127,21 @@ async def download_media_safe(message):
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)
141
  except Exception:
142
  await asyncio.sleep(2)
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,17 +153,16 @@ async def status_command(client, message):
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:
@@ -180,29 +171,24 @@ async def scan_history_worker():
180
  last_id = res[0] if res else 0
181
 
182
  async for message in app.get_chat_history(dialog.chat.id):
183
- if message.id <= last_id:
184
- break
185
- if message.media:
186
- await download_media_safe(message)
187
 
188
  cursor.execute("INSERT OR REPLACE INTO scan_progress (chat_id, last_msg_id) VALUES (?, ?)", (dialog.chat.id, message.id))
189
  conn.commit()
 
 
190
 
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:
205
- await asyncio.sleep(5)
206
  continue
207
 
208
  q_id, chat_id, msg_id, group_id, m_type, caption, file_path, ts = row
@@ -215,8 +201,7 @@ async def upload_worker():
215
  # تجميع الألبومات
216
  if group_id:
217
  time_passed = time.time() - ts
218
- if time_passed < 15:
219
- await asyncio.sleep(15 - time_passed)
220
 
221
  cursor.execute(
222
  "SELECT id, media_type, caption, file_path FROM queue WHERE chat_id=? AND media_group_id=? AND status='DOWNLOADED' ORDER BY msg_id ASC",
@@ -227,9 +212,7 @@ async def upload_worker():
227
  if group_rows:
228
  for i in range(0, len(group_rows), 10):
229
  chunk = group_rows[i:i+10]
230
- media_list = []
231
- ids_to_update = []
232
- files_to_delete = []
233
 
234
  for idx, g_item in enumerate(chunk):
235
  g_id, g_type, g_cap, g_path = g_item
@@ -245,8 +228,7 @@ async def upload_worker():
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()
251
 
252
  for f_path in files_to_delete:
@@ -256,7 +238,7 @@ async def upload_worker():
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)
@@ -269,37 +251,42 @@ async def upload_worker():
269
  conn.commit()
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()
303
 
304
  if __name__ == "__main__":
305
- app.run(main())
 
 
 
 
 
 
1
  import os
2
  import gc
3
+ import sys
4
  import asyncio
5
  import sqlite3
6
  import time
 
12
  from pyrogram.enums import ChatType
13
  from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument, InputMediaAudio
14
 
15
+ # --- 1. التهيئة وهيكلة التخزين وقواعد البيانات ---
16
  DATA_DIR = "./data"
17
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
18
  DB_PATH = os.path.join(DATA_DIR, 'bot_archiver.db')
 
25
  conn = sqlite3.connect(DB_PATH, check_same_thread=False)
26
  cursor = conn.cursor()
27
  cursor.execute("PRAGMA journal_mode=WAL;")
28
+ cursor.execute("PRAGMA auto_vacuum = FULL;")
29
 
30
  cursor.execute('''
31
  CREATE TABLE IF NOT EXISTS queue (
 
46
  cursor.execute('''CREATE TABLE IF NOT EXISTS scan_progress (chat_id INTEGER PRIMARY KEY, last_msg_id INTEGER)''')
47
  conn.commit()
48
 
49
+ DYNAMIC_UPLOAD_DELAY = 6
50
 
51
+ # --- 2. سيرفر الويب والبقاء حياً ---
52
  class RequestHandler(BaseHTTPRequestHandler):
53
  def do_GET(self):
54
  self.send_response(200)
55
  self.end_headers()
56
+ self.wfile.write(b"God-Mode Telegram Archiver Engine Active!")
57
  def log_message(self, format, *args): pass
58
 
59
  Thread(target=lambda: HTTPServer(('0.0.0.0', 7860), RequestHandler).serve_forever(), daemon=True).start()
60
 
61
+ # --- 3. إعدادات الحساب مع دعم البروكسي (اختياري) ---
62
  API_ID = int(os.environ.get("API_ID", 0))
63
  API_HASH = os.environ.get("API_HASH", "")
64
  SESSION_STRING = os.environ.get("SESSION_STRING", "")
65
  TARGET_CHAT = int(os.environ.get("TARGET_CHAT", 0))
66
 
67
+ # يمكنك إضافة بروكسي هنا إذا كنت تستخدم بيئة محظورة IP
68
  app = Client(
69
  "memory_session",
70
  api_id=API_ID,
71
  api_hash=API_HASH,
72
  session_string=SESSION_STRING,
73
  in_memory=True,
74
+ max_concurrent_transmissions=3
75
  )
76
 
77
+ # --- 4. الأدوات المساعدة وحماية القرص ---
78
  async def send_admin_alert(text):
79
+ try: await app.send_message("me", f"🚨 **تنبيه النظام:**\n{text}")
80
+ except: pass
 
 
81
 
82
  def check_disk_space():
83
  total, used, free = shutil.disk_usage(DATA_DIR)
84
  return (free / (1024 * 1024)) > 500
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: return None, None, 0
 
89
 
90
  m_type = "doc"
91
  if message.photo: m_type = "photo"
 
99
  file_size = getattr(media, "file_size", 0)
100
  return unique_id, m_type, file_size
101
 
102
+ # --- 5. محرك التنزيل المحصن ---
103
  async def download_media_safe(message):
104
  while not check_disk_space():
105
+ await asyncio.sleep(20)
 
106
 
107
  unique_id, m_type, file_size = get_media_details(message)
108
+ if not unique_id: return None
 
109
 
 
110
  cursor.execute("SELECT id FROM queue WHERE file_unique_id=?", (unique_id,))
111
+ if cursor.fetchone(): return None
 
112
 
113
  unique_name = f"{DOWNLOAD_DIR}/{message.chat.id}_{message.id}"
114
 
 
118
 
119
  if file_path and os.path.exists(file_path) and os.path.getsize(file_path) > 0:
120
  group_id = str(message.media_group_id) if message.media_group_id else None
 
 
121
  src_title = message.chat.title or "المصدر"
122
  raw_cap = message.caption or ""
123
  final_caption = f"{raw_cap}\n\n📌 *المصدر:* {src_title}".strip()
 
127
  (message.chat.id, message.id, unique_id, group_id, m_type, final_caption, file_path, file_size, time.time())
128
  )
129
  conn.commit()
130
+ print(f"⚡ [تنزيل ناجح] ID: {message.id}")
131
  return file_path
132
  else:
133
  if file_path and os.path.exists(file_path): os.remove(file_path)
134
  except Exception:
135
  await asyncio.sleep(2)
 
136
  return None
137
 
138
+ # --- 6. المستمع الفوري ---
139
  @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
140
  async def catch_incoming(client, message):
141
  if message.chat.id != TARGET_CHAT:
142
  await download_media_safe(message)
143
 
144
+ # --- 7. لوحة الإحصائيات والأوامر ---
145
  @app.on_message(filters.me & filters.command("status"))
146
  async def status_command(client, message):
147
  cursor.execute("SELECT COUNT(*) FROM queue WHERE status='DOWNLOADED'")
 
153
  free_mb = round(free / (1024 * 1024), 2)
154
 
155
  report = (
156
+ f"👑 **حالة المحرك النووي المطلق:**\n\n"
157
+ f"📥 بالطابور: `{pending}` ملف\n"
158
+ f"📤 مكتمل الرفع: `{uploaded}` ملف\n"
159
+ f"💾 المساحة الحرة: `{free_mb} MB`\n"
160
+ f"⏱️ التأخير التكيفي: `{DYNAMIC_UPLOAD_DELAY}s`"
161
  )
162
  await message.reply_text(report)
163
 
164
+ # --- 8. مسح الأرشيف القديم ---
165
  async def scan_history_worker():
 
166
  async for dialog in app.get_dialogs():
167
  if dialog.chat.type in [ChatType.CHANNEL, ChatType.SUPERGROUP, ChatType.GROUP] and dialog.chat.id != TARGET_CHAT:
168
  try:
 
171
  last_id = res[0] if res else 0
172
 
173
  async for message in app.get_chat_history(dialog.chat.id):
174
+ if message.id <= last_id: break
175
+ if message.media: await download_media_safe(message)
 
 
176
 
177
  cursor.execute("INSERT OR REPLACE INTO scan_progress (chat_id, last_msg_id) VALUES (?, ?)", (dialog.chat.id, message.id))
178
  conn.commit()
179
+ except Exception: pass
180
+ await asyncio.sleep(2)
181
 
182
+ # --- 9. محرك الرفع والتنظيف التلقائي ---
 
 
 
 
183
  async def upload_worker():
184
  global DYNAMIC_UPLOAD_DELAY
185
  while True:
186
  try:
 
187
  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")
188
  row = cursor.fetchone()
189
 
190
  if not row:
191
+ await asyncio.sleep(4)
192
  continue
193
 
194
  q_id, chat_id, msg_id, group_id, m_type, caption, file_path, ts = row
 
201
  # تجميع الألبومات
202
  if group_id:
203
  time_passed = time.time() - ts
204
+ if time_passed < 12: await asyncio.sleep(12 - time_passed)
 
205
 
206
  cursor.execute(
207
  "SELECT id, media_type, caption, file_path FROM queue WHERE chat_id=? AND media_group_id=? AND status='DOWNLOADED' ORDER BY msg_id ASC",
 
212
  if group_rows:
213
  for i in range(0, len(group_rows), 10):
214
  chunk = group_rows[i:i+10]
215
+ media_list, ids_to_update, files_to_delete = [], [], []
 
 
216
 
217
  for idx, g_item in enumerate(chunk):
218
  g_id, g_type, g_cap, g_path = g_item
 
228
 
229
  if media_list:
230
  await app.send_media_group(TARGET_CHAT, media=media_list)
231
+ for item_id in ids_to_update: cursor.execute("UPDATE queue SET status='UPLOADED' WHERE id=?", (item_id,))
 
232
  conn.commit()
233
 
234
  for f_path in files_to_delete:
 
238
  await asyncio.sleep(DYNAMIC_UPLOAD_DELAY)
239
  continue
240
 
241
+ # رفع منفرد
242
  if m_type == "photo": await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption)
243
  elif m_type == "video": await app.send_video(TARGET_CHAT, video=file_path, caption=caption, supports_streaming=True)
244
  elif m_type == "doc": await app.send_document(TARGET_CHAT, document=file_path, caption=caption)
 
251
  conn.commit()
252
 
253
  if os.path.exists(file_path): os.remove(file_path)
254
+ if DYNAMIC_UPLOAD_DELAY > 5: DYNAMIC_UPLOAD_DELAY -= 0.1
 
 
 
255
  gc.collect()
256
  await asyncio.sleep(DYNAMIC_UPLOAD_DELAY)
257
 
258
  except FloodWait as e:
259
+ DYNAMIC_UPLOAD_DELAY += 4
260
  await asyncio.sleep(e.value + 5)
261
+ except Exception:
262
+ await asyncio.sleep(5)
263
 
264
+ # --- 10. صيانة قاعدة البيانات والتنظيف الدوري ---
265
+ async def db_maintenance():
266
  while True:
267
+ await asyncio.sleep(86400) # كل 24 ساعة
268
+ try:
269
+ cursor.execute("VACUUM;")
270
+ conn.commit()
271
+ print("🧹 تم تنظيف وضغط قاعدة البيانات بنجاح.")
272
+ except: pass
273
 
274
+ # --- 11. التشغيل الذاتي الحامي (Self-Healing Launcher) ---
275
  async def main():
276
  await app.start()
277
+ print("🚀 إطلاق الهيكلية المطلقة المحصنة بالكامل!")
278
 
279
  asyncio.create_task(upload_worker())
280
  asyncio.create_task(scan_history_worker())
281
+ asyncio.create_task(db_maintenance())
282
 
283
  await idle()
284
  await app.stop()
285
 
286
  if __name__ == "__main__":
287
+ while True:
288
+ try:
289
+ app.run(main())
290
+ except Exception as crash_error:
291
+ print(f"⚠️ انهيار غير متوقع: {crash_error}. إعادة التشغيل الذاتي خلال 10 ثوانٍ...")
292
+ time.sleep(10)