Opera8 commited on
Commit
b1ac9c6
·
verified ·
1 Parent(s): 5e18f83

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +106 -220
main.py CHANGED
@@ -21,7 +21,7 @@ app = Flask(__name__)
21
 
22
  @app.route('/')
23
  def home():
24
- return "🚀 ربات انتقال به گیت‌هاب (پشتیبانی از وب، تلگرام و اینستاگرام) روشن است!"
25
 
26
  def run_flask():
27
  app.run(host="0.0.0.0", port=7860, threaded=True)
@@ -37,7 +37,7 @@ TG_API_ID = 6
37
  TG_API_HASH = "eb06d4abfb49dc3eeb1aeb98ae0f581e"
38
 
39
  if bot_token == "توکن_روبیکا" or GITHUB_TOKEN == "توکن_گیت‌هاب":
40
- print("❌ توکن روبیکا یا گیت‌هاب تنظیم نشده است! لطفاً تنظیمات بالای کد را بررسی کنید.")
41
  exit()
42
 
43
  bot = BotClient(bot_token)
@@ -45,19 +45,13 @@ BOT_GUID = None
45
  git_lock = asyncio.Lock()
46
 
47
  # ==============================================================================
48
- # 🟢 تنظیمات کلاینت تلگرام (Pyrogram)
49
  # ==============================================================================
50
  tg_client = None
51
  is_tg_started = False
52
 
53
  if TG_BOT_TOKEN and TG_BOT_TOKEN != "توکن_تلگرام":
54
- tg_client = Client(
55
- "tg_bot_session",
56
- api_id=TG_API_ID,
57
- api_hash=TG_API_HASH,
58
- bot_token=TG_BOT_TOKEN,
59
- in_memory=True
60
- )
61
 
62
  async def ensure_tg_started():
63
  global is_tg_started
@@ -66,34 +60,39 @@ async def ensure_tg_started():
66
  is_tg_started = True
67
 
68
  # ==============================================================================
69
- # 🟢 توابع دانلود (اینستاگرام، تلگرام و وب)
70
  # ==============================================================================
71
 
72
- # 1. تابع دانلود از اینستاگرام (و سایت‌های مشابه) با yt-dlp
73
  def sync_download_ytdlp(url, temp_dir):
74
- """این تابع همگام است و در یک ترد مجزا اجرا می‌شود"""
75
  ydl_opts = {
76
  'outtmpl': f'{temp_dir}/{uuid.uuid4().hex[:6]}_%(title)s.%(ext)s',
77
  'quiet': True,
78
  'no_warnings': True,
79
- 'format': 'best'
 
80
  }
81
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
82
  info = ydl.extract_info(url, download=True)
83
  file_path = ydl.prepare_filename(info)
 
 
 
 
 
 
 
 
84
  filename = os.path.basename(file_path)
85
- # پاکسازی نام فایل از کاراکترهای عجیب برای گیت‌هاب
86
- clean_filename = re.sub(r'[\\/*?:"<>|]', "", filename)
87
- if clean_filename != filename:
88
- clean_path = os.path.join(temp_dir, clean_filename)
89
- os.rename(file_path, clean_path)
90
- return clean_path, clean_filename
91
- return file_path, filename
92
 
93
- async def download_from_instagram(url, chat_id, message_id, client):
94
  status_msg_id = None
95
  try:
96
- status_msg = await client.send_message(chat_id, "⏳ در حال استخراج و دانلود رسانه از اینستاگرام...", reply_to_message_id=message_id)
97
  status_msg_id = getattr(status_msg, 'message_id', None)
98
  if not status_msg_id and isinstance(status_msg, dict):
99
  status_msg_id = status_msg.get('message_update', {}).get('message_id') or status_msg.get('message_id')
@@ -102,286 +101,173 @@ async def download_from_instagram(url, chat_id, message_id, client):
102
  try:
103
  temp_dir = "/app/downloads"
104
  os.makedirs(temp_dir, exist_ok=True)
105
-
106
- # اجرای کتابخانه همگام در یک ترد مجزا برای جلوگیری از قفل شدن ربات
107
  file_path, filename = await asyncio.to_thread(sync_download_ytdlp, url, temp_dir)
108
  return file_path, filename, status_msg_id
109
  except Exception as e:
110
- error_msg = f"❌ خطا در دانلود از اینستاگرام (احتمالاً پیج پرایوت است یا پست حذف شده):\n{str(e)[:150]}"
111
  if status_msg_id:
112
  try: await client.edit_message_text(chat_id, status_msg_id, error_msg)
113
  except: pass
114
  return None, None, status_msg_id
115
 
116
- # 2. تابع دانلود از تلگرام
117
  async def download_from_telegram(url, chat_id, message_id, client):
118
  status_msg_id = None
119
  try:
120
- status_msg = await client.send_message(chat_id, "⏳ در حال برقراری ارتباط با سرورهای تلگرام...", reply_to_message_id=message_id)
121
  status_msg_id = getattr(status_msg, 'message_id', None)
122
- if not status_msg_id and isinstance(status_msg, dict):
123
- status_msg_id = status_msg.get('message_update', {}).get('message_id') or status_msg.get('message_id')
124
  except: pass
125
 
126
  try:
127
- if not tg_client:
128
- raise Exception("ربات تلگرام متصل نیست. لطفاً توکن تلگرام را در سورس کد قرار دهید.")
129
-
130
  await ensure_tg_started()
131
-
132
  match = re.search(r"t\.me/([^/]+)/(\d+)", url)
133
- if not match:
134
- raise Exception("لینک تلگرام نامعتبر است.")
135
-
136
- channel_username = match.group(1)
137
- msg_id = int(match.group(2))
138
 
139
- if status_msg_id:
140
- try: await client.edit_message_text(chat_id, status_msg_id, "⏳ در حال دریافت اطلاعات فایل از کانال عمومی تلگرام...")
141
- except: pass
142
-
143
- msg = await tg_client.get_messages(channel_username, msg_id)
144
- if not msg or not msg.media:
145
- raise Exception("فایلی در این پیام یافت نشد یا پیام از کانال حذف شده است.")
146
 
147
- filename = None
148
- if getattr(msg, 'document', None): filename = msg.document.file_name
149
- elif getattr(msg, 'video', None): filename = msg.video.file_name
150
- elif getattr(msg, 'audio', None): filename = msg.audio.file_name
151
- elif getattr(msg, 'photo', None): filename = f"tg_photo_{uuid.uuid4().hex[:6]}.jpg" # پشتیبانی از عکس تلگرام
152
 
153
- if not filename: filename = f"tg_media_{uuid.uuid4().hex[:6]}.dat"
 
 
 
154
 
155
  temp_dir = "/app/downloads"
156
  os.makedirs(temp_dir, exist_ok=True)
157
  file_path = os.path.join(temp_dir, f"{uuid.uuid4().hex[:6]}_{filename}")
158
 
159
  if status_msg_id:
160
- try: await client.edit_message_text(chat_id, status_msg_id, "📥 در حال دانلود فایل از تلگرام به سرور واسط...")
161
  except: pass
162
-
163
  await tg_client.download_media(msg, file_name=file_path)
164
  return file_path, filename, status_msg_id
165
-
166
  except Exception as e:
167
- error_msg = f"❌ خطای تلگرام:\n{str(e)[:150]}"
168
- if status_msg_id:
169
- try: await client.edit_message_text(chat_id, status_msg_id, error_msg)
170
- except: pass
171
  return None, None, status_msg_id
172
 
173
- # 3. تابع دانلود وب (لینک مستقیم)
174
  async def download_large_file(url, chat_id, message_id, client):
175
- if "dropbox.com" in url:
176
- if "dl=0" in url: url = url.replace("dl=0", "dl=1")
177
- elif "?dl=" not in url and "&dl=" not in url: url += "&dl=1" if "?" in url else "?dl=1"
178
-
179
  temp_dir = "/app/downloads"
180
  os.makedirs(temp_dir, exist_ok=True)
181
-
182
- parsed_url = urllib.parse.urlparse(url)
183
- filename = os.path.basename(parsed_url.path)
184
- if not filename or '.' not in filename: filename = f"file_{uuid.uuid4().hex[:8]}.dat"
185
-
186
  file_path = os.path.join(temp_dir, f"{uuid.uuid4().hex[:6]}_{filename}")
187
  status_msg_id = None
188
-
189
  try:
190
- status_msg = await client.send_message(chat_id, "⏳ در حال دانلود فایل وب در سرور واسط...", reply_to_message_id=message_id)
191
  status_msg_id = getattr(status_msg, 'message_id', None)
192
- if not status_msg_id and isinstance(status_msg, dict):
193
- status_msg_id = status_msg.get('message_update', {}).get('message_id') or status_msg.get('message_id')
194
- except Exception: pass
195
-
196
- try:
197
- headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
198
- async with aiohttp.ClientSession(headers=headers) as session:
199
- async with session.get(url, timeout=0) as response:
200
- if response.status != 200:
201
- return None, None, status_msg_id
202
  with open(file_path, 'wb') as f:
203
- async for chunk in response.content.iter_chunked(2 * 1024 * 1024):
204
- if chunk: f.write(chunk)
205
  return file_path, filename, status_msg_id
206
- except Exception: return None, None, status_msg_id
207
 
208
  # ==============================================================================
209
- # 🟢 تابع خرد کردن فایل
210
  # ==============================================================================
211
- def split_file_for_winrar(file_path, dest_dir, base_filename):
212
- chunk_size = 95 * 1024 * 1024
213
  part_num = 1
214
  os.makedirs(dest_dir, exist_ok=True)
215
  with open(file_path, 'rb') as f:
216
  while True:
217
  chunk = f.read(chunk_size)
218
  if not chunk: break
219
- part_name = f"{base_filename}.{part_num:03d}"
220
- part_path = os.path.join(dest_dir, part_name)
221
- with open(part_path, 'wb') as p:
222
- p.write(chunk)
223
  part_num += 1
224
 
225
- # ==============================================================================
226
- # 🟢 پردازش و آپلود در گیت‌هاب
227
- # ==============================================================================
228
  async def process_and_commit_to_github(client, chat_id, message_id, url):
229
-
230
- url_lower = url.lower()
231
-
232
- # سیستم تشخیص هوشمند لینک
233
- if "instagram.com/" in url_lower:
234
- file_path, filename, status_msg_id = await download_from_instagram(url, chat_id, message_id, client)
235
- elif "t.me/" in url_lower:
 
 
236
  file_path, filename, status_msg_id = await download_from_telegram(url, chat_id, message_id, client)
237
  else:
238
  file_path, filename, status_msg_id = await download_large_file(url, chat_id, message_id, client)
239
 
240
- if not file_path or not os.path.exists(file_path):
241
- if status_msg_id:
242
- try: await client.edit_message_text(chat_id, status_msg_id, "❌ پردازش فایل ناموفق بود. لطفاً لینک را بررسی کنید.")
243
- except: pass
244
- return
245
 
246
  try:
247
- if status_msg_id:
248
- try: await client.edit_message_text(chat_id, status_msg_id, "✅ رسانه با موفقیت دریافت شد.\n✂️ در حال پارت‌بندی و انتقال به گیت‌هاب...")
249
- except: pass
250
-
251
  async with git_lock:
252
- current_timestamp = int(time.time())
253
- branch_name = f"DL-{current_timestamp}-{uuid.uuid4().hex[:6]}"
254
  repo_url = f"https://oauth2:{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git"
255
  work_dir = "/app/git_workspace"
256
 
257
  if os.path.exists(work_dir): shutil.rmtree(work_dir)
258
-
259
- subprocess.run(["git", "clone", repo_url, work_dir], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
260
- subprocess.run(["git", "config", "user.email", "bot@aialpha.local"], cwd=work_dir)
261
- subprocess.run(["git", "config", "user.name", "AI Alpha Bot"], cwd=work_dir)
262
- subprocess.run(["git", "checkout", "--orphan", branch_name], cwd=work_dir, check=True)
263
- subprocess.run(["git", "rm", "-rf", "."], cwd=work_dir, stderr=subprocess.DEVNULL)
264
 
265
- file_folder = os.path.join(work_dir, filename)
266
- split_file_for_winrar(file_path, file_folder, filename)
267
 
268
  subprocess.run(["git", "add", "."], cwd=work_dir, check=True)
269
- subprocess.run(["git", "commit", "-m", f"Add parts for {filename}"], cwd=work_dir, check=True)
270
- subprocess.run(["git", "push", "origin", branch_name], cwd=work_dir, check=True)
271
-
272
- zip_download_link = f"https://github.com/{GITHUB_REPO}/archive/refs/heads/{branch_name}.zip"
273
 
274
- success_text = (
275
- f"🎉 فایل شما با موفقیت آماده شد!\n\n"
276
- f"📁 **نام فایل:** {filename}\n"
277
- f"🔗 **لینک دانلود مستقیم (بدون مصرف ترافیک بین‌الملل):**\n{zip_download_link}\n\n"
278
- f"💡 **راهنما:** فایل بالا را دانلود کرده و استخراج (Extract) کنید.\n\n"
279
- f"⚠️ **توجه مهم:**\n"
280
- f"جهت حفظ کیفیت خدمات و آزادسازی فضای سرور، این فایل **دقیقاً ۲ ساعت دیگر به صورت خودکار حذف خواهد شد.** ⏳"
281
- )
282
-
283
- if status_msg_id:
284
- try: await client.edit_message_text(chat_id, status_msg_id, success_text)
285
- except: await client.send_message(chat_id, success_text)
286
- else:
287
- await client.send_message(chat_id, success_text)
288
-
289
  except Exception as e:
290
- error_txt = f"❌ خطای گیت‌هاب:\n{str(e)[:150]}"
291
- if status_msg_id:
292
- try: await client.edit_message_text(chat_id, status_msg_id, error_txt)
293
- except: await client.send_message(chat_id, error_txt)
294
  finally:
295
- try: os.remove(file_path)
296
- except: pass
297
- if os.path.exists("/app/git_workspace"):
298
- try: shutil.rmtree("/app/git_workspace")
299
- except: pass
300
 
301
  # ==============================================================================
302
- # 🟢 سیستم هوشمند حذف خودکار فایل‌های منقضی شده
303
  # ==============================================================================
304
- async def cleanup_expired_branches():
305
  while True:
306
  try:
307
  api_url = f"https://api.github.com/repos/{GITHUB_REPO}/git/refs/heads"
308
- headers = {"Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json"}
309
-
310
- async with aiohttp.ClientSession() as session:
311
- async with session.get(api_url, headers=headers) as resp:
312
- if resp.status == 200:
313
- refs = await resp.json()
314
- current_time = int(time.time())
315
-
316
- for ref in refs:
317
- ref_name = ref.get("ref", "")
318
- if "refs/heads/DL-" in ref_name:
319
- branch_name = ref_name.split("/")[-1]
320
- parts = branch_name.split("-")
321
-
322
- if len(parts) >= 3 and parts[1].isdigit():
323
- creation_time = int(parts[1])
324
- if current_time - creation_time > 7200:
325
- delete_url = f"https://api.github.com/repos/{GITHUB_REPO}/git/refs/heads/{branch_name}"
326
- await session.delete(delete_url, headers=headers)
327
- print(f"🗑 Branch {branch_name} automatically deleted (Expired).")
328
- except Exception as e:
329
- pass
330
  await asyncio.sleep(1800)
331
 
332
- def run_cleanup_system():
333
- loop = asyncio.new_event_loop()
334
- asyncio.set_event_loop(loop)
335
- loop.run_until_complete(cleanup_expired_branches())
336
-
337
  # ==============================================================================
338
- # 🟢 هندلر پیام‌های روبیکا
339
  # ==============================================================================
340
  @bot.on_update(filters.private)
341
- async def main_handler(client, update):
342
  global BOT_GUID
343
- try:
344
- if not BOT_GUID:
345
- me = await client.get_me()
346
- if me and hasattr(me, 'user'): BOT_GUID = getattr(me.user, 'user_guid', None)
347
-
348
- msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
349
- author_id = getattr(update, 'author_guid', None)
350
- if not author_id and msg_obj: author_id = msg_obj.get('author_object_guid') if isinstance(msg_obj, dict) else getattr(msg_obj, 'author_object_guid', None)
351
- if BOT_GUID and author_id == BOT_GUID: return
352
-
353
- chat_id = getattr(update, 'object_guid', None) or getattr(update, 'author_guid', None) or getattr(update, "chat_id", None)
354
- if not chat_id: return
355
- msg_id = getattr(update, "message_id", None)
356
-
357
- user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
358
- user_text_str = str(user_text).strip()
359
- if not user_text_str: return
360
-
361
- if user_text_str.lower() in ["/start", "سلام"]:
362
- welcome_msg = (
363
- "👋 سلام!\n\n"
364
- "من یک ربات انتقال‌دهنده هوشمند هستم. لینک‌های زیر را پشتیبانی می‌کنم:\n\n"
365
- "🌐 **لینک دانلود مستقیم** از سایت‌ها\n"
366
- "✈️ **لینک پست یا عکس کانال تلگرام** (مثال: `t.me/channel/123`)\n"
367
- "📸 **لینک ریلز یا پست اینستاگرام**\n\n"
368
- "لینک خود را بفرستید تا با نهایت سرعت آن را روی سرور گیت‌هاب (با اینترنت ملی) برایتان آماده کنم.\n\n"
369
- "⏳ **فایل‌ها جهت مدیریت سرور، دقیقاً دو ساعت بعد از ساخته‌شدن به صورت کاملاً خودکار پاک خواهند شد.**"
370
- )
371
- await client.send_message(chat_id, welcome_msg)
372
- return
373
-
374
- if user_text_str.lower().startswith("http"):
375
- asyncio.create_task(process_and_commit_to_github(client, chat_id, msg_id, user_text_str))
376
-
377
- except Exception: traceback.print_exc()
378
 
379
  if __name__ == "__main__":
380
- if not TG_BOT_TOKEN or TG_BOT_TOKEN == "توکن_تلگرام":
381
- print("⚠️ هشدار: توکن ربات تلگرام تنظیم نشده است! دانلود از تلگرام کار نخواهد کرد.")
382
-
383
  threading.Thread(target=run_flask, daemon=True).start()
384
- threading.Thread(target=run_cleanup_system, daemon=True).start()
385
-
386
- print("🚀 ربات کلاستر گیت‌هاب (پشتیبانی اینستاگرام، تلگرام و وب) روشن شد...")
387
  bot.run()
 
21
 
22
  @app.route('/')
23
  def home():
24
+ return "🚀 ربات هوشمند انتقال فایل (YouTube, Instagram, Telegram, Web) روشن است!"
25
 
26
  def run_flask():
27
  app.run(host="0.0.0.0", port=7860, threaded=True)
 
37
  TG_API_HASH = "eb06d4abfb49dc3eeb1aeb98ae0f581e"
38
 
39
  if bot_token == "توکن_روبیکا" or GITHUB_TOKEN == "توکن_گیت‌هاب":
40
+ print("❌ تنظیمات ناقص است! توکن روبیکا یا گیت‌هاب را وارد کنید.")
41
  exit()
42
 
43
  bot = BotClient(bot_token)
 
45
  git_lock = asyncio.Lock()
46
 
47
  # ==============================================================================
48
+ # 🟢 کلاینت تلگرام
49
  # ==============================================================================
50
  tg_client = None
51
  is_tg_started = False
52
 
53
  if TG_BOT_TOKEN and TG_BOT_TOKEN != "توکن_تلگرام":
54
+ tg_client = Client("tg_bot_session", api_id=TG_API_ID, api_hash=TG_API_HASH, bot_token=TG_BOT_TOKEN, in_memory=True)
 
 
 
 
 
 
55
 
56
  async def ensure_tg_started():
57
  global is_tg_started
 
60
  is_tg_started = True
61
 
62
  # ==============================================================================
63
+ # 🟢 توابع دانلود حرفه‌ای
64
  # ==============================================================================
65
 
66
+ # 1. دانلود از یوتیوب، اینستاگرام و سایر شبکه‌های اجتماعی
67
  def sync_download_ytdlp(url, temp_dir):
 
68
  ydl_opts = {
69
  'outtmpl': f'{temp_dir}/{uuid.uuid4().hex[:6]}_%(title)s.%(ext)s',
70
  'quiet': True,
71
  'no_warnings': True,
72
+ 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best', # تلاش برای دریافت بهترین کیفیت MP4
73
+ 'merge_output_format': 'mp4',
74
  }
75
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
76
  info = ydl.extract_info(url, download=True)
77
  file_path = ydl.prepare_filename(info)
78
+ # اگر فایل mkv بود یا فرمت دیگری، yt-dlp ممکن است پسوند را تغییر دهد
79
+ if not os.path.exists(file_path):
80
+ base_path = os.path.splitext(file_path)[0]
81
+ for ext in ['.mp4', '.mkv', '.webm', '.flv']:
82
+ if os.path.exists(base_path + ext):
83
+ file_path = base_path + ext
84
+ break
85
+
86
  filename = os.path.basename(file_path)
87
+ clean_filename = re.sub(r'[\\/*?:"<>|]', "", filename).replace(" ", "_")
88
+ clean_path = os.path.join(temp_dir, clean_filename)
89
+ os.rename(file_path, clean_path)
90
+ return clean_path, clean_filename
 
 
 
91
 
92
+ async def download_social_media(url, chat_id, message_id, client, platform_name):
93
  status_msg_id = None
94
  try:
95
+ status_msg = await client.send_message(chat_id, f"⏳ در حال پردازش لینک {platform_name} و دانلود با بالاترین کیفیت...", reply_to_message_id=message_id)
96
  status_msg_id = getattr(status_msg, 'message_id', None)
97
  if not status_msg_id and isinstance(status_msg, dict):
98
  status_msg_id = status_msg.get('message_update', {}).get('message_id') or status_msg.get('message_id')
 
101
  try:
102
  temp_dir = "/app/downloads"
103
  os.makedirs(temp_dir, exist_ok=True)
 
 
104
  file_path, filename = await asyncio.to_thread(sync_download_ytdlp, url, temp_dir)
105
  return file_path, filename, status_msg_id
106
  except Exception as e:
107
+ error_msg = f"❌ خطا در دانلود از {platform_name}:\nاحتمالاً محتوا حذف شده یا محدودیت جغرافیایی دارد."
108
  if status_msg_id:
109
  try: await client.edit_message_text(chat_id, status_msg_id, error_msg)
110
  except: pass
111
  return None, None, status_msg_id
112
 
113
+ # 2. دانلود از تلگرام (کانال‌های عمومی)
114
  async def download_from_telegram(url, chat_id, message_id, client):
115
  status_msg_id = None
116
  try:
117
+ status_msg = await client.send_message(chat_id, "⏳ در حال اتصال به تلگرام...", reply_to_message_id=message_id)
118
  status_msg_id = getattr(status_msg, 'message_id', None)
 
 
119
  except: pass
120
 
121
  try:
122
+ if not tg_client: raise Exception("توکن تلگرام تنظیم نشده است.")
 
 
123
  await ensure_tg_started()
 
124
  match = re.search(r"t\.me/([^/]+)/(\d+)", url)
125
+ if not match: raise Exception("لینک نامعتبر.")
 
 
 
 
126
 
127
+ channel, m_id = match.group(1), int(match.group(2))
128
+ msg = await tg_client.get_messages(channel, m_id)
 
 
 
 
 
129
 
130
+ if not msg or not msg.media: raise Exception("فایلی یافت نشد.")
 
 
 
 
131
 
132
+ filename = (getattr(msg.document, 'file_name', None) or
133
+ getattr(msg.video, 'file_name', None) or
134
+ getattr(msg.audio, 'file_name', None) or
135
+ f"photo_{uuid.uuid4().hex[:5]}.jpg")
136
 
137
  temp_dir = "/app/downloads"
138
  os.makedirs(temp_dir, exist_ok=True)
139
  file_path = os.path.join(temp_dir, f"{uuid.uuid4().hex[:6]}_{filename}")
140
 
141
  if status_msg_id:
142
+ try: await client.edit_message_text(chat_id, status_msg_id, "📥 در حال دریافت فایل از تلگرام...")
143
  except: pass
144
+
145
  await tg_client.download_media(msg, file_name=file_path)
146
  return file_path, filename, status_msg_id
 
147
  except Exception as e:
148
+ if status_msg_id: await client.edit_message_text(chat_id, status_msg_id, f"❌ خطا: {str(e)[:100]}")
 
 
 
149
  return None, None, status_msg_id
150
 
151
+ # 3. دانلود فایل وب (لینک مستقیم)
152
  async def download_large_file(url, chat_id, message_id, client):
 
 
 
 
153
  temp_dir = "/app/downloads"
154
  os.makedirs(temp_dir, exist_ok=True)
155
+ filename = os.path.basename(urllib.parse.urlparse(url).path) or f"file_{uuid.uuid4().hex[:6]}.dat"
 
 
 
 
156
  file_path = os.path.join(temp_dir, f"{uuid.uuid4().hex[:6]}_{filename}")
157
  status_msg_id = None
 
158
  try:
159
+ status_msg = await client.send_message(chat_id, "⏳ در حال دانلود فایل مستقیم...", reply_to_message_id=message_id)
160
  status_msg_id = getattr(status_msg, 'message_id', None)
161
+ async with aiohttp.ClientSession() as session:
162
+ async with session.get(url, timeout=0) as resp:
163
+ if resp.status != 200: return None, None, status_msg_id
 
 
 
 
 
 
 
164
  with open(file_path, 'wb') as f:
165
+ async for chunk in resp.content.iter_chunked(2*1024*1024): f.write(chunk)
 
166
  return file_path, filename, status_msg_id
167
+ except: return None, None, status_msg_id
168
 
169
  # ==============================================================================
170
+ # 🟢 عملیات گیت‌هاب و پارت‌بندی
171
  # ==============================================================================
172
+ def split_file(file_path, dest_dir, base_filename):
173
+ chunk_size = 94 * 1024 * 1024
174
  part_num = 1
175
  os.makedirs(dest_dir, exist_ok=True)
176
  with open(file_path, 'rb') as f:
177
  while True:
178
  chunk = f.read(chunk_size)
179
  if not chunk: break
180
+ with open(os.path.join(dest_dir, f"{base_filename}.{part_num:03d}"), 'wb') as p: p.write(chunk)
 
 
 
181
  part_num += 1
182
 
 
 
 
183
  async def process_and_commit_to_github(client, chat_id, message_id, url):
184
+ url_l = url.lower()
185
+ # تشخیص پلتفرم
186
+ if any(x in url_l for x in ["youtube.com", "youtu.be"]):
187
+ file_path, filename, status_msg_id = await download_social_media(url, chat_id, message_id, client, "یوتیوب")
188
+ elif "instagram.com" in url_l:
189
+ file_path, filename, status_msg_id = await download_social_media(url, chat_id, message_id, client, "اینستاگرام")
190
+ elif any(x in url_l for x in ["tiktok.com", "twitter.com", "x.com"]):
191
+ file_path, filename, status_msg_id = await download_social_media(url, chat_id, message_id, client, "شبکه‌های اجتماعی")
192
+ elif "t.me/" in url_l:
193
  file_path, filename, status_msg_id = await download_from_telegram(url, chat_id, message_id, client)
194
  else:
195
  file_path, filename, status_msg_id = await download_large_file(url, chat_id, message_id, client)
196
 
197
+ if not file_path or not os.path.exists(file_path): return
 
 
 
 
198
 
199
  try:
 
 
 
 
200
  async with git_lock:
201
+ ts = int(time.time())
202
+ branch = f"DL-{ts}-{uuid.uuid4().hex[:5]}"
203
  repo_url = f"https://oauth2:{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git"
204
  work_dir = "/app/git_workspace"
205
 
206
  if os.path.exists(work_dir): shutil.rmtree(work_dir)
207
+ subprocess.run(["git", "clone", repo_url, work_dir], check=True, capture_output=True)
208
+ subprocess.run(["git", "config", "user.email", "bot@ai.com"], cwd=work_dir)
209
+ subprocess.run(["git", "config", "user.name", "Bot"], cwd=work_dir)
210
+ subprocess.run(["git", "checkout", "--orphan", branch], cwd=work_dir, check=True)
211
+ subprocess.run(["git", "rm", "-rf", "."], cwd=work_dir, capture_output=True)
 
212
 
213
+ split_file(file_path, os.path.join(work_dir, filename), filename)
 
214
 
215
  subprocess.run(["git", "add", "."], cwd=work_dir, check=True)
216
+ subprocess.run(["git", "commit", "-m", "add file"], cwd=work_dir, check=True)
217
+ subprocess.run(["git", "push", "origin", branch], cwd=work_dir, check=True)
 
 
218
 
219
+ link = f"https://github.com/{GITHUB_REPO}/archive/refs/heads/{branch}.zip"
220
+ msg = (f"🎉 **فایل شما آماده دانلود است!**\n\n📁 نام: {filename}\n🔗 لینک دانلود (اینترنت ملی):\n{link}\n\n"
221
+ f"⏳ این لینک **۲ ساعت دیگر** به طور خودکار حذف می‌شود.")
222
+ await client.edit_message_text(chat_id, status_msg_id, msg)
 
 
 
 
 
 
 
 
 
 
 
223
  except Exception as e:
224
+ if status_msg_id: await client.send_message(chat_id, f"❌ خطای گیت: {str(e)[:100]}")
 
 
 
225
  finally:
226
+ if file_path and os.path.exists(file_path): os.remove(file_path)
 
 
 
 
227
 
228
  # ==============================================================================
229
+ # 🟢 سیستم حذف خودکار
230
  # ==============================================================================
231
+ async def cleanup_loop():
232
  while True:
233
  try:
234
  api_url = f"https://api.github.com/repos/{GITHUB_REPO}/git/refs/heads"
235
+ headers = {"Authorization": f"token {GITHUB_TOKEN}"}
236
+ async with aiohttp.ClientSession() as sess:
237
+ async with sess.get(api_url, headers=headers) as r:
238
+ if r.status == 200:
239
+ for ref in await r.json():
240
+ name = ref["ref"].split("/")[-1]
241
+ if name.startswith("DL-"):
242
+ try:
243
+ creation_ts = int(name.split("-")[1])
244
+ if int(time.time()) - creation_ts > 7200:
245
+ await sess.delete(f"{api_url}/{name}", headers=headers)
246
+ print(f"Deleted {name}")
247
+ except: pass
248
+ except: pass
 
 
 
 
 
 
 
 
249
  await asyncio.sleep(1800)
250
 
 
 
 
 
 
251
  # ==============================================================================
252
+ # 🟢 شروع به کار
253
  # ==============================================================================
254
  @bot.on_update(filters.private)
255
+ async def handler(client, update):
256
  global BOT_GUID
257
+ if not BOT_GUID:
258
+ me = await client.get_me()
259
+ BOT_GUID = me.user.user_guid
260
+
261
+ msg = getattr(update, "message", update)
262
+ if not msg or msg.get('author_object_guid') == BOT_GUID: return
263
+
264
+ text = msg.get("text", "").strip()
265
+ if text.lower().startswith("http"):
266
+ asyncio.create_task(process_and_commit_to_github(client, msg.get("author_object_guid"), msg.get("message_id"), text))
267
+ elif text == "/start":
268
+ await client.send_message(msg.get("author_object_guid"), "🚀 لینک یوتیوب، اینستاگرام، تلگرام یا لینک مستقیم بفرست تا برات آماده کنم!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
  if __name__ == "__main__":
 
 
 
271
  threading.Thread(target=run_flask, daemon=True).start()
272
+ threading.Thread(target=lambda: asyncio.run(cleanup_loop()), daemon=True).start()
 
 
273
  bot.run()