Reooiq commited on
Commit
08cebb1
·
verified ·
1 Parent(s): 7aacde1

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +167 -101
bot.py CHANGED
@@ -6,149 +6,215 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
6
  from pyrogram import Client, filters, idle
7
  from pyrogram.errors import FloodWait
8
  from pyrogram.enums import ChatType
 
9
 
10
- # --- 1. إعداد مساحات التخزين (متوافق مع Hugging Face) ---
11
- DATA_DIR = "./data"
12
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
13
- DB_PATH = os.path.join(DATA_DIR, 'bot_memory.db')
14
  os.makedirs(DOWNLOAD_DIR, exist_ok=True)
15
 
16
  conn = sqlite3.connect(DB_PATH, check_same_thread=False)
17
  cursor = conn.cursor()
18
- cursor.execute('''CREATE TABLE IF NOT EXISTS processed (chat_id INTEGER, msg_id INTEGER, UNIQUE(chat_id, msg_id))''')
19
- conn.commit()
20
-
21
- def is_processed(chat_id, msg_id):
22
- cursor.execute("SELECT 1 FROM processed WHERE chat_id=? AND msg_id=?", (chat_id, msg_id))
23
- return cursor.fetchone() is not None
24
 
25
- def mark_processed(chat_id, msg_id):
26
- cursor.execute("INSERT OR IGNORE INTO processed VALUES (?, ?)", (chat_id, msg_id))
27
- conn.commit()
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- # --- 2. سيرفر الويب الوهمي (لمنع Hugging Face من إغلاق البوت) ---
30
  class RequestHandler(BaseHTTPRequestHandler):
31
  def do_GET(self):
32
  self.send_response(200)
33
  self.end_headers()
34
- self.wfile.write(b"Ultimate Safe Archiver is Running Perfectly!")
35
  def log_message(self, format, *args): pass
36
 
37
  Thread(target=lambda: HTTPServer(('0.0.0.0', 7860), RequestHandler).serve_forever(), daemon=True).start()
38
 
39
- # --- 3. إعدادات الحساب (بالذاكرة المؤقتة لمنع التعارض) ---
40
  API_ID = int(os.environ.get("API_ID", 0))
41
  API_HASH = os.environ.get("API_HASH", "")
42
  SESSION_STRING = os.environ.get("SESSION_STRING", "")
43
  TARGET_CHAT = int(os.environ.get("TARGET_CHAT", 0))
44
 
45
  app = Client(
46
- "memory_session",
47
- api_id=API_ID,
48
- api_hash=API_HASH,
49
  session_string=SESSION_STRING,
50
- in_memory=True,
51
- max_concurrent_transmissions=1 # إرسال ملف واحد في نفس الوقت للأمان
52
  )
53
 
54
- process_queue = asyncio.Queue()
 
 
 
 
55
 
56
- # --- 4. الرادار اللحظي (يصطاد المقطع فور نشره حتى لو حُذف لاحقاً) ---
57
- @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
58
- async def catch_new_messages(client, message):
59
- if not is_processed(message.chat.id, message.id):
60
- await process_queue.put(message)
61
-
62
- # --- 5. حاصد الأرشيف القديم ---
63
- async def scan_old_history():
64
- print("🔎 [جيمي] بدء مسح الأرشيف للتأكد من عدم تفويت أي مقطع...")
65
- try:
66
- async for dialog in app.get_dialogs():
67
- if dialog.chat.type in [ChatType.CHANNEL, ChatType.SUPERGROUP, ChatType.GROUP] and dialog.chat.id != TARGET_CHAT:
68
- print(f"📂 يتم الآن فحص: {dialog.chat.title}...")
69
- try:
70
- async for message in app.get_chat_history(dialog.chat.id):
71
- if message.media and not is_processed(message.chat.id, message.id):
72
- await process_queue.put(message)
73
- except Exception:
74
- pass
75
-
76
- await asyncio.sleep(7) # فاصل زمني لتجنب حظر المسح
77
- print("✅ اكتمل سحب كافة الرسائل إلى طابور المعالجة المحكم!")
78
- except Exception as e:
79
- print(f"❌ خطأ بالمسح (لن يؤثر على العمليات الحالية): {e}")
80
-
81
- # --- 6. المحرك المدرع للكسر والرفع (لحل مشكلة 0 بايت وضمان النقل) ---
82
- async def restricted_bypass_worker():
83
- while True:
84
- message = await process_queue.get()
85
-
86
- if is_processed(message.chat.id, message.id):
87
- process_queue.task_done()
88
- continue
89
-
90
- file_path = None
91
  try:
92
- # إعطاء اسم فريد للملف لتجنب التداخل
93
- unique_file_name = f"{DOWNLOAD_DIR}/{message.chat.id}_{message.id}"
94
- print(f"⬇️ جاري سحب الملف (ID: {message.id})...")
95
-
96
- # محاولة التنزيل
97
- file_path = await message.download(file_name=unique_file_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- # فحص رقم 1: هل تم إنشاء الملف؟
100
- if not file_path or not os.path.exists(file_path):
101
- raise Exception("فشل تيليجرام في إنشاء الملف.")
102
 
103
- # فحص رقم 2 (المهم جداً لحل مشكلتك): هل الملف فارغ؟
104
- if os.path.getsize(file_path) == 0:
105
- os.remove(file_path) # حذف الملف التالف
106
- raise Exception("حجم الملف 0 بايت (سيتم الإعادة لاحقاً).")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
- print(f"⬆️ جاري الرفع إلى قناتك (حجم الملف سليم)...")
109
- caption = message.caption or ""
110
-
111
- if message.photo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption)
113
- elif message.video:
114
  await app.send_video(TARGET_CHAT, video=file_path, caption=caption, supports_streaming=True)
115
- elif message.document:
116
  await app.send_document(TARGET_CHAT, document=file_path, caption=caption)
117
- else:
118
  await app.send_audio(TARGET_CHAT, audio=file_path, caption=caption)
119
-
120
- # لن يتم التوثيق كـ "مكتمل" إلا إذا نجح التنزيل والرفع معاً 100%
121
- mark_processed(message.chat.id, message.id)
122
- print(f"✅ تم النقل بنجاح (ID: {message.id})!")
123
-
124
- await asyncio.sleep(12) # استراحة لتهدئة السيرفر
 
125
 
126
  except FloodWait as e:
127
- print(f"🛑 تيليجرام يطلب التوقف {e.value} ثانية! (الملف بأمان في الطابور)")
128
  await asyncio.sleep(e.value + 5)
129
- await process_queue.put(message) # إعادة المقطع للطابور لعدم فقدانه
130
-
131
  except Exception as e:
132
- print(f"⚠️ خطأ مؤقت: {e} | سيتم إعادة محاولة نقل (ID: {message.id}) بعد قليل.")
133
- await asyncio.sleep(5)
134
- await process_queue.put(message) # إعادة المقطع للطابور لحين نجاحه
135
-
136
- finally:
137
- # التنظيف الذكي: حذف الملف من السيرفر بعد رفعه أو فشله لتوفير المساحة
138
- if file_path and os.path.exists(file_path):
139
- try:
140
- os.remove(file_path)
141
- except:
142
- pass
143
- process_queue.task_done()
144
 
145
- # --- 7. التشغيل ---
146
  async def main():
147
  await app.start()
148
- print("🚀 البوت المدرع يعمل الآن! لن يفوتك أي مقطع بإذن الله.")
149
 
150
- asyncio.create_task(restricted_bypass_worker())
151
- asyncio.create_task(scan_old_history())
152
 
153
  await idle()
154
  await app.stop()
 
6
  from pyrogram import Client, filters, idle
7
  from pyrogram.errors import FloodWait
8
  from pyrogram.enums import ChatType
9
+ from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument, InputMediaAudio
10
 
11
+ # --- 1. إعداد مساحات التخزين وقاعدة البيانات المستدامة ---
12
+ DATA_DIR = "./data"
13
  DOWNLOAD_DIR = os.path.join(DATA_DIR, "downloads")
14
+ DB_PATH = os.path.join(DATA_DIR, 'bot_archiver.db')
15
  os.makedirs(DOWNLOAD_DIR, exist_ok=True)
16
 
17
  conn = sqlite3.connect(DB_PATH, check_same_thread=False)
18
  cursor = conn.cursor()
 
 
 
 
 
 
19
 
20
+ # جدول إدارة الطابور وحفظ الألبومات
21
+ cursor.execute('''
22
+ CREATE TABLE IF NOT EXISTS queue (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ chat_id INTEGER,
25
+ msg_id INTEGER,
26
+ media_group_id TEXT,
27
+ media_type TEXT,
28
+ caption TEXT,
29
+ file_path TEXT,
30
+ status TEXT DEFAULT 'DOWNLOADED',
31
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
32
+ UNIQUE(chat_id, msg_id)
33
+ )
34
+ ''')
35
+ conn.commit()
36
 
37
+ # --- 2. سيرفر الويب لمنع إغلاق منصة Hugging Face ---
38
  class RequestHandler(BaseHTTPRequestHandler):
39
  def do_GET(self):
40
  self.send_response(200)
41
  self.end_headers()
42
+ self.wfile.write(b"Dual-Engine Telegram Archiver Active!")
43
  def log_message(self, format, *args): pass
44
 
45
  Thread(target=lambda: HTTPServer(('0.0.0.0', 7860), RequestHandler).serve_forever(), daemon=True).start()
46
 
47
+ # --- 3. إعدادات الحساب ---
48
  API_ID = int(os.environ.get("API_ID", 0))
49
  API_HASH = os.environ.get("API_HASH", "")
50
  SESSION_STRING = os.environ.get("SESSION_STRING", "")
51
  TARGET_CHAT = int(os.environ.get("TARGET_CHAT", 0))
52
 
53
  app = Client(
54
+ "memory_session",
55
+ api_id=API_ID,
56
+ api_hash=API_HASH,
57
  session_string=SESSION_STRING,
58
+ in_memory=True,
59
+ max_concurrent_transmissions=3 # تسريع التحميل المتوازي
60
  )
61
 
62
+ # --- 4. دالة التنزيل الآمن مع إعادة المحاولة لكسر خطأ 0 بايت ---
63
+ async def download_media_safe(message):
64
+ cursor.execute("SELECT status FROM queue WHERE chat_id=? AND msg_id=?", (message.chat.id, message.id))
65
+ if cursor.fetchone():
66
+ return None # تم تنزيله سابقاً
67
 
68
+ unique_name = f"{DOWNLOAD_DIR}/{message.chat.id}_{message.id}"
69
+
70
+ for attempt in range(3): # 3 محاولات تنزيل عند حدوث خلل شبكي
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  try:
72
+ file_path = await message.download(file_name=unique_name)
73
+ if file_path and os.path.exists(file_path) and os.path.getsize(file_path) > 0:
74
+ # تحديد نوع الوسائط
75
+ m_type = "doc"
76
+ if message.photo: m_type = "photo"
77
+ elif message.video: m_type = "video"
78
+ elif message.audio: m_type = "audio"
79
+
80
+ group_id = str(message.media_group_id) if message.media_group_id else None
81
+ caption = message.caption or ""
82
+
83
+ cursor.execute(
84
+ "INSERT OR IGNORE INTO queue (chat_id, msg_id, media_group_id, media_type, caption, file_path) VALUES (?, ?, ?, ?, ?, ?)",
85
+ (message.chat.id, message.id, group_id, m_type, caption, file_path)
86
+ )
87
+ conn.commit()
88
+ print(f"⚡ [صائد لحظي] تم سحب وحفظ الملف: ID {message.id}")
89
+ return file_path
90
+ else:
91
+ if file_path and os.path.exists(file_path):
92
+ os.remove(file_path)
93
+ except Exception as e:
94
+ print(f"⚠️ محاولة تنزيل فاشلة ({attempt+1}/3) للرسالة {message.id}: {e}")
95
+ await asyncio.sleep(2)
96
 
97
+ return None
 
 
98
 
99
+ # --- 5. صائد الرسائل اللحظي (التقاط فوري قبل الحذف) ---
100
+ @app.on_message(filters.incoming & ~filters.me & (filters.channel | filters.group) & filters.media)
101
+ async def catch_incoming(client, message):
102
+ if message.chat.id != TARGET_CHAT:
103
+ await download_media_safe(message)
104
+
105
+ # --- 6. حاصد السجل القديم ---
106
+ async def scan_history_worker():
107
+ print("🔎 بدء مسح السجل القديم للمجموعات والقنوات...")
108
+ async for dialog in app.get_dialogs():
109
+ if dialog.chat.type in [ChatType.CHANNEL, ChatType.SUPERGROUP, ChatType.GROUP] and dialog.chat.id != TARGET_CHAT:
110
+ try:
111
+ async for message in app.get_chat_history(dialog.chat.id):
112
+ if message.media:
113
+ await download_media_safe(message)
114
+ except Exception as e:
115
+ print(f"تجاوز القناة {dialog.chat.title} بسبب: {e}")
116
+ await asyncio.sleep(5)
117
+ print("✅ اكتمل مسح كافة الأرشيفات القديمة.")
118
 
119
+ # --- 7. محرك الرفع الذكي وتجميع الألبومات (Uploader & Album Engine) ---
120
+ async def upload_worker():
121
+ while True:
122
+ try:
123
+ # البحث عن أقدم عنصر نزل ولم يرفع بعد
124
+ cursor.execute("SELECT id, chat_id, msg_id, media_group_id, media_type, caption, file_path FROM queue WHERE status='DOWNLOADED' ORDER BY id ASC LIMIT 1")
125
+ row = cursor.fetchone()
126
+
127
+ if not row:
128
+ await asyncio.sleep(5)
129
+ continue
130
+
131
+ q_id, chat_id, msg_id, group_id, m_type, caption, file_path = row
132
+
133
+ # التأكد من وجود الملف على القرص
134
+ if not os.path.exists(file_path):
135
+ cursor.execute("UPDATE queue SET status='FAILED' WHERE id=?", (q_id,))
136
+ conn.commit()
137
+ continue
138
+
139
+ # معالجة الألبومات (Media Groups)
140
+ if group_id:
141
+ await asyncio.sleep(4) # مهلة زمنية للتأكد من اكتمال تنزيل جميع عناصر الألبوم
142
+ cursor.execute(
143
+ "SELECT id, media_type, caption, file_path FROM queue WHERE chat_id=? AND media_group_id=? AND status='DOWNLOADED' ORDER BY msg_id ASC",
144
+ (chat_id, group_id)
145
+ )
146
+ group_rows = cursor.fetchall()
147
+
148
+ if group_rows:
149
+ media_list = []
150
+ ids_to_update = []
151
+ files_to_delete = []
152
+
153
+ for idx, g_item in enumerate(group_rows[:10]): # تيليجرام يدعم حتى 10 وسائط في الألبوم الواحد
154
+ g_id, g_type, g_cap, g_path = g_item
155
+ # وضع النص الوصفي على العنصر الأول فقط في الألبوم
156
+ item_caption = g_cap if idx == 0 else ""
157
+
158
+ if g_type == "photo":
159
+ media_list.append(InputMediaPhoto(media=g_path, caption=item_caption))
160
+ elif g_type == "video":
161
+ media_list.append(InputMediaVideo(media=g_path, caption=item_caption, supports_streaming=True))
162
+ elif g_type == "doc":
163
+ media_list.append(InputMediaDocument(media=g_path, caption=item_caption))
164
+ elif g_type == "audio":
165
+ media_list.append(InputMediaAudio(media=g_path, caption=item_caption))
166
+
167
+ ids_to_update.append(g_id)
168
+ files_to_delete.append(g_path)
169
+
170
+ if media_list:
171
+ print(f"📦 جاري رفع ألبوم يحتوي على {len(media_list)} عناصر...")
172
+ await app.send_media_group(TARGET_CHAT, media=media_list)
173
+
174
+ # تحديث الحالة وحذف الملفات بعد نجاح الرفع
175
+ for item_id in ids_to_update:
176
+ cursor.execute("UPDATE queue SET status='UPLOADED' WHERE id=?", (item_id,))
177
+ conn.commit()
178
+
179
+ for f_path in files_to_delete:
180
+ if os.path.exists(f_path): os.remove(f_path)
181
+
182
+ print("✅ تم رفع الألبوم بنجاح!")
183
+ await asyncio.sleep(15) # فاصل حماية بعد رفع الألبوم
184
+ continue
185
+
186
+ # معالجة الرسائل المنفردة
187
+ print(f"⬆️ جاري رفع ملف منفرد (ID: {msg_id})...")
188
+ if m_type == "photo":
189
  await app.send_photo(TARGET_CHAT, photo=file_path, caption=caption)
190
+ elif m_type == "video":
191
  await app.send_video(TARGET_CHAT, video=file_path, caption=caption, supports_streaming=True)
192
+ elif m_type == "doc":
193
  await app.send_document(TARGET_CHAT, document=file_path, caption=caption)
194
+ elif m_type == "audio":
195
  await app.send_audio(TARGET_CHAT, audio=file_path, caption=caption)
196
+
197
+ cursor.execute("UPDATE queue SET status='UPLOADED' WHERE id=?", (q_id,))
198
+ conn.commit()
199
+
200
+ if os.path.exists(file_path): os.remove(file_path)
201
+ print(f"✅ تم رفع الملف بنجاح (ID: {msg_id})!")
202
+ await asyncio.sleep(12)
203
 
204
  except FloodWait as e:
205
+ print(f"🛑 [FloodWait] طلب تيليجرام التوقف لـ {e.value} ثانية. سيتم الانتظار دون فقدان البيانات.")
206
  await asyncio.sleep(e.value + 5)
 
 
207
  except Exception as e:
208
+ print(f"❌ خطأ أثناء الرفع: {e}")
209
+ await asyncio.sleep(10)
 
 
 
 
 
 
 
 
 
 
210
 
211
+ # --- 8. نقطة الانطلاق ---
212
  async def main():
213
  await app.start()
214
+ print("🚀 تم تشغيل نظام الأرشيف المدرع (سحب فوري + رفع ألبومات)...")
215
 
216
+ asyncio.create_task(upload_worker())
217
+ asyncio.create_task(scan_history_worker())
218
 
219
  await idle()
220
  await app.stop()