├── Procfile ├── start.sh ├── utils ├── __init__.py └── logging_setup.py ├── heroku.yml ├── requirements.txt ├── sample.env ├── Dockerfile ├── app.json ├── config.py ├── adminpanel ├── logs │ └── logs.py ├── restart │ └── restart.py └── admin │ └── admin.py ├── cookies └── ItsSmartToolBot.txt ├── pinterest └── pinterest.py ├── tiktok └── tiktok.py ├── facebook └── facebook.py ├── instagram └── instagram.py ├── main.py ├── README.md ├── spotify └── spotify.py └── youtube └── youtube.py /Procfile: -------------------------------------------------------------------------------- 1 | web: python main.py 2 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | python3 main.py 2 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .logging_setup import LOGGER -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | web: Dockerfile 4 | 5 | run: 6 | web: python main.py 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram 2 | yt-dlp 3 | spotipy 4 | requests 5 | pillow 6 | asyncio 7 | aiofiles 8 | aiohttp 9 | youtube-search-python 10 | tgcrypto 11 | pymongo 12 | moviepy 13 | Flask 14 | -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | API_ID=28210 2 | API_HASH=7fc5b356973318b86481ab5eca3 3 | BOT_TOKEN=7234398375:AqarF90O20Dcza8XGA-L33f904ywk 4 | ADMIN_IDS=7303810912,6249257243,5991909954 5 | SPOTIFY_CLIENT_ID=5941bb55d4a52a91c5297f616e325 6 | SPOTIFY_CLIENT_SECRET=408f04b237add2ba1b8bfc5da9eff8 7 | MONGO_URL=mongodb+srv://ytpremium4434360:zxx1VPDzGssmarttoolbot.dhsl4.mongodb.net/?retryWrites=true&w=majority&appName=ItsSmartToolBot 8 | COMMAND_PREFIX="!|.|#|,|/" 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Python image (full version) as a base 2 | FROM python:3.10 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Install system dependencies (only ffmpeg now) 8 | RUN apt-get update && \ 9 | apt-get install -y ffmpeg && \ 10 | apt-get clean && \ 11 | rm -rf /var/lib/apt/lists/* 12 | 13 | # Copy the current directory contents into the container at /app 14 | COPY . /app 15 | 16 | # Install Python dependencies from requirements.txt 17 | COPY requirements.txt . 18 | RUN pip install --no-cache-dir -r requirements.txt 19 | 20 | # Expose the port (optional for Heroku, as it's dynamically assigned) 21 | EXPOSE 5000 22 | 23 | # Run the Flask server and bot when the container starts 24 | CMD ["python", "main.py"] 25 | -------------------------------------------------------------------------------- /utils/logging_setup.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from logging.handlers import RotatingFileHandler 3 | 4 | # Setup logging configuration 5 | logging.basicConfig( 6 | level=logging.INFO, 7 | format="[%(asctime)s - %(levelname)s] - %(name)s - %(message)s", 8 | datefmt='%d-%b-%y %H:%M:%S', 9 | handlers=[ 10 | RotatingFileHandler( 11 | "botlog.txt", 12 | maxBytes=50000000, 13 | backupCount=10 14 | ), 15 | logging.StreamHandler() 16 | ] 17 | ) 18 | 19 | # Set logging levels for specific libraries 20 | logging.getLogger("pyrogram").setLevel(logging.ERROR) 21 | logging.getLogger("pytgcalls").setLevel(logging.ERROR) 22 | logging.getLogger("apscheduler").setLevel(logging.ERROR) 23 | 24 | LOGGER = logging.getLogger(__name__) 25 | LOGGER.info("Bot Successfully Started! 💥") 26 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SmartDlBot", 3 | "description": "A smart Telegram bot with Spotify and MongoDB integration.", 4 | "repository": "https://github.com/abirxdhack/SmartDlBot", 5 | "keywords": ["telegram", "bot", "docker", "flask", "ffmpeg"], 6 | "stack": "container", 7 | "env": { 8 | "API_ID": { 9 | "description": "Telegram API ID", 10 | "required": true 11 | }, 12 | "API_HASH": { 13 | "description": "Telegram API Hash", 14 | "required": true 15 | }, 16 | "BOT_TOKEN": { 17 | "description": "Telegram Bot Token", 18 | "required": true 19 | }, 20 | "ADMIN_IDS": { 21 | "description": "Comma-separated list of Telegram admin user IDs", 22 | "required": true 23 | }, 24 | "SPOTIFY_CLIENT_ID": { 25 | "description": "Spotify Client ID", 26 | "required": true 27 | }, 28 | "SPOTIFY_CLIENT_SECRET": { 29 | "description": "Spotify Client Secret", 30 | "required": true 31 | }, 32 | "MONGO_URL": { 33 | "description": "MongoDB connection string", 34 | "required": true 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | # Load environment variables from .env file 5 | load_dotenv() 6 | 7 | # Telegram Bot & API Configuration 8 | API_ID = int(os.getenv("API_ID", 0)) 9 | API_HASH = os.getenv("API_HASH") 10 | BOT_TOKEN = os.getenv("BOT_TOKEN") 11 | 12 | # Admin IDs (as a list of integers) 13 | ADMIN_IDS_RAW = os.getenv("ADMIN_IDS", "") 14 | try: 15 | ADMIN_IDS = list(map(int, ADMIN_IDS_RAW.split(","))) 16 | except ValueError: 17 | raise ValueError("Invalid ADMIN_IDS. Must be comma-separated integers.") 18 | 19 | # Spotify API 20 | SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID") 21 | SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET") 22 | 23 | # MongoDB 24 | MONGO_URL = os.getenv("MONGO_URL") 25 | 26 | # Command Prefix Support 27 | COMMAND_PREFIX_RAW = os.getenv("COMMAND_PREFIX", "!|.|#|,|/") 28 | COMMAND_PREFIX = [prefix.strip() for prefix in COMMAND_PREFIX_RAW.split("|") if prefix.strip()] 29 | 30 | print("Loaded COMMAND_PREFIX:", COMMAND_PREFIX) 31 | 32 | if not COMMAND_PREFIX: 33 | raise ValueError("Sorry Bro No Command Prefix Found. First Fix It!") 34 | 35 | # YouTube cookies file path (if required for yt-dlp or similar) 36 | YT_COOKIES_PATH = "./cookies/ItsSmartToolBot.txt" 37 | -------------------------------------------------------------------------------- /adminpanel/logs/logs.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pyrogram import Client, filters 3 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, InputMediaDocument 4 | from pyrogram.enums import ParseMode 5 | from config import ADMIN_IDS # Import ADMIN_IDS 6 | 7 | def setup_logs_handler(app: Client): 8 | """Sets up the logs handler to send logs from botlog.txt only.""" 9 | 10 | @app.on_message(filters.command(["dump", "logs"], prefixes=["/", "."]) & (filters.private | filters.group)) 11 | async def logs_command(client, message): 12 | """Sends logs from botlog.txt to admins only.""" 13 | 14 | # Check if the user is an admin 15 | if message.from_user.id not in ADMIN_IDS: 16 | await client.send_message( 17 | chat_id=message.chat.id, 18 | text="**❌ You do not have permission to use this command.**", 19 | parse_mode=ParseMode.MARKDOWN 20 | ) 21 | return 22 | 23 | # Send loading message 24 | loading_msg = await client.send_message( 25 | chat_id=message.chat.id, 26 | text="🚀 **Fetching Logs Database🔥**", 27 | parse_mode=ParseMode.MARKDOWN 28 | ) 29 | 30 | logs = "No logs available yet." 31 | 32 | # Read the logs from botlog.txt if it exists 33 | if os.path.exists("botlog.txt"): 34 | with open("botlog.txt", "r", encoding="utf-8") as f: 35 | logs = f.read().strip() 36 | 37 | # If logs are too long, send as a text file 38 | if len(logs) > 4000: 39 | log_file_path = "botlog.txt" 40 | await client.send_document( 41 | chat_id=message.chat.id, 42 | document=log_file_path, 43 | caption="📜 **Here are the latest logs:**", 44 | parse_mode=ParseMode.MARKDOWN 45 | ) 46 | await loading_msg.delete() 47 | else: 48 | # Format logs as plain text 49 | logs_formatted = f"📜 Latest Logs:\n\n{logs}" 50 | 51 | # Create an inline "Close" button 52 | keyboard = InlineKeyboardMarkup( 53 | [[InlineKeyboardButton("❌ Close", callback_data="close_logs")]] 54 | ) 55 | 56 | # Edit the loading message with logs 57 | await loading_msg.edit_text(logs_formatted, parse_mode=ParseMode.DISABLED, reply_markup=keyboard) 58 | 59 | @app.on_callback_query(filters.regex("close_logs")) 60 | async def close_logs(client: Client, query: CallbackQuery): 61 | """Deletes the log message when the 'Close' button is clicked.""" 62 | await query.message.delete() 63 | -------------------------------------------------------------------------------- /cookies/ItsSmartToolBot.txt: -------------------------------------------------------------------------------- 1 | # Netscape HTTP Cookie File 2 | # This file is generated by yt-dlp. Do not edit. 3 | 4 | .youtube.com TRUE / FALSE 1774622896 APISID CBIU23q_IRz8zUMC/AK5EKs8V1J3cIo4Yv 5 | .youtube.com TRUE / TRUE 1755684748 DEVICE_INFO ChxOelEzTXpjNU5EUXdOVGN5TXpJeE5Ea3lNUT09EIyj4b0GGOqA4b0G 6 | .youtube.com TRUE / FALSE 1774622896 HSID A_-19jXoFqKYujfTz 7 | .youtube.com TRUE / TRUE 1774577579 LOGIN_INFO AFmmF2swRQIhANfJ2G2diIVBK4EYG6bqkKnWh67HjeHDuea2l4ceFDnwAiBeHeB5n30zGHnL1n1jomEjP7poOtr3lDTX3E1J_fLSFA:QUQ3MjNmemZTaGZLSWhkNjdrQW9rNGw4NHdqRUhFdHV4WTlLcVA3YjBqSWFsZmhtRGxSNERYdWdmVTRBYkF2TzRMS0pBOU5OVzVUQVhBOXdfOWljUTJpdGUtVFBFeXRiZmhralJCRFFCY1pvNEZ5NWxaY0kyMHkyckZIbEpYZVRESk81NVdoQjVfTzc2X0RqRVZkSzZPSUVSbWlRZmxCem93 8 | .youtube.com TRUE / FALSE 0 PREF tz=UTC&hl=en 9 | .youtube.com TRUE / TRUE 1774622896 SAPISID 2Emco1lnxcDmbLGY/ANgzdhAo24coY-UIA 10 | .youtube.com TRUE / FALSE 1774622896 SID g.a000twgnImlPrIzD18Kef2opfvo_pOQnd1O7fb2--ZxsyVjCdeBD-B02UtSy0gfeAvWrmXL3hwACgYKAcoSARQSFQHGX2MiQXAQYcYr-eE_Fglrx5PC3BoVAUF8yKpmysJww1ncQVrseGjyjjXI0076 11 | .youtube.com TRUE / FALSE 1771668748 SIDCC AKEyXzUpAhUnUw7PqjZrbg-BQWl0xXuezjZAW0sFRO3gBVDMxDs83N1Xuk7j89z62Nql-mHsNg 12 | .youtube.com TRUE / TRUE 1774622896 SSID AZgiZbxZq9X-aLRJv 13 | .youtube.com TRUE / TRUE 1755684748 VISITOR_INFO1_LIVE SoP5njC7B50 14 | .youtube.com TRUE / TRUE 1755684748 VISITOR_PRIVACY_METADATA CgJHQhIEGgAgQA%3D%3D 15 | .youtube.com TRUE / TRUE 0 YSC Q7J-x5s_qbs 16 | .youtube.com TRUE / TRUE 1774622896 __Secure-1PAPISID 2Emco1lnxcDmbLGY/ANgzdhAo24coY-UIA 17 | .youtube.com TRUE / TRUE 1774622896 __Secure-1PSID g.a000twgnImlPrIzD18Kef2opfvo_pOQnd1O7fb2--ZxsyVjCdeBD4gkDUBvFdcY-2l42OLwfkgACgYKAakSARQSFQHGX2MisSnXuiV09Q6FxLr0bHd6oRoVAUF8yKpbZSlHqqRHdXeCbNN-lZQ_0076 18 | .youtube.com TRUE / TRUE 1771668748 __Secure-1PSIDCC AKEyXzVTFXTTCKOH5KRDGp8hqPfsrfELYD8ZKmysTgCR8RJ28LFVY9NfktczqLnVrnXp7U81bbo 19 | .youtube.com TRUE / TRUE 1771664360 __Secure-1PSIDTS sidts-CjIBEJ3XVzJGcl0NceEdgnrI8FwmeYcCAH9UJEUC4urMBD5p7E1Lb1D_qa4aALxICsuvuBAA 20 | .youtube.com TRUE / TRUE 1774622896 __Secure-3PAPISID 2Emco1lnxcDmbLGY/ANgzdhAo24coY-UIA 21 | .youtube.com TRUE / TRUE 1774622896 __Secure-3PSID g.a000twgnImlPrIzD18Kef2opfvo_pOQnd1O7fb2--ZxsyVjCdeBDifk5G0j5eWL0lQTWLfGBJgACgYKAdYSARQSFQHGX2MiC2jwMq06dmBcbSmo3LG5phoVAUF8yKqGVFfJNVxJnJVgqHGz3uqc0076 22 | .youtube.com TRUE / TRUE 1771668748 __Secure-3PSIDCC AKEyXzVs9_qhXrBw64BYIIA1t0aL-Uz0BzejFsIxzkYI8k8WVQV1qgGs1QGnFMwd83SKhIfB6Q 23 | .youtube.com TRUE / TRUE 1771664360 __Secure-3PSIDTS sidts-CjIBEJ3XVzJGcl0NceEdgnrI8FwmeYcCAH9UJEUC4urMBD5p7E1Lb1D_qa4aALxICsuvuBAA 24 | .youtube.com TRUE / TRUE 1755680362 __Secure-ROLLOUT_TOKEN CJCmtMHEyPeccxCl3Nams9SLAxjv3oans9SLAw%3D%3D 25 | .youtube.com TRUE / TRUE 1774256360 __Secure-YEC CgsyTzBvTkk1RmNEMCjpgOG9BjIKCgJHQhIEGgAgWw%3D%3D 26 | .youtube.com TRUE / TRUE 1803204748 __Secure-YT_TVFAS t=483368&s=3 27 | -------------------------------------------------------------------------------- /adminpanel/restart/restart.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import os 3 | import asyncio 4 | from pyrogram import Client, filters 5 | from pyrogram.enums import ParseMode 6 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 7 | from config import ADMIN_IDS 8 | 9 | def setup_restart_handler(app: Client): 10 | @app.on_message(filters.command(["restart", "reboot", "reload"], prefixes=["/", "."]) & (filters.private | filters.group)) 11 | async def restart(_, message): 12 | if message.from_user.id not in ADMIN_IDS: 13 | await message.reply_text( 14 | "❌ You are not authorized to use this command.", 15 | parse_mode=ParseMode.HTML, 16 | reply_markup=InlineKeyboardMarkup( 17 | [ 18 | [ 19 | InlineKeyboardButton("👨🏼‍💻 Developer", url='https://t.me/abirxdhackz'), 20 | InlineKeyboardButton("🤖 Other Bots", url="https://t.me/Modvip_rm") 21 | ], 22 | [ 23 | InlineKeyboardButton("🔗 Source Code", url="https://github.com/abirxdhack/RestartModule"), 24 | InlineKeyboardButton("⚙️ Update Channel", url="https://t.me/Modvip_rm") 25 | ] 26 | ] 27 | ) 28 | ) 29 | return 30 | 31 | response = await message.reply_text( 32 | "Restarting Bot...", 33 | parse_mode=ParseMode.HTML 34 | ) 35 | 36 | # Active chats handling logic here if applicable 37 | # Example: 38 | # served_chats = await get_active_chats() 39 | # for x in served_chats: 40 | # try: 41 | # await app.send_message( 42 | # x, 43 | # "The bot is restarting for updating purposes. Sorry for the inconvenience." 44 | # ) 45 | # await remove_active_chat(x) 46 | # except Exception: 47 | # pass 48 | 49 | # Directories to be removed 50 | directories = ["downloads", "temp", "temp_media"] 51 | for directory in directories: 52 | try: 53 | shutil.rmtree(directory) 54 | except FileNotFoundError: 55 | pass 56 | 57 | # Delete the botlogs.txt file if it exists 58 | if os.path.exists("botlog.txt"): 59 | os.remove("botlog.txt") 60 | 61 | await asyncio.sleep(6) 62 | 63 | await response.edit( 64 | "Bot Successfully Started! 💥", 65 | parse_mode=ParseMode.HTML 66 | ) 67 | os.system(f"kill -9 {os.getpid()} && bash start.sh") 68 | 69 | @app.on_message(filters.command(["stop", "kill", "off"], prefixes=["/", "."]) & (filters.private | filters.group)) 70 | async def stop(_, message): 71 | if message.from_user.id not in ADMIN_IDS: 72 | await message.reply_text( 73 | "❌ You are not authorized to use this command.", 74 | parse_mode=ParseMode.HTML, 75 | reply_markup=InlineKeyboardMarkup( 76 | [ 77 | [ 78 | InlineKeyboardButton("👨🏼‍💻 Developer", url='https://t.me/abirxdhackz'), 79 | InlineKeyboardButton("🤖 Other Bots", url="https://t.me/Modvip_rm") 80 | ], 81 | [ 82 | InlineKeyboardButton("🔗 Source Code", url="https://github.com/abirxdhack/RestartModule"), 83 | InlineKeyboardButton("⚙️ Update Channel", url="https://t.me/Modvip_rm") 84 | ] 85 | ] 86 | ) 87 | ) 88 | return 89 | 90 | await message.reply_text( 91 | "Bot Off Successfully", 92 | parse_mode=ParseMode.HTML 93 | ) 94 | os.system("pkill -f main.py") 95 | -------------------------------------------------------------------------------- /pinterest/pinterest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import time 4 | from pathlib import Path 5 | from typing import Optional 6 | import aiohttp 7 | import re 8 | import asyncio 9 | import aiofiles 10 | from pyrogram import Client, filters 11 | from pyrogram.types import Message 12 | from pyrogram.enums import ParseMode 13 | from config import COMMAND_PREFIX 14 | 15 | # Configure logging 16 | logging.basicConfig( 17 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 18 | level=logging.INFO 19 | ) 20 | logger = logging.getLogger(__name__) 21 | 22 | # Configuration 23 | class Config: 24 | TEMP_DIR = Path("temp") 25 | 26 | Config.TEMP_DIR.mkdir(exist_ok=True) 27 | 28 | class PinterestDownloader: 29 | def __init__(self, temp_dir: Path): 30 | self.temp_dir = temp_dir 31 | 32 | async def download_media(self, url: str, downloading_message: Message) -> Optional[dict]: 33 | self.temp_dir.mkdir(exist_ok=True) 34 | api_url = f"https://tele-social.vercel.app/down?url={url}" 35 | 36 | try: 37 | async with aiohttp.ClientSession() as session: 38 | async with session.get(api_url) as response: 39 | logger.info(f"API request to {api_url} returned status {response.status}") 40 | if response.status == 200: 41 | data = await response.json() 42 | logger.info(f"API response: {data}") 43 | if data["status"]: 44 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN) 45 | media_url = data["url"] 46 | title = data.get("filename", "Pinterest Media") 47 | filename = self.temp_dir / title 48 | await self._download_file(session, media_url, filename) 49 | return { 50 | 'title': title, 51 | 'filename': str(filename), 52 | 'webpage_url': url 53 | } 54 | return None 55 | except Exception as e: 56 | logger.error(f"Pinterest download error: {e}") 57 | return None 58 | 59 | async def _download_file(self, session, url, dest): 60 | async with session.get(url) as response: 61 | if response.status == 200: 62 | logger.info(f"Downloading media from {url} to {dest}") 63 | f = await aiofiles.open(dest, mode='wb') 64 | await f.write(await response.read()) 65 | await f.close() 66 | 67 | async def progress_bar(current, total, status_message, start_time, last_update_time): 68 | """ 69 | Display a progress bar for uploads. 70 | """ 71 | elapsed_time = time.time() - start_time 72 | percentage = (current / total) * 100 73 | progress = "▓" * int(percentage // 5) + "░" * (20 - int(percentage // 5)) 74 | speed = current / elapsed_time / 1024 / 1024 # Speed in MB/s 75 | uploaded = current / 1024 / 1024 # Uploaded size in MB 76 | total_size = total / 1024 / 1024 # Total size in MB 77 | 78 | # Throttle updates: Only update if at least second has passed since the last update 79 | if time.time() - last_update_time[0] < 1: 80 | return 81 | last_update_time[0] = time.time() # Update the last update time 82 | 83 | text = ( 84 | f"📥 Upload Progress 📥\n\n" 85 | f"{progress}\n\n" 86 | f"🚧 Percentage: {percentage:.2f}%\n" 87 | f"⚡️ Speed: {speed:.2f} MB/s\n" 88 | f"📶 Uploaded: {uploaded:.2f} MB of {total_size:.2f} MB" 89 | ) 90 | try: 91 | await status_message.edit(text) 92 | except Exception as e: 93 | logger.error(f"Error updating progress: {e}") 94 | 95 | def setup_pinterest_handler(app: Client): 96 | pin_downloader = PinterestDownloader(Config.TEMP_DIR) 97 | 98 | # Create a regex pattern from the COMMAND_PREFIX list 99 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]" 100 | 101 | @app.on_message(filters.regex(rf"^{command_prefix_regex}pin(\s+https?://\S+)?$") & (filters.private | filters.group)) 102 | async def pin_handler(client: Client, message: Message): 103 | command_parts = message.text.split(maxsplit=1) 104 | if len(command_parts) < 2: 105 | await client.send_message( 106 | chat_id=message.chat.id, 107 | text="**Please provide a Pinterest link ❌**", 108 | parse_mode=ParseMode.MARKDOWN 109 | ) 110 | return 111 | 112 | url = command_parts[1] 113 | downloading_message = await client.send_message( 114 | chat_id=message.chat.id, 115 | text="**Searching The Media**", 116 | parse_mode=ParseMode.MARKDOWN 117 | ) 118 | 119 | try: 120 | media_info = await pin_downloader.download_media(url, downloading_message) 121 | if media_info: 122 | title = media_info['title'] 123 | filename = media_info['filename'] 124 | webpage_url = media_info['webpage_url'] 125 | 126 | if message.from_user: 127 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 128 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 129 | else: 130 | group_name = message.chat.title or "this group" 131 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 132 | user_info = f"[{group_name}]({group_url})" 133 | 134 | caption = ( 135 | f"🎥 **Title**: **{title}**\n" 136 | f"━━━━━━━━━━━━━━━━━━━━━\n" 137 | f"🔗 **Url**: [Watch On Pinterest]({webpage_url})\n" 138 | f"━━━━━━━━━━━━━━━━━━━━━\n" 139 | f"**Downloaded By**: {user_info}" 140 | ) 141 | 142 | async with aiofiles.open(filename, 'rb') as media_file: 143 | start_time = time.time() 144 | last_update_time = [start_time] 145 | await client.send_video( 146 | chat_id=message.chat.id, 147 | video=filename, 148 | supports_streaming=True, 149 | caption=caption, 150 | parse_mode=ParseMode.MARKDOWN, 151 | progress=progress_bar, 152 | progress_args=(downloading_message, start_time, last_update_time) 153 | ) 154 | 155 | await downloading_message.delete() 156 | os.remove(filename) 157 | else: 158 | await downloading_message.edit_text("**Unable To Extract Url**") 159 | except Exception as e: 160 | logger.error(f"Error downloading Pinterest Media: {e}") 161 | await downloading_message.edit_text("**Pinterest Downloader API Dead**") 162 | -------------------------------------------------------------------------------- /tiktok/tiktok.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import re 4 | import logging 5 | import aiohttp 6 | from pyrogram import Client, filters 7 | from pyrogram.enums import ParseMode 8 | from pyrogram.types import Message 9 | from config import COMMAND_PREFIX 10 | 11 | # Set up logging 12 | logging.basicConfig(level=logging.INFO) 13 | logger = logging.getLogger(__name__) 14 | 15 | # Progress bar function for uploads 16 | async def progress_bar(current, total, status_message: Message, start_time, last_update_time): 17 | elapsed_time = time.time() - start_time 18 | percentage = (current / total) * 100 19 | progress = "▓" * int(percentage // 5) + "░" * (20 - int(percentage // 5)) 20 | speed = current / elapsed_time / 1024 / 1024 # Speed in MB/s 21 | uploaded = current / 1024 / 1024 # Uploaded size in MB 22 | total_size = total / 1024 / 1024 # Total size in MB 23 | 24 | # Throttle updates: Only update if at least 1 second has passed since the last update 25 | if time.time() - last_update_time[0] < 1: 26 | return 27 | last_update_time[0] = time.time() # Update the last update time 28 | 29 | text = ( 30 | f"📥 Upload Progress 📥\n\n" 31 | f"{progress}\n\n" 32 | f"🚧 Percentage: {percentage:.2f}%\n" 33 | f"⚡️ Speed: {speed:.2f} MB/s\n" 34 | f"📶 Uploaded: {uploaded:.2f} MB of {total_size:.2f} MB" 35 | ) 36 | try: 37 | await status_message.edit(text) 38 | except Exception as e: 39 | logger.error(f"Error updating progress: {e}") 40 | 41 | # Function to download video and audio using the provided API 42 | async def download_video(url, downloading_message: Message): 43 | api_url = f"https://tele-social.vercel.app/down?url={url}" 44 | 45 | try: 46 | async with aiohttp.ClientSession() as session: 47 | async with session.get(api_url) as response: 48 | if response.status == 200: 49 | data = await response.json() 50 | if data["status"]: 51 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN) 52 | video_url = data["data"]["video"] 53 | audio_url = data["data"]["audio"] 54 | 55 | video_output = "tiktok_video.mp4" 56 | async with session.get(video_url) as video_response: 57 | if video_response.status == 200: 58 | with open(video_output, 'wb') as f: 59 | f.write(await video_response.read()) 60 | logger.info(f"Video downloaded successfully to: {video_output}") 61 | return video_output 62 | else: 63 | logger.error("Failed to download video") 64 | return None 65 | else: 66 | logger.error("Failed to get a valid response from the API") 67 | return None 68 | else: 69 | logger.error("Failed to reach the download API") 70 | return None 71 | except Exception as e: 72 | logger.error(f"Error downloading video: {e}") 73 | return None 74 | 75 | # Function to set up TikTok handler 76 | def setup_tt_handler(app: Client): 77 | @app.on_message(filters.command(["tt"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group)) 78 | async def tiktok_handler(client, message): 79 | match = re.findall(r"^[/.]tt(\s+https?://\S+)?$", message.text) 80 | if not match or len(match[0].strip()) == 0: 81 | await client.send_message( 82 | chat_id=message.chat.id, 83 | text="**❌ Please provide a TikTok video link.**", 84 | parse_mode=ParseMode.MARKDOWN 85 | ) 86 | return 87 | 88 | url = match[0].strip() 89 | 90 | # Step 1: Send the initial "Searching Video" message 91 | status_message = await client.send_message( 92 | chat_id=message.chat.id, 93 | text="**Searching The Video...**", 94 | parse_mode=ParseMode.MARKDOWN 95 | ) 96 | 97 | try: 98 | # Step 2: Download the TikTok video using the provided API 99 | video_path = await download_video(url, status_message) 100 | 101 | if not video_path: 102 | await status_message.edit("**❌Invalid Video URL Inputed**") 103 | return 104 | 105 | # Step 3: Get user information 106 | if message.from_user: 107 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 108 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 109 | else: 110 | group_name = message.chat.title or "this group" 111 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 112 | user_info = f"[{group_name}]({group_url})" 113 | 114 | # Dummy metadata for demonstration purposes 115 | title = "TikTok Video" 116 | views = 1000 117 | duration_minutes = 0 118 | duration_seconds = 20 119 | 120 | # Step 4: Create the formatted message with dummy data 121 | caption = ( 122 | f"🎵 **Title**: **{title}**\n" 123 | f"━━━━━━━━━━━━━━━━━━━━━\n" 124 | f"👁️‍🗨️ **Views**: **{views} views**\n" 125 | f"🔗 **Url**: [Watch On TikTok]({url})\n" 126 | f"⏱️ **Duration**: **{duration_minutes}:{duration_seconds:02d}**\n" 127 | f"━━━━━━━━━━━━━━━━━━━━━\n" 128 | f"**Downloaded By**: {user_info}" 129 | ) 130 | 131 | # Step 5: Start uploading the video with progress 132 | start_time = time.time() 133 | last_update_time = [start_time] # Store the last update time to throttle the progress updates 134 | 135 | await client.send_video( 136 | chat_id=message.chat.id, 137 | video=video_path, 138 | caption=caption, 139 | parse_mode=ParseMode.MARKDOWN, 140 | progress=progress_bar, 141 | progress_args=(status_message, start_time, last_update_time) 142 | ) 143 | 144 | # Step 6: Delete the status message after upload starts 145 | await status_message.delete() 146 | 147 | # Clean up the downloaded video file after sending 148 | os.remove(video_path) 149 | logger.info(f"Deleted the video file: {video_path}") 150 | 151 | except Exception as e: 152 | logger.error(f"An error occurred: {e}") 153 | # If something goes wrong during the download, show error message 154 | await client.send_message( 155 | chat_id=message.chat.id, 156 | text="**TikTok Downloader API Dead**", 157 | parse_mode=ParseMode.MARKDOWN 158 | ) 159 | -------------------------------------------------------------------------------- /facebook/facebook.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import time 4 | import re 5 | from pathlib import Path 6 | from typing import Optional 7 | import aiohttp 8 | import asyncio 9 | import aiofiles 10 | from pyrogram import Client, filters 11 | from pyrogram.types import Message 12 | from pyrogram.enums import ParseMode 13 | from config import COMMAND_PREFIX # Import COMMAND_PREFIX from config 14 | 15 | # Configure logging 16 | logging.basicConfig( 17 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 18 | level=logging.INFO 19 | ) 20 | logger = logging.getLogger(__name__) 21 | 22 | # Configuration 23 | class Config: 24 | TEMP_DIR = Path("temp") 25 | 26 | Config.TEMP_DIR.mkdir(exist_ok=True) 27 | 28 | class FacebookDownloader: 29 | def __init__(self, temp_dir: Path): 30 | self.temp_dir = temp_dir 31 | 32 | async def download_video(self, url: str, downloading_message: Message) -> Optional[dict]: 33 | self.temp_dir.mkdir(exist_ok=True) 34 | api_url = f"https://tooly.chative.io/facebook/video?url={url}" 35 | 36 | try: 37 | connector = aiohttp.TCPConnector(limit_per_host=10) 38 | async with aiohttp.ClientSession(connector=connector) as session: 39 | async with session.get(api_url) as response: 40 | logger.info(f"API request to {api_url} returned status {response.status}") 41 | if response.status == 200: 42 | data = await response.json() 43 | logger.info(f"API response: {data}") 44 | if data["success"]: 45 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN) 46 | video_url = data["videos"]["hd"]["url"] 47 | title = data["title"] 48 | filename = self.temp_dir / f"{title}.mp4" 49 | await self._download_file(session, video_url, filename) 50 | return { 51 | 'title': title, 52 | 'filename': str(filename), 53 | 'webpage_url': url 54 | } 55 | return None 56 | except Exception as e: 57 | logger.error(f"Facebook download error: {e}") 58 | return None 59 | 60 | async def _download_file(self, session, url, dest): 61 | async with session.get(url) as response: 62 | if response.status == 200: 63 | logger.info(f"Downloading video from {url} to {dest}") 64 | f = await aiofiles.open(dest, mode='wb') 65 | await f.write(await response.read()) 66 | await f.close() 67 | 68 | def setup_dl_handlers(app: Client): 69 | fb_downloader = FacebookDownloader(Config.TEMP_DIR) 70 | 71 | # Create a regex pattern from the COMMAND_PREFIX list 72 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]" 73 | 74 | @app.on_message(filters.regex(rf"^{command_prefix_regex}fb(\s+https?://\S+)?$") & (filters.private | filters.group)) 75 | async def fb_handler(client: Client, message: Message): 76 | command_parts = message.text.split(maxsplit=1) 77 | if len(command_parts) < 2: 78 | await client.send_message( 79 | chat_id=message.chat.id, 80 | text="**Please provide a Facebook link ❌**", 81 | parse_mode=ParseMode.MARKDOWN 82 | ) 83 | return 84 | 85 | url = command_parts[1] 86 | downloading_message = await client.send_message( 87 | chat_id=message.chat.id, 88 | text="**Searching The Video**", 89 | parse_mode=ParseMode.MARKDOWN 90 | ) 91 | 92 | try: 93 | video_info = await fb_downloader.download_video(url, downloading_message) 94 | if video_info: 95 | title = video_info['title'] 96 | filename = video_info['filename'] 97 | webpage_url = video_info['webpage_url'] 98 | 99 | if message.from_user: 100 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 101 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 102 | else: 103 | group_name = message.chat.title or "this group" 104 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 105 | user_info = f"[{group_name}]({group_url})" 106 | 107 | caption = ( 108 | f"🎵 **Title**: **{title}**\n" 109 | f"━━━━━━━━━━━━━━━━━━━━━\n" 110 | f"🔗 **Url**: [Watch On Facebook]({webpage_url})\n" 111 | f"━━━━━━━━━━━━━━━━━━━━━\n" 112 | f"**Downloaded By**: {user_info}" 113 | ) 114 | 115 | async with aiofiles.open(filename, 'rb') as video_file: 116 | start_time = time.time() 117 | last_update_time = [start_time] 118 | await client.send_video( 119 | chat_id=message.chat.id, 120 | video=filename, 121 | supports_streaming=True, 122 | caption=caption, 123 | parse_mode=ParseMode.MARKDOWN, 124 | progress=progress_bar, 125 | progress_args=(downloading_message, start_time, last_update_time) 126 | ) 127 | 128 | await downloading_message.delete() 129 | os.remove(filename) 130 | else: 131 | logger.info("Invalid Video URL or Video is Private") 132 | await downloading_message.edit_text("**Invalid Video URL Or Video Private**") 133 | except Exception as e: 134 | logger.error(f"Error downloading Facebook video: {e}") 135 | await downloading_message.edit_text("**Facebook Downloader API Dead**") 136 | 137 | async def progress_bar(current, total, status_message, start_time, last_update_time): 138 | """ 139 | Display a progress bar for uploads. 140 | """ 141 | elapsed_time = time.time() - start_time 142 | percentage = (current / total) * 100 143 | progress = "▓" * int(percentage // 5) + "░" * (20 - int(percentage // 5)) 144 | speed = current / elapsed_time / 1024 / 1024 # Speed in MB/s 145 | uploaded = current / 1024 / 1024 # Uploaded size in MB 146 | total_size = total / 1024 / 1024 # Total size in MB 147 | 148 | # Throttle updates: Only update if at least 1 second has passed since the last update 149 | if time.time() - last_update_time[0] < 1: 150 | return 151 | last_update_time[0] = time.time() # Update the last update time 152 | 153 | text = ( 154 | f"📥 Upload Progress 📥\n\n" 155 | f"{progress}\n\n" 156 | f"🚧 Percentage: {percentage:.2f}%\n" 157 | f"⚡️ Speed: {speed:.2f} MB/s\n" 158 | f"📶 Uploaded: {uploaded:.2f} MB of {total_size:.2f} MB" 159 | ) 160 | try: 161 | await status_message.edit(text) 162 | except Exception as e: 163 | logger.error(f"Error updating progress: {e}") 164 | -------------------------------------------------------------------------------- /instagram/instagram.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartDevs 2 | #Channel t.me/TheSmartDev 3 | import os 4 | import re 5 | import logging 6 | import time 7 | from pathlib import Path 8 | from typing import Optional 9 | import aiohttp 10 | import asyncio 11 | import aiofiles 12 | from pyrogram import Client, filters 13 | from pyrogram.types import Message 14 | from pyrogram.enums import ParseMode 15 | from config import COMMAND_PREFIX 16 | 17 | # Configure logging 18 | logging.basicConfig( 19 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 20 | level=logging.INFO 21 | ) 22 | logger = logging.getLogger(__name__) 23 | 24 | # Configuration 25 | class Config: 26 | TEMP_DIR = Path("temp") 27 | 28 | Config.TEMP_DIR.mkdir(exist_ok=True) 29 | 30 | class InstagramDownloader: 31 | def __init__(self, temp_dir: Path): 32 | self.temp_dir = temp_dir 33 | 34 | async def download_reel(self, url: str, downloading_message: Message) -> Optional[dict]: 35 | self.temp_dir.mkdir(exist_ok=True) 36 | api_url = f"https://www.alphaapis.org/Instagram/dl/v1?url={url}" 37 | 38 | try: 39 | async with aiohttp.ClientSession() as session: 40 | async with session.get(api_url) as response: 41 | logger.info(f"API request to {api_url} returned status {response.status}") 42 | if response.status == 200: 43 | data = await response.json() 44 | logger.info(f"API response: {data}") 45 | if data["success"]: 46 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN) 47 | video_url = data["result"][0]["downloadLink"] 48 | title = data["result"][0].get("filename", "Instagram Reel") 49 | filename = self.temp_dir / title 50 | await self._download_file(session, video_url, filename) 51 | return { 52 | 'title': title, 53 | 'filename': str(filename), 54 | 'webpage_url': url 55 | } 56 | return None 57 | except Exception as e: 58 | logger.error(f"Instagram Reels download error: {e}") 59 | return None 60 | 61 | async def _download_file(self, session, url, dest): 62 | async with session.get(url) as response: 63 | if response.status == 200: 64 | logger.info(f"Downloading video from {url} to {dest}") 65 | f = await aiofiles.open(dest, mode='wb') 66 | await f.write(await response.read()) 67 | await f.close() 68 | 69 | async def progress_bar(current, total, status_message, start_time, last_update_time): 70 | """ 71 | Display a progress bar for uploads. 72 | """ 73 | elapsed_time = time.time() - start_time 74 | percentage = (current / total) * 100 75 | progress = "▓" * int(percentage // 5) + "░" * (20 - int(percentage // 5)) 76 | speed = current / elapsed_time / 1024 / 1024 # Speed in MB/s 77 | uploaded = current / 1024 / 1024 # Uploaded size in MB 78 | total_size = total / 1024 / 1024 # Total size in MB 79 | 80 | # Throttle updates: Only update if at least second has passed since the last update 81 | if time.time() - last_update_time[0] < 1: 82 | return 83 | last_update_time[0] = time.time() # Update the last update time 84 | 85 | text = ( 86 | f"📥 Upload Progress 📥\n\n" 87 | f"{progress}\n\n" 88 | f"🚧 Percentage: {percentage:.2f}%\n" 89 | f"⚡️ Speed: {speed:.2f} MB/s\n" 90 | f"📶 Uploaded: {uploaded:.2f} MB of {total_size:.2f} MB" 91 | ) 92 | try: 93 | await status_message.edit(text) 94 | except Exception as e: 95 | logger.error(f"Error updating progress: {e}") 96 | 97 | def setup_in_handlers(app: Client): 98 | ig_downloader = InstagramDownloader(Config.TEMP_DIR) 99 | 100 | # Create a regex pattern from the COMMAND_PREFIX list 101 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]" 102 | 103 | @app.on_message(filters.regex(rf"^{command_prefix_regex}in(\s+https?://\S+)?$") & (filters.private | filters.group)) 104 | async def ig_handler(client: Client, message: Message): 105 | command_parts = message.text.split(maxsplit=1) 106 | if len(command_parts) < 2: 107 | await client.send_message( 108 | chat_id=message.chat.id, 109 | text="**Please provide an Instagram Reels link ❌**", 110 | parse_mode=ParseMode.MARKDOWN 111 | ) 112 | return 113 | 114 | url = command_parts[1] 115 | downloading_message = await client.send_message( 116 | chat_id=message.chat.id, 117 | text="**Searching The Reel**", 118 | parse_mode=ParseMode.MARKDOWN 119 | ) 120 | 121 | try: 122 | reel_info = await ig_downloader.download_reel(url, downloading_message) 123 | if reel_info: 124 | title = reel_info['title'] 125 | filename = reel_info['filename'] 126 | webpage_url = reel_info['webpage_url'] 127 | 128 | if message.from_user: 129 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 130 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 131 | else: 132 | group_name = message.chat.title or "this group" 133 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 134 | user_info = f"[{group_name}]({group_url})" 135 | 136 | caption = ( 137 | f"🎥 **Title**: **{title}**\n" 138 | f"━━━━━━━━━━━━━━━━━━━━━\n" 139 | f"🔗 **Url**: [Watch On Instagram]({webpage_url})\n" 140 | f"━━━━━━━━━━━━━━━━━━━━━\n" 141 | f"**Downloaded By**: {user_info}" 142 | ) 143 | 144 | async with aiofiles.open(filename, 'rb') as video_file: 145 | start_time = time.time() 146 | last_update_time = [start_time] 147 | await client.send_video( 148 | chat_id=message.chat.id, 149 | video=filename, 150 | supports_streaming=True, 151 | caption=caption, 152 | parse_mode=ParseMode.MARKDOWN, 153 | progress=progress_bar, 154 | progress_args=(downloading_message, start_time, last_update_time) 155 | ) 156 | 157 | await downloading_message.delete() 158 | os.remove(filename) 159 | else: 160 | await downloading_message.edit_text("**Unable To Extract Url**") 161 | except Exception as e: 162 | logger.error(f"Error downloading Instagram Reel: {e}") 163 | await downloading_message.edit_text("**Instagram Downloader API Dead**") 164 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | from threading import Thread 4 | from flask import Flask 5 | from pyrogram import Client, filters 6 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 7 | from pyrogram.enums import ParseMode 8 | from config import API_ID, API_HASH, BOT_TOKEN 9 | from utils import LOGGER 10 | 11 | # Import the handlers 12 | from youtube.youtube import setup_downloader_handler 13 | from pinterest.pinterest import setup_pinterest_handler 14 | from facebook.facebook import setup_dl_handlers 15 | from spotify.spotify import setup_spotify_handler 16 | from tiktok.tiktok import setup_tt_handler 17 | from instagram.instagram import setup_in_handlers 18 | from adminpanel.restart.restart import setup_restart_handler 19 | from adminpanel.admin.admin import setup_admin_handler 20 | from adminpanel.logs.logs import setup_logs_handler 21 | 22 | # Setup minimal Flask server to prevent Heroku R10 error 23 | flask_app = Flask(__name__) 24 | 25 | @flask_app.route('/') 26 | def index(): 27 | return "Smart Tool Bot is running!" 28 | 29 | def run_flask(): 30 | port = int(os.environ.get("PORT", 5000)) 31 | flask_app.run(host="0.0.0.0", port=port) 32 | 33 | # Start Flask in background 34 | Thread(target=run_flask).start() 35 | 36 | # Initialize the bot client 37 | app = Client( 38 | "app_session", 39 | api_id=API_ID, 40 | api_hash=API_HASH, 41 | bot_token=BOT_TOKEN 42 | ) 43 | 44 | # Setup all handlers 45 | setup_downloader_handler(app) 46 | setup_pinterest_handler(app) 47 | setup_dl_handlers(app) 48 | setup_spotify_handler(app) 49 | setup_restart_handler(app) 50 | setup_admin_handler(app) 51 | setup_logs_handler(app) 52 | setup_in_handlers(app) 53 | setup_tt_handler(app) 54 | 55 | @app.on_message(filters.command(["start"], prefixes=["/", "."]) & filters.private) 56 | async def send_start_message(client, message): 57 | chat_id = message.chat.id 58 | full_name = f"{message.from_user.first_name} {message.from_user.last_name}" if message.from_user.last_name else message.from_user.first_name 59 | 60 | animation_message = await message.reply_text("Starting Smart Tool ⚙️...", parse_mode=ParseMode.HTML) 61 | await asyncio.sleep(0.4) 62 | await animation_message.edit_text("Generating Session Keys Please Wait...", parse_mode=ParseMode.HTML) 63 | await asyncio.sleep(0.4) 64 | await animation_message.delete() 65 | 66 | start_message = ( 67 | f"Hi {full_name}! Welcome To This Bot...\n" 68 | "━━━━━━━━━━━━━━━━━━━━━━━━━━\n" 69 | "Smart Tool ⚙️: The ultimate toolkit on Telegram, offering Facebook,YouTube,Pinterest,Spotify Downloader. Simplify your tasks with ease!\n" 70 | "━━━━━━━━━━━━━━━━━━━━━━━━━━\n" 71 | "Don't Forget To Join Here For Updates!" 72 | ) 73 | 74 | await message.reply_text( 75 | start_message, 76 | parse_mode=ParseMode.HTML, 77 | reply_markup=InlineKeyboardMarkup([ 78 | [InlineKeyboardButton("⚙️ Help", callback_data="help_menu"), 79 | InlineKeyboardButton("➕ Add Me", url="https://t.me/ItsSmartToolBot?startgroup=new&admin=post_messages+delete_messages+edit_messages+pin_messages+change_info+invite_users+promote_members")], 80 | [InlineKeyboardButton("🔄 Updates", url="https://t.me/ModVipRM"), 81 | InlineKeyboardButton("ℹ️ About Me", callback_data="about_me")] 82 | ]), 83 | disable_web_page_preview=True, 84 | ) 85 | 86 | @app.on_callback_query(filters.regex("help_menu")) 87 | async def help_menu_callback(client: Client, callback_query: CallbackQuery): 88 | help_message = ( 89 | "🎥 Social Media and Music Downloader\n" 90 | "━━━━━━━━━━━━━━━━━━━━━━\n" 91 | "USAGE:\n" 92 | "Download videos and tracks from popular platforms using these commands:\n\n" 93 | "➢ /fb [Video URL] - Download a Facebook video.\n" 94 | "➢ /pin [Video URL] - Download a Pinterest video.\n" 95 | "➢ /tt [Video URL] - Download a TikTok video.\n" 96 | "➢ /in [Video URL] - Download Instagram Reels.\n" 97 | "➢ /sp [Track URL] - Download a Spotify track.\n" 98 | "➢ /yt [Video URL] - Download a YouTube video.\n" 99 | "➢ /song [Video URL] - Download as MP3\n" 100 | "━━━━━━━━━━━━━━━━━━━━━━\n" 101 | "🔔 For Bot Updates: Join Now" 102 | ) 103 | 104 | await callback_query.message.edit_text( 105 | help_message, 106 | parse_mode=ParseMode.HTML, 107 | disable_web_page_preview=True, 108 | reply_markup=InlineKeyboardMarkup([ 109 | [InlineKeyboardButton("🔙 Back", callback_data="start_menu")] 110 | ]) 111 | ) 112 | 113 | @app.on_callback_query(filters.regex("about_me")) 114 | async def about_me_callback(client: Client, callback_query: CallbackQuery): 115 | about_message = ( 116 | "Name: Smart Tool ⚙️\n" 117 | "Version: 3.0 (Beta Testing)\n\n" 118 | "Creator: ⏤͟͞〲ᗩᗷiᖇ 𓊈乂ᗪ𓊉 👨‍💻\n" 119 | "Tech: Python · Pyrogram · Telethon · MongoDB\n" 120 | "About: Download from YouTube, Instagram, Facebook, Pinterest, TikTok, Spotify.\n" 121 | "━━━━━━━━━━━━━━━━━━━━━━━━━━\n" 122 | "🔔 Updates: Join Here" 123 | ) 124 | 125 | await callback_query.message.edit_text( 126 | about_message, 127 | parse_mode=ParseMode.HTML, 128 | disable_web_page_preview=True, 129 | reply_markup=InlineKeyboardMarkup([ 130 | [InlineKeyboardButton("🔙 Back", callback_data="start_menu")] 131 | ]) 132 | ) 133 | 134 | @app.on_callback_query(filters.regex("start_menu")) 135 | async def start_menu_callback(client: Client, callback_query: CallbackQuery): 136 | full_name = f"{callback_query.from_user.first_name} {callback_query.from_user.last_name}" if callback_query.from_user.last_name else callback_query.from_user.first_name 137 | 138 | start_message = ( 139 | f"Hi {full_name}! Welcome To This Bot...\n" 140 | "━━━━━━━━━━━━━━━━━━━━━━━━━━\n" 141 | "Smart Tool ⚙️: The ultimate toolkit on Telegram, offering Facebook,YouTube,Pinterest,Spotify Downloader. Simplify your tasks with ease!\n" 142 | "━━━━━━━━━━━━━━━━━━━━━━━━━━\n" 143 | "Don't Forget To Join Here For Updates!" 144 | ) 145 | 146 | await callback_query.message.edit_text( 147 | start_message, 148 | parse_mode=ParseMode.HTML, 149 | reply_markup=InlineKeyboardMarkup([ 150 | [InlineKeyboardButton("⚙️ Help", callback_data="help_menu"), 151 | InlineKeyboardButton("➕ Add Me", url="https://t.me/ItsSmartToolBot?startgroup=new&admin=post_messages+delete_messages+edit_messages+pin_messages+change_info+invite_users+promote_members")], 152 | [InlineKeyboardButton("🔄 Updates", url="https://t.me/ModVipRM"), 153 | InlineKeyboardButton("ℹ️ About Me", callback_data="about_me")] 154 | ]), 155 | disable_web_page_preview=True, 156 | ) 157 | 158 | # Final confirmation that the bot has started 159 | print("✅ Bot Successfully Started and Flask is running on Heroku.") 160 | app.run() 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SmartDlBot Telegram Bot 🌌

2 | 3 |

4 | SmartDlBot: The ultimate toolkit on Telegram, offering Facebook,YouTube,Pinterest,Spotify Downloader. Simplify your tasks with ease! 5 |

6 | 7 |

8 | GitHub stars 9 | GitHub forks 10 | GitHub issues 11 | GitHub pull requests 12 | PyPI - Python Version 13 |

14 | 15 |
16 | 17 | ## 🌟 Features 18 | 19 | - **Facebook Video Downloader**: Download videos from Facebook. 20 | - **YouTube Video Downloader**: Download videos from YouTube. 21 | - **Pinterest Video Downloader**: Download videos from Pinterest. 22 | - **Spotify Track Downloader**: Download tracks from Spotify. 23 | - **Instagram Reel Downloader**: Download Reels From Instagram 24 | - **TikTok Video Downloader**: Download TikTok Video From TikTok 25 | 26 | ## Installation 27 | 28 | To install `pyrogram` , `yt-dlp` , `spotipy` , `requests` , `pillow` , `asyncio` , `aiofiles` , `aiohttp` run the following command: 29 | 30 | ```bash 31 | pip install -r requirements.txt 32 | ``` 33 | 34 | **Note: If you previously installed `pyrofork`, uninstall it before installing `pyrogram`.** 35 | 36 | ## Handling YouTube Download Errors with Cookies 37 | 38 | To avoid errors related to YouTube sign-in requirements, using a cookie file is effective. Here's how to set it up: 39 | 40 | ### Steps to Export and Use Cookies: 41 | 42 | 1. **Create a Dedicated Chrome Profile:** 43 | - It's recommended to create a new Chrome profile for managing your bot's cookies. 44 | 45 | 2. **Install a Cookie Management Extension:** 46 | - Use [Cookie Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) or similar extensions to manage your cookies. 47 | 48 | 3. **Export Cookies from YouTube:** 49 | - Log into YouTube in your new browser profile and export cookies in Netscape format via the cookie extension. 50 | 51 | 4. **Save the Cookies File:** 52 | - Update your `cookies.txt` in the `SmartDlBot/cookies` directory of your project. 53 | 54 | ### Managing Cookies: 55 | 56 | - **Cookie Expiry:** 57 | - Refresh your cookies by exporting new ones if you encounter download issues. 58 | 59 | - **Cookie Depletion:** 60 | - Avoid frequent bot restarts and excessive YouTube requests to prevent early cookie expiry. 61 | 62 | This setup should help manage YouTube content access efficiently without encountering sign-in or bot protection errors. 63 | 64 | ## Configuration 65 | 66 | 1. Open the `config.py` file in your favorite text editor. 67 | 2. Replace the placeholders for `API_ID`, `API_HASH`, `BOT_TOKEN`, `SPOTIFY_CLIENT_ID`, and `SPOTIFY_CLIENT_SECRET` with your actual values: 68 | - **`API_ID`**: Your API ID from [my.telegram.org](https://my.telegram.org). 69 | - **`API_HASH`**: Your API Hash from [my.telegram.org](https://my.telegram.org). 70 | - **`BOT_TOKEN`**: The token you obtained from [@BotFather](https://t.me/BotFather). 71 | - **`SPOTIFY_CLIENT_ID`**: SPOTIFY_CLIENT_ID from [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) 72 | - **`SPOTIFY_CLIENT_SECRET`**: SPOTIFY_CLIENT_SECRET from [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) 73 | - **`MONGO_URL`**: MONGO_URL from [MONGODB DATABASE](http://mongodb.com/) 74 | 75 | ## Deploy the Bot 76 | 77 | ```sh 78 | git clone https://github.com/abirxdhack/SmartDlBot 79 | cd SmartDlBot 80 | python3 main.py 81 | ``` 82 | 83 | ## Deploy the Bot With Screen 84 | 85 | ```sh 86 | git clone https://github.com/abirxdhack/SmartDlBot 87 | cd SmartDlBot 88 | screen -S SmartDlBot 89 | python3 main.py 90 | ``` 91 | 92 | ## Usage 🛠️ 93 | 94 | The bot supports the following commands: 95 | 96 | Download videos and tracks from popular platforms using these commands: 97 | 98 | ➢ **`/fb [Video URL]`** - Download a Facebook video. 99 | - Example: **/fb https://www.facebook.com/share/v/18VH1yNXoq/** **`Downloads the specified Facebook video`** 100 | - Note: Private Facebook videos cannot be downloaded. 101 | 102 | ➢ **`/pin [Video URL]`** - Download a Pinterest video. 103 | - Example: **/pin https://pin.it/6GoDMRwmE** **`Downloads the specified Pinterest video`** 104 | 105 | ➢ **`/in [Video URL]`** - Download a Instagram video. 106 | - Example: **/in https://www.instagram.com/reel/C_vOYErJBm7/?igsh=YzljYTk1ODg3Zg== ** **`Downloads the specified Instagram video`** 107 | 108 | ➢ **`/tt [Video URL]`** - Download a TikTok Video. 109 | - Example: **/tt https://vt.tiktok.com/ZSMV19Kfu/** **`Downloads the specified TikTok Video**` 110 | 111 | ➢ **`/sp [Track URL]`** - Download a Spotify track. 112 | - Example: **/sp https://open.spotify.com/track/7ouBSPZKQpm7zQz2leJXta** **`Downloads the specified Spotify track**` 113 | 114 | ➢ **`/yt [Video URL]`** - Download a YouTube video. 115 | - Example: **/yt https://youtu.be/In8bfGnXavw** **`Downloads the specified YouTube video`** 116 | 117 | ➢ **`/song [Video URL]`** - Download a YouTube video as an MP3 file. 118 | - Example: **/song https://youtu.be/In8bfGnXavw** **`Converts and downloads the video as MP3`** 119 | 120 | **Or You Can Use Below Commands ** 121 | 122 | ➢ **`.fb [Video URL]`** - Download a Facebook video. 123 | - Example: **.fb https://www.facebook.com/share/v/18VH1yNXoq/** **`Downloads the specified Facebook video`** 124 | - Note: Private Facebook videos cannot be downloaded. 125 | 126 | ➢ **`.pin [Video URL]`** - Download a Pinterest video. 127 | - Example: **.pin https://pin.it/6GoDMRwmE** **`Downloads the specified Pinterest video`** 128 | - Note: 18+ Instagram Reels cannot be downloaded. 129 | 130 | ➢ **`.in [Video URL]`** - Download a Instagram video. 131 | - Example: **/in https://www.instagram.com/reel/C_vOYErJBm7/?igsh=YzljYTk1ODg3Zg== ** **`Downloads the specified Instagram video`** 132 | 133 | ➢ **`.tt [Video URL]`** - Download a TikTok Video. 134 | - Example: **/tt https://vt.tiktok.com/ZSMV19Kfu/** **`Downloads the specified TikTok Video**` 135 | 136 | ➢ **`.sp [Track URL]`** - Download a Spotify track. 137 | - Example: **.sp https://open.spotify.com/track/7ouBSPZKQpm7zQz2leJXta** **`Downloads the specified Spotify track`** 138 | 139 | ➢ **`.yt [Video URL]`** - Download a YouTube video. 140 | - Example: **.yt https://youtu.be/In8bfGnXavw** **`Downloads the specified YouTube video`** 141 | 142 | ➢ **`.song [Video URL]`** - Download a YouTube video as an MP3 file. 143 | - Example: **.song https://youtu.be/In8bfGnXavw** **`Converts and downloads the video as MP3`** 144 | 145 | ## NOTE 146 | **Provide a valid public URL for each platform to download successfully** 147 | For inquiries or feedback, please feel free to reach out via Telegram. 148 | 149 | ## Ethical Notice 🔔 150 | > **Ethics Reminder:** Simply modifying a few lines of code does not constitute original authorship. When forking a project, always fork responsibly and give proper credit to the original creators. 151 | 152 | # Project Contributors 153 | 154 | ## Author 📝 155 | 156 | - Name: Abir Arafat Chawdhury 157 | - Telegram: [@abirxdhackz](https://t.me/abirxdhackz) 158 | 159 | ## Update Author 160 | - Name ᔰ ᦲ ᔰ 「🇲🇲」 161 | - Telegram: [@nkka404](https://t.me/nkka404) 162 | -------------------------------------------------------------------------------- /adminpanel/admin/admin.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pyrogram import Client, filters 3 | from pyrogram.handlers import MessageHandler 4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 5 | from pyrogram.enums import ParseMode 6 | from datetime import datetime, timedelta 7 | import pymongo 8 | from config import MONGO_URL, ADMIN_IDS 9 | 10 | # MongoDB connection details 11 | client = pymongo.MongoClient(MONGO_URL) 12 | db = client["user_activity_db"] 13 | user_activity_collection = db["user_activity"] 14 | 15 | 16 | # Function to update user activity in the MongoDB database 17 | def update_user_activity(user_id, is_group=False): 18 | now = datetime.utcnow() 19 | user = user_activity_collection.find_one({"user_id": user_id}) 20 | if not user: 21 | user_activity_collection.insert_one({ 22 | "user_id": user_id, 23 | "is_group": is_group, 24 | "last_activity": now, 25 | "daily": 0, 26 | "weekly": 0, 27 | "monthly": 0, 28 | "yearly": 0 29 | }) 30 | else: 31 | user_activity_collection.update_one( 32 | {"user_id": user_id}, 33 | {"$set": {"last_activity": now}}, 34 | upsert=True 35 | ) 36 | user_activity_collection.update_one( 37 | {"user_id": user_id}, 38 | {"$inc": {"daily": 1, "weekly": 1, "monthly": 1, "yearly": 1}}, 39 | ) 40 | 41 | def is_admin(user_id): 42 | return user_id in ADMIN_IDS 43 | 44 | async def broadcast_handler(client: Client, message: Message): 45 | if not is_admin(message.from_user.id): 46 | await message.reply_text("🚫 **You are not authorized to use this command.**") 47 | return 48 | 49 | if message.reply_to_message: 50 | broadcast_message = message.reply_to_message 51 | await process_broadcast(client, broadcast_message) 52 | else: 53 | await message.reply_text("📢 **Please send the message you want to broadcast.**") 54 | broadcast_message_handler = None 55 | 56 | async def broadcast_message_callback(client: Client, broadcast_msg: Message): 57 | nonlocal broadcast_message_handler 58 | if broadcast_msg.chat.id == message.chat.id and is_admin(broadcast_msg.from_user.id): 59 | await process_broadcast(client, broadcast_msg) 60 | client.remove_handler(*broadcast_message_handler) 61 | 62 | broadcast_message_handler = client.add_handler( 63 | MessageHandler(broadcast_message_callback, filters.group & filters.chat(message.chat.id)), 64 | group=1 65 | ) 66 | 67 | async def process_broadcast(client: Client, broadcast_msg: Message): 68 | global total_users, blocked_users, broadcast_start_time 69 | total_users, blocked_users = 0, 0 70 | broadcast_start_time = datetime.now() 71 | 72 | # Notify admin that the broadcast is being processed 73 | processing_message = await broadcast_msg.reply_text('**⏳Processing Broadcast⚡️**') 74 | 75 | # Get the message to broadcast 76 | broadcast_message_id = broadcast_msg.id 77 | 78 | # Get all user ids and group ids 79 | user_ids = [user["user_id"] for user in user_activity_collection.find()] 80 | group_ids = [group["user_id"] for group in user_activity_collection.find({"is_group": True})] 81 | 82 | # Broadcast the message to users and groups 83 | for user_id in user_ids + group_ids: 84 | try: 85 | await client.copy_message( 86 | chat_id=user_id, 87 | from_chat_id=broadcast_msg.chat.id, 88 | message_id=broadcast_message_id, 89 | reply_markup=InlineKeyboardMarkup( 90 | [[InlineKeyboardButton("Update Channel", url="https://t.me/Modvip_rm")]] 91 | ) 92 | ) 93 | total_users += 1 94 | except Exception as e: 95 | if "blocked" in str(e): 96 | blocked_users += 1 97 | continue 98 | 99 | # Calculate time taken for the broadcast 100 | broadcast_end_time = datetime.now() 101 | time_taken = (broadcast_end_time - broadcast_start_time).total_seconds() 102 | 103 | # Delete the processing message 104 | await processing_message.delete() 105 | 106 | # Send a completion message to the admin 107 | await broadcast_msg.reply_text( 108 | f"**✅ Successfully Broadcast Complete to {total_users} users in {time_taken:.2f} seconds**\n\n" 109 | f"**👥 To Users: {total_users}\n" 110 | f"🚫 Blocked: {blocked_users}**", 111 | reply_markup=InlineKeyboardMarkup( 112 | [[InlineKeyboardButton("Update Channel", url="https://t.me/Modvip_rm")]] 113 | ) 114 | ) 115 | 116 | async def stats_handler(client: Client, message: Message): 117 | if not is_admin(message.from_user.id): 118 | await message.reply_text("🚫 **You are not authorized to use this command.**") 119 | return 120 | 121 | now = datetime.utcnow() 122 | daily_users = user_activity_collection.count_documents({"last_activity": {"$gt": now - timedelta(days=1)}}) 123 | weekly_users = user_activity_collection.count_documents({"last_activity": {"$gt": now - timedelta(weeks=1)}}) 124 | monthly_users = user_activity_collection.count_documents({"last_activity": {"$gt": now - timedelta(days=30)}}) 125 | yearly_users = user_activity_collection.count_documents({"last_activity": {"$gt": now - timedelta(days=365)}}) 126 | total_users = yearly_users 127 | total_groups = user_activity_collection.count_documents({"is_group": True}) 128 | 129 | stats_text = ( 130 | "**📊 Bot Usage Report\n" 131 | "━━━━━━━━━━━\n" 132 | "🚀 User Engagements:\n" 133 | f"- Daily Starts: {daily_users}\n" 134 | f"- Weekly Starts: {weekly_users}\n" 135 | f"- Monthly Starts: {monthly_users}\n" 136 | f"- Annual Starts: {yearly_users}\n\n" 137 | "📈 Total Metrics:\n" 138 | f"- Total Groups: {total_groups}\n" 139 | f"- Users Registered: {total_users}**\n" 140 | ) 141 | 142 | keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("🔔 Bot Updates", url="https://t.me/Modvip_rm")]]) 143 | await message.reply_text(stats_text, parse_mode=ParseMode.MARKDOWN, reply_markup=keyboard, disable_web_page_preview=True) 144 | 145 | async def group_added_handler(client: Client, message: Message): 146 | for new_member in message.new_chat_members: 147 | if new_member.is_self: 148 | chat_id = message.chat.id 149 | update_user_activity(chat_id, is_group=True) 150 | await client.send_message( 151 | chat_id, 152 | "**Thank You For Adding Me In This Group! 👨‍💻**\n\n" 153 | "**I'm here to assist you with various tasks and make your group experience better. " 154 | "Feel free to explore my features and let me know if you need any help! 😊**", 155 | parse_mode=ParseMode.MARKDOWN, 156 | reply_markup=InlineKeyboardMarkup([ 157 | [InlineKeyboardButton("➕ Add Me", url="https://t.me/ItsSmartToolBot?startgroup=new&admin=post_messages+delete_messages+edit_messages+pin_messages+change_info+invite_users+promote_members"), 158 | InlineKeyboardButton("My Dev👨‍💻", user_id=7303810912)] 159 | ]) 160 | ) 161 | 162 | # Function to set up the admin handlers for the bot 163 | def setup_admin_handler(app: Client): 164 | """ 165 | Set up command handlers for the Pyrogram bot. 166 | This includes specific commands like /broadcast and /stats, as well as general activity tracking. 167 | """ 168 | # Add the /broadcast, .broadcast, /send and .send command handlers for broadcasting messages 169 | app.add_handler( 170 | MessageHandler(broadcast_handler, (filters.command("broadcast") | filters.command("broadcast", prefixes=[".", "/"]) | filters.command("send") | filters.command("send", prefixes=[".", "/"])) & (filters.private | filters.group)), 171 | group=1, # High priority to ensure it executes first 172 | ) 173 | 174 | # Add the /stats, .stats, /report, .report, /status and .status command handlers for bot statistics (works in both private and group) 175 | app.add_handler( 176 | MessageHandler(stats_handler, (filters.command("stats") | filters.command("stats", prefixes=[".", "/"]) | filters.command("report") | filters.command("report", prefixes=[".", "/"]) | filters.command("status") | filters.command("status", prefixes=[".", "/"])) & (filters.private | filters.group)), 177 | group=1, # High priority to ensure it executes first 178 | ) 179 | 180 | # Add a general handler to track all user activity 181 | app.add_handler( 182 | MessageHandler(lambda client, message: update_user_activity(message.from_user.id) if message.from_user else None, filters.all), 183 | group=2, # Lower priority so it runs after command handlers 184 | ) 185 | 186 | # Add the handler for when the bot is added to a group 187 | app.add_handler( 188 | MessageHandler(group_added_handler, filters.group & filters.new_chat_members), 189 | group=1 # High priority to ensure it executes first 190 | ) 191 | -------------------------------------------------------------------------------- /spotify/spotify.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import time 4 | import requests 5 | import aiohttp 6 | import re 7 | import asyncio 8 | import aiofiles 9 | from concurrent.futures import ThreadPoolExecutor 10 | from pyrogram import Client, filters 11 | from pyrogram.enums import ParseMode 12 | from pyrogram.types import Message 13 | from typing import Optional 14 | from config import COMMAND_PREFIX 15 | import urllib.parse # Added for URL encoding 16 | 17 | # Configure logging 18 | logging.basicConfig( 19 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 20 | level=logging.INFO 21 | ) 22 | logger = logging.getLogger(__name__) 23 | 24 | # ThreadPoolExecutor for blocking I/O operations 25 | executor = ThreadPoolExecutor(max_workers=10) 26 | 27 | async def sanitize_filename(title: str) -> str: 28 | """Sanitize file name by removing invalid characters.""" 29 | title = re.sub(r'[<>:"/\\|?*]', '', title) 30 | title = title.replace(' ', '_') 31 | return f"{title[:50]}_{int(time.time())}" 32 | 33 | async def download_image(url: str, output_path: str) -> Optional[str]: 34 | """Download image from a URL.""" 35 | try: 36 | response = requests.get(url, stream=True) 37 | if response.status_code == 200: 38 | async with aiofiles.open(output_path, 'wb') as file: 39 | for chunk in response.iter_content(1024): 40 | await file.write(chunk) 41 | return output_path 42 | except Exception as e: 43 | logger.error(f"Failed to download image: {e}") 44 | return None 45 | 46 | async def handle_spotify_request(client, message, input_text): 47 | # Check if input_text is a Spotify URL or a search query 48 | is_url = input_text and ('open.spotify.com' in input_text or 'spotify.link' in input_text) 49 | 50 | if not input_text: 51 | await client.send_message( 52 | chat_id=message.chat.id, 53 | text="**Please provide a track Spotify URL or a search query ❌**", 54 | parse_mode=ParseMode.MARKDOWN 55 | ) 56 | return 57 | 58 | status_message = await client.send_message( 59 | chat_id=message.chat.id, 60 | text="**Searching The Music**", 61 | parse_mode=ParseMode.MARKDOWN 62 | ) 63 | 64 | try: 65 | if is_url: 66 | # Handle Spotify URL 67 | api_url = f"https://iam404.serv00.net/sp.php?url={input_text}" 68 | async with aiohttp.ClientSession() as session: 69 | async with session.get(api_url) as response: 70 | if response.status == 200: 71 | data = await response.json() 72 | if data["status"]: 73 | await status_message.edit("**Found ☑️ Downloading...**") 74 | else: 75 | await status_message.edit("**Please Provide A Valid Spotify URL ❌**") 76 | return 77 | else: 78 | await status_message.edit("**❌ Sorry Bro Spotify DL API Dead**") 79 | return 80 | else: 81 | # Handle search query 82 | # Encode the query to handle spaces and special characters 83 | encoded_query = urllib.parse.quote(input_text) 84 | api_url = f"https://iam404.serv00.net/spotify_search.php?q={encoded_query}" 85 | async with aiohttp.ClientSession() as session: 86 | async with session.get(api_url) as response: 87 | if response.status == 200: 88 | data = await response.json() 89 | if data["type"] == "search" and data["data"]: 90 | await status_message.edit("**Found ☑️ Downloading...**") 91 | # Use the first result 92 | track = data["data"][0] 93 | # Fetch track details using the Spotify URL 94 | track_url = track["external_urls"]["spotify"] 95 | track_api_url = f"https://iam404.serv00.net/sp.php?url={track_url}" 96 | async with session.get(track_api_url) as track_response: 97 | if track_response.status == 200: 98 | data = await track_response.json() 99 | if not data["status"]: 100 | await status_message.edit("**Failed to fetch track details ❌**") 101 | return 102 | else: 103 | await status_message.edit("**❌ Sorry Bro Spotify DL API Dead**") 104 | return 105 | else: 106 | await status_message.edit("**No results found for the query ❌**") 107 | return 108 | else: 109 | await status_message.edit("**❌ Sorry Bro Spotify Search API Dead**") 110 | return 111 | 112 | # Extract track details from API response 113 | title = data["title"] 114 | artists = data["artist"] 115 | duration = data["duration"] 116 | album = data["album"] 117 | release_date = data["releaseDate"] 118 | spotify_url = data["spotify_url"] 119 | download_url = data["download_link"] 120 | cover_url = data.get("image") or data.get("cover") 121 | 122 | # Download cover image 123 | cover_path = None 124 | if cover_url: 125 | os.makedirs("temp_media", exist_ok=True) 126 | cover_path = f"temp_media/{await sanitize_filename(title)}.jpg" 127 | await download_image(cover_url, cover_path) 128 | 129 | # Download audio 130 | safe_title = await sanitize_filename(title) 131 | output_filename = f"temp_media/{safe_title}.mp3" 132 | async with aiohttp.ClientSession() as session: 133 | async with session.get(download_url) as response: 134 | if response.status == 200: 135 | async with aiofiles.open(output_filename, 'wb') as file: 136 | await file.write(await response.read()) 137 | else: 138 | await status_message.edit("**❌ Sorry Bro Spotify DL API Dead**") 139 | return 140 | 141 | # Prepare user info for caption 142 | if message.from_user: 143 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 144 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 145 | else: 146 | group_name = message.chat.title or "this group" 147 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 148 | user_info = f"[{group_name}]({group_url})" 149 | 150 | # Format caption according to the requested format 151 | audio_caption = ( 152 | f"🌟 **Title** `{title}`\n" 153 | f"💥 **Artist** `{artists}`\n" 154 | f"✨ **Duration** `{duration}`\n" 155 | f"👀 **Album** `{album}`\n" 156 | f"🎵 **Release Date** `{release_date}`\n" 157 | f"🎸 **Listen On Spotify** [Click Here]({spotify_url})\n" 158 | f"━━━━━━━━━━━━━━━━━━━\n" 159 | f"**Downloaded By** {user_info}" 160 | ) 161 | 162 | last_update_time = [0] 163 | start_time = time.time() 164 | 165 | await client.send_audio( 166 | chat_id=message.chat.id, 167 | audio=output_filename, 168 | caption=audio_caption, 169 | title=title, 170 | performer=artists, 171 | parse_mode=ParseMode.MARKDOWN, 172 | thumb=cover_path if cover_path else None, 173 | progress=progress_bar, 174 | progress_args=(status_message, start_time, last_update_time) 175 | ) 176 | 177 | os.remove(output_filename) 178 | if cover_path: 179 | os.remove(cover_path) 180 | 181 | await status_message.delete() # Delete the progress message after completion 182 | except Exception as e: 183 | await status_message.edit("**❌ Sorry Bro Spotify DL API Dead**") 184 | logger.error(f"Error processing Spotify request: {e}") 185 | 186 | async def progress_bar(current, total, status_message, start_time, last_update_time): 187 | """Display a progress bar for uploads.""" 188 | elapsed_time = time.time() - start_time 189 | percentage = (current / total) * 100 190 | progress = "▓" * int(percentage // 5) + "░" * (20 - int(percentage // 5)) 191 | speed = current / elapsed_time / 1024 / 1024 # Speed in MB/s 192 | uploaded = current / 1024 / 1024 # Uploaded size in MB 193 | total_size = total / 1024 / 1024 # Total size in MB 194 | 195 | # Throttle updates: Only update if at least 2 seconds have passed since the last update 196 | if time.time() - last_update_time[0] < 2: 197 | return 198 | last_update_time[0] = time.time() # Update the last update time 199 | 200 | text = ( 201 | f"📥 Upload Progress 📥\n\n" 202 | f"{progress}\n\n" 203 | f"🚧 Percentage: {percentage:.2f}%\n" 204 | f"⚡️ Speed: {speed:.2f} MB/s\n" 205 | f"📶 Uploaded: {uploaded:.2f} MB of {total_size:.2f} MB" 206 | ) 207 | try: 208 | await status_message.edit(text) 209 | except Exception as e: 210 | logger.error(f"Error updating progress: {e}") 211 | 212 | def setup_spotify_handler(app: Client): 213 | # Create a regex pattern from the COMMAND_PREFIX list 214 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]" 215 | 216 | @app.on_message(filters.regex(rf"^{command_prefix_regex}sp(\s+.*)?$") & (filters.private | filters.group)) 217 | async def spotify_command(client, message): 218 | # Check if the message contains a Spotify URL or query 219 | command_parts = message.text.split(maxsplit=1) 220 | input_text = command_parts[1].strip() if len(command_parts) > 1 else None 221 | await handle_spotify_request(client, message, input_text) 222 | -------------------------------------------------------------------------------- /youtube/youtube.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartDevs 2 | #Channel t.me/TheSmartDev 3 | import os 4 | import logging 5 | from pathlib import Path 6 | from typing import Optional 7 | import yt_dlp 8 | import asyncio 9 | import aiofiles 10 | from pyrogram import Client, filters 11 | from pyrogram.types import Message 12 | from pyrogram.enums import ParseMode 13 | import re 14 | import math 15 | import time 16 | import requests 17 | from PIL import Image 18 | from concurrent.futures import ThreadPoolExecutor 19 | from moviepy import VideoFileClip 20 | from config import COMMAND_PREFIX, YT_COOKIES_PATH 21 | 22 | # Configure logging 23 | logging.basicConfig( 24 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 25 | level=logging.INFO 26 | ) 27 | logger = logging.getLogger(__name__) 28 | 29 | # Configuration 30 | class Config: 31 | TEMP_DIR = Path("temp") 32 | HEADERS = { 33 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 34 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 35 | 'Accept-Language': 'en-US,en;q=0.5', 36 | 'Connection': 'keep-alive', 37 | 'Referer': 'https://www.youtube.com', 38 | } 39 | 40 | Config.TEMP_DIR.mkdir(exist_ok=True) 41 | 42 | executor = ThreadPoolExecutor() 43 | 44 | def sanitize_filename(title: str) -> str: 45 | """ 46 | Sanitize file name by removing invalid characters. 47 | """ 48 | title = re.sub(r'[<>:"/\\|?*]', '', title) 49 | title = title.replace(' ', '_') 50 | return f"{title[:50]}_{int(time.time())}" 51 | 52 | def validate_url(url: str) -> bool: 53 | """ 54 | Validate if the provided URL is a valid YouTube link. 55 | """ 56 | return url.startswith(('https://www.youtube.com/', 'https://youtube.com/', 'https://youtu.be/')) 57 | 58 | def format_size(size_bytes: int) -> str: 59 | """ 60 | Format file size into human-readable string. 61 | """ 62 | if size_bytes == 0: 63 | return "0B" 64 | size_name = ("B", "KB", "MB", "GB") 65 | i = int(math.floor(math.log(size_bytes, 1024))) 66 | p = math.pow(1024, i) 67 | s = round(size_bytes / p, 2) 68 | return f"{s} {size_name[i]}" 69 | 70 | def format_duration(seconds: int) -> str: 71 | """ 72 | Format video duration into human-readable string. 73 | """ 74 | hours = seconds // 3600 75 | minutes = (seconds % 3600) // 60 76 | seconds = seconds % 60 77 | if hours > 0: 78 | return f"{hours}h {minutes}m {seconds}s" 79 | elif minutes > 0: 80 | return f"{minutes}m {seconds}s" 81 | else: 82 | return f"{seconds}s" 83 | 84 | def get_video_duration_moviepy(video_path: str) -> float: 85 | """ 86 | Get video duration using MoviePy. 87 | Returns duration in seconds. 88 | """ 89 | try: 90 | # Load the video file 91 | clip = VideoFileClip(video_path) 92 | duration = clip.duration # Get duration in seconds 93 | clip.close() # Close the video file 94 | return duration 95 | except Exception as e: 96 | logger.error(f"Error getting video duration: {e}") 97 | return 0.0 98 | 99 | async def progress_bar(current, total, status_message, start_time, last_update_time): 100 | """ 101 | Display a progress bar for uploads. 102 | """ 103 | elapsed_time = time.time() - start_time 104 | percentage = (current / total) * 100 105 | progress = "▓" * int(percentage // 5) + "░" * (20 - int(percentage // 5)) 106 | speed = current / elapsed_time / 1024 / 1024 # Speed in MB/s 107 | uploaded = current / 1024 / 1024 # Uploaded size in MB 108 | total_size = total / 1024 / 1024 # Total size in MB 109 | 110 | # Throttle updates: Only update if at least 2 seconds have passed since the last update 111 | if time.time() - last_update_time[0] < 2: 112 | return 113 | last_update_time[0] = time.time() # Update the last update time 114 | 115 | text = ( 116 | f"**📥 Upload Progress 📥**\n\n" 117 | f"{progress}\n\n" 118 | f"**🚧 Percentage:** {percentage:.2f}%\n" 119 | f"**⚡️ Speed:** {speed:.2f} MB/s\n" 120 | f"**📶 Uploaded:** {uploaded:.2f} MB of {total_size:.2f} MB" 121 | ) 122 | try: 123 | await status_message.edit(text) 124 | except Exception as e: 125 | logger.error(f"Error updating progress: {e}") 126 | 127 | def get_ydl_opts(output_filename: str) -> dict: 128 | """ 129 | Return yt-dlp options. 130 | """ 131 | return { 132 | 'format': 'bestvideo[height<=720][width=1280][ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]', 133 | 'outtmpl': output_filename, 134 | 'cookiefile': YT_COOKIES_PATH, 135 | 'quiet': True, 136 | 'noprogress': True, 137 | 'no_warnings': True, 138 | 'nocheckcertificate': True, 139 | 'postprocessors': [{'key': 'FFmpegVideoConvertor', 'preferedformat': 'mp4'}] 140 | } 141 | 142 | def get_audio_opts(output_filename: str) -> dict: 143 | """ 144 | Return yt-dlp options for audio download. 145 | """ 146 | return { 147 | 'format': 'bestaudio/best', 148 | 'outtmpl': f'{output_filename}.%(ext)s', 149 | 'cookiefile': YT_COOKIES_PATH, 150 | 'quiet': True, 151 | 'noprogress': True, 152 | 'no_warnings': True, 153 | 'nocheckcertificate': True, 154 | 'postprocessors': [{ 155 | 'key': 'FFmpegExtractAudio', 156 | 'preferredcodec': 'mp3', 157 | 'preferredquality': '192', 158 | }] 159 | } 160 | 161 | def download_video_sync(url: str) -> tuple: 162 | """ 163 | Download a video using yt-dlp, along with its thumbnail. 164 | This function is synchronous and can be run in an executor. 165 | """ 166 | if not validate_url(url): 167 | return None, "Invalid YouTube URL" 168 | 169 | try: 170 | with yt_dlp.YoutubeDL({'quiet': True, 'cookiefile': YT_COOKIES_PATH}) as ydl: 171 | info = ydl.extract_info(url, download=False) 172 | 173 | if not info: 174 | return None, "Could not fetch video information" 175 | 176 | title = info.get('title', 'Unknown Title') 177 | views = info.get('view_count', 0) 178 | duration = info.get('duration', 0) 179 | duration_str = format_duration(duration) 180 | thumbnail_url = info.get('thumbnail', None) 181 | 182 | safe_title = sanitize_filename(title) 183 | output_path = f"temp_media/{safe_title}.mp4" 184 | os.makedirs("temp_media", exist_ok=True) 185 | 186 | opts = get_ydl_opts(output_path) 187 | with yt_dlp.YoutubeDL(opts) as ydl: 188 | ydl.download([url]) 189 | 190 | if not os.path.exists(output_path): 191 | return None, "Download failed: File not created" 192 | 193 | file_size = os.path.getsize(output_path) 194 | if file_size > 2_000_000_000: 195 | os.remove(output_path) 196 | return None, "Video file exceeds Telegram's 2GB limit." 197 | 198 | # Get duration using MoviePy 199 | duration = get_video_duration_moviepy(output_path) 200 | duration_str = format_duration(int(duration)) 201 | 202 | # Download and prepare thumbnail 203 | thumbnail_path = None 204 | if thumbnail_url: 205 | thumbnail_path = prepare_thumbnail_sync(thumbnail_url, output_path) 206 | 207 | metadata = { 208 | 'file_path': output_path, 209 | 'title': title, 210 | 'views': views, 211 | 'duration': duration_str, 212 | 'file_size': format_size(file_size), 213 | 'thumbnail_path': thumbnail_path 214 | } 215 | 216 | logger.info(f"Video Metadata: {metadata}") 217 | print(f"Video Metadata: {metadata}") # Print metadata to the terminal 218 | return metadata, None 219 | 220 | except yt_dlp.utils.DownloadError: 221 | logger.error("Download failed: Video unavailable or restricted") 222 | return None, "Download failed: Video unavailable or restricted" 223 | except Exception as e: 224 | logger.error(f"An unexpected error occurred: {e}") 225 | return None, f"An unexpected error occurred: {e}" 226 | 227 | def download_audio_sync(url: str) -> tuple: 228 | """ 229 | Download audio from YouTube using yt-dlp. 230 | This function is synchronous and can be run in an executor. 231 | """ 232 | if not validate_url(url): 233 | return None, "Invalid YouTube URL" 234 | 235 | try: 236 | with yt_dlp.YoutubeDL({'quiet': True, 'cookiefile': YT_COOKIES_PATH}) as ydl: 237 | info = ydl.extract_info(url, download=False) 238 | 239 | if not info: 240 | return None, "Could not fetch video information" 241 | 242 | title = info.get('title', 'Unknown Title') 243 | views = info.get('view_count', 0) 244 | duration = info.get('duration', 0) 245 | duration_str = format_duration(duration) 246 | 247 | safe_title = sanitize_filename(title) 248 | base_path = f"temp_media/{safe_title}" 249 | os.makedirs("temp_media", exist_ok=True) 250 | 251 | opts = get_audio_opts(base_path) 252 | with yt_dlp.YoutubeDL(opts) as ydl: 253 | ydl.download([url]) 254 | 255 | output_path = f"{base_path}.mp3" 256 | if not os.path.exists(output_path): 257 | possible_files = [f for f in os.listdir("temp_media") if f.startswith(safe_title)] 258 | if possible_files: 259 | original_file = os.path.join("temp_media", possible_files[0]) 260 | if os.path.exists(original_file): 261 | return None, f"File exists but conversion failed: {original_file}" 262 | return None, "Download failed: File not created" 263 | 264 | file_size = os.path.getsize(output_path) 265 | if file_size > 2_000_000_000: 266 | os.remove(output_path) 267 | return None, "Audio file exceeds Telegram's 2GB limit." 268 | 269 | metadata = { 270 | 'file_path': output_path, 271 | 'title': title, 272 | 'views': views, 273 | 'duration': duration_str, 274 | 'file_size': format_size(file_size), 275 | 'thumbnail_path': prepare_thumbnail_sync(info['thumbnail'], output_path) if 'thumbnail' in info else None 276 | } 277 | 278 | logger.info(f"Audio Metadata: {metadata}") 279 | print(f"Audio Metadata: {metadata}") # Print metadata to the terminal 280 | return metadata, None 281 | 282 | except yt_dlp.utils.DownloadError as e: 283 | logger.error(f"Download failed: {e}") 284 | return None, f"Download failed: {e}" 285 | except Exception as e: 286 | logger.error(f"An unexpected error occurred: {e}") 287 | return None, f"An unexpected error occurred: {e}" 288 | 289 | def prepare_thumbnail_sync(thumbnail_url: str, output_path: str) -> str: 290 | """ 291 | Download and prepare the thumbnail image. 292 | This function is synchronous and can be run in an executor. 293 | """ 294 | try: 295 | response = requests.get(thumbnail_url) 296 | if response.status_code == 200: 297 | thumbnail_temp_path = f"{output_path}_thumbnail.jpg" 298 | with open(thumbnail_temp_path, 'wb') as f: 299 | f.write(response.content) 300 | 301 | thumbnail_resized_path = f"{output_path}_thumb.jpg" 302 | with Image.open(thumbnail_temp_path) as img: 303 | img = img.convert('RGB') 304 | img.save(thumbnail_resized_path, "JPEG", quality=85) 305 | 306 | os.remove(thumbnail_temp_path) 307 | return thumbnail_resized_path 308 | except Exception as e: 309 | logger.error(f"Error preparing thumbnail: {e}") 310 | return None 311 | 312 | async def search_youtube(query: str) -> Optional[str]: 313 | """ 314 | Search YouTube for the first video result matching the query. 315 | """ 316 | ydl_opts = { 317 | 'format': 'bestaudio/best', 318 | 'default_search': 'ytsearch1:', 319 | 'nooverwrites': True, 320 | 'cookiefile': YT_COOKIES_PATH, 321 | 'no_warnings': True, 322 | 'quiet': True, 323 | 'no_color': True, 324 | 'simulate': True, 325 | } 326 | 327 | try: 328 | with yt_dlp.YoutubeDL(ydl_opts) as ydl: 329 | info = await asyncio.get_event_loop().run_in_executor(executor, ydl.extract_info, query, False) 330 | if 'entries' in info and info['entries']: 331 | return info['entries'][0]['webpage_url'] 332 | except Exception as e: 333 | logger.error(f"YouTube search error: {e}") 334 | 335 | return None 336 | 337 | async def handle_download_request(client: Client, message: Message, query: str): 338 | search_message = await client.send_message( 339 | chat_id=message.chat.id, 340 | text="**Searching The Video**", 341 | parse_mode=ParseMode.MARKDOWN 342 | ) 343 | 344 | if not validate_url(query): 345 | video_url = await search_youtube(query) 346 | if not video_url: 347 | await search_message.edit_text( 348 | text="**❌No Video Matched To Your Search**" 349 | ) 350 | return 351 | else: 352 | video_url = query 353 | 354 | try: 355 | loop = asyncio.get_event_loop() 356 | result, error = await loop.run_in_executor(executor, download_video_sync, video_url) 357 | if error: 358 | await search_message.edit( 359 | text=f"**Incomplete or invalid YouTube URL**", 360 | parse_mode=ParseMode.MARKDOWN 361 | ) 362 | return 363 | 364 | await search_message.edit( 365 | text="**Found ☑️ Downloading...**", 366 | parse_mode=ParseMode.MARKDOWN 367 | ) 368 | 369 | video_path = result['file_path'] 370 | title = result['title'] 371 | views = result['views'] 372 | duration = result['duration'] 373 | file_size = result['file_size'] 374 | thumbnail_path = result.get('thumbnail_path') 375 | 376 | logger.info(f"Video Downloaded: {title}, Views: {views}, Duration: {duration}, Size: {file_size}") 377 | 378 | if message.from_user: 379 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 380 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 381 | else: 382 | group_name = message.chat.title or "this group" 383 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 384 | user_info = f"[{group_name}]({group_url})" 385 | 386 | video_caption = ( 387 | f"🎵 **Title:** **{title}**\n" 388 | f"━━━━━━━━━━━━━━━━━━━━━\n" 389 | f"👁️‍🗨️ **Views:** **{views}** views\n" 390 | f"🔗 [Watch On YouTube]({video_url})\n" 391 | f"⏱️ **Duration:** **{duration}**\n" 392 | f"━━━━━━━━━━━━━━━━━━━━━\n" 393 | f"⚡️ **Downloaded By** {user_info}" 394 | ) 395 | 396 | last_update_time = [0] 397 | start_time = time.time() 398 | 399 | await client.send_video( 400 | chat_id=message.chat.id, 401 | video=video_path, 402 | caption=video_caption, 403 | parse_mode=ParseMode.MARKDOWN, 404 | supports_streaming=True, 405 | thumb=thumbnail_path, 406 | height=720, 407 | width=1280, 408 | duration=int(get_video_duration_moviepy(video_path)), 409 | progress=progress_bar, 410 | progress_args=(search_message, start_time, last_update_time) 411 | ) 412 | 413 | # Cleanup 414 | if os.path.exists(video_path): 415 | os.remove(video_path) 416 | if thumbnail_path and os.path.exists(thumbnail_path): 417 | os.remove(thumbnail_path) 418 | 419 | await search_message.delete() 420 | 421 | except Exception as e: 422 | logger.error(f"An unexpected error occurred during video download: {e}") 423 | await search_message.edit( 424 | text=f"**YouTube Downloader API Dead **", 425 | parse_mode=ParseMode.MARKDOWN 426 | ) 427 | 428 | async def handle_audio_request(client: Client, message: Message, query: str): 429 | status_message = await client.send_message( 430 | chat_id=message.chat.id, 431 | text="**Searching For The Song**", 432 | parse_mode=ParseMode.MARKDOWN 433 | ) 434 | 435 | if not validate_url(query): 436 | video_url = await search_youtube(query) 437 | if not video_url: 438 | await status_message.edit_text( 439 | text="**❌ No Song Matched To Your Search**" 440 | ) 441 | return 442 | else: 443 | video_url = query 444 | 445 | try: 446 | loop = asyncio.get_event_loop() 447 | result, error = await loop.run_in_executor(executor, download_audio_sync, video_url) 448 | if error: 449 | await status_message.edit( 450 | text=f"**Incomplete or invalid YouTube URL**", 451 | parse_mode=ParseMode.MARKDOWN 452 | ) 453 | return 454 | 455 | await status_message.edit( 456 | text="**Found ☑️ Downloading...**", 457 | parse_mode=ParseMode.MARKDOWN 458 | ) 459 | 460 | audio_path = result['file_path'] 461 | title = result['title'] 462 | views = result['views'] 463 | duration = result['duration'] 464 | file_size = result['file_size'] 465 | thumbnail_path = result.get('thumbnail_path') 466 | 467 | logger.info(f"Audio Downloaded: {title}, Views: {views}, Duration: {duration}, Size: {file_size}") 468 | 469 | if message.from_user: 470 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip() 471 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})" 472 | else: 473 | group_name = message.chat.title or "this group" 474 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group" 475 | user_info = f"[{group_name}]({group_url})" 476 | 477 | audio_caption = ( 478 | f"🎵 **Title:** **{title}**\n" 479 | f"━━━━━━━━━━━━━━━━━━━━━\n" 480 | f"👁️‍🗨️ **Views:** **{views}** views\n" 481 | f"🔗 [Listen On YouTube]({video_url})\n" 482 | f"⏱️ **Duration:** **{duration}**\n" 483 | f"━━━━━━━━━━━━━━━━━━━━━\n" 484 | f"⚡️ **Downloaded By** {user_info}" 485 | ) 486 | 487 | last_update_time = [0] 488 | start_time = time.time() 489 | 490 | await client.send_audio( 491 | chat_id=message.chat.id, 492 | audio=audio_path, 493 | caption=audio_caption, 494 | title=title, 495 | performer="Smart Tools 💥", 496 | parse_mode=ParseMode.MARKDOWN, 497 | thumb=thumbnail_path, 498 | progress=progress_bar, 499 | progress_args=(status_message, start_time, last_update_time) 500 | ) 501 | 502 | # Cleanup 503 | if os.path.exists(audio_path): 504 | os.remove(audio_path) 505 | if thumbnail_path and os.path.exists(thumbnail_path): 506 | os.remove(thumbnail_path) 507 | 508 | await status_message.delete() 509 | 510 | except Exception as e: 511 | logger.error(f"An unexpected error occurred during audio download: {e}") 512 | await status_message.edit_text( 513 | text=f"**YouTube Downloader API Dead **" 514 | ) 515 | if os.path.exists(audio_path): 516 | os.remove(audio_path) 517 | 518 | def setup_downloader_handler(app: Client): 519 | # Create a regex pattern from the COMMAND_PREFIX list 520 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]" 521 | 522 | @app.on_message(filters.regex(rf"^{command_prefix_regex}(yt|video)(\s+.+)?$")) 523 | async def video_command(client, message): 524 | command_parts = message.text.split(maxsplit=1) 525 | if len(command_parts) == 1 or not command_parts[1]: 526 | await client.send_message( 527 | chat_id=message.chat.id, 528 | text="**Please provide your video name or link❌**", 529 | parse_mode=ParseMode.MARKDOWN 530 | ) 531 | else: 532 | url_or_query = command_parts[1] 533 | await handle_download_request(client, message, url_or_query) 534 | 535 | @app.on_message(filters.regex(rf"^{command_prefix_regex}song(\s+.+)?$")) 536 | async def song_command(client, message): 537 | command_parts = message.text.split(maxsplit=1) 538 | if len(command_parts) == 1 or not command_parts[1]: 539 | await client.send_message( 540 | chat_id=message.chat.id, 541 | text="**Please provide a Music Name Or Link❌**", 542 | parse_mode=ParseMode.MARKDOWN 543 | ) 544 | else: 545 | url_or_query = command_parts[1] 546 | await handle_audio_request(client, message, url_or_query) 547 | --------------------------------------------------------------------------------