|
|
import os |
|
|
import humanize |
|
|
import logging |
|
|
import re |
|
|
import shutil |
|
|
import uuid |
|
|
import time |
|
|
import subprocess |
|
|
import json |
|
|
from datetime import datetime |
|
|
import telebot |
|
|
from telebot.types import InputFile |
|
|
from pymegatools import Megatools |
|
|
from pymegatools.pymegatools import MegaError |
|
|
|
|
|
|
|
|
API_ID = int(os.environ.get("API_ID", 20687211)) |
|
|
API_HASH = os.environ.get("API_HASH", "4523f58b045175baaeaf1ba29733f31c") |
|
|
BOT_TOKEN = os.environ.get("BOT_TOKEN", "8318388017:AAGfxwJhAUiFB3xMQ5Sid4rgF0nJHsVUqsw") |
|
|
BOT_OWNER_ID = int(os.environ.get("BOT_OWNER_ID", 7014665654)) |
|
|
|
|
|
|
|
|
|
|
|
DOWNLOAD_DIR = "/data/downloads" |
|
|
|
|
|
|
|
|
logging.basicConfig( |
|
|
level=logging.INFO, |
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", |
|
|
handlers=[logging.StreamHandler()] |
|
|
) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
try: |
|
|
bot = telebot.TeleBot(BOT_TOKEN, parse_mode='Markdown') |
|
|
mega = Megatools() |
|
|
logger.info("Bot initialized with pyTelegramBotAPI") |
|
|
except Exception as e: |
|
|
logger.critical(f"Failed to initialize bot or Megatools: {e}") |
|
|
exit(1) |
|
|
|
|
|
|
|
|
|
|
|
def progress_bar(percentage, bar_length=20): |
|
|
"""Generates a text-based progress bar.""" |
|
|
filled = int(bar_length * percentage / 100) |
|
|
empty = bar_length - filled |
|
|
return "โ" * filled + "โ" * empty |
|
|
|
|
|
def edit_message(message, text): |
|
|
"""Safely edits a message.""" |
|
|
try: |
|
|
bot.edit_message_text(text, message.chat.id, message.message_id) |
|
|
except Exception as e: |
|
|
if "message not modified" not in str(e).lower(): |
|
|
logger.error(f"Error editing message: {str(e)}") |
|
|
|
|
|
def delete_message(message, delay=5): |
|
|
"""Safely deletes a message after a delay.""" |
|
|
try: |
|
|
if delay > 0: |
|
|
time.sleep(delay) |
|
|
bot.delete_message(message.chat.id, message.message_id) |
|
|
except Exception as e: |
|
|
logger.error(f"Error deleting message: {str(e)}") |
|
|
|
|
|
def clean_directory(directory): |
|
|
"""Safely and recursively removes a directory.""" |
|
|
try: |
|
|
if os.path.exists(directory): |
|
|
shutil.rmtree(directory) |
|
|
logger.info(f"๐ชฅ Successfully cleaned directory: {directory}") |
|
|
return True |
|
|
else: |
|
|
logger.warning(f"โ ๏ธ Directory does not exist, cannot clean: {directory}") |
|
|
return False |
|
|
except Exception as e: |
|
|
logger.error(f"โ Error cleaning directory {directory}: {repr(e)}") |
|
|
return False |
|
|
|
|
|
def get_video_duration(file_path): |
|
|
"""Tries to get video duration using ffprobe (synchronous).""" |
|
|
try: |
|
|
command = [ |
|
|
"ffprobe", "-v", "quiet", "-print_format", "json", |
|
|
"-show_format", file_path |
|
|
] |
|
|
|
|
|
result = subprocess.run( |
|
|
command, |
|
|
stdout=subprocess.PIPE, |
|
|
stderr=subprocess.PIPE, |
|
|
text=True |
|
|
) |
|
|
|
|
|
if result.returncode == 0: |
|
|
data = json.loads(result.stdout) |
|
|
return int(float(data['format']['duration'])) |
|
|
else: |
|
|
logger.warning(f"ffprobe failed (duration): {result.stderr}") |
|
|
return 0 |
|
|
except FileNotFoundError: |
|
|
logger.warning("ffprobe not found. Cannot get video duration. (Did you install ffmpeg?)") |
|
|
return 0 |
|
|
except Exception as e: |
|
|
logger.error(f"Error in get_video_duration: {e}") |
|
|
return 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def upload_file(chat_id, status_msg, file_path, filename, file_index, total_files): |
|
|
""" |
|
|
Handles the upload process for a single file (synchronous). |
|
|
""" |
|
|
try: |
|
|
size_bytes = os.path.getsize(file_path) |
|
|
size_mb = round(size_bytes / (1024 * 1024), 2) |
|
|
ext = os.path.splitext(filename)[1].lower() or ".file" |
|
|
|
|
|
caption = ( |
|
|
f"๐ **File:** `{filename}`\n" |
|
|
f"๐ฆ **Size:** {size_mb} MB\n" |
|
|
f"๐ **Extension:** `{ext}`" |
|
|
) |
|
|
|
|
|
edit_message(status_msg, f"๐ค **Uploading file {file_index}/{total_files}:**\n`{filename}`\n(Upload progress not available with telebot)") |
|
|
|
|
|
|
|
|
is_video = ext in [".mp4", ".mov", ".mkv", ".avi"] |
|
|
|
|
|
duration = 0 |
|
|
if is_video: |
|
|
duration = get_video_duration(file_path) |
|
|
|
|
|
|
|
|
with open(file_path, 'rb') as f: |
|
|
if is_video: |
|
|
bot.send_video( |
|
|
chat_id, |
|
|
f, |
|
|
caption=caption, |
|
|
duration=duration, |
|
|
supports_streaming=True, |
|
|
has_spoiler=True |
|
|
) |
|
|
else: |
|
|
bot.send_document( |
|
|
chat_id, |
|
|
f, |
|
|
caption=caption, |
|
|
has_spoiler=True |
|
|
) |
|
|
|
|
|
logger.info(f"Uploaded {filename}.") |
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Failed to upload file {file_path}: {e}", exc_info=True) |
|
|
bot.send_message(chat_id, f"โ ๏ธ Failed to upload file: `{filename}`. Error: {e}") |
|
|
return False |
|
|
|
|
|
finally: |
|
|
|
|
|
if os.path.exists(file_path): |
|
|
os.remove(file_path) |
|
|
|
|
|
@bot.message_handler(regexp="https://mega\.nz/") |
|
|
def handle_mega(message): |
|
|
message_text = message.text |
|
|
chat_id = message.chat.id |
|
|
user_id = message.from_user.id |
|
|
|
|
|
status_msg = bot.send_message(chat_id, "๐ **Processing your link...**") |
|
|
|
|
|
match = re.search(r"(https://mega\.nz/[^\s)\]]+)", message_text) |
|
|
|
|
|
if not match: |
|
|
edit_message(status_msg, "โ **Error:** Could not find a valid mega.nz link.") |
|
|
delete_message(status_msg, delay=10) |
|
|
return |
|
|
|
|
|
url = match.group(1).strip() |
|
|
|
|
|
|
|
|
job_id = str(uuid.uuid4()) |
|
|
download_dir = os.path.join(DOWNLOAD_DIR, f"user_{user_id}", job_id) |
|
|
os.makedirs(download_dir, exist_ok=True) |
|
|
|
|
|
try: |
|
|
is_folder = "/folder/" in url |
|
|
|
|
|
if is_folder: |
|
|
|
|
|
logger.info(f"User {user_id} sent folder link: {url}") |
|
|
edit_message(status_msg, "๐ **Folder link detected.**\nโฌ๏ธ Downloading all files... (This may take a while)") |
|
|
|
|
|
try: |
|
|
|
|
|
mega.download(url, path=download_dir) |
|
|
logger.info(f"Folder downloaded to: {download_dir}") |
|
|
except Exception as e: |
|
|
logger.error(f"Folder download failed for {url}: {e}", exc_info=True) |
|
|
raise Exception(f"Folder download failed: {e}") |
|
|
|
|
|
edit_message(status_msg, "โ
**Download complete!**\nPreparing to upload...") |
|
|
|
|
|
files_to_upload = [] |
|
|
for root, dirs, files in os.walk(download_dir): |
|
|
for file in files: |
|
|
files_to_upload.append((os.path.join(root, file), file)) |
|
|
|
|
|
if not files_to_upload: |
|
|
edit_message(status_msg, "โน๏ธ The folder appears to be empty.") |
|
|
delete_message(status_msg, delay=10) |
|
|
return |
|
|
|
|
|
edit_message(status_msg, f"Found {len(files_to_upload)} files. Starting upload...") |
|
|
|
|
|
uploaded_count = 0 |
|
|
for i, (file_path, file_name) in enumerate(files_to_upload): |
|
|
if upload_file(chat_id, status_msg, file_path, file_name, i+1, len(files_to_upload)): |
|
|
uploaded_count += 1 |
|
|
|
|
|
edit_message(status_msg, f"โ
**Upload complete!**\nSuccessfully sent {uploaded_count} file(s).") |
|
|
|
|
|
else: |
|
|
|
|
|
logger.info(f"User {user_id} sent file link: {url}") |
|
|
edit_message(status_msg, "โณ **Fetching file info...**") |
|
|
|
|
|
try: |
|
|
filename = mega.filename(url) |
|
|
except MegaError as e: |
|
|
logger.error(f"MegaError (filename): {e}", exc_info=True) |
|
|
edit_message(status_msg, f"โ **Error:** Link seems invalid or expired.\n`{e}`") |
|
|
return |
|
|
|
|
|
if not filename: |
|
|
edit_message(status_msg, "โ **Error:** Could not fetch filename.") |
|
|
return |
|
|
|
|
|
file_path = os.path.join(download_dir, filename) |
|
|
edit_message(status_msg, f"โฌ๏ธ **Downloading:**\n`{filename}`") |
|
|
|
|
|
try: |
|
|
mega.download(url, path=file_path) |
|
|
logger.info(f"File downloaded to: {file_path}") |
|
|
except Exception as e: |
|
|
logger.error(f"File download failed for {url}: {e}", exc_info=True) |
|
|
raise Exception(f"File download failed: {e}") |
|
|
|
|
|
upload_file(chat_id, status_msg, file_path, filename, 1, 1) |
|
|
edit_message(status_msg, "โ
**Upload complete!**") |
|
|
|
|
|
delete_message(status_msg, delay=10) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"An unexpected error occurred processing {url}: {e}", exc_info=True) |
|
|
edit_message(status_msg, f"โ ๏ธ **An unexpected error occurred:**\n`{e}`") |
|
|
delete_message(status_msg, delay=10) |
|
|
|
|
|
finally: |
|
|
clean_directory(download_dir) |
|
|
|
|
|
|
|
|
|
|
|
@bot.message_handler(commands=['start']) |
|
|
def start(message): |
|
|
user_id = message.from_user.id |
|
|
logger.info(f"User {user_id} started the bot.") |
|
|
|
|
|
start_caption = ( |
|
|
"โญโโโโโโโโโโโโโโโโโฎ\n" |
|
|
"โกโโฑโ **MEGA.NZ BOT** โโฐโโก\n" |
|
|
"โฐโโโโโโโโโโโโโโโโโฏ\n\n" |
|
|
"**Welcome!** I can download files and folders from Mega.nz for you.\n\n" |
|
|
"๐ **How It Works:**\n" |
|
|
"โค Paste your Mega.nz URL below ๐\n" |
|
|
"โค The bot will fetch & send the file(s) โก\n\n" |
|
|
"๐ฆ **Limit:** `None (depends on server)`\n" |
|
|
"โจโโญ๏ธโโโโโโโโโโโโโโโโญ๏ธโโจ" |
|
|
) |
|
|
bot.reply_to(message, start_caption) |
|
|
|
|
|
@bot.message_handler(commands=['help']) |
|
|
def help_command(message): |
|
|
logger.info(f"User {message.from_user.id} requested help.") |
|
|
help_text = ( |
|
|
"**Welcome to the Mega.nz Bot!**\n\n" |
|
|
"**How to use:**\n" |
|
|
"1. Send me any public `mega.nz` file link.\n" |
|
|
"2. Send me any public `mega.nz` folder link.\n" |
|
|
"3. I will download the file(s) and upload them directly to this chat.\n\n" |
|
|
"**Features:**\n" |
|
|
"โ
Supports both files and folders.\n" |
|
|
"โ
Gets video duration (if `ffprobe` is installed).\n" |
|
|
"โ
Cleans up files after upload to save space." |
|
|
) |
|
|
bot.reply_to(message, help_text) |
|
|
|
|
|
@bot.message_handler(commands=['ping']) |
|
|
def ping_command(message): |
|
|
logger.info(f"User {message.from_user.id} used /ping.") |
|
|
start_time = time.time() |
|
|
msg = bot.reply_to(message, "Pong!") |
|
|
end_time = time.time() |
|
|
ping_time = round((end_time - start_time) * 1000, 2) |
|
|
edit_message(msg, f"**Pong!**\n`{ping_time} ms`") |
|
|
|
|
|
|
|
|
|
|
|
def main(): |
|
|
try: |
|
|
|
|
|
os.makedirs(DOWNLOAD_DIR, exist_ok=True) |
|
|
|
|
|
logger.info("๐ Mega.nz Telegram Bot is running...") |
|
|
if BOT_OWNER_ID != 0: |
|
|
logger.info(f"Bot Owner ID is set: {BOT_OWNER_ID}") |
|
|
else: |
|
|
logger.warning("BOT_OWNER_ID is not set.") |
|
|
|
|
|
|
|
|
bot.polling(non_stop=True) |
|
|
|
|
|
except Exception as e: |
|
|
logger.critical(f"Bot polling failed: {e}", exc_info=True) |
|
|
time.sleep(10) |
|
|
main() |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |
|
|
|
|
|
|