Update main.py
Browse files
main.py
CHANGED
|
@@ -15,12 +15,17 @@ from telethon.errors import FloodWaitError
|
|
| 15 |
from telethon.tl.types import DocumentAttributeVideo
|
| 16 |
|
| 17 |
# πΉ Configuration
|
|
|
|
| 18 |
API_ID = int(os.environ.get("API_ID", 20687211))
|
| 19 |
API_HASH = os.environ.get("API_HASH", "4523f58b045175baaeaf1ba29733f31c")
|
| 20 |
BOT_TOKEN = os.environ.get("BOT_TOKEN", "8318388017:AAGCgx_XJqBcyOpdt-5DlMNTS7wU9Qk6J4c")
|
|
|
|
| 21 |
BOT_OWNER_ID = int(os.environ.get("BOT_OWNER_ID", 7014665654))
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
# πΉ Configure Logging
|
| 26 |
logging.basicConfig(
|
|
@@ -33,13 +38,14 @@ logging.getLogger('telethon').setLevel(logging.WARNING)
|
|
| 33 |
|
| 34 |
# πΉ Initialize
|
| 35 |
try:
|
| 36 |
-
|
|
|
|
| 37 |
mega = Megatools()
|
| 38 |
except Exception as e:
|
| 39 |
logger.critical(f"Failed to initialize bot or Megatools: {e}")
|
| 40 |
exit(1)
|
| 41 |
|
| 42 |
-
# ---
|
| 43 |
|
| 44 |
def progress_bar(percentage, bar_length=20):
|
| 45 |
"""Generates a text-based progress bar."""
|
|
@@ -84,53 +90,52 @@ def clean_directory(directory):
|
|
| 84 |
|
| 85 |
async def get_video_duration(file_path):
|
| 86 |
"""Tries to get video duration using ffprobe."""
|
| 87 |
-
command = [
|
| 88 |
-
"ffprobe", "-v", "quiet", "-print_format", "json",
|
| 89 |
-
"-show_format", file_path
|
| 90 |
-
]
|
| 91 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
process = await asyncio.create_subprocess_exec(
|
| 93 |
*command,
|
| 94 |
stdout=asyncio.subprocess.PIPE,
|
| 95 |
stderr=asyncio.subprocess.PIPE
|
| 96 |
)
|
| 97 |
stdout, stderr = await process.communicate()
|
|
|
|
| 98 |
if process.returncode == 0:
|
| 99 |
data = json.loads(stdout)
|
| 100 |
return int(float(data['format']['duration']))
|
| 101 |
else:
|
| 102 |
-
logger.warning(f"ffprobe failed: {stderr.decode()}")
|
| 103 |
return 0
|
| 104 |
except FileNotFoundError:
|
| 105 |
-
logger.warning("ffprobe not found. Cannot get video duration.")
|
| 106 |
return 0
|
| 107 |
except Exception as e:
|
| 108 |
-
logger.error(f"Error
|
| 109 |
return 0
|
| 110 |
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
| 112 |
"""
|
| 113 |
-
Handles the
|
| 114 |
-
|
| 115 |
-
- Shows upload progress
|
| 116 |
-
- Gets video duration
|
| 117 |
-
- Sends the file
|
| 118 |
-
- Deletes the local file
|
| 119 |
"""
|
| 120 |
try:
|
| 121 |
size_bytes = os.path.getsize(file_path)
|
| 122 |
size_mb = round(size_bytes / (1024 * 1024), 2)
|
| 123 |
ext = os.path.splitext(filename)[1].lower() or ".file"
|
| 124 |
-
|
| 125 |
caption = (
|
| 126 |
f"π **File:** `{filename}`\n"
|
| 127 |
f"π¦ **Size:** {size_mb} MB\n"
|
| 128 |
f"π **Extension:** `{ext}`"
|
| 129 |
)
|
| 130 |
-
|
| 131 |
-
status_text_prefix = f"π€ **Uploading {status_prefix}:**\n`{filename}`"
|
| 132 |
-
await edit_message(status_msg, f"{status_text_prefix}\n{progress_bar(0)} 0%")
|
| 133 |
|
|
|
|
|
|
|
| 134 |
last_update_time = 0
|
| 135 |
async def progress_callback(current, total):
|
| 136 |
nonlocal last_update_time
|
|
@@ -139,144 +144,142 @@ async def upload_file(chat, file_path, filename, status_msg, status_prefix=""):
|
|
| 139 |
last_update_time = current_time
|
| 140 |
percent = int(current * 100 / total)
|
| 141 |
bar = progress_bar(percent)
|
| 142 |
-
await edit_message(status_msg, f"{
|
| 143 |
|
|
|
|
| 144 |
is_video = ext in [".mp4", ".mov", ".mkv", ".avi"]
|
| 145 |
-
is_image = ext in [".jpg", ".jpeg", ".png", ".webp"]
|
| 146 |
|
| 147 |
attributes = []
|
| 148 |
if is_video:
|
| 149 |
duration = await get_video_duration(file_path)
|
| 150 |
attributes.append(DocumentAttributeVideo(duration=duration, w=0, h=0, supports_streaming=True))
|
| 151 |
-
|
| 152 |
await bot.send_file(
|
| 153 |
chat,
|
| 154 |
file_path,
|
| 155 |
caption=caption,
|
| 156 |
attributes=attributes or None,
|
| 157 |
-
spoiler=True,
|
| 158 |
-
force_document=not
|
| 159 |
progress_callback=progress_callback
|
| 160 |
)
|
| 161 |
-
logger.info(f"Uploaded {filename}.")
|
| 162 |
|
|
|
|
|
|
|
|
|
|
| 163 |
except Exception as e:
|
| 164 |
logger.error(f"Failed to upload file {file_path}: {e}", exc_info=True)
|
| 165 |
-
await
|
|
|
|
|
|
|
| 166 |
finally:
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
# --- End of Helper Functions ---
|
| 171 |
-
|
| 172 |
|
| 173 |
@bot.on(events.NewMessage(pattern="(?i)https://mega\\.nz/"))
|
| 174 |
async def handle_mega(event):
|
| 175 |
message_text = event.message.text
|
| 176 |
chat = event.chat_id
|
| 177 |
user_id = event.sender_id
|
| 178 |
-
|
| 179 |
status_msg = await event.reply("π **Processing your link...**")
|
| 180 |
-
|
| 181 |
match = re.search(r"(https://mega\.nz/[^\s)\]]+)", message_text)
|
|
|
|
| 182 |
if not match:
|
| 183 |
-
logger.warning(f"Could not parse a mega.nz link from: {message_text}")
|
| 184 |
await edit_message(status_msg, "β **Error:** Could not find a valid mega.nz link.")
|
| 185 |
await delete_message(status_msg, delay=10)
|
| 186 |
return
|
| 187 |
-
|
| 188 |
url = match.group(1).strip()
|
| 189 |
|
| 190 |
-
#
|
| 191 |
job_id = str(uuid.uuid4())
|
| 192 |
download_dir = os.path.join(DOWNLOAD_DIR, f"user_{user_id}", job_id)
|
| 193 |
os.makedirs(download_dir, exist_ok=True)
|
| 194 |
|
| 195 |
try:
|
| 196 |
-
|
| 197 |
-
|
|
|
|
|
|
|
| 198 |
logger.info(f"User {user_id} sent folder link: {url}")
|
| 199 |
await edit_message(status_msg, "π **Folder link detected.**\nβ¬οΈ Downloading all files... (This may take a while)")
|
| 200 |
-
|
| 201 |
try:
|
| 202 |
-
# This is
|
|
|
|
| 203 |
mega.download(url, path=download_dir)
|
| 204 |
logger.info(f"Folder downloaded to: {download_dir}")
|
| 205 |
except Exception as e:
|
| 206 |
logger.error(f"Folder download failed for {url}: {e}", exc_info=True)
|
| 207 |
-
|
| 208 |
-
await delete_message(status_msg, delay=10)
|
| 209 |
-
clean_directory(download_dir)
|
| 210 |
-
return
|
| 211 |
|
| 212 |
-
await edit_message(status_msg, "β
**Download complete!**\nPreparing to upload
|
| 213 |
|
| 214 |
-
|
| 215 |
files_to_upload = []
|
| 216 |
for root, dirs, files in os.walk(download_dir):
|
| 217 |
for file in files:
|
| 218 |
files_to_upload.append((os.path.join(root, file), file))
|
| 219 |
-
|
| 220 |
if not files_to_upload:
|
| 221 |
await edit_message(status_msg, "βΉοΈ The folder appears to be empty.")
|
| 222 |
await delete_message(status_msg, delay=10)
|
| 223 |
-
clean_directory(download_dir)
|
| 224 |
return
|
| 225 |
|
| 226 |
await edit_message(status_msg, f"Found {len(files_to_upload)} files. Starting upload...")
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
await upload_file(
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
filename,
|
| 234 |
-
status_msg,
|
| 235 |
-
status_prefix=f"file {i+1}/{len(files_to_upload)}"
|
| 236 |
-
)
|
| 237 |
-
uploaded_count += 1
|
| 238 |
-
# The helper function now handles file deletion
|
| 239 |
-
|
| 240 |
await edit_message(status_msg, f"β
**Upload complete!**\nSuccessfully sent {uploaded_count} file(s).")
|
| 241 |
-
await delete_message(status_msg, delay=10)
|
| 242 |
|
| 243 |
else:
|
| 244 |
# --- SINGLE FILE LOGIC ---
|
| 245 |
logger.info(f"User {user_id} sent file link: {url}")
|
| 246 |
await edit_message(status_msg, "β³ **Fetching file info...**")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
|
| 248 |
-
filename = mega.filename(url)
|
| 249 |
if not filename:
|
| 250 |
-
|
| 251 |
-
await edit_message(status_msg, "β **Error:** Could not fetch filename. The link might be invalid.")
|
| 252 |
-
await delete_message(status_msg, delay=10)
|
| 253 |
return
|
| 254 |
|
| 255 |
-
|
| 256 |
await edit_message(status_msg, f"β¬οΈ **Downloading:**\n`{filename}`")
|
| 257 |
|
| 258 |
try:
|
| 259 |
-
#
|
| 260 |
-
mega.download(url, path=
|
| 261 |
-
logger.info(f"
|
| 262 |
except Exception as e:
|
| 263 |
-
logger.error(f"
|
| 264 |
-
|
| 265 |
-
await delete_message(status_msg, delay=10)
|
| 266 |
-
return # temp_path will be cleaned in finally
|
| 267 |
|
| 268 |
-
|
| 269 |
-
await
|
| 270 |
-
|
|
|
|
| 271 |
|
| 272 |
except Exception as e:
|
| 273 |
-
logger.error(f"
|
| 274 |
await edit_message(status_msg, f"β οΈ **An unexpected error occurred:**\n`{e}`")
|
| 275 |
await delete_message(status_msg, delay=10)
|
|
|
|
| 276 |
finally:
|
| 277 |
# Clean up the entire job directory
|
| 278 |
clean_directory(download_dir)
|
| 279 |
|
|
|
|
| 280 |
|
| 281 |
@bot.on(events.NewMessage(pattern="/start"))
|
| 282 |
async def start(event):
|
|
@@ -304,11 +307,10 @@ async def help_command(event):
|
|
| 304 |
"**How to use:**\n"
|
| 305 |
"1. Send me any public `mega.nz` file link.\n"
|
| 306 |
"2. Send me any public `mega.nz` folder link.\n"
|
| 307 |
-
"3. I will download the file(s) and upload them to this chat.\n\n"
|
| 308 |
"**Features:**\n"
|
| 309 |
"β
Upload progress.\n"
|
| 310 |
"β
Supports both files and folders.\n"
|
| 311 |
-
"β
Folder files are sent 1-by-1.\n"
|
| 312 |
"β
Gets video duration (if `ffprobe` is installed).\n"
|
| 313 |
"β
Cleans up files after upload to save space."
|
| 314 |
)
|
|
|
|
| 15 |
from telethon.tl.types import DocumentAttributeVideo
|
| 16 |
|
| 17 |
# πΉ Configuration
|
| 18 |
+
# Best practice: Use environment variables, with fallbacks for easy testing
|
| 19 |
API_ID = int(os.environ.get("API_ID", 20687211))
|
| 20 |
API_HASH = os.environ.get("API_HASH", "4523f58b045175baaeaf1ba29733f31c")
|
| 21 |
BOT_TOKEN = os.environ.get("BOT_TOKEN", "8318388017:AAGCgx_XJqBcyOpdt-5DlMNTS7wU9Qk6J4c")
|
| 22 |
+
# Set this env variable to your own Telegram User ID
|
| 23 |
BOT_OWNER_ID = int(os.environ.get("BOT_OWNER_ID", 7014665654))
|
| 24 |
|
| 25 |
+
# --- File Management ---
|
| 26 |
+
# We use a /data directory which is created in the Dockerfile for persistent storage
|
| 27 |
+
SESSION_FILE = "/data/MegaNZBot"
|
| 28 |
+
DOWNLOAD_DIR = "downloads" # This will be created inside /app
|
| 29 |
|
| 30 |
# πΉ Configure Logging
|
| 31 |
logging.basicConfig(
|
|
|
|
| 38 |
|
| 39 |
# πΉ Initialize
|
| 40 |
try:
|
| 41 |
+
# Use the /data path for the session file to fix permission errors in Docker
|
| 42 |
+
bot = TelegramClient(SESSION_FILE, API_ID, API_HASH)
|
| 43 |
mega = Megatools()
|
| 44 |
except Exception as e:
|
| 45 |
logger.critical(f"Failed to initialize bot or Megatools: {e}")
|
| 46 |
exit(1)
|
| 47 |
|
| 48 |
+
# --- Helper Functions ---
|
| 49 |
|
| 50 |
def progress_bar(percentage, bar_length=20):
|
| 51 |
"""Generates a text-based progress bar."""
|
|
|
|
| 90 |
|
| 91 |
async def get_video_duration(file_path):
|
| 92 |
"""Tries to get video duration using ffprobe."""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
try:
|
| 94 |
+
command = [
|
| 95 |
+
"ffprobe", "-v", "quiet", "-print_format", "json",
|
| 96 |
+
"-show_format", file_path
|
| 97 |
+
]
|
| 98 |
process = await asyncio.create_subprocess_exec(
|
| 99 |
*command,
|
| 100 |
stdout=asyncio.subprocess.PIPE,
|
| 101 |
stderr=asyncio.subprocess.PIPE
|
| 102 |
)
|
| 103 |
stdout, stderr = await process.communicate()
|
| 104 |
+
|
| 105 |
if process.returncode == 0:
|
| 106 |
data = json.loads(stdout)
|
| 107 |
return int(float(data['format']['duration']))
|
| 108 |
else:
|
| 109 |
+
logger.warning(f"ffprobe failed (duration): {stderr.decode()}")
|
| 110 |
return 0
|
| 111 |
except FileNotFoundError:
|
| 112 |
+
logger.warning("ffprobe not found. Cannot get video duration. (Did you install ffmpeg?)")
|
| 113 |
return 0
|
| 114 |
except Exception as e:
|
| 115 |
+
logger.error(f"Error in get_video_duration: {e}")
|
| 116 |
return 0
|
| 117 |
|
| 118 |
+
# --- End of Helper Functions ---
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
async def upload_file(chat, status_msg, file_path, filename, file_index, total_files):
|
| 122 |
"""
|
| 123 |
+
Handles the upload process for a single file, including progress,
|
| 124 |
+
captions, and cleanup.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
"""
|
| 126 |
try:
|
| 127 |
size_bytes = os.path.getsize(file_path)
|
| 128 |
size_mb = round(size_bytes / (1024 * 1024), 2)
|
| 129 |
ext = os.path.splitext(filename)[1].lower() or ".file"
|
| 130 |
+
|
| 131 |
caption = (
|
| 132 |
f"π **File:** `{filename}`\n"
|
| 133 |
f"π¦ **Size:** {size_mb} MB\n"
|
| 134 |
f"π **Extension:** `{ext}`"
|
| 135 |
)
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
+
await edit_message(status_msg, f"π€ **Uploading file {file_index}/{total_files}:**\n`{filename}`\n{progress_bar(0)} 0%")
|
| 138 |
+
|
| 139 |
last_update_time = 0
|
| 140 |
async def progress_callback(current, total):
|
| 141 |
nonlocal last_update_time
|
|
|
|
| 144 |
last_update_time = current_time
|
| 145 |
percent = int(current * 100 / total)
|
| 146 |
bar = progress_bar(percent)
|
| 147 |
+
await edit_message(status_msg, f"π€ **Uploading file {file_index}/{total_files}:**\n`{filename}`\n{bar} {percent}%")
|
| 148 |
|
| 149 |
+
# Handle file types
|
| 150 |
is_video = ext in [".mp4", ".mov", ".mkv", ".avi"]
|
|
|
|
| 151 |
|
| 152 |
attributes = []
|
| 153 |
if is_video:
|
| 154 |
duration = await get_video_duration(file_path)
|
| 155 |
attributes.append(DocumentAttributeVideo(duration=duration, w=0, h=0, supports_streaming=True))
|
| 156 |
+
|
| 157 |
await bot.send_file(
|
| 158 |
chat,
|
| 159 |
file_path,
|
| 160 |
caption=caption,
|
| 161 |
attributes=attributes or None,
|
| 162 |
+
spoiler=True,
|
| 163 |
+
force_document=not is_video, # Send as document if not a video
|
| 164 |
progress_callback=progress_callback
|
| 165 |
)
|
|
|
|
| 166 |
|
| 167 |
+
logger.info(f"Uploaded {filename}.")
|
| 168 |
+
return True
|
| 169 |
+
|
| 170 |
except Exception as e:
|
| 171 |
logger.error(f"Failed to upload file {file_path}: {e}", exc_info=True)
|
| 172 |
+
await bot.send_message(chat, f"β οΈ Failed to upload file: `{filename}`. Error: {e}")
|
| 173 |
+
return False
|
| 174 |
+
|
| 175 |
finally:
|
| 176 |
+
# Clean up individual file after upload
|
| 177 |
+
if os.path.exists(file_path):
|
| 178 |
+
os.remove(file_path)
|
|
|
|
|
|
|
| 179 |
|
| 180 |
@bot.on(events.NewMessage(pattern="(?i)https://mega\\.nz/"))
|
| 181 |
async def handle_mega(event):
|
| 182 |
message_text = event.message.text
|
| 183 |
chat = event.chat_id
|
| 184 |
user_id = event.sender_id
|
| 185 |
+
|
| 186 |
status_msg = await event.reply("π **Processing your link...**")
|
| 187 |
+
|
| 188 |
match = re.search(r"(https://mega\.nz/[^\s)\]]+)", message_text)
|
| 189 |
+
|
| 190 |
if not match:
|
|
|
|
| 191 |
await edit_message(status_msg, "β **Error:** Could not find a valid mega.nz link.")
|
| 192 |
await delete_message(status_msg, delay=10)
|
| 193 |
return
|
| 194 |
+
|
| 195 |
url = match.group(1).strip()
|
| 196 |
|
| 197 |
+
# Sandboxed directory for this specific job
|
| 198 |
job_id = str(uuid.uuid4())
|
| 199 |
download_dir = os.path.join(DOWNLOAD_DIR, f"user_{user_id}", job_id)
|
| 200 |
os.makedirs(download_dir, exist_ok=True)
|
| 201 |
|
| 202 |
try:
|
| 203 |
+
is_folder = "/folder/" in url
|
| 204 |
+
|
| 205 |
+
if is_folder:
|
| 206 |
+
# --- FOLDER LOGIC ---
|
| 207 |
logger.info(f"User {user_id} sent folder link: {url}")
|
| 208 |
await edit_message(status_msg, "π **Folder link detected.**\nβ¬οΈ Downloading all files... (This may take a while)")
|
| 209 |
+
|
| 210 |
try:
|
| 211 |
+
# This is a blocking call. It will download the entire folder.
|
| 212 |
+
# This is the only method that works in your environment.
|
| 213 |
mega.download(url, path=download_dir)
|
| 214 |
logger.info(f"Folder downloaded to: {download_dir}")
|
| 215 |
except Exception as e:
|
| 216 |
logger.error(f"Folder download failed for {url}: {e}", exc_info=True)
|
| 217 |
+
raise Exception(f"Folder download failed: {e}")
|
|
|
|
|
|
|
|
|
|
| 218 |
|
| 219 |
+
await edit_message(status_msg, "β
**Download complete!**\nPreparing to upload...")
|
| 220 |
|
| 221 |
+
# --- Upload 1-by-1 ---
|
| 222 |
files_to_upload = []
|
| 223 |
for root, dirs, files in os.walk(download_dir):
|
| 224 |
for file in files:
|
| 225 |
files_to_upload.append((os.path.join(root, file), file))
|
| 226 |
+
|
| 227 |
if not files_to_upload:
|
| 228 |
await edit_message(status_msg, "βΉοΈ The folder appears to be empty.")
|
| 229 |
await delete_message(status_msg, delay=10)
|
|
|
|
| 230 |
return
|
| 231 |
|
| 232 |
await edit_message(status_msg, f"Found {len(files_to_upload)} files. Starting upload...")
|
| 233 |
+
|
| 234 |
+
uploaded_count = 0
|
| 235 |
+
for i, (file_path, file_name) in enumerate(files_to_upload):
|
| 236 |
+
if await upload_file(chat, status_msg, file_path, file_name, i+1, len(files_to_upload)):
|
| 237 |
+
uploaded_count += 1
|
| 238 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
await edit_message(status_msg, f"β
**Upload complete!**\nSuccessfully sent {uploaded_count} file(s).")
|
|
|
|
| 240 |
|
| 241 |
else:
|
| 242 |
# --- SINGLE FILE LOGIC ---
|
| 243 |
logger.info(f"User {user_id} sent file link: {url}")
|
| 244 |
await edit_message(status_msg, "β³ **Fetching file info...**")
|
| 245 |
+
|
| 246 |
+
try:
|
| 247 |
+
filename = mega.filename(url)
|
| 248 |
+
except MegaError as e:
|
| 249 |
+
logger.error(f"MegaError (filename): {e}", exc_info=True)
|
| 250 |
+
await edit_message(status_msg, f"β **Error:** Link seems invalid or expired.\n`{e}`")
|
| 251 |
+
return
|
| 252 |
|
|
|
|
| 253 |
if not filename:
|
| 254 |
+
await edit_message(status_msg, "β **Error:** Could not fetch filename.")
|
|
|
|
|
|
|
| 255 |
return
|
| 256 |
|
| 257 |
+
file_path = os.path.join(download_dir, filename)
|
| 258 |
await edit_message(status_msg, f"β¬οΈ **Downloading:**\n`{filename}`")
|
| 259 |
|
| 260 |
try:
|
| 261 |
+
# Blocking call to download the single file
|
| 262 |
+
mega.download(url, path=file_path)
|
| 263 |
+
logger.info(f"File downloaded to: {file_path}")
|
| 264 |
except Exception as e:
|
| 265 |
+
logger.error(f"File download failed for {url}: {e}", exc_info=True)
|
| 266 |
+
raise Exception(f"File download failed: {e}")
|
|
|
|
|
|
|
| 267 |
|
| 268 |
+
await upload_file(chat, status_msg, file_path, filename, 1, 1)
|
| 269 |
+
await edit_message(status_msg, "β
**Upload complete!**")
|
| 270 |
+
|
| 271 |
+
await delete_message(status_msg, delay=10)
|
| 272 |
|
| 273 |
except Exception as e:
|
| 274 |
+
logger.error(f"An unexpected error occurred processing {url}: {e}", exc_info=True)
|
| 275 |
await edit_message(status_msg, f"β οΈ **An unexpected error occurred:**\n`{e}`")
|
| 276 |
await delete_message(status_msg, delay=10)
|
| 277 |
+
|
| 278 |
finally:
|
| 279 |
# Clean up the entire job directory
|
| 280 |
clean_directory(download_dir)
|
| 281 |
|
| 282 |
+
# --- Standard Bot Commands ---
|
| 283 |
|
| 284 |
@bot.on(events.NewMessage(pattern="/start"))
|
| 285 |
async def start(event):
|
|
|
|
| 307 |
"**How to use:**\n"
|
| 308 |
"1. Send me any public `mega.nz` file link.\n"
|
| 309 |
"2. Send me any public `mega.nz` folder link.\n"
|
| 310 |
+
"3. I will download the file(s) and upload them directly to this chat.\n\n"
|
| 311 |
"**Features:**\n"
|
| 312 |
"β
Upload progress.\n"
|
| 313 |
"β
Supports both files and folders.\n"
|
|
|
|
| 314 |
"β
Gets video duration (if `ffprobe` is installed).\n"
|
| 315 |
"β
Cleans up files after upload to save space."
|
| 316 |
)
|