pmrony commited on
Commit
9b8df11
·
verified ·
1 Parent(s): 9ab6b0d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -243
app.py CHANGED
@@ -18,18 +18,18 @@ API_HASH = "b18441a1ff607e10a989891a5462e627"
18
 
19
  SUPABASE_URL = "https://yctirvnryrzygoxbpvoy.supabase.co"
20
  SUPABASE_KEY = "sb_publishable_aBcD-atruskWwoCiLr0lWw_inT8GLoN"
21
- WEB_APP_URL = WEB_APP_URL = "https://rony90790.github.io/Forward-bot/index.html?v=2"
 
 
 
22
  BYSE_API_KEY = "133323knboif885fhgwxvf"
23
  ADMIN_IDS = [7307789267]
24
 
25
  app = Flask(__name__)
26
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
27
  admin_states = {}
28
-
29
- # টেম্পোরারিভাবে OTP সেভ রাখার জন্য একটি ডিকশনারি
30
  temp_otps = {}
31
 
32
- # Pyrogram Client Setup
33
  bot = Client(
34
  "file_unlocker_bot",
35
  api_id=API_ID,
@@ -37,7 +37,6 @@ bot = Client(
37
  bot_token=BOT_TOKEN
38
  )
39
 
40
- # Helper function to prevent blocking the event loop
41
  async def db_query(func):
42
  return await asyncio.to_thread(func)
43
 
@@ -46,42 +45,39 @@ async def db_query(func):
46
  def index():
47
  return "Bot and API are Running smoothly! 🚀"
48
 
 
 
 
 
 
 
49
  @app.route('/api/videos')
50
  def api_videos():
51
  try:
52
  res = supabase.table('videos').select('*').order('id', desc=True).execute()
53
- response = make_response(jsonify(res.data))
54
  except Exception as e:
55
- print(f"API Error: {e}")
56
- response = make_response(jsonify([]))
57
- response.headers['Access-Control-Allow-Origin'] = '*'
58
- return response
59
 
60
  @app.route('/api/send_code', methods=['POST', 'OPTIONS'])
61
  def api_send_code():
62
  if request.method == 'OPTIONS':
63
- res = make_response()
64
- res.headers['Access-Control-Allow-Origin'] = '*'
65
- res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
66
- return res
67
 
68
- data = request.json
69
  phone = data.get('phone')
70
  user_id = data.get('user_id')
71
 
72
  if not user_id:
73
- res = make_response(jsonify({"status": "error", "msg": "User ID missing"}))
74
- res.headers['Access-Control-Allow-Origin'] = '*'
75
- return res
 
76
 
77
- # ৫ ডিজিটের একটি র্যান্ডম OTP তৈরি করা হচ্ছে
78
  otp = str(random.randint(10000, 99999))
79
  hash_val = str(random.randint(1000000, 9999999))
80
-
81
- # OTP টি সেভ রাখা হচ্ছে
82
  temp_otps[hash_val] = otp
83
 
84
- # টেলিগ্রাম বট API-এর মাধ্যমে ইউজারের ইনবক্সে মেসেজ পাঠানো
85
  url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
86
  payload = {
87
  "chat_id": user_id,
@@ -90,32 +86,22 @@ def api_send_code():
90
  }
91
  requests.post(url, json=payload)
92
 
93
- res = make_response(jsonify({"status": "ok", "hash": hash_val}))
94
- res.headers['Access-Control-Allow-Origin'] = '*'
95
- return res
96
 
97
  @app.route('/api/verify_code', methods=['POST', 'OPTIONS'])
98
  def api_verify_code():
99
  if request.method == 'OPTIONS':
100
- res = make_response()
101
- res.headers['Access-Control-Allow-Origin'] = '*'
102
- res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
103
- return res
104
 
105
- data = request.json
106
  hash_val = data.get('hash')
107
  user_otp = data.get('otp')
108
 
109
- # ইউজারের দেওয়া OTP এর সাথে মেলানো হচ্ছে
110
  if temp_otps.get(hash_val) == user_otp:
111
- # ভেরিফিকেশন সফল হলে ডাটা ডিলিট করে দিন
112
  del temp_otps[hash_val]
113
- res = make_response(jsonify({"status": "ok"}))
114
  else:
115
- res = make_response(jsonify({"status": "error", "msg": "Invalid OTP!"}))
116
-
117
- res.headers['Access-Control-Allow-Origin'] = '*'
118
- return res
119
 
120
  # ================= TELEGRAM BOT COMMANDS =================
121
  @bot.on_message(filters.command("start"))
@@ -126,15 +112,15 @@ async def start(client, message):
126
  bot_link = f"https://t.me/{bot_me.username}"
127
  markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Videos Now", url=bot_link)]])
128
  await message.reply("🔥 **Watch Premium Viral Videos for FREE!**\n\n👉 Click the button below to watch:", reply_markup=markup)
129
- except Exception as e: print(e)
130
  return
131
 
132
  try:
133
  user_id = message.from_user.id
134
  first_name = message.from_user.first_name
135
-
136
  args = message.command
137
  referrer_id = None
 
138
  if len(args) > 1:
139
  try: referrer_id = int(args[1])
140
  except ValueError: pass
@@ -142,12 +128,7 @@ async def start(client, message):
142
  user_check = await db_query(lambda: supabase.table('referrals').select('*').eq('user_id', user_id).execute())
143
 
144
  if not user_check.data:
145
- await db_query(lambda: supabase.table('referrals').insert({
146
- 'user_id': user_id,
147
- 'referral_count': 0,
148
- 'referrer_id': referrer_id if referrer_id != user_id else None
149
- }).execute())
150
-
151
  if referrer_id and referrer_id != user_id:
152
  ref_data = await db_query(lambda: supabase.table('referrals').select('referral_count').eq('user_id', referrer_id).execute())
153
  if ref_data.data:
@@ -166,15 +147,9 @@ async def start(client, message):
166
  [InlineKeyboardButton("📢 Add to Group", url=f"https://t.me/{bot_me.username}?startgroup=true")]
167
  ])
168
 
169
- welcome_text = (
170
- f"Hello <b>{first_name}</b>! 👋\n\n"
171
- f"🎁 <b>Welcome to Video Unlocker Pro!</b>\n"
172
- f"Here you can watch premium leaked and viral videos completely for FREE.\n\n"
173
- f"👇 <b>Click the button below to Open App:</b>"
174
- )
175
  await message.reply(welcome_text, parse_mode=enums.ParseMode.HTML, reply_markup=markup)
176
- except Exception as e:
177
- print(f"Start error: {e}")
178
 
179
  @bot.on_message(filters.new_chat_members)
180
  async def bot_added_to_group(client, message):
@@ -190,8 +165,7 @@ async def bot_added_to_group(client, message):
190
  group_name = message.chat.title
191
  admin_msg = f"✅ <b>বট নতুন একটি গ্রুপে অ্যাড হয়েছে!</b>\n\n📌 <b>গ্রুপের নাম:</b> {group_name}\n🆔 <b>ID:</b> <code>{message.chat.id}</code>"
192
  for admin_id in ADMIN_IDS:
193
- try:
194
- await client.send_message(chat_id=admin_id, text=admin_msg, parse_mode=enums.ParseMode.HTML)
195
  except: pass
196
  except: pass
197
 
@@ -199,12 +173,11 @@ async def bot_added_to_group(client, message):
199
  async def set_blur_state(client, message):
200
  try:
201
  args = message.text.split()
202
-
203
  if len(args) > 1 and args[1].lower() in ['0', '0%', 'off', 'cancel']:
204
  if message.chat.id in admin_states:
205
  admin_states[message.chat.id].pop("blur_percent", None)
206
  admin_states[message.chat.id].pop("clear_percent", None)
207
- await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>\nএখন থেকে আপলোড করা ভিডিও আর ব্লার হবে না, আগের মতো শুধুমাত্র ওয়াটারমার্ক হবে।", parse_mode=enums.ParseMode.HTML)
208
  return
209
 
210
  match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', message.text, re.IGNORECASE)
@@ -216,33 +189,22 @@ async def set_blur_state(client, message):
216
  if message.chat.id in admin_states:
217
  admin_states[message.chat.id].pop("blur_percent", None)
218
  admin_states[message.chat.id].pop("clear_percent", None)
219
- await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>", parse_mode=enums.ParseMode.HTML)
220
  return
221
 
222
- if message.chat.id not in admin_states:
223
- admin_states[message.chat.id] = {}
224
-
225
  admin_states[message.chat.id]["blur_percent"] = percent
226
  admin_states[message.chat.id]["clear_percent"] = clear_percent
227
 
228
- clear_msg = f"এবং উপরের <b>{clear_percent}%</b> অংশ ক্লিয়ার থাকবে।" if clear_percent > 0 else "পুরো ছবি/ভিডিও ব্লার হবে।"
229
-
230
- reply_text = (
231
- f"✅ <b>ব্লার সেট করা হয়েছে: {percent}%</b>\n"
232
- f"📌 {clear_msg}\n\n"
233
- f"এখন থেকে আপলোড করা সব ভিডিও/ছবিতে স্বয়ংক্রিয়ভাবে এটি অ্যাপ্লাই হবে।\n\n"
234
- f"<i>(বি.দ্র: বন্ধ করতে <code>/blur 0</code> লিখে সেন্ড করুন।)</i>"
235
- )
236
- await message.reply(reply_text, parse_mode=enums.ParseMode.HTML)
237
  else:
238
- await message.reply("❌ <b>ভুল কমান্ড!</b>\nসঠিক নিয়ম: `/blur 60` অথবা `/blur 60 20`")
239
  except Exception as e: print(e)
240
 
241
  def upload_file_sync(upload_url, file_path, api_key):
242
  with open(file_path, 'rb') as f:
243
- payload = {'key': api_key}
244
- files = {'file': f}
245
- return requests.post(upload_url, data=payload, files=files, timeout=900).json()
246
 
247
  @bot.on_message((filters.video | filters.animation | filters.photo) & filters.private & filters.user(ADMIN_IDS))
248
  async def handle_media_upload(client, message):
@@ -267,17 +229,14 @@ async def handle_media_upload(client, message):
267
  file_name = f"thumb_{int(time.time())}.jpg"
268
  supabase.storage.from_('thumbnails').upload(file_name, file_bytes, {"content-type": "image/jpeg"})
269
  return supabase.storage.from_('thumbnails').get_public_url(file_name)
270
-
271
  direct_link = await asyncio.to_thread(upload_to_supabase)
272
  if os.path.exists(local_path): os.remove(local_path)
273
  await status.edit_text(f"✅ <b>থাম্বনেইল সফলভাবে সেভ হয়েছে!</b>\n\n<code>{direct_link}</code>", parse_mode=enums.ParseMode.HTML)
274
- except Exception as e:
275
- await status.edit_text(f"⚠️ আপলোড এরর: {e}")
276
  return
277
 
278
  raw_caption = message.caption or ""
279
  blur_match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', raw_caption, re.IGNORECASE)
280
-
281
  is_blur = False
282
  blur_percent = 0
283
  clear_percent = 0
@@ -297,59 +256,33 @@ async def handle_media_upload(client, message):
297
  if media_type == "video":
298
  duration = message.video.duration if message.video and message.video.duration else 0
299
  file_size = message.video.file_size if message.video and message.video.file_size else 0
300
-
301
- MAX_DURATION = 600
302
- MAX_SIZE = 150 * 1024 * 1024
303
-
304
- if duration > MAX_DURATION or file_size > MAX_SIZE:
305
  is_large_video = True
306
  is_blur = False
307
 
308
- if is_large_video:
309
- status_msg = await message.reply("⏳ <b>ভিডিওটি বড়!</b> সার্ভার ক্র্যাশ এড়াতে ব্লার স্কিপ করে সরাসরি byse.sx এ আপলোড করা হচ্ছে...")
310
- else:
311
- status_msg = await message.reply("⏳ মিডিয়া ডাউনলোড হচ্ছে...")
312
-
313
  bot_me = client.me if client.me else await client.get_me()
314
  bot_link = f"https://t.me/{bot_me.username}"
315
-
316
- original_file = None
317
- watermarked_file = None
318
- blurred_file = None
319
- final_file = None
320
- embed_link = None
321
 
322
  try:
323
  original_file = await message.download()
324
-
325
  if not original_file:
326
- await status_msg.edit_text("❌ মিডিয়া ফাইলটি ডাউনলোড করা সম্ভব হয়নি! (খুব বড় বা সার্ভার সমস্যা)")
327
  return
328
 
329
  final_file = original_file
330
 
331
  if media_type == "video" and not is_large_video:
332
- await status_msg.edit_text("⏳ ভিডিও ওয়াটারমার্ক করা হচ্ছে... (কম র‍্যাম ব্যবহার করে)")
333
  watermarked_file = f"{original_file}_wm.mp4"
334
-
335
- cmd = [
336
- "ffmpeg", "-y", "-i", original_file,
337
- "-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)'",
338
- "-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-crf", "28",
339
- "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k",
340
- "-movflags", "+faststart", watermarked_file
341
- ]
342
  process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
343
  await process.communicate()
344
- if process.returncode == 0 and os.path.exists(watermarked_file):
345
- final_file = watermarked_file
346
 
347
  if is_blur and not is_large_video:
348
- msg_txt = f"⏳ টেলিগ্রামের জন্য {blur_percent}% ব্লার তৈরি করা হচ্ছে..."
349
- if clear_percent > 0:
350
- msg_txt = f"⏳ {blur_percent}% ব্লার (উপরের {clear_percent}% ক্লিয়ার) তৈরি করা হচ্ছে..."
351
- await status_msg.edit_text(msg_txt)
352
-
353
  radius = max(2, min(20, int((blur_percent / 100.0) * 30)))
354
  ext = "jpg" if media_type == "photo" else "mp4"
355
  blurred_file = f"{original_file}_blurred.{ext}"
@@ -360,146 +293,74 @@ async def handle_media_upload(client, message):
360
  else:
361
  ff_filter = ["-vf", f"boxblur={radius}:1"]
362
 
363
- if media_type == "photo":
364
- cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + [blurred_file]
365
- elif media_type == "animation":
366
- cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-pix_fmt", "yuv420p", blurred_file]
367
- else:
368
- cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-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
-
373
- if process_blur.returncode == 0 and os.path.exists(blurred_file):
374
- final_file = blurred_file
375
 
376
  if media_type == "video":
377
  await status_msg.edit_text("⏳ byse.sx সার্ভারে ভিডিও আপলোড করা হচ্ছে...")
378
- api_endpoint = "https://api.byse.sx/upload/server"
379
- params = {'key': BYSE_API_KEY}
380
  loop = asyncio.get_event_loop()
381
- response = await loop.run_in_executor(None, lambda: requests.get(api_endpoint, params=params, timeout=30))
382
- result = response.json()
383
 
384
  if result.get('status') == 200:
385
- upload_url = result.get('result')
386
- upload_res = await loop.run_in_executor(None, upload_file_sync, upload_url, final_file, BYSE_API_KEY)
387
  if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
388
  file_code = upload_res['files'][0].get('filecode')
389
- file_status = upload_res['files'][0].get('status', '')
390
- if "not allowed" in str(file_status).lower():
391
- await status_msg.edit_text(f"❌ byse.sx ফাইল রিজেক্ট করেছে: <code>{file_status}</code>", parse_mode=enums.ParseMode.HTML)
392
- return
393
- if file_code:
394
- embed_link = f"https://bysesayeveum.com/e/{file_code}"
395
 
396
  if not embed_link:
397
  await status_msg.edit_text("❌ byse.sx আপলোড হয়েছে কিন্তু Embed Link পাওয়া যায়নি।")
398
  return
399
 
400
  if is_large_video:
401
- admin_cap = (
402
- f"✅ <b>সফল! (বড় ভিডিও)</b>\n\n"
403
- f"🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>\n\n"
404
- f"📌 <i>ভিডিওটি অনেক বড় হওয়ায় গ্রুপে ব্রডকাস্ট স্কিপ করা হয়েছে। আপনি চাইলে লিংকটি দিয়ে নিজেই Web App এ ভিডিও অ্যাড করতে পারবেন।</i>"
405
- )
406
- await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
407
  await status_msg.delete()
408
  return
409
 
410
  await status_msg.edit_text("⏳ গ্রুপে পাঠানোর প্রস্তুতি চলছে...")
411
 
412
- if media_type == "video":
413
- if is_blur:
414
- caption_text = (
415
- f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n"
416
- f"🎬 <b>Watch HD Video Here:</b>\n"
417
- f"👉 <b><a href='{embed_link}'>▶️ Click Here to Watch HD</a></b>\n\n"
418
- f"🤖 <b><a href='{bot_link}'>Open Bot for More Videos!</a></b>\n"
419
- f"👇 <i>Click the button below to open Bot!</i>"
420
- )
421
- else:
422
- caption_text = (
423
- f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n"
424
- f"🎬 <b>Watch Full Video Here:</b>\n"
425
- f"👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n"
426
- f"👇 <i>Click the button below to open Bot!</i>"
427
- )
428
- else:
429
- if clean_caption:
430
- caption_text = f"{clean_caption}\n\n👇 <i>Click the button below to open Bot!</i>"
431
- else:
432
- caption_text = (
433
- f"🔥 <b>New Premium Viral Content!</b> 🔞\n\n"
434
- f"🎬 <b>Watch HD Video Here:</b>\n"
435
- f"👉 <b><a href='{bot_link}'>▶️ Click Here to Watch</a></b>\n\n"
436
- f"👇 <i>Click the button below to open Bot!</i>"
437
- )
438
-
439
- group_markup = InlineKeyboardMarkup([
440
- [InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]
441
- ])
442
 
443
- admin_cap = f"✅ <b>সফল!</b> মিডিয়াটি এখন গ্রুপগুলোতে পাঠানো হচ্ছে...\n\n🔗 <b>Embed Link (আপনার জন্য):</b>\n<code>{embed_link or 'N/A'}</code>"
444
  thumb_path = None
445
 
446
  if media_type == "video":
447
  v_duration = message.video.duration if message.video else 0
448
  v_width = message.video.width if message.video else 0
449
  v_height = message.video.height if message.video else 0
450
-
451
  thumb_path = f"{original_file}_thumb.jpg"
452
- cmd_thumb = ["ffmpeg", "-y", "-i", final_file, "-vframes", "1", thumb_path]
453
- proc = await asyncio.create_subprocess_exec(*cmd_thumb, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
454
  await proc.communicate()
455
- if not os.path.exists(thumb_path):
456
- thumb_path = None
457
-
458
- if media_type == "photo":
459
- sent_to_admin = await client.send_photo(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
460
- tg_file_id = sent_to_admin.photo.file_id
461
- elif media_type == "animation":
462
- sent_to_admin = await client.send_animation(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
463
- tg_file_id = sent_to_admin.animation.file_id
464
- else:
465
- sent_to_admin = await client.send_video(
466
- message.chat.id,
467
- final_file,
468
- caption=admin_cap,
469
- parse_mode=enums.ParseMode.HTML,
470
- duration=v_duration,
471
- width=v_width,
472
- height=v_height,
473
- thumb=thumb_path
474
- )
475
- tg_file_id = sent_to_admin.video.file_id
476
 
477
  await status_msg.delete()
478
-
479
  groups_res = await db_query(lambda: supabase.table('groups').select('group_id').execute())
480
  group_ids = [g['group_id'] for g in groups_res.data]
481
  success_count, fail_count = 0, 0
482
 
483
  for gid in set(group_ids):
484
  try:
485
- if media_type == "photo":
486
- await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
487
- elif media_type == "animation":
488
- await client.send_animation(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
489
- else:
490
- await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
491
  success_count += 1
492
  await asyncio.sleep(1.5)
493
- except Exception:
494
- fail_count += 1
495
 
496
- await message.reply(f"📢 <b>ব্রডকাস্ট সম্পন্ন!</b>\n\n✅ সফল: {success_count} টি গ্রুপে\n❌ ব্যর্থ (রিমুভড): {fail_count} টি গ্রুপে", parse_mode=enums.ParseMode.HTML)
497
 
498
- except Exception as e:
499
- await message.reply(f"⚠️ এরর হয়েছে: {str(e)}")
500
  finally:
501
- thumb_file = f"{original_file}_thumb.jpg" if original_file else None
502
- for f in [original_file, watermarked_file, blurred_file, thumb_file]:
503
  if f and os.path.exists(f):
504
  try: os.remove(f)
505
  except: pass
@@ -510,8 +371,7 @@ async def bot_stats(client, message):
510
  users = await db_query(lambda: supabase.table('referrals').select('user_id', count='exact').execute())
511
  videos = await db_query(lambda: supabase.table('videos').select('*', count='exact').execute())
512
  groups = await db_query(lambda: supabase.table('groups').select('group_id', count='exact').execute())
513
- 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> টি"
514
- await message.reply(stat_msg, parse_mode=enums.ParseMode.HTML)
515
  except Exception as e: print(e)
516
 
517
  @bot.on_message(filters.command("broadcast") & filters.private & filters.user(ADMIN_IDS))
@@ -523,20 +383,16 @@ async def process_broadcast(client, message):
523
  text = message.text or message.caption
524
  if text == '/cancel':
525
  admin_states.pop(message.chat.id, None)
526
- await message.reply("❌ বাতিল করা হয়েছে।")
527
- return
528
 
529
  await message.reply("⏳ ব্রডকাস্ট শুরু হয়েছে...")
530
  admin_states.pop(message.chat.id, None)
531
 
532
  try:
533
- all_users = []
534
- start = 0
535
- step = 1000
536
  while True:
537
  res = await db_query(lambda: supabase.table('referrals').select('user_id').range(start, start + step - 1).execute())
538
- if not res.data:
539
- break
540
  all_users.extend(res.data)
541
  start += step
542
 
@@ -546,8 +402,7 @@ async def process_broadcast(client, message):
546
  await message.copy(chat_id=u['user_id'])
547
  success += 1
548
  await asyncio.sleep(0.15)
549
- except Exception:
550
- failed += 1
551
 
552
  await message.reply(f"✅ ব্রডকাস্ট সম্পন্ন!\nসফল: {success}\nব্যর্থ: {failed}")
553
  except Exception as e: print(e)
@@ -556,50 +411,35 @@ async def process_broadcast(client, message):
556
  async def add_png(client, message):
557
  try:
558
  parts = message.command
559
- needed_ref = 3
560
- duration = "random"
561
- if len(parts) == 4 and parts[1].isdigit():
562
- needed_ref = int(parts[1]); duration = parts[2]; thumbnail_url = parts[3]
563
- elif len(parts) == 3 and parts[1].isdigit():
564
- needed_ref = int(parts[1]); thumbnail_url = parts[2]
565
  elif len(parts) == 2: thumbnail_url = parts[1]
566
- else:
567
- await message.reply("❌ নিয়ম ভুল।")
568
- return
569
 
570
- packed_thumb = f"{thumbnail_url}||{duration}"
571
- admin_states[message.chat.id] = {"step": 1, "thumbnail_url": packed_thumb, "needed_ref": needed_ref}
572
  await message.reply("✅ এখন Video/Embed Link দিন।")
573
  except Exception as e: print(e)
574
 
575
  @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur"]))
576
  async def catch_admin_steps(client, message):
577
  state = admin_states.get(message.chat.id, {})
578
-
579
  if state.get("step") == 1:
580
  if not message.text: return
581
  video_url = message.text.strip()
582
-
583
  if video_url == "/cancel":
584
  admin_states.pop(message.chat.id, None)
585
- await message.reply("❌ বাতিল করা হয়েছে।")
586
- return
587
 
588
- thumb_url = state["thumbnail_url"]
589
- needed_ref = state["needed_ref"]
590
-
591
  try:
592
- await db_query(lambda: supabase.table('videos').insert({"video_url": video_url, "thumbnail_url": thumb_url, "needed_ref": needed_ref}).execute())
593
  await message.reply("🎉 ভিডিও সফলভাবে অ্যাড হয়েছে!")
594
  except Exception as e: print(e)
595
  finally: admin_states.pop(message.chat.id, None)
596
 
597
- elif state.get("step") == "broadcast":
598
- await process_broadcast(client, message)
599
 
600
- # ================= RUNNER =================
601
- def run_flask():
602
- app.run(host="0.0.0.0", port=7860)
603
 
604
  if __name__ == "__main__":
605
  threading.Thread(target=run_flask, daemon=True).start()
 
18
 
19
  SUPABASE_URL = "https://yctirvnryrzygoxbpvoy.supabase.co"
20
  SUPABASE_KEY = "sb_publishable_aBcD-atruskWwoCiLr0lWw_inT8GLoN"
21
+
22
+ # ক্যাশ রিমুভ করার জন্য লিংকের শেষে ?v=6 যুক্ত করা হয়েছে
23
+ WEB_APP_URL = "https://rony90790.github.io/Forward-bot/index.html?v=6"
24
+
25
  BYSE_API_KEY = "133323knboif885fhgwxvf"
26
  ADMIN_IDS = [7307789267]
27
 
28
  app = Flask(__name__)
29
  supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
30
  admin_states = {}
 
 
31
  temp_otps = {}
32
 
 
33
  bot = Client(
34
  "file_unlocker_bot",
35
  api_id=API_ID,
 
37
  bot_token=BOT_TOKEN
38
  )
39
 
 
40
  async def db_query(func):
41
  return await asyncio.to_thread(func)
42
 
 
45
  def index():
46
  return "Bot and API are Running smoothly! 🚀"
47
 
48
+ def add_cors_headers(response):
49
+ response.headers['Access-Control-Allow-Origin'] = '*'
50
+ response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
51
+ response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
52
+ return response
53
+
54
  @app.route('/api/videos')
55
  def api_videos():
56
  try:
57
  res = supabase.table('videos').select('*').order('id', desc=True).execute()
58
+ return add_cors_headers(make_response(jsonify(res.data)))
59
  except Exception as e:
60
+ return add_cors_headers(make_response(jsonify([])))
 
 
 
61
 
62
  @app.route('/api/send_code', methods=['POST', 'OPTIONS'])
63
  def api_send_code():
64
  if request.method == 'OPTIONS':
65
+ return add_cors_headers(make_response())
 
 
 
66
 
67
+ data = request.json or {}
68
  phone = data.get('phone')
69
  user_id = data.get('user_id')
70
 
71
  if not user_id:
72
+ return add_cors_headers(make_response(jsonify({"status": "error", "msg": "User ID missing! Please try again inside Telegram."})))
73
+
74
+ if str(user_id) == '123456':
75
+ return add_cors_headers(make_response(jsonify({"status": "error", "msg": "Please Open WebApp inside Telegram Bot!"})))
76
 
 
77
  otp = str(random.randint(10000, 99999))
78
  hash_val = str(random.randint(1000000, 9999999))
 
 
79
  temp_otps[hash_val] = otp
80
 
 
81
  url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
82
  payload = {
83
  "chat_id": user_id,
 
86
  }
87
  requests.post(url, json=payload)
88
 
89
+ return add_cors_headers(make_response(jsonify({"status": "ok", "hash": hash_val})))
 
 
90
 
91
  @app.route('/api/verify_code', methods=['POST', 'OPTIONS'])
92
  def api_verify_code():
93
  if request.method == 'OPTIONS':
94
+ return add_cors_headers(make_response())
 
 
 
95
 
96
+ data = request.json or {}
97
  hash_val = data.get('hash')
98
  user_otp = data.get('otp')
99
 
 
100
  if temp_otps.get(hash_val) == user_otp:
 
101
  del temp_otps[hash_val]
102
+ return add_cors_headers(make_response(jsonify({"status": "ok"})))
103
  else:
104
+ return add_cors_headers(make_response(jsonify({"status": "error", "msg": "Invalid OTP!"})))
 
 
 
105
 
106
  # ================= TELEGRAM BOT COMMANDS =================
107
  @bot.on_message(filters.command("start"))
 
112
  bot_link = f"https://t.me/{bot_me.username}"
113
  markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Videos Now", url=bot_link)]])
114
  await message.reply("🔥 **Watch Premium Viral Videos for FREE!**\n\n👉 Click the button below to watch:", reply_markup=markup)
115
+ except Exception: pass
116
  return
117
 
118
  try:
119
  user_id = message.from_user.id
120
  first_name = message.from_user.first_name
 
121
  args = message.command
122
  referrer_id = None
123
+
124
  if len(args) > 1:
125
  try: referrer_id = int(args[1])
126
  except ValueError: pass
 
128
  user_check = await db_query(lambda: supabase.table('referrals').select('*').eq('user_id', user_id).execute())
129
 
130
  if not user_check.data:
131
+ await db_query(lambda: supabase.table('referrals').insert({'user_id': user_id, 'referral_count': 0, 'referrer_id': referrer_id if referrer_id != user_id else None}).execute())
 
 
 
 
 
132
  if referrer_id and referrer_id != user_id:
133
  ref_data = await db_query(lambda: supabase.table('referrals').select('referral_count').eq('user_id', referrer_id).execute())
134
  if ref_data.data:
 
147
  [InlineKeyboardButton("📢 Add to Group", url=f"https://t.me/{bot_me.username}?startgroup=true")]
148
  ])
149
 
150
+ welcome_text = (f"Hello <b>{first_name}</b>! 👋\n\n🎁 <b>Welcome to Video Unlocker Pro!</b>\nHere you can watch premium leaked and viral videos completely for FREE.\n\n👇 <b>Click the button below to Open App:</b>")
 
 
 
 
 
151
  await message.reply(welcome_text, parse_mode=enums.ParseMode.HTML, reply_markup=markup)
152
+ except Exception as e: print(f"Start error: {e}")
 
153
 
154
  @bot.on_message(filters.new_chat_members)
155
  async def bot_added_to_group(client, message):
 
165
  group_name = message.chat.title
166
  admin_msg = f"✅ <b>বট নতুন একটি গ্রুপে অ্যাড হয়েছে!</b>\n\n📌 <b>গ্রুপের নাম:</b> {group_name}\n🆔 <b>ID:</b> <code>{message.chat.id}</code>"
167
  for admin_id in ADMIN_IDS:
168
+ try: await client.send_message(chat_id=admin_id, text=admin_msg, parse_mode=enums.ParseMode.HTML)
 
169
  except: pass
170
  except: pass
171
 
 
173
  async def set_blur_state(client, message):
174
  try:
175
  args = message.text.split()
 
176
  if len(args) > 1 and args[1].lower() in ['0', '0%', 'off', 'cancel']:
177
  if message.chat.id in admin_states:
178
  admin_states[message.chat.id].pop("blur_percent", None)
179
  admin_states[message.chat.id].pop("clear_percent", None)
180
+ await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>")
181
  return
182
 
183
  match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', message.text, re.IGNORECASE)
 
189
  if message.chat.id in admin_states:
190
  admin_states[message.chat.id].pop("blur_percent", None)
191
  admin_states[message.chat.id].pop("clear_percent", None)
192
+ await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>")
193
  return
194
 
195
+ if message.chat.id not in admin_states: admin_states[message.chat.id] = {}
 
 
196
  admin_states[message.chat.id]["blur_percent"] = percent
197
  admin_states[message.chat.id]["clear_percent"] = clear_percent
198
 
199
+ clear_msg = f"এবং উপরের <b>{clear_percent}%</b> অংশ ক্লিয়ার থাকবে।" if clear_percent > 0 else "পুরো অংশ ব্লার হবে।"
200
+ await message.reply(f"✅ <b>ব্লার সেট করা হয়েছে: {percent}%</b>\n📌 {clear_msg}", parse_mode=enums.ParseMode.HTML)
 
 
 
 
 
 
 
201
  else:
202
+ await message.reply("❌ <b>ভুল কমান্ড!</b> নিয়ম: `/blur 60` অথবা `/blur 60 20`")
203
  except Exception as e: print(e)
204
 
205
  def upload_file_sync(upload_url, file_path, api_key):
206
  with open(file_path, 'rb') as f:
207
+ return requests.post(upload_url, data={'key': api_key}, files={'file': f}, timeout=900).json()
 
 
208
 
209
  @bot.on_message((filters.video | filters.animation | filters.photo) & filters.private & filters.user(ADMIN_IDS))
210
  async def handle_media_upload(client, message):
 
229
  file_name = f"thumb_{int(time.time())}.jpg"
230
  supabase.storage.from_('thumbnails').upload(file_name, file_bytes, {"content-type": "image/jpeg"})
231
  return supabase.storage.from_('thumbnails').get_public_url(file_name)
 
232
  direct_link = await asyncio.to_thread(upload_to_supabase)
233
  if os.path.exists(local_path): os.remove(local_path)
234
  await status.edit_text(f"✅ <b>থাম্বনেইল সফলভাবে সেভ হয়েছে!</b>\n\n<code>{direct_link}</code>", parse_mode=enums.ParseMode.HTML)
235
+ except Exception as e: await status.edit_text(f"⚠️ আপলোড এরর: {e}")
 
236
  return
237
 
238
  raw_caption = message.caption or ""
239
  blur_match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', raw_caption, re.IGNORECASE)
 
240
  is_blur = False
241
  blur_percent = 0
242
  clear_percent = 0
 
256
  if media_type == "video":
257
  duration = message.video.duration if message.video and message.video.duration else 0
258
  file_size = message.video.file_size if message.video and message.video.file_size else 0
259
+ if duration > 600 or file_size > 150 * 1024 * 1024:
 
 
 
 
260
  is_large_video = True
261
  is_blur = False
262
 
263
+ status_msg = await message.reply("⏳ <b>ভিডিওটি বড়!</b> ব্লার স্কিপ হচ্ছে..." if is_large_video else "⏳ মিডিয়া ডাউনলোড হচ্ছে...")
 
 
 
 
264
  bot_me = client.me if client.me else await client.get_me()
265
  bot_link = f"https://t.me/{bot_me.username}"
266
+ original_file = watermarked_file = blurred_file = final_file = embed_link = None
 
 
 
 
 
267
 
268
  try:
269
  original_file = await message.download()
 
270
  if not original_file:
271
+ await status_msg.edit_text("❌ মিডিয়া ফাইলটি ডাউনলোড করা সম্ভব হয়নি!")
272
  return
273
 
274
  final_file = original_file
275
 
276
  if media_type == "video" and not is_large_video:
277
+ await status_msg.edit_text("⏳ ভিডিও ওয়াটারমার্ক করা হচ্ছে...")
278
  watermarked_file = f"{original_file}_wm.mp4"
279
+ cmd = ["ffmpeg", "-y", "-i", original_file, "-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)'", "-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-crf", "28", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", watermarked_file]
 
 
 
 
 
 
 
280
  process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
281
  await process.communicate()
282
+ if process.returncode == 0 and os.path.exists(watermarked_file): final_file = watermarked_file
 
283
 
284
  if is_blur and not is_large_video:
285
+ await status_msg.edit_text(f"⏳ {blur_percent}% ব্লার তৈরি করা হচ্ছে...")
 
 
 
 
286
  radius = max(2, min(20, int((blur_percent / 100.0) * 30)))
287
  ext = "jpg" if media_type == "photo" else "mp4"
288
  blurred_file = f"{original_file}_blurred.{ext}"
 
293
  else:
294
  ff_filter = ["-vf", f"boxblur={radius}:1"]
295
 
296
+ if media_type == "photo": cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + [blurred_file]
297
+ elif media_type == "animation": cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-pix_fmt", "yuv420p", blurred_file]
298
+ else: cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + ["-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-crf", "28", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", blurred_file]
 
 
 
299
 
300
  process_blur = await asyncio.create_subprocess_exec(*cmd_blur, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
301
  await process_blur.communicate()
302
+ if process_blur.returncode == 0 and os.path.exists(blurred_file): final_file = blurred_file
 
 
303
 
304
  if media_type == "video":
305
  await status_msg.edit_text("⏳ byse.sx সার্ভারে ভিডিও আপলোড করা হচ্ছে...")
 
 
306
  loop = asyncio.get_event_loop()
307
+ result = await loop.run_in_executor(None, lambda: requests.get("https://api.byse.sx/upload/server", params={'key': BYSE_API_KEY}, timeout=30).json())
 
308
 
309
  if result.get('status') == 200:
310
+ upload_res = await loop.run_in_executor(None, upload_file_sync, result.get('result'), final_file, BYSE_API_KEY)
 
311
  if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
312
  file_code = upload_res['files'][0].get('filecode')
313
+ if file_code: embed_link = f"https://bysesayeveum.com/e/{file_code}"
 
 
 
 
 
314
 
315
  if not embed_link:
316
  await status_msg.edit_text("❌ byse.sx আপলোড হয়েছে কিন্তু Embed Link পাওয়া যায়নি।")
317
  return
318
 
319
  if is_large_video:
320
+ await client.send_video(message.chat.id, message.video.file_id, caption=f"✅ <b>সফল! (বড় ভিডিও)</b>\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>", parse_mode=enums.ParseMode.HTML)
 
 
 
 
 
321
  await status_msg.delete()
322
  return
323
 
324
  await status_msg.edit_text("⏳ গ্রুপে পাঠানোর প্রস্তুতি চলছে...")
325
 
326
+ caption_text = (f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n🎬 <b>Watch HD Video Here:</b>\n👉 <b><a href='{embed_link or bot_link}'>▶️ Click Here to Watch</a></b>\n\n👇 <i>Click the button below to open Bot!</i>")
327
+ group_markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
+ admin_cap = f"✅ <b>সফল!</b> মিডিয়াটি এখন গ্রুপগুলোতে পাঠানো হচ্ছে...\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>"
330
  thumb_path = None
331
 
332
  if media_type == "video":
333
  v_duration = message.video.duration if message.video else 0
334
  v_width = message.video.width if message.video else 0
335
  v_height = message.video.height if message.video else 0
 
336
  thumb_path = f"{original_file}_thumb.jpg"
337
+ proc = await asyncio.create_subprocess_exec(*["ffmpeg", "-y", "-i", final_file, "-vframes", "1", thumb_path], stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
 
338
  await proc.communicate()
339
+ if not os.path.exists(thumb_path): thumb_path = None
340
+
341
+ if media_type == "photo": tg_file_id = (await client.send_photo(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)).photo.file_id
342
+ elif media_type == "animation": tg_file_id = (await client.send_animation(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML)).animation.file_id
343
+ else: tg_file_id = (await client.send_video(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML, duration=v_duration, width=v_width, height=v_height, thumb=thumb_path)).video.file_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
 
345
  await status_msg.delete()
 
346
  groups_res = await db_query(lambda: supabase.table('groups').select('group_id').execute())
347
  group_ids = [g['group_id'] for g in groups_res.data]
348
  success_count, fail_count = 0, 0
349
 
350
  for gid in set(group_ids):
351
  try:
352
+ if media_type == "photo": await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
353
+ elif media_type == "animation": await client.send_animation(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
354
+ else: await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
 
 
 
355
  success_count += 1
356
  await asyncio.sleep(1.5)
357
+ except Exception: fail_count += 1
 
358
 
359
+ await message.reply(f"📢 <b>ব্রডকাস্ট সম্পন্ন!</b>\n\n✅ সফল: {success_count} টি গ্রুপে\n❌ ব্যর্থ: {fail_count} টি গ্রুপে", parse_mode=enums.ParseMode.HTML)
360
 
361
+ except Exception as e: await message.reply(f"⚠️ এরর হয়েছে: {str(e)}")
 
362
  finally:
363
+ for f in [original_file, watermarked_file, blurred_file, f"{original_file}_thumb.jpg" if original_file else None]:
 
364
  if f and os.path.exists(f):
365
  try: os.remove(f)
366
  except: pass
 
371
  users = await db_query(lambda: supabase.table('referrals').select('user_id', count='exact').execute())
372
  videos = await db_query(lambda: supabase.table('videos').select('*', count='exact').execute())
373
  groups = await db_query(lambda: supabase.table('groups').select('group_id', count='exact').execute())
374
+ await message.reply(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> টি", parse_mode=enums.ParseMode.HTML)
 
375
  except Exception as e: print(e)
376
 
377
  @bot.on_message(filters.command("broadcast") & filters.private & filters.user(ADMIN_IDS))
 
383
  text = message.text or message.caption
384
  if text == '/cancel':
385
  admin_states.pop(message.chat.id, None)
386
+ return await message.reply("❌ বাতিল করা হয়েছে।")
 
387
 
388
  await message.reply("⏳ ব্রডকাস্ট শুরু হয়েছে...")
389
  admin_states.pop(message.chat.id, None)
390
 
391
  try:
392
+ all_users, start, step = [], 0, 1000
 
 
393
  while True:
394
  res = await db_query(lambda: supabase.table('referrals').select('user_id').range(start, start + step - 1).execute())
395
+ if not res.data: break
 
396
  all_users.extend(res.data)
397
  start += step
398
 
 
402
  await message.copy(chat_id=u['user_id'])
403
  success += 1
404
  await asyncio.sleep(0.15)
405
+ except Exception: failed += 1
 
406
 
407
  await message.reply(f"✅ ব্রডকাস্ট সম্পন্ন!\nসফল: {success}\nব্যর্থ: {failed}")
408
  except Exception as e: print(e)
 
411
  async def add_png(client, message):
412
  try:
413
  parts = message.command
414
+ needed_ref, duration = 3, "random"
415
+ if len(parts) == 4 and parts[1].isdigit(): needed_ref, duration, thumbnail_url = int(parts[1]), parts[2], parts[3]
416
+ elif len(parts) == 3 and parts[1].isdigit(): needed_ref, thumbnail_url = int(parts[1]), parts[2]
 
 
 
417
  elif len(parts) == 2: thumbnail_url = parts[1]
418
+ else: return await message.reply("❌ নিয়ম ভুল।")
 
 
419
 
420
+ admin_states[message.chat.id] = {"step": 1, "thumbnail_url": f"{thumbnail_url}||{duration}", "needed_ref": needed_ref}
 
421
  await message.reply("✅ এখন Video/Embed Link দিন।")
422
  except Exception as e: print(e)
423
 
424
  @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur"]))
425
  async def catch_admin_steps(client, message):
426
  state = admin_states.get(message.chat.id, {})
 
427
  if state.get("step") == 1:
428
  if not message.text: return
429
  video_url = message.text.strip()
 
430
  if video_url == "/cancel":
431
  admin_states.pop(message.chat.id, None)
432
+ return await message.reply("❌ বাতিল করা হয়েছে।")
 
433
 
 
 
 
434
  try:
435
+ await db_query(lambda: supabase.table('videos').insert({"video_url": video_url, "thumbnail_url": state["thumbnail_url"], "needed_ref": state["needed_ref"]}).execute())
436
  await message.reply("🎉 ভিডিও সফলভাবে অ্যাড হয়েছে!")
437
  except Exception as e: print(e)
438
  finally: admin_states.pop(message.chat.id, None)
439
 
440
+ elif state.get("step") == "broadcast": await process_broadcast(client, message)
 
441
 
442
+ def run_flask(): app.run(host="0.0.0.0", port=7860)
 
 
443
 
444
  if __name__ == "__main__":
445
  threading.Thread(target=run_flask, daemon=True).start()