├── runtime.txt
├── Procfile
├── cookies
└── SecureXTools.txt
├── assets
└── wlc.jpg
├── start.sh
├── heroku.yml
├── callback
├── __init__.py
└── misc.py
├── requirements.txt
├── core
├── __init__.py
├── mongo.py
├── database.py
└── start.py
├── docker-compose.yml
├── utils
├── __init__.py
├── logging_setup.py
├── dc_locations.py
├── pgbar.py
└── nfy.py
├── app.py
├── Dockerfile
├── main.py
├── sample.env
├── sudoers
├── __init__.py
├── set
│ └── set.py
├── speedtest
│ └── speedtest.py
├── restart
│ └── restart.py
├── gban
│ └── gban.py
├── sudo
│ └── sudo.py
├── logs
│ └── logs.py
├── settings
│ └── settings.py
└── admin
│ └── admin.py
├── cmds.txt
├── modules
├── __init__.py
├── privxutils
│ └── privacy.py
├── netxutils
│ ├── ip.py
│ └── px.py
├── webxutils
│ └── ss.py
├── dlxutils
│ ├── tik.py
│ ├── pin.py
│ ├── fb.py
│ ├── insta.py
│ └── spfy.py
├── hlpxutils
│ └── help.py
└── infoxutils
│ └── info.py
├── LICENSE
├── app.json
├── config.py
└── README.md
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.10.13
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: bash start.sh
--------------------------------------------------------------------------------
/cookies/SecureXTools.txt:
--------------------------------------------------------------------------------
1 | # Netscape HTTP Cookie File
2 |
--------------------------------------------------------------------------------
/assets/wlc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSmartDevs/SecureXTools/main/assets/wlc.jpg
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 | #!/bin/bash
4 | python3 main.py
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | web: Dockerfile
4 |
5 | run:
6 | worker: python3 main.py
--------------------------------------------------------------------------------
/callback/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from .misc import handle_callback_query
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pyrogram
2 | tgcrypto
3 | aiohttp
4 | requests
5 | asyncio
6 | telegraph
7 | pillow
8 | yt-dlp
9 | python-dateutil
10 | pymongo
11 | aiofiles
12 | python-dotenv
13 | moviepy
--------------------------------------------------------------------------------
/core/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from .mongo import user_activity_collection
5 | from .database import auth_admins, banned_users
6 | from .start import setup_start_handler
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | smarttoolbot:
3 | build: .
4 | container_name: securextools
5 | ports:
6 | - "8000:8000"
7 | env_file:
8 | - .env
9 | volumes:
10 | - .:/app
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from .logging_setup import LOGGER
5 | from .dc_locations import get_dc_locations
6 | from .pgbar import progress_bar
7 | from .nfy import notify_admin
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from pyrogram import Client
5 | from utils import LOGGER
6 | from config import API_ID, API_HASH, BOT_TOKEN
7 |
8 | LOGGER.info("Creating Bot Client From BOT_TOKEN")
9 |
10 | app = Client(
11 | "SecureXTools",
12 | api_id=API_ID,
13 | api_hash=API_HASH,
14 | bot_token=BOT_TOKEN,
15 | workers=1000
16 | )
17 |
18 | LOGGER.info("Bot Client Created Successfully!")
--------------------------------------------------------------------------------
/core/mongo.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from pymongo import MongoClient
5 | from utils import LOGGER
6 | from config import MONGO_URL
7 |
8 | # Initialize MongoDB Client
9 | LOGGER.info("Creating MONGO_CLIENT From MONGO_URL")
10 | try:
11 | MONGO_CLIENT = MongoClient(MONGO_URL)
12 | LOGGER.info("MONGO_CLIENT Successfully Created!")
13 | except Exception as e:
14 | LOGGER.error(f"Failed to create MONGO_CLIENT: {e}")
15 | raise
16 |
17 | # Access the database and collections
18 | db = MONGO_CLIENT["user_activity_db"]
19 | user_activity_collection = db["user_activity"]
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 | # Use an official Python image
4 | FROM python:3.9-slim-buster
5 |
6 | # Install system dependencies
7 | RUN apt update && apt install -y git curl ffmpeg && apt clean
8 |
9 | # Set working directory
10 | WORKDIR /app
11 |
12 | # Copy requirements and install them
13 | COPY requirements.txt .
14 | RUN pip install --no-cache-dir -r requirements.txt
15 |
16 | # Copy rest of the bot files
17 | COPY . .
18 |
19 | # Make start.sh executable
20 | RUN chmod +x start.sh
21 |
22 | # Expose port for Flask
23 | EXPOSE 8000
24 |
25 | # Run the bot and Flask server
26 | CMD ["bash", "start.sh"]
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from app import app
5 | from utils import LOGGER
6 | from core import setup_start_handler
7 | from modules import setup_modules_handlers
8 | from sudoers import setup_sudoers_handlers
9 | from callback import handle_callback_query
10 | from pyrogram import filters
11 |
12 | setup_start_handler(app)
13 | setup_modules_handlers(app)
14 | setup_sudoers_handlers(app)
15 |
16 | @app.on_callback_query()
17 | async def handle_callback(client, callback_query):
18 | await handle_callback_query(client, callback_query)
19 |
20 | if __name__ == "__main__":
21 | LOGGER.info("SecureXTools Successfully Started!🔥")
22 | app.run()
--------------------------------------------------------------------------------
/sample.env:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | # Telegram API credentials (required)
5 | API_ID=YOUR_API_ID
6 | API_HASH=YOUR_API_HASH
7 | BOT_TOKEN=YOUR_BOT_TOKEN
8 | BOT_USERNAME=YOUR_BOT_USERNAME WITH @
9 | BOT_NAME=YOUR_BOT_NAME
10 |
11 | # Admin and owner IDs (required)
12 | OWNER_ID=7303810912
13 | DEVELOPER_USER_ID=7303810912
14 |
15 | # Database URLs (required)
16 | MONGO_URL=YOUR_MONGO_URL
17 | DATABASE_URL=YOUR_DATABASE_URL
18 |
19 | # Bot settings
20 | UPDATE_CHANNEL_URL=t.me/TheSmartDevs
21 | COMMAND_PREFIX=!|.|#|,|/
22 | MAX_VIDEO_SIZE=2147483648
23 | YT_COOKIES_PATH=./cookies/SecureXTools.txt
24 | VIDEO_RESOLUTION=1280x720
25 |
26 | # Proxy checker setting
27 | PROXY_CHECK_LIMIT=20
--------------------------------------------------------------------------------
/sudoers/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from .admin.admin import setup_admin_handler
5 | from .logs.logs import setup_logs_handler
6 | from .restart.restart import setup_restart_handler
7 | from .sudo.sudo import setup_sudo_handler
8 | from .speedtest.speedtest import setup_speed_handler
9 | from .settings.settings import setup_settings_handler
10 | from .gban.gban import setup_gban_handler
11 | from .set.set import setup_set_commands_handler
12 |
13 | def setup_sudoers_handlers(app):
14 | setup_admin_handler(app)
15 | setup_logs_handler(app)
16 | setup_restart_handler(app)
17 | setup_sudo_handler(app)
18 | setup_speed_handler(app)
19 | setup_settings_handler(app)
20 | setup_gban_handler(app)
21 | setup_set_commands_handler(app)
--------------------------------------------------------------------------------
/utils/logging_setup.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | import logging
5 | from logging.handlers import RotatingFileHandler
6 |
7 | logging.basicConfig(
8 | level=logging.INFO,
9 | format="%(asctime)s - %(levelname)s - %(message)s",
10 | datefmt='%Y-%m-%d %H:%M:%S',
11 | handlers=[
12 | RotatingFileHandler(
13 | "botlog.txt",
14 | maxBytes=50000000,
15 | backupCount=10
16 | ),
17 | logging.StreamHandler()
18 | ]
19 | )
20 |
21 | logging.getLogger("pyrogram").setLevel(logging.ERROR)
22 | logging.getLogger("telethon").setLevel(logging.ERROR)
23 | logging.getLogger("aiohttp").setLevel(logging.ERROR)
24 | logging.getLogger("apscheduler").setLevel(logging.ERROR)
25 |
26 | LOGGER = logging.getLogger(__name__)
--------------------------------------------------------------------------------
/utils/dc_locations.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | def get_dc_locations():
5 | """Returns a dictionary mapping Data Center IDs to their locations"""
6 | return {
7 | 1: "MIA, Miami, USA, US",
8 | 2: "AMS, Amsterdam, Netherlands, NL",
9 | 3: "MBA, Mumbai, India, IN",
10 | 4: "STO, Stockholm, Sweden, SE",
11 | 5: "SIN, Singapore, SG",
12 | 6: "LHR, London, United Kingdom, GB",
13 | 7: "FRA, Frankfurt, Germany, DE",
14 | 8: "JFK, New York, USA, US",
15 | 9: "HKG, Hong Kong, HK",
16 | 10: "TYO, Tokyo, Japan, JP",
17 | 11: "SYD, Sydney, Australia, AU",
18 | 12: "GRU, São Paulo, Brazil, BR",
19 | 13: "DXB, Dubai, UAE, AE",
20 | 14: "CDG, Paris, France, FR",
21 | 15: "ICN, Seoul, South Korea, KR",
22 | }
--------------------------------------------------------------------------------
/cmds.txt:
--------------------------------------------------------------------------------
1 | start - Start SecureXTools Bot
2 | help - Get Help Menu & Commands
3 | info - Get User Info From Database
4 | id- Get Group Or Channel Info
5 | ip - Get Any Ip Information
6 | px - Check Proxy Any Type
7 | ss - Get SS Of Any Website
8 | fb - Download Facebook Video
9 | in - Download Insta Reel & Posts
10 | sp - Download Spotify Track
11 | song - Download Yt Music
12 | video - Download Yt Video
13 | tt- Download TikTok Video
14 | pnt - Download Pinterest Video
15 | settings - Change Bot All Vars [Admin]
16 | auth - Promote Sudo User [Admin]
17 | unauth - Demote Sudo User [Admin]
18 | gban - Ban User From Using Bot [Admin]
19 | gunban - Unban User From Using Bot [Admin]
20 | logs - Get Bot's Logs From Console [Admin]
21 | restart - Restart Bot And Freshly Start [Admin]
22 | speedtest - Get Bot Server Speed [Admin]
23 | send - Send Brodcast [Admin]
24 | stats - Get Statistics [Admin]
--------------------------------------------------------------------------------
/core/database.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | from pymongo import MongoClient
5 | from config import DATABASE_URL
6 | from utils import LOGGER
7 |
8 | # Log the initialization attempt
9 | LOGGER.info("Creating Database Client From DATABASE_URL")
10 |
11 | try:
12 | # Create MongoDB client using DATABASE_URL from config.py
13 | mongo_client = MongoClient(DATABASE_URL)
14 | # Access the "ItsSmartTool" database
15 | db = mongo_client["ItsSmartTool"]
16 | # Access the "auth_admins" collection for authorized admins
17 | auth_admins = db["auth_admins"]
18 | # Access the "banned_users" collection for banned users
19 | banned_users = db["banned_users"]
20 | LOGGER.info("Database Client Successfully Created!")
21 | except Exception as e:
22 | # Log the error with details and raise it to halt execution
23 | LOGGER.error(f"Database Client Create Error: {e}")
24 | raise
--------------------------------------------------------------------------------
/modules/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from .dlxutils.fb import setup_fb_handlers
5 | from .dlxutils.insta import setup_insta_handlers
6 | from .dlxutils.pin import setup_pinterest_handler
7 | from .dlxutils.spfy import setup_spotify_handler
8 | from .dlxutils.tik import setup_tt_handler
9 | from .dlxutils.yt import setup_yt_handler
10 | from .hlpxutils.help import setup_help_handler
11 | from .infoxutils.info import setup_info_handler
12 | from .netxutils.ip import setup_ip_handlers
13 | from .netxutils.px import setup_px_handler
14 | from .privxutils.privacy import setup_privacy_handler
15 | from .webxutils.ss import setup_ss_handler
16 |
17 | def setup_modules_handlers(app):
18 | setup_fb_handlers(app)
19 | setup_insta_handlers(app)
20 | setup_pinterest_handler(app)
21 | setup_spotify_handler(app)
22 | setup_tt_handler(app)
23 | setup_yt_handler(app)
24 | setup_help_handler(app)
25 | setup_info_handler(app)
26 | setup_ip_handlers(app)
27 | setup_px_handler(app)
28 | setup_privacy_handler(app)
29 | setup_ss_handler(app)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 ISmartDevs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/utils/pgbar.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | import logging
5 | import time
6 |
7 | logging.basicConfig(
8 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
9 | level=logging.INFO
10 | )
11 | logger = logging.getLogger(__name__)
12 |
13 | async def progress_bar(current, total, status_message, start_time, last_update_time):
14 | """
15 | Display a progress bar for uploads.
16 | """
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
21 | uploaded = current / 1024 / 1024
22 | total_size = total / 1024 / 1024
23 |
24 | if time.time() - last_update_time[0] < 1:
25 | return
26 | last_update_time[0] = time.time()
27 |
28 | text = (
29 | f"📥 Upload Progress 📥\n\n"
30 | f"{progress}\n\n"
31 | f"🚧 Percentage: {percentage:.2f}%\n"
32 | f"⚡️ Speed: {speed:.2f} MB/s\n"
33 | f"📶 Uploaded: {uploaded:.2f} MB of {total_size:.2f} MB"
34 | )
35 | try:
36 | await status_message.edit(text)
37 | except Exception as e:
38 | logger.error(f"Error updating progress: {e}")
--------------------------------------------------------------------------------
/utils/nfy.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | import logging
5 | from pyrogram import Client
6 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
7 | from config import OWNER_ID, DEVELOPER_USER_ID
8 |
9 | async def notify_admin(client: Client, command: str, error: Exception, message: Message):
10 | try:
11 | user = message.from_user
12 | user_fullname = f"{user.first_name} {user.last_name or ''}".strip()
13 | user_id = user.id
14 | error_message = (
15 | "**✘ Hey Sir, New Bug Found ↯**\n\n"
16 | f"**✘ Command: `{command}` ↯**\n"
17 | f"**✘ Issue: `{str(error)}` ↯**\n"
18 | f"**✘ User: `{user_fullname}` (ID: {user_id}) ↯**\n"
19 | f"**✘ Chat ID: `{message.chat.id}` ↯**\n"
20 | f"**✘ Time: `{message.date.strftime('%Y-%m-%d %H:%M:%S')}` ↯**"
21 | )
22 | keyboard = InlineKeyboardMarkup(
23 | [
24 | [InlineKeyboardButton("✘ Dev ↯", user_id=DEVELOPER_USER_ID)]
25 | ]
26 | )
27 | try:
28 | await client.send_message(
29 | chat_id=OWNER_ID,
30 | text=error_message,
31 | reply_markup=keyboard
32 | )
33 | except Exception as admin_error:
34 | logging.error(f"Failed to notify admin {OWNER_ID}: {admin_error}")
35 | except Exception as e:
36 | logging.error(f"Error in notify_admin: {e}")
37 |
--------------------------------------------------------------------------------
/core/start.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from pyrogram import filters
5 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
6 | from config import BOT_USERNAME, OWNER_ID, UPDATE_CHANNEL_URL, COMMAND_PREFIX, BOT_NAME
7 | from utils import LOGGER
8 |
9 | def setup_start_handler(app):
10 | @app.on_message(filters.command(["start"], prefixes=COMMAND_PREFIX) & filters.private)
11 | async def start_command(client, message):
12 | LOGGER.info(f"Received /start command from user {message.from_user.id}")
13 |
14 | user = message.from_user
15 | full_name = (user.first_name or "") + (f" {user.last_name}" if user.last_name else "")
16 | full_name = full_name.strip() or "User"
17 | user_mention = f"[{full_name}](tg://user?id={user.id})"
18 |
19 | welcome_text = (
20 | f"**◑ ʜᴇʏ {user_mention}, ᴡᴇʟᴄᴏᴍᴇ ᴛᴏ ᴛʜᴇ ʙᴏᴛ**\n"
21 | f"**➻ ᴛʜɪs ɪs [{BOT_NAME}](t.me/{BOT_USERNAME.lstrip('@')}), ᴀ ғᴀsᴛ & ᴘᴏᴡᴇʀғᴜʟ ᴛᴏᴏʟᴋɪᴛ.**\n"
22 | "**━━━━━━━━━━━━━━━━━━━━━━**\n"
23 | "**Tᴏᴏʟs:** ᴛᴇʟᴇɢʀᴀᴍ ᴜsᴇʀ ɪɴғᴏ ᴄʜᴇᴄᴋᴇʀ, ɪᴘ ᴄʜᴇᴄᴋᴇʀ, ᴘʀᴏxʏ ᴄʜᴇᴄᴋᴇʀ, sᴄʀᴇᴇɴsʜᴏᴛ.\n\n"
24 | "**ᴅᴏᴡɴʟᴏᴀᴅ ᴘʟᴀᴛғᴏʀᴍs:** ʏᴏᴜᴛᴜʙᴇ, ғᴀᴄᴇʙᴏᴏᴋ, ɪɴsᴛᴀɢʀᴀᴍ, ᴘɪɴᴛᴇʀᴇsᴛ, sᴘᴏᴛɪғʏ, ᴛɪᴋᴛᴏᴋ."
25 | )
26 |
27 | buttons = InlineKeyboardMarkup(
28 | [
29 | [InlineKeyboardButton("➕ Add Me", url=f"https://t.me/{BOT_USERNAME.lstrip('@')}?startgroup=true")],
30 | [
31 | InlineKeyboardButton("🔧 Developer", user_id=OWNER_ID),
32 | InlineKeyboardButton("✍🏻 Support", url=UPDATE_CHANNEL_URL)
33 | ],
34 | [InlineKeyboardButton("ℹ️ Help & Command", callback_data="helpmenu")]
35 | ]
36 | )
37 |
38 | await client.send_photo(
39 | chat_id=message.chat.id,
40 | photo="assets/wlc.jpg",
41 | caption=welcome_text,
42 | reply_markup=buttons
43 | )
44 | LOGGER.info(f"Sent welcome message to user {message.from_user.id}")
--------------------------------------------------------------------------------
/sudoers/set/set.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from pyrogram import filters
5 | from pyrogram.types import BotCommand
6 | from pyrogram.enums import ParseMode
7 | from config import OWNER_ID
8 | from utils import LOGGER
9 |
10 | BOT_COMMANDS = [
11 | BotCommand("start", "みStart SecureXTools Bot↯"),
12 | BotCommand("help", "みGet Help Menu & Commands↯"),
13 | BotCommand("info", "みGet User Info From Database↯"),
14 | BotCommand("id", "みGet Group Or Channel Info↯"),
15 | BotCommand("ip", "みGet Any Ip Information↯"),
16 | BotCommand("px", "みCheck Proxy Any Type↯"),
17 | BotCommand("ss", "みGet SS Of Any Website↯"),
18 | BotCommand("fb", "みDownload Facebook Video↯"),
19 | BotCommand("in", "みDownload Insta Reel & Posts↯"),
20 | BotCommand("sp", "みDownload Spotify Track↯"),
21 | BotCommand("song", "みDownload Yt Music↯"),
22 | BotCommand("video", "みDownload Yt Video↯"),
23 | BotCommand("tt", "みDownload TikTok Video↯"),
24 | BotCommand("pnt", "みDownload Pinterest Video↯"),
25 | BotCommand("settings", "みChange Bot All Vars [Admin]↯"),
26 | BotCommand("auth", "みPromote Sudo User [Admin]↯"),
27 | BotCommand("unauth", "みDemote Sudo User [Admin]↯"),
28 | BotCommand("gban", "みBan User From Using Bot [Admin]↯"),
29 | BotCommand("gunban", "みUnban User From Using Bot [Admin]↯"),
30 | BotCommand("logs", "みGet Bot's Logs From Console [Admin]↯"),
31 | BotCommand("restart", "みRestart Bot And Freshly Start [Admin]↯"),
32 | BotCommand("speedtest", "みGet Bot Server Speed [Admin]↯"),
33 | BotCommand("send", "みSend Brodcast [Admin]↯"),
34 | BotCommand("stats", "みGet Statistics [Admin]↯"),
35 | ]
36 |
37 | def setup_set_commands_handler(app):
38 | @app.on_message(filters.command("set") & filters.user(OWNER_ID))
39 | async def set_commands(client, message):
40 | await client.set_bot_commands(BOT_COMMANDS)
41 | await client.send_message(
42 | chat_id=message.chat.id,
43 | text="み ¡**BotFather Commands Successfully Set**↯",
44 | parse_mode=ParseMode.MARKDOWN
45 | )
46 | LOGGER.info(f"BotFather commands set by owner {message.from_user.id}")
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SecureXTools",
3 | "description": "A fast and powerful Telegram bot with tools for user info, IP checking, proxy checking, screenshots, and media downloads from YouTube, Facebook, Instagram, Pinterest, Spotify, and TikTok.",
4 | "repository": "https://github.com/TheSmartDevs/SecureXTools",
5 | "keywords": ["telegram", "bot", "pyrogram", "python"],
6 | "env": {
7 | "API_ID": {
8 | "description": "Telegram API ID obtained from https://my.telegram.org",
9 | "required": true
10 | },
11 | "API_HASH": {
12 | "description": "Telegram API Hash obtained from https://my.telegram.org",
13 | "required": true
14 | },
15 | "BOT_TOKEN": {
16 | "description": "Bot Token obtained from @BotFather on Telegram",
17 | "required": true
18 | },
19 | "BOT_USERNAME": {
20 | "description": "Bot Username (e.g., @SecureXToolsBot)",
21 | "required": true
22 | },
23 | "BOT_NAME": {
24 | "description": "Bot Name (e.g., SecureXToolsBot)",
25 | "required": true
26 | },
27 | "OWNER_ID": {
28 | "description": "Telegram User ID of the bot owner",
29 | "required": true
30 | },
31 | "MONGO_URL": {
32 | "description": "MongoDB connection URL (e.g., mongodb://localhost:27017/securextools)",
33 | "required": true
34 | },
35 | "UPDATE_CHANNEL_URL": {
36 | "description": "URL of the Telegram channel for updates and support",
37 | "value": "t.me/TheSmartDev"
38 | },
39 | "COMMAND_PREFIX": {
40 | "description": "Command prefixes for bot commands (e.g., !, ., #, ,, /)",
41 | "value": "!|.|#|,|/"
42 | },
43 | "MAX_VIDEO_SIZE": {
44 | "description": "Maximum video file size in bytes",
45 | "value": "2147483648"
46 | },
47 | "YT_COOKIES_PATH": {
48 | "description": "Path to YouTube cookies file",
49 | "value": "./cookies/ItsSmartToolBot.txt"
50 | },
51 | "VIDEO_RESOLUTION": {
52 | "description": "Video resolution for YouTube downloads (e.g., 1280x720)",
53 | "value": "1280x720"
54 | }
55 | },
56 | "buildpacks": [
57 | {
58 | "url": "heroku/python"
59 | }
60 | ],
61 | "formation": {
62 | "worker": {
63 | "quantity": 1,
64 | "size": "free"
65 | }
66 | },
67 | "stack": "heroku-22",
68 | "addons": []
69 | }
--------------------------------------------------------------------------------
/modules/privxutils/privacy.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | from pyrogram import Client, filters
5 | from pyrogram.enums import ParseMode
6 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
7 | from config import COMMAND_PREFIX
8 | from utils import LOGGER # (optional, for logging consistency)
9 | from core import banned_users
10 |
11 | # Define your privacy policy text
12 | PRIVACY_POLICY = """
13 | 📜 Privacy Policy for SecureXTools 💥
14 | ━━━━━━━━━━━━━━━━━
15 |
16 | Welcome to SecureXTools 💥. By using our services, you agree to this privacy policy.
17 |
18 | 1. Information We Collect:
19 | - Personal Information: User ID and username for personalization.
20 | - Usage Data: Information on how you use the app to improve our services.
21 |
22 | 2. Usage of Information:
23 | - Service Enhancement: To provide and improve SecureXTools.
24 | - Communication: Updates and new features.
25 | - Security: To prevent unauthorized access.
26 | - Advertisements: Display of promotions.
27 |
28 | 3. Data Security:
29 | - These tools do not store any data, ensuring your privacy.
30 | - We use strong security measures, although no system is 100% secure.
31 |
32 | Thank you for using SecureXTools 💥. We prioritize your privacy and security.
33 | """
34 |
35 | def setup_privacy_handler(app: Client):
36 | @app.on_message(filters.command(["privacy"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
37 | async def show_privacy_policy(client, message):
38 | user_id = message.from_user.id if message.from_user else None
39 | if user_id and banned_users.find_one({"user_id": user_id}):
40 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**")
41 | return
42 |
43 | await client.send_message(
44 | message.chat.id,
45 | PRIVACY_POLICY,
46 | parse_mode=ParseMode.HTML,
47 | reply_markup=InlineKeyboardMarkup([
48 | [InlineKeyboardButton("Close", callback_data="close_privacy_policy")]
49 | ])
50 | )
51 |
52 | @app.on_callback_query(filters.regex("close_privacy_policy"))
53 | async def close_privacy_policy(client, callback_query):
54 | await callback_query.message.delete()
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | # Note: Configure via .env (VPS/Heroku local), direct edits to this file (VPS), or Heroku config vars (app.json/dashboard).
5 | import os
6 | from dotenv import load_dotenv
7 |
8 | # Load .env file if it exists (for VPS or local Heroku testing), but allow Heroku config vars to take precedence
9 | load_dotenv()
10 |
11 | def get_env_or_default(key, default=None, cast_func=str):
12 | """Helper function to load environment variables with type casting and default values."""
13 | value = os.getenv(key)
14 | if value is not None and value.strip() != "":
15 | try:
16 | return cast_func(value)
17 | except (ValueError, TypeError) as e:
18 | print(f"Error casting {key} with value '{value}' to {cast_func.__name__}: {e}")
19 | return default
20 | return default
21 |
22 | # TELEGRAM WITH PYROGRAM MTPROTO API CONNECTION AND AUTHORIZATION SETUP
23 | API_ID = get_env_or_default("API_ID", "Your_API_ID_Here")
24 | API_HASH = get_env_or_default("API_HASH", "Your_API_HASH_Here")
25 | BOT_TOKEN = get_env_or_default("BOT_TOKEN", "Your_BOT_TOKEN_Here")
26 | BOT_USERNAME = get_env_or_default("BOT_USERNAME", "@YourBotUsername")
27 | BOT_NAME = get_env_or_default("BOT_NAME", "YourBotName")
28 |
29 | # ADMINS AND SUDO USERS FOR BROADCAST AND OTHER SUDO WORKS
30 | OWNER_ID = get_env_or_default("OWNER_ID", "Your_OWNER_ID_Here", int)
31 | DEVELOPER_USER_ID = get_env_or_default("DEVELOPER_USER_ID", "Your_OWNER_ID_Here", int)
32 |
33 | # MONGODB URL AND DATABASE URL FOR USER DATABASE AND GROUP SETTINGS DATABASE
34 | MONGO_URL = get_env_or_default("MONGO_URL", "Your_MONGO_URL_Here")
35 | DATABASE_URL = get_env_or_default("DATABASE_URL", "Your_MONGO_URL_Here")
36 |
37 | # ALL COMMANDS PREFIXES FOR ALLOWING ALL COMMANDS SUPPORT
38 | raw_prefixes = get_env_or_default("COMMAND_PREFIX", "!|.|#|,|/")
39 | COMMAND_PREFIX = [prefix.strip() for prefix in raw_prefixes.split("|") if prefix.strip()]
40 |
41 | # BOT DEVS CHANNEL URL AND PROFILE ERROR URL
42 | UPDATE_CHANNEL_URL = get_env_or_default("UPDATE_CHANNEL_URL", "t.me/TheSmartDev")
43 |
44 | # MAX FILE SIZE LIMITS FOR OCR TOOLS AND IMGAI
45 | IMGAI_SIZE_LIMIT = get_env_or_default("IMGAI_SIZE_LIMIT", 5242880, int)
46 | MAX_TXT_SIZE = get_env_or_default("MAX_TXT_SIZE", 15728640, int)
47 | MAX_VIDEO_SIZE = get_env_or_default("MAX_VIDEO_SIZE", 2147483648, int)
48 |
49 | # YOUTUBE COOKIES PATH
50 | YT_COOKIES_PATH = get_env_or_default("YT_COOKIES_PATH", "./cookies/ItsSmartToolBot.txt")
51 |
52 | # VIDEO RESOLUTION FOR YOUTUBE DOWNLOADS
53 | VIDEO_RESOLUTION = get_env_or_default("VIDEO_RESOLUTION", "1280x720", lambda x: tuple(map(int, x.split('x'))))
54 |
55 | # PROXY CHECK LIMIT
56 | PROXY_CHECK_LIMIT = get_env_or_default("PROXY_CHECK_LIMIT", 20, int)
57 |
58 | # Validation for critical variables
59 | required_vars = {
60 | "API_ID": API_ID,
61 | "API_HASH": API_HASH,
62 | "BOT_TOKEN": BOT_TOKEN,
63 | "BOT_USERNAME": BOT_USERNAME,
64 | "OWNER_ID": OWNER_ID,
65 | "DEVELOPER_USER_ID": DEVELOPER_USER_ID,
66 | "MONGO_URL": MONGO_URL
67 | }
68 |
69 | for var_name, var_value in required_vars.items():
70 | if var_value is None or var_value == f"Your_{var_name}_Here" or (isinstance(var_value, str) and var_value.strip() == ""):
71 | raise ValueError(f"Required variable {var_name} is missing or invalid. Set it in .env (VPS), config.py (VPS), or Heroku config vars.")
72 |
73 | # Logging for debugging
74 | print("Loaded COMMAND_PREFIX:", COMMAND_PREFIX)
75 |
76 | if not COMMAND_PREFIX:
77 | raise ValueError("No command prefixes found. Set COMMAND_PREFIX in .env, config.py, or Heroku config vars.")
--------------------------------------------------------------------------------
/modules/netxutils/ip.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import aiohttp
5 | import asyncio
6 | from pyrogram import Client, filters
7 | from pyrogram.types import Message
8 | from pyrogram.enums import ParseMode
9 | from config import COMMAND_PREFIX
10 | from utils import LOGGER, notify_admin # Use LOGGER and notify_admin from utils
11 | from core import banned_users # Use banned_users from core
12 |
13 | async def get_ip_info(ip: str) -> str:
14 | url = f"https://ipinfo.io/{ip}/json"
15 | try:
16 | async with aiohttp.ClientSession() as session:
17 | async with session.get(url) as response:
18 | response.raise_for_status()
19 | data = await response.json()
20 |
21 | ip = data.get("ip", "Unknown")
22 | asn = data.get("org", "Unknown")
23 | isp = data.get("org", "Unknown")
24 | country = data.get("country", "Unknown")
25 | city = data.get("city", "Unknown")
26 | timezone = data.get("timezone", "Unknown")
27 |
28 | # Simulated IP fraud score and risk level for demonstration
29 | fraud_score = 0
30 | risk_level = "low" if fraud_score < 50 else "high"
31 |
32 | details = (
33 | f"**YOUR IP INFORMATION 🌐**\n"
34 | f"━━━━━━━━━━━━━━━━━━\n"
35 | f"**IP:** `{ip}`\n"
36 | f"**ASN:** `{asn}`\n"
37 | f"**ISP:** `{isp}`\n"
38 | f"**Country City:** `{country} {city}`\n"
39 | f"**Timezone:** `{timezone}`\n"
40 | f"**IP Fraud Score:** `{fraud_score}`\n"
41 | f"**Risk LEVEL:** `{risk_level} Risk`\n"
42 | f"━━━━━━━━━━━━━━━━━━\n"
43 | )
44 |
45 | return details
46 | except aiohttp.ClientError as e:
47 | LOGGER.error(f"Failed to fetch IP info for {ip}: {e}")
48 | await notify_admin(None, f"{COMMAND_PREFIX}ip", e, None)
49 | return "Invalid IP address or API error"
50 | except Exception as e:
51 | LOGGER.error(f"Unexpected error fetching IP info for {ip}: {e}")
52 | await notify_admin(None, f"{COMMAND_PREFIX}ip", e, None)
53 | return "Invalid IP address or API error"
54 |
55 | async def ip_info_handler(client: Client, message: Message):
56 | user_id = message.from_user.id if message.from_user else None
57 | if user_id and banned_users.find_one({"user_id": user_id}):
58 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**")
59 | return
60 |
61 | if len(message.command) <= 1:
62 | await client.send_message(
63 | message.chat.id,
64 | "**❌ Please provide a single IP address.**",
65 | parse_mode=ParseMode.MARKDOWN,
66 | disable_web_page_preview=True
67 | )
68 | return
69 |
70 | ip = message.command[1]
71 | fetching_msg = await client.send_message(
72 | message.chat.id,
73 | "**Fetching IP Info Please Wait.....✨**",
74 | parse_mode=ParseMode.MARKDOWN,
75 | disable_web_page_preview=True
76 | )
77 |
78 | try:
79 | details = await get_ip_info(ip)
80 | if details.startswith("Invalid"):
81 | raise Exception("Failed to retrieve IP information")
82 |
83 | if message.from_user:
84 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip()
85 | user_info = f"\n**Ip-Info Grab By:** [{user_full_name}](tg://user?id={message.from_user.id})"
86 | else:
87 | group_name = message.chat.title or "this group"
88 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group"
89 | user_info = f"\n**Ip-Info Grab By:** [{group_name}]({group_url})"
90 |
91 | details += user_info
92 |
93 | await fetching_msg.edit_text(
94 | details,
95 | parse_mode=ParseMode.MARKDOWN,
96 | disable_web_page_preview=True
97 | )
98 | except Exception as e:
99 | LOGGER.error(f"Error processing IP info for {ip}: {e}")
100 | await notify_admin(client, f"{COMMAND_PREFIX}ip", e, message)
101 | await fetching_msg.edit_text(
102 | "**❌ Sorry Bro IP Info API Dead**",
103 | parse_mode=ParseMode.MARKDOWN,
104 | disable_web_page_preview=True
105 | )
106 |
107 | def setup_ip_handlers(app: Client):
108 | @app.on_message(filters.command(["ip", ".ip"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
109 | async def ip_info(client: Client, message: Message):
110 | await ip_info_handler(client, message)
--------------------------------------------------------------------------------
/callback/misc.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
5 | from pyrogram.enums import ParseMode, ChatType
6 | from config import BOT_USERNAME, OWNER_ID, UPDATE_CHANNEL_URL, BOT_NAME, COMMAND_PREFIX
7 | from utils import LOGGER
8 | from core import banned_users
9 | from pyrogram import filters
10 | import os
11 |
12 | def setup_help_handler(app):
13 | @app.on_message(filters.command(["help", "tutorial"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
14 | async def help_message(client, message):
15 | user_id = message.from_user.id if message.from_user else None
16 | if user_id and banned_users.find_one({"user_id": user_id}):
17 | await client.send_message(message.chat.id, "**✘ Sorry, You're Banned From Using Me!**")
18 | return
19 |
20 | user = message.from_user
21 | full_name = (user.first_name or "") + (f" {user.last_name}" if user and user.last_name else "")
22 | full_name = full_name.strip() or "User"
23 | user_mention = f"[{full_name}](tg://user?id={user.id})" if user else "User"
24 |
25 | if message.chat.type == ChatType.PRIVATE:
26 | help_text = (
27 | f"**◑ Hey {user_mention}, Welcome to the Bot**\n"
28 | f"**➻ This is [{BOT_NAME}](t.me/{BOT_USERNAME.lstrip('@')}), a fast & powerful toolkit.**\n"
29 | "**━━━━━━━━━━━━━━━━━━━━━━**\n"
30 | "**This bot helps you manage your groups and provides various tools.**\n\n"
31 | "**Here are the available commands:**\n"
32 | "━━━━━━━━━━━━━━━━\n"
33 | "**/info** - **Get User Information.**\n"
34 | "**/id** - **Get Group & Channel Information.**\n"
35 | "**/ip** - **Get IP information.**\n"
36 | "**/px** - **Http/Https Proxy Checker.**\n"
37 | "**/ss** - **Take Screenshot of Website.**\n"
38 | "**/fb** - **Download Facebook video.**\n"
39 | "**/in** - **Download Instagram Photos, Reel Or Stories.**\n"
40 | "**/sp** - **Download Spotify Track/Album Or Playlist.**\n"
41 | "**/song** - **Download Music or Youtube as Mp3 Format.**\n"
42 | "**/video** - **Download Youtube Video.**\n"
43 | "**/tt** - **Download Tiktok Video.**\n"
44 | "**/pnt** - **Download Pinterest Video.**\n"
45 | "**━━━━━━━━━━━━━━━━━━━━━━**\n"
46 | "**Tools:** Telegram user info checker, IP checker, proxy checker, screenshot.\n\n"
47 | "**Download platforms:** YouTube, Facebook, Instagram, Pinterest, Spotify, TikTok."
48 | )
49 | else:
50 | group_name = message.chat.title if message.chat.title else "this group"
51 | help_text = (
52 | f"**◑ Hey {user_mention}, Welcome to {group_name}!**\n"
53 | f"**➻ This is [{BOT_NAME}](t.me/{BOT_USERNAME.lstrip('@')}), a fast & powerful toolkit.**\n"
54 | "**━━━━━━━━━━━━━━━━━━━━━━**\n"
55 | "**This bot helps you manage your groups and provides various tools.**\n\n"
56 | "**Here are the available commands:**\n"
57 | "━━━━━━━━━━━━━━━━\n"
58 | "**/info** - **Get User Information.**\n"
59 | "**/id** - **Get Group & Channel Information.**\n"
60 | "**/ip** - **Get IP information.**\n"
61 | "**/px** - **Http/Https Proxy Checker.**\n"
62 | "**/ss** - **Take Screenshot of Website.**\n"
63 | "**/fb** - **Download Facebook video.**\n"
64 | "**/in** - **Download Instagram Photos, Reel Or Stories.**\n"
65 | "**/sp** - **Download Spotify Track/Album Or Playlist.**\n"
66 | "**/song** - **Download Music or Youtube as Mp3 Format.**\n"
67 | "**/video** - **Download Youtube Video.**\n"
68 | "**/tt** - **Download Tiktok Video.**\n"
69 | "**/pnt** - **Download Pinterest Video.**\n"
70 | "**━━━━━━━━━━━━━━━━━━━━━━**\n"
71 | "**Tools:** Telegram user info checker, IP checker, proxy checker, screenshot.\n\n"
72 | "**Download platforms:** YouTube, Facebook, Instagram, Pinterest, Spotify, TikTok."
73 | )
74 |
75 | buttons = InlineKeyboardMarkup(
76 | [
77 | [InlineKeyboardButton("➕ Add Me", url=f"https://t.me/{BOT_USERNAME.lstrip('@')}?startgroup=true")],
78 | [
79 | InlineKeyboardButton("🔧 Developer", user_id=OWNER_ID),
80 | InlineKeyboardButton("✍🏻 Support", url=UPDATE_CHANNEL_URL)
81 | ],
82 | [InlineKeyboardButton("ℹ️ Help & Command", callback_data="helpmenu")]
83 | ]
84 | )
85 |
86 | # Path to the welcome image
87 | photo_path = "assets/wlc.jpg"
88 |
89 | if os.path.exists(photo_path):
90 | await client.send_photo(
91 | chat_id=message.chat.id,
92 | photo=photo_path,
93 | caption=help_text,
94 | parse_mode=ParseMode.MARKDOWN,
95 | reply_markup=buttons
96 | )
97 | LOGGER.info(f"Sent help message with photo to chat {message.chat.id}")
98 | else:
99 | await client.send_message(
100 | chat_id=message.chat.id,
101 | text=help_text,
102 | parse_mode=ParseMode.MARKDOWN,
103 | reply_markup=buttons,
104 | disable_web_page_preview=True
105 | )
106 | LOGGER.warning(f"Photo {photo_path} not found, sent help message without photo to chat {message.chat.id}")
--------------------------------------------------------------------------------
/modules/webxutils/ss.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import requests
6 | import time
7 | import asyncio
8 | from pyrogram import Client, filters
9 | from pyrogram.types import Message
10 | from pyrogram.enums import ParseMode
11 | from config import COMMAND_PREFIX
12 | from urllib.parse import quote
13 | from utils import LOGGER, notify_admin # Import LOGGER and notify_admin from utils
14 | from core import banned_users # Import banned_users for banned user check
15 |
16 | # ScreenshotOne API endpoint
17 | SCREENSHOT_API_URL = "https://api.screenshotone.com/take"
18 | ACCESS_KEY = "Z8LQ6Z0DsTQV_A" # Your API access key
19 | MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB limit for Telegram photos
20 |
21 | def validate_url(url: str) -> bool:
22 | """Basic URL validation for user input"""
23 | return '.' in url and len(url) < 2048
24 |
25 | def normalize_url(url: str) -> str:
26 | """Add scheme if missing"""
27 | if url.startswith(('http://', 'https://')):
28 | return url
29 | else:
30 | return f"https://{url}"
31 |
32 | async def fetch_screenshot(url: str):
33 | """Fetch screenshot from ScreenshotOne API"""
34 | api_url = f"{SCREENSHOT_API_URL}?access_key={ACCESS_KEY}&url={quote(url)}&format=jpg&block_ads=true&block_cookie_banners=true&block_banners_by_heuristics=false&block_trackers=true&delay=0&timeout=60&response_type=by_format&image_quality=80"
35 |
36 | try:
37 | response = requests.get(api_url, stream=True)
38 | response.raise_for_status()
39 |
40 | # Check content type and size
41 | content_type = response.headers.get('Content-Type', '')
42 | if 'image' not in content_type:
43 | raise ValueError(f"Unexpected content type: {content_type}")
44 |
45 | # Check file size
46 | content_length = int(response.headers.get('Content-Length', 0))
47 | if content_length > MAX_FILE_SIZE:
48 | raise ValueError(f"Screenshot too large ({content_length / 1024 / 1024:.1f}MB)")
49 |
50 | return response
51 |
52 | except requests.exceptions.RequestException as e:
53 | LOGGER.error(f"Failed to fetch screenshot: {e}")
54 | return None
55 |
56 | def setup_ss_handler(app: Client):
57 | @app.on_message(filters.command(["ss", "sshot", "screenshot", "snap"], prefixes=COMMAND_PREFIX) &
58 | (filters.private | filters.group))
59 | async def capture_screenshot(client, message: Message):
60 | """Handle screenshot capture command"""
61 | user_id = message.from_user.id if message.from_user else None
62 | if user_id and banned_users.find_one({"user_id": user_id}):
63 | await client.send_message(
64 | chat_id=message.chat.id,
65 | text="**✘Sorry You're Banned From Using Me↯**",
66 | parse_mode=ParseMode.MARKDOWN
67 | )
68 | return
69 |
70 | # Extract URL from the command
71 | if len(message.command) < 2:
72 | await client.send_message(
73 | chat_id=message.chat.id,
74 | text="**❌ Please provide a URL after the command**",
75 | parse_mode=ParseMode.MARKDOWN
76 | )
77 | return
78 |
79 | url = message.command[1].strip()
80 |
81 | if not validate_url(url):
82 | await client.send_message(
83 | chat_id=message.chat.id,
84 | text="**❌ Invalid URL format**",
85 | parse_mode=ParseMode.MARKDOWN
86 | )
87 | return
88 |
89 | processing_msg = await client.send_message(
90 | chat_id=message.chat.id,
91 | text="**Capturing ScreenShot Please Wait**",
92 | parse_mode=ParseMode.MARKDOWN
93 | )
94 |
95 | temp_file = None
96 | try:
97 | # Normalize URL
98 | url = normalize_url(url)
99 |
100 | # Fetch screenshot from ScreenshotOne API
101 | start_time = time.time()
102 | response = await fetch_screenshot(url)
103 |
104 | if not response:
105 | raise ValueError("Failed to capture screenshot.")
106 |
107 | # Generate a unique filename
108 | timestamp = int(time.time())
109 | temp_file = f"screenshot_{timestamp}.jpg"
110 |
111 | # Save the image
112 | with open(temp_file, 'wb') as file:
113 | for chunk in response.iter_content(chunk_size=8192):
114 | if chunk:
115 | file.write(chunk)
116 |
117 | # Verify file size before sending
118 | file_size = os.path.getsize(temp_file)
119 | if file_size > MAX_FILE_SIZE:
120 | raise ValueError(f"Resulting file too large ({file_size/1024/1024:.1f}MB)")
121 |
122 | # Send the photo without caption
123 | await client.send_photo(
124 | chat_id=message.chat.id,
125 | photo=temp_file
126 | )
127 |
128 | # Delete loading message after successful screenshot send
129 | await client.delete_messages(
130 | chat_id=processing_msg.chat.id,
131 | message_ids=processing_msg.id
132 | )
133 |
134 | except Exception as e:
135 | # For ANY error, edit the message to "Sorry Bro API Dead"
136 | error_msg = "**Sorry Bro SS Capture API Dead**"
137 | try:
138 | await client.edit_message_text(
139 | chat_id=processing_msg.chat.id,
140 | message_id=processing_msg.id,
141 | text=error_msg,
142 | parse_mode=ParseMode.MARKDOWN
143 | )
144 | except Exception as edit_error:
145 | LOGGER.warning(f"Failed to edit processing message: {edit_error}")
146 | LOGGER.error(f"Error in capture_screenshot: {e}")
147 | # Notify admins of error
148 | await notify_admin(client, "/ss", e, message)
149 | finally:
150 | # Clean up in all cases
151 | if temp_file and os.path.exists(temp_file):
152 | try:
153 | os.remove(temp_file)
154 | except Exception as cleanup_error:
155 | LOGGER.warning(f"Failed to remove temp file: {cleanup_error}")
--------------------------------------------------------------------------------
/sudoers/speedtest/speedtest.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import asyncio
5 | import subprocess
6 | import json
7 | from concurrent.futures import ThreadPoolExecutor
8 | from pyrogram import Client, filters
9 | from pyrogram.handlers import MessageHandler
10 | from pyrogram.enums import ParseMode, ChatType
11 | from pyrogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup
12 | from config import OWNER_ID, COMMAND_PREFIX, UPDATE_CHANNEL_URL
13 | from core import auth_admins
14 | from utils import LOGGER
15 |
16 | # Helper function to convert speed to human-readable format
17 | def speed_convert(size: float, is_mbps: bool = False) -> str:
18 | if is_mbps:
19 | return f"{size:.2f} Mbps"
20 | power = 2**10
21 | n = 0
22 | power_labels = {0: '', 1: 'K', 2: 'M', 3: 'G', 4: 'T'}
23 | while size > power:
24 | size /= power
25 | n += 1
26 | return f"{size:.2f} {power_labels[n]}bps"
27 |
28 | # Helper function to convert bytes to human-readable file size
29 | def get_readable_file_size(size_in_bytes: int) -> str:
30 | if size_in_bytes < 1024:
31 | return f"{size_in_bytes} B"
32 | power = 1024
33 | n = 0
34 | power_labels = {0: 'B', 1: 'KB', 2: 'MB', 3: 'GB', 4: 'TB'}
35 | while size_in_bytes >= power:
36 | size_in_bytes /= power
37 | n += 1
38 | return f"{size_in_bytes:.2f} {power_labels[n]}"
39 |
40 | # Function to perform speed test
41 | def run_speedtest():
42 | try:
43 | # Use speedtest-cli for detailed JSON output
44 | result = subprocess.run(["speedtest-cli", "--secure", "--json"], capture_output=True, text=True)
45 | if result.returncode != 0:
46 | raise Exception("Speedtest failed.")
47 | data = json.loads(result.stdout)
48 | return data
49 | except Exception as e:
50 | LOGGER.error(f"Speedtest error: {e}")
51 | return {"error": str(e)}
52 |
53 | # Async function to handle speed test logic
54 | async def run_speedtest_task(client: Client, chat_id: int, status_message: Message):
55 | # Run speed test in background thread
56 | with ThreadPoolExecutor() as pool:
57 | try:
58 | result = await asyncio.get_running_loop().run_in_executor(pool, run_speedtest)
59 | except Exception as e:
60 | LOGGER.error(f"Error running speedtest task: {e}")
61 | await status_message.edit_text("✘ Speed Test API Dead ↯", parse_mode=ParseMode.HTML)
62 | return
63 |
64 | if "error" in result:
65 | await status_message.edit_text(f"✘ Speed Test Failed: {result['error']} ↯", parse_mode=ParseMode.HTML)
66 | return
67 |
68 | # Format the results with a stylized design using ✘, ↯, and other symbols
69 | response_text = (
70 | "✘《 💥 SPEEDTEST RESULTS ↯ 》\n"
71 | f"↯ Upload Speed: {speed_convert(result['upload'])}\n"
72 | f"↯ Download Speed: {speed_convert(result['download'])}\n"
73 | f"↯ Ping: {result['ping']:.2f} ms\n"
74 | f"↯ Timestamp: {result['timestamp']}\n"
75 | f"↯ Data Sent: {get_readable_file_size(int(result['bytes_sent']))}\n"
76 | f"↯ Data Received: {get_readable_file_size(int(result['bytes_received']))}\n"
77 | "✘《 🌐 SERVER INFO ↯ 》\n"
78 | f"↯ Name: {result['server']['name']}\n"
79 | f"↯ Country: {result['server']['country']}, {result['server']['cc']}\n"
80 | f"↯ Sponsor: {result['server']['sponsor']}\n"
81 | f"↯ Latency: {result['server']['latency']:.2f} ms\n"
82 | f"↯ Latitude: {result['server']['lat']}\n"
83 | f"↯ Longitude: {result['server']['lon']}\n"
84 | "✘《 👾 CLIENT INFO ↯ 》\n"
85 | f"↯ IP Address: {result['client']['ip']}\n"
86 | f"↯ Latitude: {result['client']['lat']}\n"
87 | f"↯ Longitude: {result['client']['lon']}\n"
88 | f"↯ Country: {result['client']['country']}\n"
89 | f"↯ ISP: {result['client']['isp']}\n"
90 | f"↯ ISP Rating: {result['client'].get('isprating', 'N/A')}\n"
91 | "✘ Powered by @TheSmartDev ↯"
92 | )
93 |
94 | # Create inline keyboard with Update News button
95 | keyboard = InlineKeyboardMarkup([
96 | [InlineKeyboardButton("🔔 Update News", url=UPDATE_CHANNEL_URL)]
97 | ])
98 |
99 | # Delete the status message
100 | await status_message.delete()
101 |
102 | # Send the final result with the inline button
103 | await client.send_message(
104 | chat_id=chat_id,
105 | text=response_text,
106 | parse_mode=ParseMode.HTML,
107 | reply_markup=keyboard
108 | )
109 |
110 | # Handler for speed test command
111 | async def speedtest_handler(client: Client, message: Message):
112 | user_id = message.from_user.id
113 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
114 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
115 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
116 | LOGGER.info("User not admin or owner, sending restricted message")
117 | await client.send_message(
118 | chat_id=message.chat.id,
119 | text="✘ Kids Not Allowed To Do This ↯",
120 | parse_mode=ParseMode.HTML
121 | )
122 | return
123 |
124 | # Send initial status message directly (no reply)
125 | status_message = await client.send_message(
126 | chat_id=message.chat.id,
127 | text="✘ Running Speedtest On Your Server ↯",
128 | parse_mode=ParseMode.HTML
129 | )
130 |
131 | # Schedule the speed test task to run in the background
132 | asyncio.create_task(run_speedtest_task(client, message.chat.id, status_message))
133 |
134 | # Setup function to add the speed test handler
135 | def setup_speed_handler(app: Client):
136 | app.add_handler(MessageHandler(
137 | speedtest_handler,
138 | filters.command("speedtest", prefixes=COMMAND_PREFIX) & (filters.private | filters.group)
139 | ))
140 |
--------------------------------------------------------------------------------
/sudoers/restart/restart.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 | #This Code Basically Forked From https://github.com/abirxdhackz/RestartModule
4 |
5 | import shutil
6 | import os
7 | import asyncio
8 | from pyrogram import Client, filters
9 | from pyrogram.enums import ParseMode
10 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
11 | from config import OWNER_ID, UPDATE_CHANNEL_URL, COMMAND_PREFIX
12 | from core import auth_admins
13 | from utils import LOGGER
14 |
15 | def setup_restart_handler(app: Client):
16 |
17 | @app.on_message(filters.command(["restart", "reboot", "reload"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
18 | async def restart(client, message):
19 | user_id = message.from_user.id
20 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
21 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
22 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
23 | LOGGER.info("User not admin or owner, sending restricted message")
24 | await client.send_message(
25 | chat_id=message.chat.id,
26 | text="✘Kids Not Allowed To Do This↯",
27 | parse_mode=ParseMode.HTML,
28 | reply_markup=InlineKeyboardMarkup(
29 | [
30 | [
31 | InlineKeyboardButton("👨🏼💻 Developer", url="https://t.me/abirxdhackz"),
32 | InlineKeyboardButton("🤖 Other Bots", url=UPDATE_CHANNEL_URL)
33 | ],
34 | [
35 | InlineKeyboardButton("🔗 Source Code", url="https://github.com/abirxdhack/RestartModule"),
36 | InlineKeyboardButton("🔔 Update News", url=UPDATE_CHANNEL_URL)
37 | ]
38 | ]
39 | )
40 | )
41 | return
42 |
43 | LOGGER.info(f"Restart command initiated by user {user_id}")
44 | response = await client.send_message(
45 | chat_id=message.chat.id,
46 | text="SecureXTools Restarting....",
47 | parse_mode=ParseMode.HTML
48 | )
49 |
50 | # Directories to be removed
51 | directories = ["downloads", "temp", "temp_media", "data", "repos"]
52 | for directory in directories:
53 | try:
54 | shutil.rmtree(directory)
55 | LOGGER.info(f"Removed directory: {directory}")
56 | except FileNotFoundError:
57 | LOGGER.debug(f"Directory not found: {directory}")
58 | except Exception as e:
59 | LOGGER.error(f"Failed to remove directory {directory}: {e}")
60 |
61 | # Delete the botlogs.txt file if it exists
62 | if os.path.exists("botlog.txt"):
63 | try:
64 | os.remove("botlog.txt")
65 | LOGGER.info("Removed botlog.txt")
66 | except Exception as e:
67 | LOGGER.error(f"Failed to remove botlog.txt: {e}")
68 |
69 | # Delete session files (assuming session name is "SecureXTools"; replace with actual session name or import from config if different)
70 | session_files = ["SecureXTools.session-journal"]
71 | deleted = []
72 | not_deleted = []
73 | for file in session_files:
74 | try:
75 | os.remove(file)
76 | deleted.append(file)
77 | LOGGER.info(f"Removed session file: {file}")
78 | except FileNotFoundError:
79 | LOGGER.debug(f"Session file not found: {file}")
80 | except Exception as e:
81 | not_deleted.append(file)
82 | LOGGER.error(f"Failed to delete session file {file}: {e}")
83 |
84 | # Construct status message for session file deletion
85 | status_parts = []
86 | if deleted:
87 | status_parts.append(f"Deleted: {', '.join(deleted)}")
88 | if not_deleted:
89 | status_parts.append(f"Failed to delete: {', '.join(not_deleted)}")
90 | status = "No session files to delete." if not status_parts else " ".join(status_parts)
91 | LOGGER.info(f"Session file deletion status: {status}")
92 |
93 | await asyncio.sleep(10)
94 |
95 | await client.edit_message_text(
96 | chat_id=message.chat.id,
97 | message_id=response.id,
98 | text=f"SecureXTools Successfully Restarted!",
99 | parse_mode=ParseMode.HTML
100 | )
101 | LOGGER.info("Bot restart completed, executing system restart")
102 | os.system(f"kill -9 {os.getpid()} && bash start.sh")
103 |
104 | @app.on_message(filters.command(["stop", "kill", "off"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
105 | async def stop(client, message):
106 | user_id = message.from_user.id
107 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
108 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
109 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
110 | LOGGER.info("User not admin or owner, sending restricted message")
111 | await client.send_message(
112 | chat_id=message.chat.id,
113 | text="✘Kids Not Allowed To Do This↯",
114 | parse_mode=ParseMode.HTML,
115 | reply_markup=InlineKeyboardMarkup(
116 | [
117 | [
118 | InlineKeyboardButton("👨🏼💻 Developer", url="https://t.me/abirxdhackz"),
119 | InlineKeyboardButton("🤖 Other Bots", url=UPDATE_CHANNEL_URL)
120 | ],
121 | [
122 | InlineKeyboardButton("🔗 Source Code", url="https://github.com/abirxdhack/RestartModule"),
123 | InlineKeyboardButton("🔔 Update News", url=UPDATE_CHANNEL_URL)
124 | ]
125 | ]
126 | )
127 | )
128 | return
129 |
130 | LOGGER.info(f"Stop command initiated by user {user_id}")
131 | await client.send_message(
132 | chat_id=message.chat.id,
133 | text="Bot Off Successfully All Database Cleared",
134 | parse_mode=ParseMode.HTML
135 | )
136 | LOGGER.info("Bot stop executed, terminating process")
137 | os.system("pkill -f main.py")
138 |
--------------------------------------------------------------------------------
/sudoers/gban/gban.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | from pyrogram import Client, filters
5 | from pyrogram.errors import UserIdInvalid, UsernameInvalid, PeerIdInvalid
6 | from config import OWNER_ID, COMMAND_PREFIX
7 | from core import auth_admins, banned_users
8 | from utils import LOGGER
9 |
10 | def setup_gban_handler(app: Client):
11 | @app.on_message(filters.command(["gban"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
12 | async def gban_command(client, message):
13 | user_id = message.from_user.id
14 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
15 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
16 |
17 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
18 | sent_message = await client.send_message(message.chat.id, "**✘Kids Not Allowed To Do This↯**")
19 | LOGGER.info(f"Unauthorized gban attempt by user {user_id}")
20 | return
21 |
22 | # Check if a user is specified
23 | if len(message.command) < 2 and not message.reply_to_message:
24 | sent_message = await client.send_message(message.chat.id, "**✘Please Specify User To Ban Forever↯**")
25 | return
26 |
27 | # Get target user
28 | target_user = None
29 | target_identifier = None
30 | if message.reply_to_message and message.reply_to_message.from_user:
31 | target_user = message.reply_to_message.from_user
32 | target_identifier = target_user.id
33 | else:
34 | target_identifier = message.command[1]
35 | try:
36 | # Try to resolve as user ID first
37 | target_user = await client.get_users(int(target_identifier))
38 | except (ValueError, UserIdInvalid, PeerIdInvalid):
39 | try:
40 | # If not a valid user ID, try as username
41 | target_identifier = target_identifier.lstrip('@')
42 | target_user = await client.get_users(target_identifier)
43 | except (UsernameInvalid, PeerIdInvalid) as e:
44 | sent_message = await client.send_message(message.chat.id, "**✘Error: Invalid User ID/Username↯**")
45 | LOGGER.error(f"Error resolving user {target_identifier}: {e}")
46 | return
47 |
48 | target_id = target_user.id
49 | target_name = target_user.username or target_user.first_name or str(target_id)
50 |
51 | # Check if user is already banned
52 | if banned_users.find_one({"user_id": target_id}):
53 | sent_message = await client.send_message(message.chat.id, f"**✘User {target_name} is already banned↯**")
54 | return
55 |
56 | # Ban the user
57 | banned_users.insert_one({"user_id": target_id, "username": target_name})
58 |
59 | # Notify the banned user
60 | try:
61 | await client.send_message(target_id, "**✘Bro You're Banned Forever↯**")
62 | except Exception as e:
63 | LOGGER.error(f"Failed to notify banned user {target_id}: {e}")
64 |
65 | # Notify owner and admins
66 | sent_message = await client.send_message(message.chat.id, f"**✘Successfully Banned {target_name}↯**")
67 | for admin_id in [OWNER_ID] + AUTH_ADMIN_IDS:
68 | if admin_id != user_id:
69 | try:
70 | await client.send_message(admin_id, f"**✘Successfully Banned {target_name}↯**")
71 | except Exception as e:
72 | LOGGER.error(f"Failed to notify admin {admin_id}: {e}")
73 |
74 | @app.on_message(filters.command(["gunban"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
75 | async def gunban_command(client, message):
76 | user_id = message.from_user.id
77 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
78 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
79 |
80 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
81 | sent_message = await client.send_message(message.chat.id, "**✘Kids Not Allowed To Do This↯**")
82 | LOGGER.info(f"Unauthorized gunban attempt by user {user_id}")
83 | return
84 |
85 | # Check if a user is specified
86 | if len(message.command) < 2 and not message.reply_to_message:
87 | sent_message = await client.send_message(message.chat.id, "**✘Please Specify User To UnBan ↯**")
88 | return
89 |
90 | # Get target user
91 | target_user = None
92 | target_identifier = None
93 | if message.reply_to_message and message.reply_to_message.from_user:
94 | target_user = message.reply_to_message.from_user
95 | target_identifier = target_user.id
96 | else:
97 | target_identifier = message.command[1]
98 | try:
99 | # Try to resolve as user ID first
100 | target_user = await client.get_users(int(target_identifier))
101 | except (ValueError, UserIdInvalid, PeerIdInvalid):
102 | try:
103 | # If not a valid user ID, try as username
104 | target_identifier = target_identifier.lstrip('@')
105 | target_user = await client.get_users(target_identifier)
106 | except (UsernameInvalid, PeerIdInvalid) as e:
107 | sent_message = await client.send_message(message.chat.id, "**✘Error: Invalid User ID/Username↯**")
108 | LOGGER.error(f"Error resolving user {target_identifier}: {e}")
109 | return
110 |
111 | target_id = target_user.id
112 | target_name = target_user.username or target_user.first_name or str(target_id)
113 |
114 | # Check if user is banned
115 | if not banned_users.find_one({"user_id": target_id}):
116 | sent_message = await client.send_message(message.chat.id, f"**✘User {target_name} is not banned↯**")
117 | return
118 |
119 | # Unban the user
120 | banned_users.delete_one({"user_id": target_id})
121 |
122 | # Notify the unbanned user
123 | try:
124 | await client.send_message(target_id, "**✘Bro You're Unbanned↯**")
125 | except Exception as e:
126 | LOGGER.error(f"Failed to notify unbanned user {target_id}: {e}")
127 |
128 | # Notify owner and admins
129 | sent_message = await client.send_message(message.chat.id, f"**✘Successfully Unbanned {target_name}↯**")
130 | for admin_id in [OWNER_ID] + AUTH_ADMIN_IDS:
131 | if admin_id != user_id:
132 | try:
133 | await client.send_message(admin_id, f"**✘Successfully Unbanned {target_name}↯**")
134 | except Exception as e:
135 | LOGGER.error(f"Failed to notify admin {admin_id}: {e}")
136 |
--------------------------------------------------------------------------------
/modules/netxutils/px.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import asyncio
5 | import socket
6 | import aiohttp
7 | from pyrogram import Client, filters
8 | from pyrogram.types import Message
9 | from config import COMMAND_PREFIX, PROXY_CHECK_LIMIT
10 | from utils import LOGGER, notify_admin # Use LOGGER and notify_admin
11 | from core import banned_users # Use banned_users
12 |
13 | PROXY_TIMEOUT = 10
14 | GEOLOCATION_TIMEOUT = 3
15 |
16 | class HTTPProxyChecker:
17 | def __init__(self):
18 | self.geo_service = {
19 | 'name': 'ipinfo.io',
20 | 'url': "https://ipinfo.io/{ip}/json",
21 | 'parser': lambda data: f"{data.get('region', 'Unknown')} ({data.get('country', 'Unknown')})",
22 | 'headers': {'User-Agent': 'Mozilla/5.0'}
23 | }
24 |
25 | async def get_location(self, session, ip):
26 | try:
27 | url = self.geo_service['url'].format(ip=ip)
28 | async with session.get(
29 | url,
30 | headers=self.geo_service.get('headers', {}),
31 | timeout=GEOLOCATION_TIMEOUT
32 | ) as response:
33 | data = await response.json()
34 | LOGGER.info(f"Location API Response: {data}")
35 | if response.status == 200:
36 | return self.geo_service['parser'](data)
37 | return f"❌ HTTP {response.status}"
38 | except asyncio.TimeoutError:
39 | return "⏳ Timeout"
40 | except Exception as e:
41 | LOGGER.error(f"Error fetching location: {e}")
42 | return f"❌ Error ({str(e)[:30]})"
43 |
44 | async def check_anonymity(self, session, proxy_url):
45 | try:
46 | async with session.get(
47 | "http://httpbin.org/headers",
48 | proxy=proxy_url,
49 | timeout=PROXY_TIMEOUT,
50 | headers={'User-Agent': 'Mozilla/5.0'}
51 | ) as response:
52 | if response.status == 200:
53 | headers_data = await response.json()
54 | client_headers = headers_data.get('headers', {})
55 | if 'X-Forwarded-For' in client_headers:
56 | return 'Transparent'
57 | elif 'Via' in client_headers:
58 | return 'Anonymous'
59 | else:
60 | return 'Elite'
61 | return 'Unknown'
62 | except:
63 | return 'Unknown'
64 |
65 | async def check_proxy(self, proxy, proxy_type='http', auth=None):
66 | result = {
67 | 'proxy': f"{proxy}",
68 | 'status': 'Dead 🔴',
69 | 'location': '• Not determined',
70 | 'anonymity': 'Unknown'
71 | }
72 |
73 | ip = proxy.split(':')[0]
74 |
75 | try:
76 | proxy_url = f"{proxy_type}://{auth['username']}:{auth['password']}@{proxy}" if auth else f"{proxy_type}://{proxy}"
77 | connector = aiohttp.TCPConnector()
78 |
79 | async with aiohttp.ClientSession(connector=connector) as session:
80 | async with session.get(
81 | "http://httpbin.org/ip",
82 | proxy=proxy_url,
83 | timeout=PROXY_TIMEOUT,
84 | headers={'User-Agent': 'Mozilla/5.0'}
85 | ) as response:
86 | data = await response.json()
87 | LOGGER.info(f"Proxy Check API Response: {data}")
88 | if response.status == 200:
89 | result.update({
90 | 'status': 'Live ✅',
91 | 'ip': ip
92 | })
93 | result['anonymity'] = await self.check_anonymity(session, proxy_url)
94 |
95 | result['location'] = await self.get_location(session, ip)
96 |
97 | except Exception as e:
98 | LOGGER.error(f"Error checking proxy: {e}")
99 | async with aiohttp.ClientSession() as session:
100 | result['location'] = await self.get_location(session, ip)
101 |
102 | return result
103 |
104 | checker = HTTPProxyChecker()
105 |
106 | def setup_px_handler(app):
107 | @app.on_message(filters.command(["px", "proxy"], prefixes=COMMAND_PREFIX) & (filters.group | filters.private))
108 | async def px_command_handler(client, message: Message):
109 | user_id = message.from_user.id if message.from_user else None
110 | if user_id and banned_users.find_one({"user_id": user_id}):
111 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**")
112 | return
113 |
114 | args = message.text.split()[1:]
115 | if len(args) > 0:
116 | if len(args) >= 3 and ':' not in args[-1] and ':' not in args[-2]:
117 | auth = {'username': args[-2], 'password': args[-1]}
118 | proxy_args = args[:-2]
119 | else:
120 | auth = None
121 | proxy_args = args
122 | else:
123 | if message.reply_to_message and message.reply_to_message.text:
124 | proxy_text = message.reply_to_message.text
125 | potential_proxies = proxy_text.split()
126 | proxy_args = [p for p in potential_proxies if ':' in p]
127 | auth = None
128 | else:
129 | return await client.send_message(
130 | message.chat.id,
131 | "❌ Provide at least one proxy for check"
132 | )
133 |
134 | if len(proxy_args) > PROXY_CHECK_LIMIT:
135 | return await client.send_message(
136 | message.chat.id,
137 | " ❌ Sorry Bro Maximum Proxy Check Limit Is 20 "
138 | )
139 |
140 | proxies_to_check = []
141 | for proxy in proxy_args:
142 | if '://' in proxy:
143 | parts = proxy.split('://')
144 | if len(parts) == 2 and parts[0].lower() in ['http', 'https']:
145 | proxy_type = parts[0].lower()
146 | proxy_addr = parts[1]
147 | if ':' in proxy_addr:
148 | proxies_to_check.append((proxy_type, proxy_addr))
149 | else:
150 | if ':' in proxy:
151 | proxies_to_check.append(('http', proxy))
152 |
153 | if not proxies_to_check:
154 | return await client.send_message(
155 | message.chat.id,
156 | "❌ The Proxies Are Not Valid At All"
157 | )
158 |
159 | processing_msg = await client.send_message(
160 | chat_id=message.chat.id,
161 | text=f" Smart Proxy Checker Checking Proxies 💥"
162 | )
163 |
164 | try:
165 | tasks = [checker.check_proxy(proxy, proxy_type, auth) for proxy_type, proxy in proxies_to_check]
166 | results = await asyncio.gather(*tasks)
167 | await send_results(client, message, processing_msg, results)
168 | except Exception as e:
169 | LOGGER.error(f"Error during proxy check: {e}")
170 | await processing_msg.edit_text("Sorry Bro Proxy Checker API Dead")
171 | await notify_admin(client, "/px", e, message)
172 |
173 | async def send_results(client, original_msg, processing_msg, results):
174 | response = []
175 |
176 | for res in results:
177 | response.append(f"Proxy: {res['proxy']}\n")
178 | response.append(f"Status: {res['status']}\n")
179 | if res['status'] == 'Live ✅':
180 | response.append(f"Anonymity: {res['anonymity']}\n")
181 | response.append(f"Region: {res['location']}\n")
182 | response.append("\n")
183 |
184 | full_response = ''.join(response)
185 | await processing_msg.edit_text(full_response)
--------------------------------------------------------------------------------
/modules/dlxutils/tik.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import time
6 | import re
7 | from pathlib import Path
8 | from typing import Optional
9 | import aiohttp
10 | import aiofiles
11 | import requests
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 | from utils import LOGGER, progress_bar, notify_admin # Import LOGGER, progress_bar, and notify_admin from utils
17 | from core import banned_users # Check if user is banned
18 |
19 | # Use the imported LOGGER
20 | logger = LOGGER
21 |
22 | # Configuration
23 | class Config:
24 | TEMP_DIR = Path("temp")
25 |
26 | Config.TEMP_DIR.mkdir(exist_ok=True)
27 |
28 | async def sanitize_filename(title: str) -> str:
29 | """Sanitize file name by removing invalid characters."""
30 | title = re.sub(r'[<>:"/\\|?*]', '', title[:50]).strip()
31 | return f"{title.replace(' ', '_')}_{int(time.time())}"
32 |
33 | async def download_video(url: str, downloading_message: Message) -> Optional[dict]:
34 | """Download video from TikTok URL using API."""
35 | api_url = "https://downloader.bot/api/tiktok/info"
36 | payload = {"url": url}
37 | headers = {
38 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
39 | "Referer": "https://downloader.bot"
40 | }
41 |
42 | try:
43 | response = requests.post(api_url, json=payload, headers=headers)
44 | response.raise_for_status()
45 | data = response.json()
46 |
47 | if data.get("error"):
48 | logger.error(f"API error: {data['error']}")
49 | # Notify admins
50 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}tt", Exception(f"API error: {data['error']}"), downloading_message)
51 | return None
52 |
53 | info = data["data"]
54 | video_url = info.get("mp4")
55 | if not video_url:
56 | logger.error("No video URL found in API response")
57 | # Notify admins
58 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}tt", Exception("No video URL found in API response"), downloading_message)
59 | return None
60 |
61 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN)
62 |
63 | title = info.get("title", "TikTok_Video")
64 | safe_title = await sanitize_filename(title)
65 | video_output = Config.TEMP_DIR / f"{safe_title}.mp4"
66 |
67 | async with aiohttp.ClientSession() as session:
68 | async with session.get(video_url, headers=headers) as video_response:
69 | if video_response.status == 200:
70 | async with aiofiles.open(video_output, 'wb') as f:
71 | async for chunk in video_response.content.iter_chunked(8192):
72 | await f.write(chunk)
73 | logger.info(f"Video downloaded successfully to: {video_output}")
74 | return {
75 | 'filename': str(video_output),
76 | 'title': title,
77 | 'webpage_url': url
78 | }
79 | else:
80 | logger.error(f"Failed to download video: HTTP {video_response.status}")
81 | # Notify admins
82 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}tt", Exception(f"Failed to download video: HTTP {video_response.status}"), downloading_message)
83 | return None
84 | except Exception as e:
85 | logger.error(f"Error downloading video: {e}")
86 | # Notify admins
87 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}tt", e, downloading_message)
88 | return None
89 |
90 | def setup_tt_handler(app: Client):
91 | # Create a regex pattern from the COMMAND_PREFIX list
92 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]"
93 |
94 | @app.on_message(filters.regex(rf"^{command_prefix_regex}tt(\s+https?://\S+)?$") & (filters.private | filters.group))
95 | async def tiktok_handler(client: Client, message: Message):
96 | # Check if user is banned
97 | user_id = message.from_user.id if message.from_user else None
98 | if user_id and banned_users.find_one({"user_id": user_id}):
99 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**")
100 | return
101 |
102 | url = None
103 | # Check if the message is a reply to another message containing a URL
104 | if message.reply_to_message and message.reply_to_message.text:
105 | replied_text = message.reply_to_message.text
106 | if re.match(r'https?://\S+', replied_text):
107 | url = replied_text
108 |
109 | # If no URL from reply, check the command arguments
110 | if not url:
111 | command_parts = message.text.split(maxsplit=1)
112 | if len(command_parts) < 2:
113 | await client.send_message(
114 | chat_id=message.chat.id,
115 | text="**Please provide a TikTok link ❌**",
116 | parse_mode=ParseMode.MARKDOWN
117 | )
118 | logger.warning(f"No TikTok URL provided, user: {user_id or 'unknown'}, chat: {message.chat.id}")
119 | return
120 | url = command_parts[1]
121 |
122 | status_message = await client.send_message(
123 | chat_id=message.chat.id,
124 | text="**Searching The Video...**",
125 | parse_mode=ParseMode.MARKDOWN
126 | )
127 |
128 | try:
129 | video_info = await download_video(url, status_message)
130 | if not video_info:
131 | await status_message.edit_text("**❌ Invalid Video URL Inputted**", parse_mode=ParseMode.MARKDOWN)
132 | logger.error(f"Failed to download video for URL: {url}")
133 | return
134 |
135 | title = video_info['title']
136 | filename = video_info['filename']
137 | webpage_url = video_info['webpage_url']
138 |
139 | if message.from_user:
140 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip()
141 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})"
142 | else:
143 | group_name = message.chat.title or "this group"
144 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group"
145 | user_info = f"[{group_name}]({group_url})"
146 |
147 | caption = (
148 | f"🎵 **Title**: **{title}**\n"
149 | f"━━━━━━━━━━━━━━━━━━━━━\n"
150 | f"🔗 **Url**: [Watch On TikTok]({webpage_url})\n"
151 | f"━━━━━━━━━━━━━━━━━━━━━\n"
152 | f"**Downloaded By**: {user_info}"
153 | )
154 |
155 | start_time = time.time()
156 | last_update_time = [start_time]
157 |
158 | await client.send_video(
159 | chat_id=message.chat.id,
160 | video=filename,
161 | caption=caption,
162 | parse_mode=ParseMode.MARKDOWN,
163 | supports_streaming=True,
164 | progress=progress_bar,
165 | progress_args=(status_message, start_time, last_update_time)
166 | )
167 |
168 | await status_message.delete()
169 | if os.path.exists(filename):
170 | os.remove(filename)
171 | logger.info(f"Deleted the video file: {filename}")
172 |
173 | except Exception as e:
174 | logger.error(f"An error occurred: {e}")
175 | # Notify admins
176 | await notify_admin(client, f"{COMMAND_PREFIX}tt", e, message)
177 | # Send user-facing error message
178 | await status_message.edit_text("**TikTok Downloader API Dead**", parse_mode=ParseMode.MARKDOWN)
--------------------------------------------------------------------------------
/modules/hlpxutils/help.py:
--------------------------------------------------------------------------------
1 | #Copyright @ISmartDevs
2 | #Channel t.me/TheSmartDev
3 |
4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
5 | from pyrogram.enums import ParseMode, ChatType
6 | from config import BOT_USERNAME, OWNER_ID, UPDATE_CHANNEL_URL, BOT_NAME, COMMAND_PREFIX
7 | from utils import LOGGER
8 | from core import banned_users
9 | from pyrogram import filters
10 | import os
11 |
12 | def setup_help_handler(app):
13 | @app.on_message(filters.command(["help", "tutorial"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
14 | async def help_message(client, message):
15 | user_id = message.from_user.id if message.from_user else None
16 | if user_id and banned_users.find_one({"user_id": user_id}):
17 | await client.send_message(message.chat.id, "**✘ Sorry You're Banned From Using Me↯**")
18 | return
19 |
20 | user = message.from_user
21 | full_name = (user.first_name or "") + (f" {user.last_name}" if user and user.last_name else "")
22 | full_name = full_name.strip() or "User"
23 | user_mention = f"[{full_name}](tg://user?id={user.id})" if user else "User"
24 |
25 | if message.chat.type == ChatType.PRIVATE:
26 | help_text = (
27 | f"**This bot helps you manage your groups and provides various tools.**\n\n"
28 | "**Here are the available commands:**\n"
29 | "**━━━━━━━━━━━━━━━━**\n"
30 | "**/info** - **Get User Information.**\n"
31 | "**/id** - **Get Group & Channel Information.**\n"
32 | "**/ip** - **Get IP information.**\n"
33 | "**/px** - **Http/Https Proxy Checker.**\n"
34 | "**/ss** - **Take Screenshot of Website.**\n"
35 | "**/fb** - **Download Facebook video.**\n"
36 | "**/in** - **Download Instagram Photos, Reel Or Stories.**\n"
37 | "**/sp Or Send Direct Url** - **Download Spotify Track/Album Or Playlist.**\n"
38 | "**/song** - **Download Music or Youtube as Mp3 Format.**\n"
39 | "**/video** - **Download Youtube Video.**\n"
40 | "**/tt** - **Download Tiktok Video.**\n"
41 | "**/pnt** - **Download Pinterest Video.**"
42 | )
43 | else:
44 | group_name = message.chat.title if message.chat.title else "this group"
45 | help_text = (
46 | f"**Hey {user_mention}, Welcome to {group_name}!**\n"
47 | f"**This bot helps you manage your groups and provides various tools.**\n\n"
48 | "**Here are the available commands:**\n"
49 | "**━━━━━━━━━━━━━━━━**\n"
50 | "**/info** - **Get User Information.**\n"
51 | "**/id** - **Get Group & Channel Information.**\n"
52 | "**/ip** - **Get IP information.**\n"
53 | "**/px** - **Http/Https Proxy Checker.**\n"
54 | "**/ss** - **Take Screenshot of Website.**\n"
55 | "**/fb** - **Download Facebook video.**\n"
56 | "**/in** - **Download Instagram Photos, Reel Or Stories.**\n"
57 | "**/sp Or Send Direct Url** - **Download Spotify Track/Album Or Playlist.**\n"
58 | "**/song** - **Download Music or Youtube as Mp3 Format.**\n"
59 | "**/video** - **Download Youtube Video.**\n"
60 | "**/tt** - **Download Tiktok Video.**\n"
61 | "**/pnt** - **Download Pinterest Video.**"
62 | )
63 |
64 | buttons = InlineKeyboardMarkup([
65 | [
66 | InlineKeyboardButton("Back to Start", callback_data="backtostartmsg"),
67 | InlineKeyboardButton("Close", callback_data="Closemsg")
68 | ]
69 | ])
70 |
71 | await client.send_message(
72 | chat_id=message.chat.id,
73 | text=help_text,
74 | parse_mode=ParseMode.MARKDOWN,
75 | reply_markup=buttons,
76 | disable_web_page_preview=True,
77 | )
78 | LOGGER.info(f"Sent help message to chat {message.chat.id}")
79 |
80 | @app.on_callback_query(filters.regex("backtostartmsg|helpmenu|Closemsg"))
81 | async def handle_callback_query(client, callback_query):
82 | LOGGER.info(f"Processing callback '{callback_query.data}' from user {callback_query.from_user.id}")
83 |
84 | user = callback_query.from_user
85 | full_name = (user.first_name or "") + (f" {user.last_name}" if user.last_name else "")
86 | full_name = full_name.strip() or "User"
87 | user_mention = f"[{full_name}](tg://user?id={user.id})"
88 |
89 | if callback_query.data == "helpmenu":
90 | help_text = (
91 | "**This bot helps you manage your groups and provides various tools.**\n\n"
92 | "**Here are the available commands:**\n"
93 | "**━━━━━━━━━━━━━━━━**\n"
94 | "**/info** - **Get User Information.**\n"
95 | "**/id** - **Get Group & Channel Information.**\n"
96 | "**/ip** - **Get IP information.**\n"
97 | "**/px** - **Http/Https Proxy Checker.**\n"
98 | "**/ss** - **Take Screenshot of Website.**\n"
99 | "**/fb** - **Download Facebook video.**\n"
100 | "**/in** - **Download Instagram Photos, Reel Or Stories.**\n"
101 | "**/sp ** - **Download Spotify Track/Album Or Playlist.**\n"
102 | "**/song** - **Download Music or Youtube as Mp3 Format.**\n"
103 | "**/video** - **Download Youtube Video.**\n"
104 | "**/tt** - **Download Tiktok Video.**\n"
105 | "**/pnt** - **Download Pinterest Video.**"
106 | )
107 | buttons = InlineKeyboardMarkup(
108 | [
109 | [
110 | InlineKeyboardButton("Back", callback_data="backtostartmsg"),
111 | InlineKeyboardButton("Close", callback_data="Closemsg")
112 | ]
113 | ]
114 | )
115 | await client.edit_message_text(
116 | chat_id=callback_query.message.chat.id,
117 | message_id=callback_query.message.id,
118 | text=help_text,
119 | reply_markup=buttons,
120 | parse_mode=ParseMode.MARKDOWN
121 | )
122 | LOGGER.info(f"Edited message to show help menu for user {user.id}")
123 |
124 | elif callback_query.data == "backtostartmsg":
125 | welcome_text = (
126 | f"**◑ ʜᴇʏ {user_mention}, ᴡᴇʟᴄᴏᴍᴇ ᴛᴏ ᴛʜᴇ ʙᴏᴛ**\n"
127 | f"**➻ ᴛʜɪs ɪs [{BOT_NAME}](t.me/{BOT_USERNAME.lstrip('@')}), ᴀ ғᴀsᴛ & ᴘᴏᴡᴇʀғᴜʟ ᴛᴏᴏʟᴋɪᴛ.**\n"
128 | "**━━━━━━━━━━━━━━━━━━━━━━**\n"
129 | "**Tᴏᴏʟs:** ᴛᴇʟᴇɢʀᴀᴍ ᴜsᴇʀ ɪɴғᴏ ᴄʜᴇᴄᴋᴇʀ, ɪᴘ ᴄʜᴇᴄᴋᴇʀ, ᴘʀᴏxʏ ᴄʜᴇᴄᴋᴇʀ, sᴄʀᴇᴇɴsʜᴏᴛ.\n\n"
130 | "**ᴅᴏᴡɴʟᴏᴀᴅ ᴘʟᴀᴛғᴏʀᴍs:** ʏᴏᴜᴛᴜʙᴇ, ғᴀᴄᴇʙᴏᴏᴋ, ɪɴsᴛᴀɢʀᴀᴍ, ᴘɪɴᴛᴇʀᴇsᴛ, sᴘᴏᴛɪғʏ, ᴛɪᴋᴛᴏᴋ."
131 | )
132 | buttons = InlineKeyboardMarkup(
133 | [
134 | [InlineKeyboardButton("➕ Add Me", url=f"https://t.me/{BOT_USERNAME.lstrip('@')}?startgroup=true")],
135 | [
136 | InlineKeyboardButton("🔧 Developer", user_id=OWNER_ID),
137 | InlineKeyboardButton("✍🏻 Support", url=UPDATE_CHANNEL_URL)
138 | ],
139 | [InlineKeyboardButton("ℹ️ Help & Command", callback_data="helpmenu")]
140 | ]
141 | )
142 | photo_path = "assets/wlc.jpg"
143 |
144 | if os.path.exists(photo_path):
145 | await client.send_photo(
146 | chat_id=callback_query.message.chat.id,
147 | photo=photo_path,
148 | caption=welcome_text,
149 | parse_mode=ParseMode.MARKDOWN,
150 | reply_markup=buttons
151 | )
152 | await client.delete_messages(
153 | chat_id=callback_query.message.chat.id,
154 | message_ids=callback_query.message.id
155 | )
156 | LOGGER.info(f"Sent welcome photo message and deleted original for user {user.id}")
157 | else:
158 | await client.edit_message_text(
159 | chat_id=callback_query.message.chat.id,
160 | message_id=callback_query.message.id,
161 | text=welcome_text,
162 | reply_markup=buttons,
163 | parse_mode=ParseMode.MARKDOWN
164 | )
165 | LOGGER.warning(f"Photo {photo_path} not found, edited message to show welcome message for user {user.id}")
166 |
167 | elif callback_query.data == "Closemsg":
168 | await client.delete_messages(
169 | chat_id=callback_query.message.chat.id,
170 | message_ids=callback_query.message.id
171 | )
172 | LOGGER.info(f"Deleted message for user {user.id}")
173 |
--------------------------------------------------------------------------------
/modules/dlxutils/pin.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import re
6 | import asyncio
7 | import time
8 | import aiohttp
9 | import aiofiles
10 | from pathlib import Path
11 | from typing import Optional
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 | from utils import LOGGER, progress_bar, notify_admin # Import LOGGER, progress_bar, and notify_admin from utils
17 | from core import banned_users # Check if user is banned
18 |
19 | # Use the imported LOGGER
20 | logger = LOGGER
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 sanitize_filename(self, title: str) -> str:
33 | """Sanitize file name by removing invalid characters."""
34 | title = re.sub(r'[<>:"/\\|?*]', '', title[:50]).strip()
35 | return f"{title.replace(' ', '_')}_{int(time.time())}"
36 |
37 | async def download_media(self, url: str, downloading_message: Message) -> Optional[dict]:
38 | self.temp_dir.mkdir(exist_ok=True)
39 | api_url = f"https://pin-ten-pi.vercel.app/dl?url={url}"
40 |
41 | try:
42 | async with aiohttp.ClientSession(
43 | connector=aiohttp.TCPConnector(limit=100),
44 | timeout=aiohttp.ClientTimeout(total=30)
45 | ) as session:
46 | async with session.get(api_url) as response:
47 | logger.info(f"API request to {api_url} returned status {response.status}")
48 | if response.status == 200:
49 | data = await response.json()
50 | logger.info(f"API response: {data}")
51 | media_url = data.get("dl_url")
52 | title = data.get("title", "Pinterest Media")
53 | if not media_url:
54 | logger.error("No download URL found in API response")
55 | await downloading_message.edit_text("**Unable To Extract Url**", parse_mode=ParseMode.MARKDOWN)
56 | return None
57 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN)
58 | safe_title = await self.sanitize_filename(title)
59 | filename = self.temp_dir / f"{safe_title}.mp4"
60 | await self._download_file(session, media_url, filename)
61 | return {
62 | 'title': title,
63 | 'filename': str(filename),
64 | 'webpage_url': url
65 | }
66 | logger.error(f"API request failed: HTTP status {response.status}")
67 | return None
68 | except aiohttp.ClientError as e:
69 | logger.error(f"Pinterest download error: {e}")
70 | # Notify admins
71 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}pnt", e, downloading_message)
72 | return None
73 | except asyncio.TimeoutError:
74 | logger.error("Request to Pinterest API timed out")
75 | # Notify admins
76 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}pnt", asyncio.TimeoutError("Request to Pinterest API timed out"), downloading_message)
77 | return None
78 | except Exception as e:
79 | logger.error(f"Pinterest download error: {e}")
80 | # Notify admins
81 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}pnt", e, downloading_message)
82 | return None
83 |
84 | async def _download_file(self, session: aiohttp.ClientSession, url: str, dest: Path):
85 | try:
86 | async with session.get(url) as response:
87 | if response.status == 200:
88 | logger.info(f"Downloading media from {url} to {dest}")
89 | async with aiofiles.open(dest, mode='wb') as f:
90 | async for chunk in response.content.iter_chunked(1024 * 1024): # 1MB chunks
91 | await f.write(chunk)
92 | logger.info(f"Media downloaded successfully to {dest}")
93 | else:
94 | logger.error(f"Failed to download file: HTTP status {response.status}")
95 | raise Exception(f"Failed to download file: {response.status}")
96 | except aiohttp.ClientError as e:
97 | logger.error(f"Error downloading file from {url}: {e}")
98 | # Notify admins
99 | await notify_admin(None, f"{COMMAND_PREFIX}pnt", e, None)
100 | raise
101 |
102 | def setup_pinterest_handler(app: Client):
103 | pin_downloader = PinterestDownloader(Config.TEMP_DIR)
104 |
105 | # Create a regex pattern from the COMMAND_PREFIX list
106 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]"
107 |
108 | @app.on_message(
109 | filters.regex(rf"^{command_prefix_regex}(pnt|pint)(\s+https?://\S+)?$") &
110 | (filters.private | filters.group)
111 | )
112 | async def pin_handler(client: Client, message: Message):
113 | # Check if user is banned
114 | user_id = message.from_user.id if message.from_user else None
115 | if user_id and banned_users.find_one({"user_id": user_id}):
116 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**", parse_mode=ParseMode.MARKDOWN)
117 | return
118 |
119 | url = None
120 | # Check if URL from reply
121 | if message.reply_to_message and message.reply_to_message.text:
122 | match = re.search(r"https?://pin\.it/\S+", message.reply_to_message.text)
123 | if match:
124 | url = match.group(0)
125 | # Check if URL from command
126 | if not url:
127 | command_parts = message.text.split(maxsplit=1)
128 | if len(command_parts) > 1:
129 | match = re.search(r"https?://pin\.it/\S+", command_parts[1])
130 | if match:
131 | url = match.group(0)
132 |
133 | if not url:
134 | await client.send_message(
135 | chat_id=message.chat.id,
136 | text="**Please provide a Pinterest link**",
137 | parse_mode=ParseMode.MARKDOWN
138 | )
139 | logger.warning(f"No Pinterest URL provided, user: {user_id or 'unknown'}, chat: {message.chat.id}")
140 | return
141 |
142 | logger.info(f"Pinterest URL received: {url}, user: {user_id or 'unknown'}, chat: {message.chat.id}")
143 | downloading_message = await client.send_message(
144 | chat_id=message.chat.id,
145 | text="**Searching The Media**",
146 | parse_mode=ParseMode.MARKDOWN
147 | )
148 |
149 | try:
150 | media_info = await pin_downloader.download_media(url, downloading_message)
151 | if not media_info:
152 | await downloading_message.edit_text("**Unable To Extract Url**", parse_mode=ParseMode.MARKDOWN)
153 | logger.error(f"Failed to download media for URL: {url}")
154 | return
155 |
156 | title = media_info['title']
157 | filename = media_info['filename']
158 | webpage_url = media_info['webpage_url']
159 |
160 | if message.from_user:
161 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip()
162 | user_info = f"[{user_full_name}](tg://user?id={user_id})"
163 | else:
164 | group_name = message.chat.title or "this group"
165 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group"
166 | user_info = f"[{group_name}]({group_url})"
167 |
168 | caption = (
169 | f"🎥 **Title**: `{title}`\n"
170 | f"━━━━━━━━━━━━━━━━━━━━━\n"
171 | f"🔗 **Link**: [Watch on Pinterest]({webpage_url})\n"
172 | f"━━━━━━━━━━━━━━━━━━━━━\n"
173 | f"**Downloaded By**: {user_info}"
174 | )
175 |
176 | async with aiofiles.open(filename, 'rb'):
177 | start_time = time.time()
178 | last_update_time = [start_time]
179 | await client.send_video(
180 | chat_id=message.chat.id,
181 | video=filename,
182 | supports_streaming=True,
183 | caption=caption,
184 | parse_mode=ParseMode.MARKDOWN,
185 | progress=progress_bar,
186 | progress_args=(downloading_message, start_time, last_update_time)
187 | )
188 |
189 | await downloading_message.delete()
190 | if os.path.exists(filename):
191 | os.remove(filename)
192 | logger.info(f"Deleted video file: {filename}")
193 |
194 | except Exception as e:
195 | logger.error(f"Error processing Pinterest media: {e}")
196 | # Notify admins
197 | await notify_admin(client, f"{COMMAND_PREFIX}pnt", e, downloading_message)
198 | # Send user-facing error message
199 | await downloading_message.edit_text("**Pinterest Downloader API Dead**", parse_mode=ParseMode.MARKDOWN)
--------------------------------------------------------------------------------
/modules/dlxutils/fb.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import re
6 | import asyncio
7 | import time
8 | import aiohttp
9 | import aiofiles
10 | from pathlib import Path
11 | from typing import Optional
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 | from utils import LOGGER, progress_bar, notify_admin # Import LOGGER, progress_bar, and notify_admin from utils
17 | from core import banned_users # Check if user is banned
18 |
19 | # Use the imported LOGGER
20 | logger = LOGGER
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 sanitize_filename(self, title: str) -> str:
33 | """Sanitize file name by removing invalid characters."""
34 | title = re.sub(r'[<>:"/\\|?*]', '', title[:50]).strip()
35 | return f"{title.replace(' ', '_')}_{int(time.time())}"
36 |
37 | async def download_video(self, url: str, downloading_message: Message) -> Optional[dict]:
38 | self.temp_dir.mkdir(exist_ok=True)
39 | api_url = f"https://tooly.chative.io/facebook/video?url={url}"
40 |
41 | try:
42 | async with aiohttp.ClientSession(
43 | connector=aiohttp.TCPConnector(limit_per_host=10),
44 | timeout=aiohttp.ClientTimeout(total=30)
45 | ) as session:
46 | async with session.get(api_url) as response:
47 | logger.info(f"API request to {api_url} returned status {response.status}")
48 | if response.status == 200:
49 | data = await response.json()
50 | logger.info(f"API response: {data}")
51 | if data.get("success"):
52 | video_url = data["videos"].get("hd", {}).get("url")
53 | if not video_url:
54 | logger.error("No HD video URL found in API response")
55 | await downloading_message.edit_text("**Unable To Extract Video URL**", parse_mode=ParseMode.MARKDOWN)
56 | return None
57 | await downloading_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN)
58 | title = data.get("title", "Facebook Video")
59 | safe_title = await self.sanitize_filename(title)
60 | filename = self.temp_dir / f"{safe_title}.mp4"
61 | await self._download_file(session, video_url, filename)
62 | return {
63 | 'title': title,
64 | 'filename': str(filename),
65 | 'webpage_url': url
66 | }
67 | logger.error("API response indicates failure")
68 | return None
69 | logger.error(f"API request failed: HTTP status {response.status}")
70 | return None
71 | except aiohttp.ClientError as e:
72 | logger.error(f"Facebook download error: {e}")
73 | # Notify admins
74 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}fb", e, downloading_message)
75 | return None
76 | except asyncio.TimeoutError:
77 | logger.error("Request to Facebook API timed out")
78 | # Notify admins
79 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}fb", asyncio.TimeoutError("Request to Facebook API timed out"), downloading_message)
80 | return None
81 | except Exception as e:
82 | logger.error(f"Facebook download error: {e}")
83 | # Notify admins
84 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}fb", e, downloading_message)
85 | return None
86 |
87 | async def _download_file(self, session: aiohttp.ClientSession, url: str, dest: Path):
88 | try:
89 | async with session.get(url) as response:
90 | if response.status == 200:
91 | logger.info(f"Downloading video from {url} to {dest}")
92 | async with aiofiles.open(dest, mode='wb') as f:
93 | async for chunk in response.content.iter_chunked(1024 * 1024): # 1MB chunks
94 | await f.write(chunk)
95 | logger.info(f"Video downloaded successfully to {dest}")
96 | else:
97 | logger.error(f"Failed to download file: HTTP status {response.status}")
98 | raise Exception(f"Failed to download file: HTTP status {response.status}")
99 | except aiohttp.ClientError as e:
100 | logger.error(f"Error downloading file from {url}: {e}")
101 | # Notify admins
102 | await notify_admin(None, f"{COMMAND_PREFIX}fb", e, None)
103 | raise
104 |
105 | def setup_fb_handlers(app: Client):
106 | fb_downloader = FacebookDownloader(Config.TEMP_DIR)
107 |
108 | # Create a regex pattern from the COMMAND_PREFIX list
109 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]"
110 |
111 | @app.on_message(filters.regex(rf"^{command_prefix_regex}fb(\s+https?://\S+)?$") & (filters.private | filters.group))
112 | async def fb_handler(client: Client, message: Message):
113 | # Check if user is banned
114 | user_id = message.from_user.id if message.from_user else None
115 | if user_id and banned_users.find_one({"user_id": user_id}):
116 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**", parse_mode=ParseMode.MARKDOWN)
117 | return
118 |
119 | url = None
120 | # Check if URL from reply
121 | if message.reply_to_message and message.reply_to_message.text:
122 | match = re.search(r"https?://(www\.facebook\.com|fb\.watch|m\.facebook\.com)/\S+", message.reply_to_message.text)
123 | if match:
124 | url = match.group(0)
125 | # Check if URL from command
126 | if not url:
127 | command_parts = message.text.split(maxsplit=1)
128 | if len(command_parts) > 1:
129 | match = re.search(r"https?://(www\.facebook\.com|fb\.watch|m\.facebook\.com)/\S+", command_parts[1])
130 | if match:
131 | url = match.group(0)
132 |
133 | if not url:
134 | await client.send_message(
135 | chat_id=message.chat.id,
136 | text="**Please provide a valid Facebook video link **",
137 | parse_mode=ParseMode.MARKDOWN
138 | )
139 | logger.warning(f"No Facebook URL provided, user: {user_id or 'unknown'}, chat: {message.chat.id}")
140 | return
141 |
142 | logger.info(f"Facebook URL received: {url}, user: {user_id or 'unknown'}, chat: {message.chat.id}")
143 | downloading_message = await client.send_message(
144 | chat_id=message.chat.id,
145 | text="**Searching The Video**",
146 | parse_mode=ParseMode.MARKDOWN
147 | )
148 |
149 | try:
150 | video_info = await fb_downloader.download_video(url, downloading_message)
151 | if not video_info:
152 | await downloading_message.edit_text("**Invalid Video URL or Video is Private**", parse_mode=ParseMode.MARKDOWN)
153 | logger.error(f"Failed to download video for URL: {url}")
154 | return
155 |
156 | title = video_info['title']
157 | filename = video_info['filename']
158 | webpage_url = video_info['webpage_url']
159 |
160 | if message.from_user:
161 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip()
162 | user_info = f"[{user_full_name}](tg://user?id={user_id})"
163 | else:
164 | group_name = message.chat.title or "this group"
165 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group"
166 | user_info = f"[{group_name}]({group_url})"
167 |
168 | caption = (
169 | f"🎥 **Title**: `{title}`\n"
170 | f"━━━━━━━━━━━━━━━━━━━━━\n"
171 | f"🔗 **Link**: [Watch on Facebook]({webpage_url})\n"
172 | f"━━━━━━━━━━━━━━━━━━━━━\n"
173 | f"**Downloaded By**: {user_info}"
174 | )
175 |
176 | async with aiofiles.open(filename, 'rb'):
177 | start_time = time.time()
178 | last_update_time = [start_time]
179 | await client.send_video(
180 | chat_id=message.chat.id,
181 | video=filename,
182 | supports_streaming=True,
183 | caption=caption,
184 | parse_mode=ParseMode.MARKDOWN,
185 | progress=progress_bar,
186 | progress_args=(downloading_message, start_time, last_update_time)
187 | )
188 |
189 | await downloading_message.delete()
190 | if os.path.exists(filename):
191 | os.remove(filename)
192 | logger.info(f"Deleted video file: {filename}")
193 |
194 | except Exception as e:
195 | logger.error(f"Error processing Facebook video: {e}")
196 | # Notify admins
197 | await notify_admin(client, f"{COMMAND_PREFIX}fb", e, downloading_message)
198 | # Send user-facing error message
199 | await downloading_message.edit_text("**Facebook Downloader API Dead**", parse_mode=ParseMode.MARKDOWN)
--------------------------------------------------------------------------------
/sudoers/sudo/sudo.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import asyncio
5 | from datetime import datetime
6 | from pyrogram import Client, filters
7 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
8 | from pyrogram.enums import ParseMode
9 | from config import OWNER_ID, COMMAND_PREFIX
10 | from core import auth_admins
11 | from utils import LOGGER
12 | from pyrogram.errors import UserIdInvalid, UsernameInvalid, PeerIdInvalid
13 |
14 | def setup_sudo_handler(app: Client):
15 |
16 | async def get_auth_admins():
17 | """Retrieve all authorized admins from MongoDB."""
18 | try:
19 | admins = auth_admins.find({}, {"user_id": 1, "title": 1, "auth_date": 1, "_id": 0})
20 | return {admin["user_id"]: {"title": admin["title"], "auth_date": admin["auth_date"]} for admin in admins}
21 | except Exception as e:
22 | LOGGER.error(f"Error fetching auth admins: {e}")
23 | return {}
24 |
25 | async def add_auth_admin(user_id: int, title: str):
26 | """Add or update an authorized admin in MongoDB with timestamp."""
27 | try:
28 | auth_admins.update_one(
29 | {"user_id": user_id},
30 | {"$set": {"user_id": user_id, "title": title, "auth_date": datetime.utcnow()}},
31 | upsert=True
32 | )
33 | LOGGER.info(f"Added/Updated admin {user_id} with title {title}")
34 | return True
35 | except Exception as e:
36 | LOGGER.error(f"Error adding/updating admin {user_id}: {e}")
37 | return False
38 |
39 | async def remove_auth_admin(user_id: int):
40 | """Remove an authorized admin from MongoDB."""
41 | try:
42 | result = auth_admins.delete_one({"user_id": user_id})
43 | if result.deleted_count > 0:
44 | LOGGER.info(f"Removed admin {user_id}")
45 | return True
46 | else:
47 | LOGGER.info(f"Admin {user_id} not found for removal")
48 | return False
49 | except Exception as e:
50 | LOGGER.error(f"Error removing admin {user_id}: {e}")
51 | return False
52 |
53 | async def resolve_user(client: Client, identifier: str):
54 | """Resolve user by ID or username to get user_id and full name."""
55 | try:
56 | if identifier.startswith("@"):
57 | user = await client.get_users(identifier)
58 | return user.id, f"{user.first_name} {user.last_name or ''}".strip()
59 | else:
60 | user_id = int(identifier)
61 | user = await client.get_users(user_id) # Verify user exists
62 | return user_id, f"{user.first_name} {user.last_name or ''}".strip()
63 | except (UserIdInvalid, UsernameInvalid, PeerIdInvalid, ValueError) as e:
64 | LOGGER.error(f"Error resolving user {identifier}: {e}")
65 | return None, None
66 |
67 | @app.on_message(filters.command(["getadmins"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
68 | async def get_admins_command(client, message):
69 | user_id = message.from_user.id
70 | if user_id != OWNER_ID:
71 | await client.send_message(
72 | chat_id=message.chat.id,
73 | text="**✘Kids Not Allowed To Do This↯**",
74 | parse_mode=ParseMode.MARKDOWN
75 | )
76 | return
77 |
78 | loading_message = await client.send_message(
79 | chat_id=message.chat.id,
80 | text="**✘Fetching Admins List↯**",
81 | parse_mode=ParseMode.MARKDOWN
82 | )
83 | await asyncio.sleep(1)
84 |
85 | admin_list = ["**✘ Bot Admins List ↯**"]
86 | # Add OWNER_ID with title "Owner"
87 | try:
88 | user = await client.get_users(OWNER_ID)
89 | full_name = f"{user.first_name} {user.last_name or ''}".strip()
90 | admin_list.append(
91 | f"\n**✘ Name: {full_name} ↯**\n"
92 | f"**✘ Title: Owner ↯**\n"
93 | f"**✘ Auth Date: Not applicable ↯**"
94 | )
95 | except Exception:
96 | admin_list.append(
97 | f"\n**✘ Name: ID {OWNER_ID} (Not found) ↯**\n"
98 | f"**✘ Title: Owner ↯**\n"
99 | f"**✘ Auth Date: Not applicable ↯**"
100 | )
101 |
102 | # Add AUTH_ADMIN_IDS from MongoDB
103 | auth_admins = await get_auth_admins()
104 | for admin_id, data in auth_admins.items():
105 | try:
106 | user = await client.get_users(admin_id)
107 | full_name = f"{user.first_name} {user.last_name or ''}".strip()
108 | auth_date = data["auth_date"].strftime("%Y-%m-%d %H:%M:%S")
109 | admin_list.append(
110 | f"\n**✘ Name: {full_name} ↯**\n"
111 | f"**✘ Title: {data['title']} ↯**\n"
112 | f"**✘ Auth Date: {auth_date} ↯**"
113 | )
114 | except Exception:
115 | auth_date = data["auth_date"].strftime("%Y-%m-%d %H:%M:%S")
116 | admin_list.append(
117 | f"\n**✘ Name: ID {admin_id} (Not found) ↯**\n"
118 | f"**✘ Title: {data['title']} ↯**\n"
119 | f"**✘ Auth Date: {auth_date} ↯**"
120 | )
121 |
122 | if len(admin_list) == 1:
123 | admin_list.append("\nNo additional admins found.")
124 |
125 | await loading_message.edit_text(
126 | text="\n".join(admin_list),
127 | parse_mode=ParseMode.MARKDOWN,
128 | reply_markup=InlineKeyboardMarkup([
129 | [InlineKeyboardButton("✘ Close ↯", callback_data="close_admins$")]
130 | ])
131 | )
132 |
133 | @app.on_message(filters.command(["auth"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
134 | async def auth_command(client, message):
135 | user_id = message.from_user.id
136 | if user_id != OWNER_ID:
137 | await client.send_message(
138 | chat_id=message.chat.id,
139 | text="**✘Kids Not Allowed To Do This↯**",
140 | parse_mode=ParseMode.MARKDOWN
141 | )
142 | return
143 |
144 | args = message.text.split(maxsplit=2)
145 | if len(args) < 3:
146 | await client.send_message(
147 | chat_id=message.chat.id,
148 | text="**✘Bruh! Provide A Username Or Userid↯**",
149 | parse_mode=ParseMode.MARKDOWN
150 | )
151 | return
152 |
153 | identifier, title = args[1], args[2]
154 | target_user_id, full_name = await resolve_user(client, identifier)
155 | if not target_user_id:
156 | await client.send_message(
157 | chat_id=message.chat.id,
158 | text="**✘Kids Can't Be Promoted↯**",
159 | parse_mode=ParseMode.MARKDOWN
160 | )
161 | return
162 |
163 | if target_user_id == OWNER_ID:
164 | await client.send_message(
165 | chat_id=message.chat.id,
166 | text="**✘Cannot Modify Owners Permission↯**",
167 | parse_mode=ParseMode.MARKDOWN
168 | )
169 | return
170 |
171 | loading_message = await client.send_message(
172 | chat_id=message.chat.id,
173 | text="**✘Adding User To Bot Admins List↯**",
174 | parse_mode=ParseMode.MARKDOWN
175 | )
176 | await asyncio.sleep(1)
177 |
178 | if await add_auth_admin(target_user_id, title):
179 | await loading_message.edit_text(
180 | text=f"**✘Successfully Authorized User {full_name or identifier} As {title}↯**",
181 | parse_mode=ParseMode.MARKDOWN
182 | )
183 | else:
184 | await loading_message.edit_text(
185 | text="**✘Kids Can't Be Promoted↯**",
186 | parse_mode=ParseMode.MARKDOWN
187 | )
188 |
189 | @app.on_message(filters.command(["unauth"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
190 | async def unauth_command(client, message):
191 | user_id = message.from_user.id
192 | if user_id != OWNER_ID:
193 | await client.send_message(
194 | chat_id=message.chat.id,
195 | text="**✘Kids Not Allowed To Do This↯**",
196 | parse_mode=ParseMode.MARKDOWN
197 | )
198 | return
199 |
200 | args = message.text.split(maxsplit=1)
201 | if len(args) < 2:
202 | await client.send_message(
203 | chat_id=message.chat.id,
204 | text="**✘Bruh! Provide A Username Or Userid↯**",
205 | parse_mode=ParseMode.MARKDOWN
206 | )
207 | return
208 |
209 | identifier = args[1]
210 | target_user_id, full_name = await resolve_user(client, identifier)
211 | if not target_user_id:
212 | await client.send_message(
213 | chat_id=message.chat.id,
214 | text="**Invalid user ID or username!**",
215 | parse_mode=ParseMode.MARKDOWN
216 | )
217 | return
218 |
219 | if target_user_id == OWNER_ID:
220 | await client.send_message(
221 | chat_id=message.chat.id,
222 | text="**✘Cannot Modify Owners Permission↯**",
223 | parse_mode=ParseMode.MARKDOWN
224 | )
225 | return
226 |
227 | loading_message = await client.send_message(
228 | chat_id=message.chat.id,
229 | text="**✘Removing User From Bot Admins List↯**", # Fixed typo
230 | parse_mode=ParseMode.MARKDOWN
231 | )
232 | await asyncio.sleep(1)
233 |
234 | if await remove_auth_admin(target_user_id):
235 | await loading_message.edit_text(
236 | text=f"**✘Successfully Removed User {full_name or identifier} From Admins↯**",
237 | parse_mode=ParseMode.MARKDOWN
238 | )
239 | else:
240 | await loading_message.edit_text(
241 | text=f"**✘The User Is Not In Admin List↯**",
242 | parse_mode=ParseMode.MARKDOWN
243 | )
244 |
245 | @app.on_callback_query(filters.regex(r"^close_admins\$"))
246 | async def handle_close_callback(client: Client, query: CallbackQuery):
247 | user_id = query.from_user.id
248 | if user_id != OWNER_ID:
249 | await query.answer(
250 | text="✘Kids Not Allowed To Do This↯",
251 | show_alert=True
252 | )
253 | return
254 | await query.message.delete()
255 | await query.answer()
256 |
--------------------------------------------------------------------------------
/sudoers/logs/logs.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import asyncio
6 | from pyrogram import Client, filters
7 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
8 | from pyrogram.enums import ParseMode
9 | from utils import LOGGER
10 | from config import OWNER_ID, COMMAND_PREFIX
11 | from core import auth_admins
12 | from telegraph import Telegraph
13 |
14 | # Initialize Telegraph client
15 | telegraph = Telegraph()
16 | telegraph.create_account(
17 | short_name="SmartUtilBot",
18 | author_name="SmartUtilBot",
19 | author_url="https://t.me/TheSmartDevs"
20 | )
21 |
22 | def setup_logs_handler(app: Client):
23 |
24 | async def create_telegraph_page(content: str) -> list:
25 | """Create Telegraph pages with the given content, each under 20 KB, and return list of URLs"""
26 | try:
27 | # Truncate content to 40,000 characters to respect Telegraph character limits
28 | truncated_content = content[:40000]
29 | # Convert content to bytes to measure size accurately
30 | content_bytes = truncated_content.encode('utf-8')
31 | max_size_bytes = 20 * 1024 # 20 KB in bytes
32 | pages = []
33 | page_content = ""
34 | current_size = 0
35 | lines = truncated_content.splitlines(keepends=True)
36 |
37 | for line in lines:
38 | line_bytes = line.encode('utf-8')
39 | # Check if adding this line would exceed 20 KB
40 | if current_size + len(line_bytes) > max_size_bytes and page_content:
41 | # Create a Telegraph page with the current content
42 | response = telegraph.create_page(
43 | title="SmartLogs",
44 | html_content=f"
{page_content}",
45 | author_name="SmartUtilBot",
46 | author_url="https://t.me/TheSmartDevs"
47 | )
48 | pages.append(f"https://telegra.ph/{response['path']}")
49 | # Reset for the next page
50 | page_content = ""
51 | current_size = 0
52 | page_content += line
53 | current_size += len(line_bytes)
54 |
55 | # Create a page for any remaining content
56 | if page_content:
57 | response = telegraph.create_page(
58 | title="SmartLogs",
59 | html_content=f"{page_content}",
60 | author_name="SmartUtilBot",
61 | author_url="https://t.me/TheSmartDevs"
62 | )
63 | pages.append(f"https://telegra.ph/{response['path']}")
64 |
65 | return pages
66 | except Exception as e:
67 | LOGGER.error(f"Failed to create Telegraph page: {e}")
68 | return []
69 |
70 | @app.on_message(filters.command(["logs"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
71 | async def logs_command(client, message):
72 | user_id = message.from_user.id
73 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
74 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
75 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
76 | LOGGER.info("User not admin or owner, sending restricted message")
77 | await client.send_message(
78 | chat_id=message.chat.id,
79 | text="**✘Kids Not Allowed To Do This↯**",
80 | parse_mode=ParseMode.MARKDOWN
81 | )
82 | return
83 |
84 | # Send a loading message
85 | loading_message = await client.send_message(
86 | chat_id=message.chat.id,
87 | text="**Checking The Logs...💥**",
88 | parse_mode=ParseMode.MARKDOWN
89 | )
90 |
91 | # Wait for 2 seconds
92 | await asyncio.sleep(2)
93 |
94 | # Check if logs exist
95 | if not os.path.exists("botlog.txt"):
96 | await loading_message.edit_text(
97 | text="**Sorry Bro No Logs Found**",
98 | parse_mode=ParseMode.MARKDOWN
99 | )
100 | return
101 |
102 | LOGGER.info("User is admin or owner, sending log document")
103 | await client.send_document(
104 | chat_id=message.chat.id,
105 | document="botlog.txt",
106 | caption="""**✘ Hey Sir! Here Is Your Logs ↯**
107 | **✘━━━━━━━━━━━━━━━━━━━━━━━━━↯**
108 | **✘ All Logs Database Succesfully Exported! ↯**
109 | **↯ Access Granted Only to Authorized Admins ↯**
110 | **✘━━━━━━━━━━━━━━━━━━━━━━━━━↯**
111 | **✘ Select an Option Below to View Logs:**
112 | **✘ Logs Here Offer the Fastest and Clearest Access! ↯**
113 | **✘━━━━━━━━━━━━━━━━━━━━━━━━━↯**
114 | **✘Huge Respect For You My Master↯**""",
115 | parse_mode=ParseMode.MARKDOWN,
116 | reply_markup=InlineKeyboardMarkup([
117 | [
118 | InlineKeyboardButton("✘ Display Logs↯", callback_data="display_logs"),
119 | InlineKeyboardButton("✘ Web Paste↯", callback_data="web_paste$")
120 | ],
121 | [InlineKeyboardButton("✘ Close↯", callback_data="close_doc$")]
122 | ])
123 | )
124 |
125 | # Delete the temporary "Checking The Logs..." message
126 | await loading_message.delete()
127 |
128 | @app.on_callback_query(filters.regex(r"^(close_doc\$|close_logs\$|web_paste\$|display_logs)$"))
129 | async def handle_callback(client: Client, query: CallbackQuery):
130 | user_id = query.from_user.id
131 | data = query.data
132 | LOGGER.info(f"Callback query from user {user_id}, data: {data}")
133 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
134 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
135 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
136 | LOGGER.info("User not admin or owner, sending callback answer")
137 | await query.answer(
138 | text="✘Kids Not Allowed To Do This↯",
139 | show_alert=True
140 | )
141 | return
142 | LOGGER.info("User is admin or owner, processing callback")
143 | if data == "close_doc$":
144 | await query.message.delete()
145 | await query.answer()
146 | elif data == "close_logs$":
147 | await query.message.delete()
148 | await query.answer()
149 | elif data == "web_paste$":
150 | await query.answer("Uploading logs to Telegraph...")
151 | # Edit the main log message to show uploading status
152 | await query.message.edit_caption(
153 | caption="**✘Uploading SmartLogs To Telegraph↯**",
154 | parse_mode=ParseMode.MARKDOWN
155 | )
156 | # Check if logs exist
157 | if not os.path.exists("botlog.txt"):
158 | await query.message.edit_caption(
159 | caption="**✘Sorry Bro Telegraph API Dead↯**",
160 | parse_mode=ParseMode.MARKDOWN
161 | )
162 | return
163 | try:
164 | # Read and truncate logs
165 | with open("botlog.txt", "r", encoding="utf-8") as f:
166 | logs_content = f.read()
167 | # Create Telegraph pages
168 | telegraph_urls = await create_telegraph_page(logs_content)
169 | if telegraph_urls:
170 | # Create buttons with two per row
171 | buttons = []
172 | for i in range(0, len(telegraph_urls), 2):
173 | row = [
174 | InlineKeyboardButton(f"✘ View Web Part {i+1}↯", url=telegraph_urls[i])
175 | ]
176 | if i + 1 < len(telegraph_urls):
177 | row.append(InlineKeyboardButton(f"✘ View Web Part {i+2}↯", url=telegraph_urls[i+1]))
178 | buttons.append(row)
179 | # Add a close button in its own row
180 | buttons.append([InlineKeyboardButton("✘ Close↯", callback_data="close_doc$")])
181 | await query.message.edit_caption(
182 | caption="""**✘ Hey Sir! Here Is Your Logs ↯**
183 | **✘━━━━━━━━━━━━━━━━━━━━━━━━━↯**
184 | **✘ All Logs Database Succesfully Exported! ↯**
185 | **↯ Access Granted Only to Authorized Admins ↯**
186 | **✘━━━━━━━━━━━━━━━━━━━━━━━━━↯**
187 | **✘ Select a Page Below to View Logs:**
188 | **✘ Logs Here Offer the Fastest and Clearest Access! ↯**
189 | **✘━━━━━━━━━━━━━━━━━━━━━━━━━↯**
190 | **✘Huge Respect For You My Master↯**""",
191 | parse_mode=ParseMode.MARKDOWN,
192 | reply_markup=InlineKeyboardMarkup(buttons)
193 | )
194 | else:
195 | await query.message.edit_caption(
196 | caption="**✘Sorry Bro Telegraph API Dead↯**",
197 | parse_mode=ParseMode.MARKDOWN
198 | )
199 | except Exception as e:
200 | LOGGER.error(f"Error uploading to Telegraph: {e}")
201 | await query.message.edit_caption(
202 | caption="**✘Sorry Bro Telegraph API Dead↯**",
203 | parse_mode=ParseMode.MARKDOWN
204 | )
205 | elif data == "display_logs":
206 | await send_logs_page(client, query.message.chat.id)
207 | await query.answer()
208 |
209 | async def send_logs_page(client: Client, chat_id: int):
210 | """Send the last 20 lines of botlog.txt, respecting Telegram's 4096-character limit"""
211 | LOGGER.info(f"Sending latest logs to chat {chat_id}")
212 | if not os.path.exists("botlog.txt"):
213 | await client.send_message(
214 | chat_id=chat_id,
215 | text="**✘Sorry Bro No Logs Found↯**",
216 | parse_mode=ParseMode.MARKDOWN
217 | )
218 | return
219 | try:
220 | with open("botlog.txt", "r", encoding="utf-8") as f:
221 | logs = f.readlines()
222 | # Get the last 20 lines (or fewer if the file is shorter)
223 | latest_logs = logs[-20:] if len(logs) > 20 else logs
224 | text = "".join(latest_logs)
225 | # Truncate to 4096 characters (Telegram's message limit)
226 | if len(text) > 4096:
227 | text = text[-4096:]
228 | await client.send_message(
229 | chat_id=chat_id,
230 | text=text if text else "No logs available.",
231 | parse_mode=ParseMode.DISABLED,
232 | reply_markup=InlineKeyboardMarkup([
233 | [InlineKeyboardButton("✘ Back↯", callback_data="close_logs$")]
234 | ])
235 | )
236 | except Exception as e:
237 | LOGGER.error(f"Error sending logs: {e}")
238 | await client.send_message(
239 | chat_id=chat_id,
240 | text="**✘Sorry There Was A Issue On My Server↯**",
241 | parse_mode=ParseMode.MARKDOWN
242 | )
243 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## SecureXTools Bot 🚀
2 |
3 | 
4 | 
5 | 
6 | 
7 | 
8 |
9 | SecureXTools is a powerful, feature-rich Telegram bot designed to provide tools for media downloading, IP and proxy checking, website screenshots, and administrative controls. It supports deployment on multiple platforms, including VPS, Heroku, and Docker, with compatibility for Ubuntu, Windows, RDP, and Debian hosting.
10 |
11 | **Project Owner:** [@ISmartDevs](https://t.me/ISmartDevs)
12 | **Repository:** [GitHub - SecureXTools](https://github.com/TheSmartDevs/SecureXTools)
13 | **Community:** Join our [Telegram Channel](https://t.me/TheSmartDev) for updates and support 📢.
14 |
15 | ## Features ✨ Of SecureXTools
16 |
17 | SecureXTools Bot offers the following commands and functionalities:
18 |
19 | - **/start**: Initialize the SecureXTools Bot.
20 | - **/help**: Display the help menu and list of available commands.
21 | - **/info**: Retrieve user information from the database.
22 | - **/id**: Get group or channel information.
23 | - **/ip**: Fetch details about any IP address.
24 | - **/px**: Check proxies of any type (up to a limit of 20).
25 | - **/ss**: Capture a screenshot of any website.
26 | - **/fb**: Download videos from Facebook.
27 | - **/in**: Download Instagram reels and posts.
28 | - **/sp**: Download tracks from Spotify.
29 | - **/song**: Download music from YouTube.
30 | - **/video**: Download videos from YouTube.
31 | - **/tt**: Download TikTok videos.
32 | - **/pnt**: Download Pinterest videos.
33 | - **/settings**: Modify bot variables (Admin only).
34 | - **/auth**: Promote a user to sudo status (Admin only).
35 | - **/unauth**: Demote a user from sudo status (Admin only).
36 | - **/gban**: Ban a user from using the bot (Admin only).
37 | - **/gunban**: Unban a user from using the bot (Admin only).
38 | - **/logs**: Retrieve bot console logs (Admin only).
39 | - **/restart**: Restart the bot (Admin only).
40 | - **/speedtest**: Check the bot server’s speed (Admin only).
41 | - **/send**: Broadcast messages to users (Admin only).
42 | - **/stats**: View bot usage statistics (Admin only).
43 | - **/broadcast**: Broadcast messages to users using (Admin only).
44 | - **/stats**: View bot usage statistics (Admin only).
45 | - **/off**: Turn Entire Bot Off (Admin only).
46 |
47 |
48 | ## Prerequisites 🛠️ For SecureXTools Bot
49 |
50 | Before deploying SecureXTools Bot, ensure the following dependencies are installed:
51 |
52 | 1. **Python 3.8+**: Required for running the bot.
53 | 2. **FFmpeg**: Necessary for media processing.
54 | - For Ubuntu/Debian:
55 | ```bash
56 | sudo apt update
57 | sudo apt install ffmpeg
58 | ```
59 | - Note: FFmpeg cannot be installed via `pip`. Use your system’s package manager.
60 | 3. **Python Dependencies**:
61 | - Install required Python packages:
62 | ```bash
63 | pip3 install -r requirements.txt
64 | ```
65 |
66 | ## Environment Variables ⚙️
67 |
68 | ### Mandatory Variables For Bot Deployment
69 | The following environment variables must be set for the bot to function correctly:
70 |
71 | | Variable | Description | Example Value |
72 | |----------------------|--------------------------------------------------|----------------------------------------------|
73 | | `API_ID` | Telegram API ID from [my.telegram.org](https://my.telegram.org). | Your_API_ID_Here |
74 | | `API_HASH` | Telegram API Hash from [my.telegram.org](https://my.telegram.org). | Your_API_HASH_Here |
75 | | `BOT_TOKEN` | Bot token from [@BotFather](https://t.me/BotFather). | Your_BOT_TOKEN_Here |
76 | | `BOT_USERNAME` | Bot’s Telegram username. | @YourBotUsername |
77 | | `BOT_NAME` | Bot’s display name. | YourBotName |
78 | | `OWNER_ID` | Telegram ID of the bot owner. | Your_OWNER_ID_Here |
79 | | `DEVELOPER_USER_ID` | Telegram ID of the developer (usually same as OWNER_ID). | Your_DEVELOPER_USER_ID_Here |
80 | | `MONGO_URL` | MongoDB connection URL. | Your_MONGO_URL_Here |
81 | | `DATABASE_URL` | Same as `MONGO_URL` for database operations. | Your_DATABASE_URL_Here |
82 |
83 | ### Optional Variables For Proper Working..
84 | The following variables are optional and have default values if not set:
85 |
86 | | Variable | Description | Default Value |
87 | |----------------------|--------------------------------------------------|----------------------------------------------|
88 | | `COMMAND_PREFIX` | Prefixes for bot commands. | `!|.|#|,|/` |
89 | | `UPDATE_CHANNEL_URL` | Telegram channel for updates. | `https://t.me/TheSmartDev` |
90 | | `IMGAI_SIZE_LIMIT` | Max file size for image AI tools (bytes). | `5242880` |
91 | | `MAX_TXT_SIZE` | Max text file size (bytes). | `15728640` |
92 | | `MAX_VIDEO_SIZE` | Max video file size (bytes). | `2147483648` |
93 | | `YT_COOKIES_PATH` | Path to YouTube cookies file. | `./cookies/SecureXTools.txt` |
94 | | `VIDEO_RESOLUTION` | Resolution for YouTube downloads. | `1280x720` |
95 | | `PROXY_CHECK_LIMIT` | Max number of proxies to check. | `20` |
96 |
97 | ## Deployment Methods & Instructions
98 |
99 | SecureXTools Bot supports multiple deployment methods: VPS, Heroku, and Docker. Follow the instructions below for your preferred platform.
100 |
101 | ### 1. Deploying on a VPS with Screen
102 |
103 | #### Install Screen
104 | Screen allows you to run the bot in a persistent session, even after closing your terminal.
105 |
106 | - For Ubuntu/Debian:
107 | ```bash
108 | sudo apt update
109 | sudo apt install screen
110 | ```
111 |
112 | - Start a new screen session:
113 | ```bash
114 | screen -S SecureXTools
115 | ```
116 |
117 | #### Deploy the Bot
118 | 1. Clone the repository:
119 | ```bash
120 | git clone https://github.com/TheSmartDevs/SecureXTools.git
121 | cd SecureXTools
122 | ```
123 |
124 | 2. Install dependencies:
125 | ```bash
126 | sudo apt install ffmpeg
127 | pip3 install -r requirements.txt
128 | ```
129 |
130 | 3. Set up environment variables:
131 | - Create a `.env` file in the project root and populate it with the variables listed above.
132 | - Alternatively, edit `config.py` directly with your values Or `cp sample.env .env`
133 |
134 | 4. Run the bot:
135 | ```bash
136 | python3 main.py
137 | ```
138 |
139 | 5. Detach from the screen session:
140 | - Press `Ctrl+A`, then `Ctrl+D` to detach.
141 | - To reattach later:
142 | ```bash
143 | screen -r SecureXTools
144 | ```
145 |
146 | 6. Stop the bot:
147 | - Reattach to the screen session (`screen -r SecureXTools`).
148 | - Press `Ctrl+C` to stop the bot.
149 |
150 | ### 2. Heroku Deploy Methods!!!
151 |
152 | #### Deploy with Heroku Button
153 | For a quick deployment, use the Heroku Deploy button:
154 |
155 | [](https://heroku.com/deploy?template=https://github.com/TheSmartDevs/SecureXTools)
156 |
157 | #### Manual Heroku Deployment For Professionals
158 | 1. Clone the repository:
159 | ```bash
160 | git clone https://github.com/TheSmartDevs/SecureXTools.git
161 | cd SecureXTools
162 | ```
163 |
164 | 2. Create a Heroku app:
165 | ```bash
166 | heroku create your-app-name
167 | ```
168 |
169 | 3. Set environment variables:
170 | - Use the Heroku dashboard or CLI to set the required variables:
171 | ```bash
172 | heroku config:set API_ID=Your_API_ID_Here
173 | heroku config:set API_HASH=Your_API_HASH_Here
174 | heroku config:set BOT_TOKEN=Your_BOT_TOKEN_Here
175 | # Add other variables as needed
176 | ```
177 |
178 | 4. Deploy the app:
179 | ```bash
180 | git push heroku main
181 | ```
182 |
183 | 5. Start the bot:
184 | ```bash
185 | heroku ps:scale worker=1
186 | ```
187 |
188 | 6. Install FFmpeg on Heroku:
189 | - Add the FFmpeg buildpack:
190 | ```bash
191 | heroku buildpacks:add https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git
192 | ```
193 |
194 | ### 3. Deploying with Docker Compose Method
195 |
196 | #### Prerequisites
197 | - Install [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/).
198 |
199 | #### Deploy with Docker Compose
200 | 1. Clone the repository:
201 | ```bash
202 | git clone https://github.com/TheSmartDevs/SecureXTools.git
203 | cd SecureXTools
204 | ```
205 |
206 | 2. Create a `.env` file in the project root with the required environment variables.
207 |
208 | 3. Build and run the bot:
209 | ```bash
210 | docker compose up --build --remove-orphans
211 | ```
212 |
213 | 4. Stop the bot:
214 | ```bash
215 | docker compose down
216 | ```
217 |
218 | ## Bot Father Commands Setup Easy Method
219 |
220 | 1. **First Open Bot Father**:
221 | - First Go To [@BotFather](https://t.me/BotFather) Then Paste The Commands By Coping From Cutting From `cmds.txt` File.
222 | 2. **Easiest Way With Sudo Mode**:
223 | - Just Send /set Command To Your Hosted Bot And Then Commands Will Auto Set If You Are Owner Of The Bot Or Your UserID Is In `OWNER_ID` VAR.
224 |
225 | ## Handling YouTube Download Errors with Cookies 📄
226 |
227 | To avoid YouTube sign-in or bot protection errors, use a cookie file for authentication. Follow these steps:
228 |
229 | 1. **Create a Dedicated Chrome Profile**:
230 | - Set up a new Chrome profile for the bot to manage cookies securely.
231 |
232 | 2. **Install a Cookie Management Extension**:
233 | - Use an extension like [Cookie Editor](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) to export cookies.
234 |
235 | 3. **Export Cookies from YouTube**:
236 | - Log into YouTube using the dedicated Chrome profile.
237 | - Use the cookie extension to export cookies in Netscape format.
238 |
239 | 4. **Save the Cookies File**:
240 | - Save the exported cookies as `SecureXTools.txt` in the `./cookies/SecureXTools.txt` directory.
241 |
242 | ### Cookie Management Tips
243 | - **Cookie Expiry**: YouTube cookies may expire. If downloads fail, export and replace the cookies file.
244 | - **Avoid Cookie Depletion**:
245 | - Do not play YouTube videos on the account used for cookies.
246 | - Avoid signing out from the associated Gmail account on your device.
247 | - Minimize frequent bot restarts to prevent cookie invalidation.
248 | - **Monitor Cookies**: Regularly check bot activity to ensure cookies remain valid.
249 |
250 | This setup ensures reliable access to YouTube content without sign-in or bot protection errors.
251 |
252 | ## Contributing 🤝 To SecureXTools
253 |
254 | We welcome contributions to improve SecureXTools Bot. To contribute:
255 | 1. Fork the repository.
256 | 2. Create a new branch for your feature or bug fix.
257 | 3. Submit a pull request with a clear description of your changes.
258 |
259 | Please ensure your code follows the project’s coding standards and includes appropriate documentation.
260 |
261 | ## Support 📞 For Any Issue
262 |
263 | For issues, questions, or feature requests:
264 | - Join our [Telegram Channel](https://t.me/TheSmartDev) for community support.
265 | - Contact the project owner: [@ISmartDevs](https://t.me/ISmartDevs).
266 | - Open an issue on the [GitHub repository](https://github.com/TheSmartDevs/SecureXTools).
267 |
268 | ## License 📝
269 |
270 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
271 |
272 | SecureXTools Bot is a project by [@ISmartDevs](https://t.me/ISmartDevs). Built for the Telegram community.
273 |
274 | ## Ethical Notice 📜
275 |
276 | Simply modifying a few lines of code does not constitute original authorship. When forking this project, always:
277 | - Fork responsibly and give proper credit to the original creators (@ISmartDevs).
278 | - Customize the code as needed, but do not claim it as your own without significant contributions.
--------------------------------------------------------------------------------
/sudoers/settings/settings.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | from pyrogram import Client, filters
5 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message, CallbackQuery
6 | from pyrogram.enums import ParseMode, ChatType
7 | from pyrogram.handlers import MessageHandler, CallbackQueryHandler
8 | import os
9 | from dotenv import load_dotenv
10 | from config import COMMAND_PREFIX, OWNER_ID
11 | from core import auth_admins
12 | from utils import LOGGER
13 |
14 | # Load The Environment Variables
15 | load_dotenv()
16 |
17 | # Temp Session Storage
18 | user_session = {}
19 |
20 | def validate_message(func):
21 | async def wrapper(client: Client, message: Message):
22 | if not message or not message.from_user:
23 | LOGGER.error("Invalid message received")
24 | return
25 | return await func(client, message)
26 | return wrapper
27 |
28 | def admin_only(func):
29 | async def wrapper(client: Client, message: Message):
30 | user_id = message.from_user.id
31 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
32 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
33 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
34 | LOGGER.info(f"Unauthorized settings access attempt by user_id {user_id}")
35 | await client.send_message(
36 | chat_id=message.chat.id,
37 | text="**🚫 Hey Gay 🏳️🌈 This Is Not For You This Only For Males👱♂️**",
38 | parse_mode=ParseMode.MARKDOWN
39 | )
40 | return
41 | return await func(client, message)
42 | return wrapper
43 |
44 | # Helper Functions
45 | def detect_duplicate_keys():
46 | """Log a warning for duplicate keys in the .env file."""
47 | try:
48 | with open(".env") as f:
49 | lines = f.readlines()
50 | seen_keys = set()
51 | duplicates = set()
52 | for line in lines:
53 | if '=' in line:
54 | key = line.split("=", 1)[0].strip()
55 | if key in seen_keys:
56 | duplicates.add(key)
57 | seen_keys.add(key)
58 | if duplicates:
59 | LOGGER.warning(f"Duplicate keys found in .env: {', '.join(duplicates)}")
60 | except Exception as e:
61 | LOGGER.error(f"Error detecting duplicate keys in .env: {e}")
62 |
63 | def load_env_vars():
64 | """Load all unique environment variables from the .env file."""
65 | try:
66 | with open(".env") as f:
67 | lines = f.readlines()
68 | variables = {}
69 | seen_keys = set()
70 | for line in lines:
71 | if '=' in line:
72 | key, value = line.split("=", 1)
73 | key = key.strip()
74 | value = value.strip()
75 | if key not in seen_keys:
76 | variables[key] = value
77 | seen_keys.add(key)
78 | LOGGER.info("Environment variables loaded successfully")
79 | return variables
80 | except Exception as e:
81 | LOGGER.error(f"Error loading environment variables: {e}")
82 | return {}
83 |
84 | def update_env_var(key, value):
85 | """Update a specific environment variable in the .env file."""
86 | try:
87 | env_vars = load_env_vars()
88 | env_vars[key] = value
89 | with open(".env", "w") as f:
90 | for k, v in env_vars.items():
91 | f.write(f"{k}={v}\n")
92 | LOGGER.info(f"Updated environment variable: {key}")
93 | except Exception as e:
94 | LOGGER.error(f"Error updating environment variable {key}: {e}")
95 |
96 | # Initialize config
97 | detect_duplicate_keys()
98 | config_keys = load_env_vars()
99 | ITEMS_PER_PAGE = 10
100 |
101 | def build_menu(page=0):
102 | """Build the inline keyboard menu for settings."""
103 | keys = list(config_keys.keys())
104 | start, end = page * ITEMS_PER_PAGE, (page + 1) * ITEMS_PER_PAGE
105 | current_keys = keys[start:end]
106 |
107 | rows = []
108 | for i in range(0, len(current_keys), 2):
109 | buttons = [
110 | InlineKeyboardButton(current_keys[i], callback_data=f"settings_edit_{current_keys[i]}")
111 | ]
112 | if i + 1 < len(current_keys):
113 | buttons.append(InlineKeyboardButton(current_keys[i + 1], callback_data=f"settings_edit_{current_keys[i + 1]}"))
114 | rows.append(buttons)
115 |
116 | nav_buttons = []
117 | if page > 0:
118 | nav_buttons.append(InlineKeyboardButton("⬅️ Previous", callback_data=f"settings_page_{page - 1}"))
119 | if end < len(keys):
120 | nav_buttons.append(InlineKeyboardButton("Next ➡️", callback_data=f"settings_page_{page + 1}"))
121 | if nav_buttons:
122 | if page == 0:
123 | nav_buttons.append(InlineKeyboardButton("Close ❌", callback_data="settings_closesettings"))
124 | rows.append(nav_buttons)
125 | else:
126 | rows.append(nav_buttons)
127 |
128 | if page > 0:
129 | rows.append([InlineKeyboardButton("Close ❌", callback_data="settings_closesettings")])
130 |
131 | return InlineKeyboardMarkup(rows)
132 |
133 | def setup_settings_handler(app: Client):
134 | """Setup the settings handler for the Pyrogram app."""
135 |
136 | @validate_message
137 | async def debug_all(client: Client, message: Message):
138 | """Catch-all handler to debug all group chat messages."""
139 | thread_id = getattr(message, "message_thread_id", None)
140 | is_reply = message.reply_to_message_id is not None
141 | message_text = message.text or message.caption or "[no text]"
142 |
143 | LOGGER.debug(
144 | f"Catch-all: user {message.from_user.id}, chat {message.chat.id}, "
145 | f"text='{message_text[:50]}', chat_type={message.chat.type}, "
146 | f"is_reply={is_reply}, reply_to={message.reply_to_message_id}, "
147 | f"thread={thread_id}"
148 | )
149 |
150 | @validate_message
151 | @admin_only
152 | async def show_settings(client: Client, message: Message):
153 | """Show the settings menu."""
154 | LOGGER.info(f"Settings command initiated by user_id {message.from_user.id}")
155 | if message.chat.type in [ChatType.GROUP, ChatType.SUPERGROUP]:
156 | try:
157 | chat = await client.get_chat(message.chat.id)
158 | if not chat.permissions.can_send_messages:
159 | await client.send_message(
160 | chat_id=message.chat.id,
161 | text="**Sorry Bro This Group Is Restricted**",
162 | parse_mode=ParseMode.MARKDOWN
163 | )
164 | return
165 | except Exception as e:
166 | LOGGER.error(f"Failed to check permissions: {e}")
167 | return
168 |
169 | await client.send_message(
170 | chat_id=message.chat.id,
171 | text="**Select a change or edit 👇**",
172 | parse_mode=ParseMode.MARKDOWN,
173 | reply_markup=build_menu()
174 | )
175 |
176 | async def paginate_menu(client: Client, callback_query: CallbackQuery):
177 | """Handle pagination in the settings menu."""
178 | user_id = callback_query.from_user.id
179 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
180 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
181 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
182 | LOGGER.info(f"Unauthorized pagination attempt by user_id {user_id}")
183 | await callback_query.answer("🚫 Hey Gay 🏳️🌈 This Is Not For You This Only For Males👱♂️", show_alert=True)
184 | return
185 |
186 | page = int(callback_query.data.split("_")[2])
187 | await callback_query.edit_message_reply_markup(reply_markup=build_menu(page))
188 | await callback_query.answer()
189 | LOGGER.debug(f"Paginated to page {page} by user_id {user_id}")
190 |
191 | async def edit_var(client: Client, callback_query: CallbackQuery):
192 | """Handle the selection of a variable to edit."""
193 | user_id = callback_query.from_user.id
194 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
195 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
196 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
197 | LOGGER.info(f"Unauthorized edit attempt by user_id {user_id}")
198 | await callback_query.answer("🚫 Hey Gay 🏳️🌈 This Is Not For You This Only For Males👱♂️", show_alert=True)
199 | return
200 |
201 | var_name = callback_query.data.split("_", 2)[2]
202 | if var_name not in config_keys:
203 | await callback_query.answer("Invalid variable selected.", show_alert=True)
204 | LOGGER.warning(f"Invalid variable {var_name} selected by user_id {user_id}")
205 | return
206 |
207 | user_session[user_id] = {
208 | "var": var_name,
209 | "chat_id": callback_query.message.chat.id
210 | }
211 |
212 | await callback_query.edit_message_text(
213 | text=f"**Editing `{var_name}`. Please send the new value below.**",
214 | parse_mode=ParseMode.MARKDOWN,
215 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Cancel ❌", callback_data="settings_cancel_edit")]])
216 | )
217 | LOGGER.info(f"User_id {user_id} started editing variable {var_name}")
218 |
219 | async def cancel_edit(client: Client, callback_query: CallbackQuery):
220 | """Cancel the editing process."""
221 | user_id = callback_query.from_user.id
222 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
223 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
224 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
225 | LOGGER.info(f"Unauthorized cancel edit attempt by user_id {user_id}")
226 | await callback_query.answer("🚫 Hey Gay 🏳️🌈 This Is Not For You This Only For Males👱♂️", show_alert=True)
227 | return
228 |
229 | user_session.pop(user_id, None)
230 | await callback_query.edit_message_text("**Variable Editing Cancelled**", parse_mode=ParseMode.MARKDOWN)
231 | await callback_query.answer()
232 | LOGGER.info(f"User_id {user_id} cancelled variable editing")
233 |
234 | @validate_message
235 | async def update_value(client: Client, message: Message):
236 | """Update the value of a selected variable."""
237 | user_id = message.from_user.id
238 | session = user_session.get(user_id)
239 | if not session:
240 | return
241 |
242 | message_text = message.text or message.caption
243 | if not message_text:
244 | await client.send_message(
245 | chat_id=message.chat.id,
246 | text="**Please provide a text value to update.**",
247 | parse_mode=ParseMode.MARKDOWN
248 | )
249 | return
250 |
251 | var, val = session["var"], message_text.strip()
252 | update_env_var(var, val)
253 | config_keys[var] = val
254 |
255 | await client.send_message(
256 | chat_id=message.chat.id,
257 | text=f"**`{var}` Has Been Successfully Updated To `{val}`. ✅**",
258 | parse_mode=ParseMode.MARKDOWN
259 | )
260 |
261 | user_session.pop(user_id, None)
262 | load_dotenv(override=True)
263 | LOGGER.info(f"User_id {user_id} updated variable {var} to {val}")
264 |
265 | async def close_menu(client: Client, callback_query: CallbackQuery):
266 | """Close the settings menu."""
267 | user_id = callback_query.from_user.id
268 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
269 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
270 | if user_id != OWNER_ID and user_id not in AUTH_ADMIN_IDS:
271 | LOGGER.info(f"Unauthorized close menu attempt by user_id {user_id}")
272 | await callback_query.answer("🚫 Hey Gay 🏳️🌈 This Is Not For You This Only For Males👱♂️", show_alert=True)
273 | return
274 |
275 | await callback_query.edit_message_text("**Settings Menu Closed Successfully ✅**", parse_mode=ParseMode.MARKDOWN)
276 | await callback_query.answer()
277 | LOGGER.info(f"User_id {user_id} closed settings menu")
278 |
279 | # Register handlers
280 | app.add_handler(MessageHandler(debug_all, filters.chat([ChatType.GROUP, ChatType.SUPERGROUP])), group=1)
281 | app.add_handler(MessageHandler(show_settings, filters.command(["settings"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group)), group=1)
282 | app.add_handler(MessageHandler(update_value, filters.text), group=1)
283 | app.add_handler(CallbackQueryHandler(paginate_menu, filters.regex(r"^settings_page_(\d+)$")), group=1)
284 | app.add_handler(CallbackQueryHandler(edit_var, filters.regex(r"^settings_edit_(.+)")), group=1)
285 | app.add_handler(CallbackQueryHandler(cancel_edit, filters.regex(r"^settings_cancel_edit$")), group=1)
286 | app.add_handler(CallbackQueryHandler(close_menu, filters.regex(r"^settings_closesettings$")), group=1)
287 |
--------------------------------------------------------------------------------
/modules/dlxutils/insta.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import re
6 | import time
7 | import asyncio
8 | import aiohttp
9 | import aiofiles
10 | from pathlib import Path
11 | from typing import Optional, List
12 | from pyrogram import Client, filters
13 | from pyrogram.types import Message, InputMediaPhoto, InputMediaVideo, InlineKeyboardMarkup, InlineKeyboardButton
14 | from pyrogram.enums import ParseMode
15 | from config import COMMAND_PREFIX
16 | from utils import LOGGER, progress_bar, notify_admin # Import LOGGER, progress_bar, and notify_admin from utils
17 | from core import banned_users # Check if user is banned
18 |
19 | # Configuration
20 | class Config:
21 | TEMP_DIR = Path("temp")
22 | MAX_MEDIA_PER_GROUP = 10 # Telegram's media group limit
23 |
24 | Config.TEMP_DIR.mkdir(exist_ok=True)
25 |
26 | class InstagramDownloader:
27 | def __init__(self, temp_dir: Path):
28 | self.temp_dir = temp_dir
29 |
30 | async def sanitize_filename(self, username: str, shortcode: str, index: int, media_type: str) -> str:
31 | """Sanitize file name using username, shortcode, and index."""
32 | safe_username = re.sub(r'[<>:"/\\|?*]', '', username[:30]).strip()
33 | safe_shortcode = re.sub(r'[<>:"/\\|?*]', '', shortcode).strip()
34 | return f"{safe_username}_{safe_shortcode}_{index}_{int(time.time())}.{ 'mp4' if media_type == 'video' else 'jpg' }"
35 |
36 | async def download_content(self, url: str, downloading_message: Message, content_type: str) -> Optional[dict]:
37 | self.temp_dir.mkdir(exist_ok=True)
38 | api_url = f"https://insta.bdbots.xyz/dl?url={url}"
39 |
40 | try:
41 | async with aiohttp.ClientSession(
42 | connector=aiohttp.TCPConnector(limit=100),
43 | timeout=aiohttp.ClientTimeout(total=30)
44 | ) as session:
45 | async with session.get(api_url) as response:
46 | LOGGER.info(f"API request to {api_url} returned status {response.status}")
47 | if response.status != 200:
48 | LOGGER.error(f"API request failed: HTTP status {response.status}")
49 | return None
50 | data = await response.json()
51 | LOGGER.info(f"API response: {data}")
52 | if data.get("status") != "success":
53 | LOGGER.error("API response indicates failure")
54 | return None
55 |
56 | api_content_type = data["data"]["type"]
57 | await downloading_message.edit_text(
58 | "**Found ☑️ Downloading...**" if content_type == "video" else "**📤 Uploading...**",
59 | parse_mode=ParseMode.MARKDOWN
60 | )
61 | media_files = []
62 | # Download all media files concurrently
63 | tasks = [
64 | self._download_file(
65 | session,
66 | media["url"],
67 | self.temp_dir / await self.sanitize_filename(
68 | data['data']['username'],
69 | data['data']['metadata']['shortcode'],
70 | index,
71 | media['type']
72 | )
73 | )
74 | for index, media in enumerate(data["data"]["media"])
75 | ]
76 | downloaded_files = await asyncio.gather(*tasks, return_exceptions=True)
77 |
78 | for index, result in enumerate(downloaded_files):
79 | if isinstance(result, Exception):
80 | LOGGER.error(f"Failed to download media {index}: {result}")
81 | continue
82 | media_files.append({
83 | "filename": str(result),
84 | "type": data["data"]["media"][index]["type"]
85 | })
86 |
87 | if not media_files:
88 | LOGGER.error("No media files downloaded successfully")
89 | return None
90 |
91 | return {
92 | "title": data["data"].get("caption", "Instagram Content"),
93 | "media_files": media_files,
94 | "webpage_url": url,
95 | "username": data["data"]["username"],
96 | "type": api_content_type
97 | }
98 | except aiohttp.ClientError as e:
99 | LOGGER.error(f"Instagram download error: {e}")
100 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}in", e, downloading_message)
101 | return None
102 | except asyncio.TimeoutError:
103 | LOGGER.error("Request to Instagram API timed out")
104 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}in", asyncio.TimeoutError("Request to Instagram API timed out"), downloading_message)
105 | return None
106 | except Exception as e:
107 | LOGGER.error(f"Instagram download error: {e}")
108 | await notify_admin(downloading_message._client, f"{COMMAND_PREFIX}in", e, downloading_message)
109 | return None
110 |
111 | async def _download_file(self, session: aiohttp.ClientSession, url: str, dest: Path) -> Path:
112 | try:
113 | async with session.get(url) as response:
114 | if response.status == 200:
115 | LOGGER.info(f"Downloading file from {url} to {dest}")
116 | async with aiofiles.open(dest, mode='wb') as f:
117 | async for chunk in response.content.iter_chunked(1024 * 1024): # 1MB chunks
118 | await f.write(chunk)
119 | LOGGER.info(f"File downloaded successfully to {dest}")
120 | return dest
121 | raise Exception(f"Failed to download {url}: Status {response.status}")
122 | except aiohttp.ClientError as e:
123 | LOGGER.error(f"Error downloading file from {url}: {e}")
124 | raise
125 |
126 | def setup_insta_handlers(app: Client):
127 | ig_downloader = InstagramDownloader(Config.TEMP_DIR)
128 |
129 | command_prefix_regex = f"[{''.join(map(re.escape, COMMAND_PREFIX))}]"
130 |
131 | @app.on_message(filters.regex(rf"^{command_prefix_regex}in(\s+https?://\S+)?$") & (filters.private | filters.group))
132 | async def ig_handler(client: Client, message: Message):
133 | # Check if user is banned
134 | user_id = message.from_user.id if message.from_user else None
135 | if user_id and banned_users.find_one({"user_id": user_id}):
136 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**", parse_mode=ParseMode.MARKDOWN)
137 | return
138 |
139 | url = None
140 | # Check if URL from reply
141 | if message.reply_to_message and message.reply_to_message.text:
142 | match = re.search(r"https?://(www\.)?instagram\.com/(reel|p)/\S+", message.reply_to_message.text)
143 | if match:
144 | url = match.group(0)
145 | # Check if URL from command
146 | if not url:
147 | command_parts = message.text.split(maxsplit=1)
148 | if len(command_parts) > 1:
149 | match = re.search(r"https?://(www\.)?instagram\.com/(reel|p)/\S+", command_parts[1])
150 | if match:
151 | url = match.group(0)
152 |
153 | if not url:
154 | await client.send_message(
155 | chat_id=message.chat.id,
156 | text="**Please provide a valid Instagram Reels/Post URL or reply to a message with one ❌**",
157 | parse_mode=ParseMode.MARKDOWN
158 | )
159 | LOGGER.warning(f"No Instagram URL provided, user: {user_id or 'unknown'}, chat: {message.chat.id}")
160 | return
161 |
162 | LOGGER.info(f"Instagram URL received: {url}, user: {user_id or 'unknown'}, chat: {message.chat.id}")
163 | content_type = "video" if "/reel/" in url else "post"
164 | downloading_message = await client.send_message(
165 | chat_id=message.chat.id,
166 | text="**Searching The Video**" if content_type == "video" else "**🔍 Fetching media from Instagram...**",
167 | parse_mode=ParseMode.MARKDOWN
168 | )
169 |
170 | try:
171 | content_info = await ig_downloader.download_content(url, downloading_message, content_type)
172 | if not content_info:
173 | await downloading_message.edit_text(
174 | "**Unable To Extract The URL 😕**", parse_mode=ParseMode.MARKDOWN
175 | )
176 | LOGGER.error(f"Failed to download content for URL: {url}")
177 | return
178 |
179 | title = content_info["title"]
180 | media_files = content_info["media_files"]
181 | webpage_url = content_info["webpage_url"]
182 | content_type = content_info["type"]
183 | username = content_info["username"]
184 |
185 | if message.from_user:
186 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip()
187 | user_info = f"[{user_full_name}](tg://user?id={user_id})"
188 | else:
189 | group_name = message.chat.title or "this squad"
190 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this squad"
191 | user_info = f"[{group_name}]({group_url})"
192 |
193 | caption = (
194 | f"🔥 **Insta DL Success!** ✅\n"
195 | f"🎬 **Title**: `{title}`\n"
196 | f"📸 **Uploader**: `@{username}`\n"
197 | f"🌐 **Link**: [View on Instagram]({webpage_url})\n"
198 | f"👉 **Downloaded By**: {user_info}\n"
199 | f"#InstaVibes #ReelIt"
200 | ) if content_type == "video" else (
201 | f"📸 **Insta Post Downloaded!** ✅\n"
202 | f"🎨 **Caption**: `{title}`\n"
203 | f"👤 **Uploader**: `@{username}`\n"
204 | f"🌐 **Link**: [View on Instagram]({webpage_url})\n"
205 | f"👉 **Downloaded By**: {user_info}\n"
206 | f"#InstaMoments #PostVibes"
207 | )
208 |
209 | inline_keyboard = InlineKeyboardMarkup(
210 | [[InlineKeyboardButton("🔍 View on Insta", url=webpage_url)]]
211 | ) if content_type == "video" else None
212 |
213 | if content_type == "carousel" and len(media_files) > 1:
214 | # Split media files into chunks of MAX_MEDIA_PER_GROUP
215 | for i in range(0, len(media_files), Config.MAX_MEDIA_PER_GROUP):
216 | media_group = []
217 | for index, media in enumerate(media_files[i:i + Config.MAX_MEDIA_PER_GROUP]):
218 | media_type = InputMediaPhoto if media["type"] == "image" else InputMediaVideo
219 | media_group.append(
220 | media_type(
221 | media=media["filename"],
222 | caption=caption if index == 0 and i == 0 else "",
223 | parse_mode=ParseMode.MARKDOWN
224 | )
225 | )
226 | await client.send_media_group(
227 | chat_id=message.chat.id,
228 | media=media_group
229 | )
230 | else:
231 | media = media_files[0]
232 | async with aiofiles.open(media["filename"], 'rb'):
233 | if media["type"] == "video":
234 | start_time = time.time()
235 | last_update_time = [start_time]
236 | await client.send_video(
237 | chat_id=message.chat.id,
238 | video=media["filename"],
239 | supports_streaming=True,
240 | caption=caption,
241 | parse_mode=ParseMode.MARKDOWN,
242 | reply_markup=inline_keyboard,
243 | progress=progress_bar,
244 | progress_args=(downloading_message, start_time, last_update_time)
245 | )
246 | else:
247 | await client.send_photo(
248 | chat_id=message.chat.id,
249 | photo=media["filename"],
250 | caption=caption,
251 | parse_mode=ParseMode.MARKDOWN
252 | )
253 |
254 | await downloading_message.delete()
255 | # Clean up files asynchronously
256 | for media in media_files:
257 | if os.path.exists(media["filename"]):
258 | os.remove(media["filename"])
259 | LOGGER.info(f"Deleted media file: {media['filename']}")
260 |
261 | except Exception as e:
262 | LOGGER.error(f"Error processing Instagram content: {e}")
263 | await notify_admin(client, f"{COMMAND_PREFIX}in", e, downloading_message)
264 | await downloading_message.edit_text(
265 | "**Instagram Downloader API Down**", parse_mode=ParseMode.MARKDOWN
266 | )
--------------------------------------------------------------------------------
/modules/dlxutils/spfy.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import os
5 | import time
6 | import requests
7 | import aiohttp
8 | import re
9 | import asyncio
10 | import aiofiles
11 | from pathlib import Path
12 | from concurrent.futures import ThreadPoolExecutor
13 | from pyrogram import Client, filters
14 | from pyrogram.enums import ParseMode
15 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
16 | from typing import Optional
17 | from config import COMMAND_PREFIX
18 | from utils import LOGGER, progress_bar, notify_admin # Import LOGGER, progress_bar, and notify_admin from utils
19 | from core import banned_users # Check if user is banned
20 | import urllib.parse
21 |
22 | # Use the imported LOGGER
23 | logger = LOGGER
24 |
25 | # Configuration
26 | class Config:
27 | TEMP_DIR = Path("temp")
28 |
29 | Config.TEMP_DIR.mkdir(exist_ok=True)
30 |
31 | # ThreadPoolExecutor for blocking I/O operations
32 | executor = ThreadPoolExecutor(max_workers=10)
33 |
34 | async def sanitize_filename(title: str) -> str:
35 | """Sanitize file name by removing invalid characters."""
36 | title = re.sub(r'[<>:"/\\|?*]', '', title[:50]).strip()
37 | return f"{title.replace(' ', '_')}_{int(time.time())}"
38 |
39 | async def download_image(url: str, output_path: str) -> Optional[str]:
40 | """Download image from a URL."""
41 | logger.info(f"Starting download of image from {url}")
42 | try:
43 | async with aiohttp.ClientSession() as session:
44 | async with session.get(url) as response:
45 | if response.status == 200:
46 | async with aiofiles.open(output_path, 'wb') as file:
47 | await file.write(await response.read())
48 | logger.info(f"Image downloaded successfully to {output_path}")
49 | return output_path
50 | else:
51 | logger.error(f"Failed to download image: HTTP status {response.status}")
52 | # Notify admins
53 | await notify_admin(None, f"{COMMAND_PREFIX}sp", Exception(f"Failed to download image: HTTP status {response.status}"), None)
54 | except Exception as e:
55 | logger.error(f"Failed to download image: {e}")
56 | # Notify admins
57 | await notify_admin(None, f"{COMMAND_PREFIX}sp", e, None)
58 | return None
59 |
60 | async def handle_spotify_request(client: Client, message: Message, input_text: Optional[str]):
61 | # Check if the message is a reply to another message
62 | if not input_text and message.reply_to_message and message.reply_to_message.text:
63 | input_text = message.reply_to_message.text.strip()
64 |
65 | if not input_text:
66 | await client.send_message(
67 | chat_id=message.chat.id,
68 | text="**Please provide a track Spotify URL**",
69 | parse_mode=ParseMode.MARKDOWN
70 | )
71 | logger.warning(f"No input provided, user: {message.from_user.id if message.from_user else 'unknown'}, chat: {message.chat.id}")
72 | return
73 |
74 | # Check if input_text is a URL (starts with http)
75 | is_url = input_text.lower().startswith('http')
76 |
77 | status_message = await client.send_message(
78 | chat_id=message.chat.id,
79 | text="**Searching The Music**",
80 | parse_mode=ParseMode.MARKDOWN
81 | )
82 |
83 | try:
84 | async with aiohttp.ClientSession() as session:
85 | if is_url:
86 | # Handle Spotify URL
87 | logger.info(f"Processing Spotify URL: {input_text}")
88 | api_url = f"https://abirthetech.serv00.net/sp.php?url={urllib.parse.quote(input_text)}"
89 | async with session.get(api_url) as response:
90 | if response.status == 200:
91 | data = await response.json()
92 | logger.info(f"Track API response: {data}")
93 | if data["status"]:
94 | await status_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN)
95 | else:
96 | await status_message.edit_text("**Please Provide A Valid Spotify URL ❌**", parse_mode=ParseMode.MARKDOWN)
97 | logger.error(f"Invalid Spotify URL: {input_text}")
98 | return
99 | else:
100 | await status_message.edit_text("**❌ Song Not Available On Spotify**", parse_mode=ParseMode.MARKDOWN)
101 | logger.error(f"API request failed: HTTP status {response.status}")
102 | # Notify admins
103 | await notify_admin(client, f"{COMMAND_PREFIX}sp", Exception(f"API request failed: HTTP status {response.status}"), message)
104 | return
105 | else:
106 | # Handle search query
107 | logger.info(f"Processing Spotify search query: {input_text}")
108 | encoded_query = urllib.parse.quote(input_text)
109 | api_url = f"https://abirthetech.serv00.net/sps.php?q={encoded_query}"
110 | async with session.get(api_url) as response:
111 | if response.status == 200:
112 | data = await response.json()
113 | logger.info(f"Search API response: {data}")
114 | if data["type"] == "search" and data["data"]:
115 | await status_message.edit_text("**Found ☑️ Downloading...**", parse_mode=ParseMode.MARKDOWN)
116 | # Use the first result
117 | track = data["data"][0]
118 | # Fetch track details using the Spotify URL
119 | track_url = track["external_urls"]["spotify"]
120 | logger.info(f"Selected track: {track['name']} (URL: {track_url})")
121 | track_api_url = f"https://abirthetech.serv00.net/sp.php?url={urllib.parse.quote(track_url)}"
122 | async with session.get(track_api_url) as track_response:
123 | if track_response.status == 200:
124 | data = await track_response.json()
125 | logger.info(f"Track API response: {data}")
126 | if not data["status"]:
127 | await status_message.edit_text("**Song Metadata Unavailable**", parse_mode=ParseMode.MARKDOWN)
128 | logger.error("Song metadata unavailable")
129 | # Notify admins
130 | await notify_admin(client, f"{COMMAND_PREFIX}sp", Exception("Song metadata unavailable"), message)
131 | return
132 | else:
133 | await status_message.edit_text("**❌ Song Unavailable Bro Try Later**", parse_mode=ParseMode.MARKDOWN)
134 | logger.error(f"Track API request failed: HTTP status {track_response.status}")
135 | # Notify admins
136 | await notify_admin(client, f"{COMMAND_PREFIX}sp", Exception(f"Track API request failed: HTTP status {track_response.status}"), message)
137 | return
138 | else:
139 | await status_message.edit_text("**Sorry No Songs Matched To Your Search!**", parse_mode=ParseMode.MARKDOWN)
140 | logger.error(f"No songs matched search query: {input_text}")
141 | return
142 | else:
143 | await status_message.edit_text("**❌ Sorry Bro Spotify Search API Dead**", parse_mode=ParseMode.MARKDOWN)
144 | logger.error(f"Search API request failed: HTTP status {response.status}")
145 | # Notify admins
146 | await notify_admin(client, f"{COMMAND_PREFIX}sp", Exception(f"Search API request failed: HTTP status {response.status}"), message)
147 | return
148 |
149 | # Extract track details from API response
150 | title = data["title"]
151 | artists = data["artist"]
152 | duration = data["duration"]
153 | album = data["album"]
154 | release_date = data["releaseDate"]
155 | spotify_url = data["spotify_url"]
156 | download_url = data["download_link"]
157 | cover_url = data.get("image") or data.get("cover")
158 |
159 | # Download cover image
160 | cover_path = None
161 | if cover_url:
162 | Config.TEMP_DIR.mkdir(exist_ok=True)
163 | cover_path = Config.TEMP_DIR / f"{await sanitize_filename(title)}.jpg"
164 | downloaded_path = await download_image(cover_url, str(cover_path))
165 | if downloaded_path:
166 | logger.info(f"Cover image downloaded to {downloaded_path}")
167 | else:
168 | logger.warning("Failed to download cover image")
169 | cover_path = None
170 |
171 | # Download audio
172 | safe_title = await sanitize_filename(title)
173 | output_filename = Config.TEMP_DIR / f"{safe_title}.mp3"
174 | logger.info(f"Starting download of audio file from {download_url}")
175 | async with session.get(download_url) as response:
176 | if response.status == 200:
177 | async with aiofiles.open(output_filename, 'wb') as file:
178 | await file.write(await response.read())
179 | logger.info(f"Audio file downloaded successfully to {output_filename}")
180 | else:
181 | await status_message.edit_text("**❌ Sorry Bro Spotify DL API Dead**", parse_mode=ParseMode.MARKDOWN)
182 | logger.error(f"Audio download failed: HTTP status {response.status}")
183 | # Notify admins
184 | await notify_admin(client, f"{COMMAND_PREFIX}sp", Exception(f"Audio download failed: HTTP status {response.status}"), message)
185 | return
186 |
187 | # Prepare user info for caption
188 | if message.from_user:
189 | user_full_name = f"{message.from_user.first_name} {message.from_user.last_name or ''}".strip()
190 | user_info = f"[{user_full_name}](tg://user?id={message.from_user.id})"
191 | else:
192 | group_name = message.chat.title or "this group"
193 | group_url = f"https://t.me/{message.chat.username}" if message.chat.username else "this group"
194 | user_info = f"[{group_name}]({group_url})"
195 |
196 | # Format caption
197 | audio_caption = (
198 | f"🌟 **Title**: `{title}`\n"
199 | f"💥 **Artist**: `{artists}`\n"
200 | f"✨ **Duration**: `{duration}`\n"
201 | f"👀 **Album**: `{album}`\n"
202 | f"🎵 **Release Date**: `{release_date}`\n"
203 | f"━━━━━━━━━━━━━━━━━━━\n"
204 | f"**Downloaded By**: {user_info}"
205 | )
206 |
207 | # Create inline button for Spotify URL
208 | reply_markup = InlineKeyboardMarkup([
209 | [InlineKeyboardButton("🎸 Listen On Spotify", url=spotify_url)]
210 | ])
211 |
212 | last_update_time = [0]
213 | start_time = time.time()
214 |
215 | logger.info("Starting upload of audio file to Telegram")
216 | await client.send_audio(
217 | chat_id=message.chat.id,
218 | audio=str(output_filename),
219 | caption=audio_caption,
220 | title=title,
221 | performer=artists,
222 | parse_mode=ParseMode.MARKDOWN,
223 | thumb=str(cover_path) if cover_path else None,
224 | reply_markup=reply_markup,
225 | progress=progress_bar,
226 | progress_args=(status_message, start_time, last_update_time)
227 | )
228 | logger.info("Upload of audio successfully completed")
229 |
230 | if os.path.exists(output_filename):
231 | os.remove(output_filename)
232 | logger.info(f"Deleted audio file: {output_filename}")
233 | if cover_path and os.path.exists(cover_path):
234 | os.remove(cover_path)
235 | logger.info(f"Deleted cover image: {cover_path}")
236 |
237 | await status_message.delete()
238 | logger.info("Status message deleted")
239 | except Exception as e:
240 | await status_message.edit_text("**❌ Sorry Bro Spotify DL API Dead**", parse_mode=ParseMode.MARKDOWN)
241 | logger.error(f"Error processing Spotify request: {str(e)}")
242 | # Notify admins
243 | await notify_admin(client, status_message, f"{COMMAND_PREFIX}sp", Exception(str(e)))
244 |
245 | def setup_spotify_handler(app: Client):
246 | """Set up the Spotify command handler."""
247 | # Create a regex pattern from the COMMAND_PREFIX list
248 | command_prefix_regex = rf"[{''.join(map(re.escape, COMMAND_PREFIX))}]"
249 |
250 | @app.on_message(filters.regex(rf"^{command_prefix_regex}sp(\s+.*)?$") & (filters.private | filters.group))
251 | async def spotify_command(client: Client, message: Message):
252 | # Check if user is banned
253 | user_id = message.from_user.id if message.from_user else None
254 | if user_id and banned_users.find_one({"user_id": user_id}):
255 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**")
256 | return
257 |
258 | # Check if the message contains a Spotify URL or query
259 | command_parts = message.text.split(maxsplit=1)
260 | input_text = command_parts[1].strip() if len(command_parts) > 1 else None
261 | logger.info(f"Spotify command received: input_text='{input_text or 'None'}', user: {user_id or 'unknown'}, chat: {message.chat.id}")
262 | await handle_spotify_request(client, message, input_text)
--------------------------------------------------------------------------------
/sudoers/admin/admin.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | import asyncio
5 | from pyrogram import Client, filters
6 | from pyrogram.handlers import MessageHandler
7 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message
8 | from pyrogram.enums import ParseMode
9 | from datetime import datetime, timedelta
10 | from config import OWNER_ID, UPDATE_CHANNEL_URL, COMMAND_PREFIX, DEVELOPER_USER_ID
11 | from core import auth_admins, user_activity_collection
12 | from utils import LOGGER
13 |
14 | # Function to update user activity in the MongoDB database
15 | def update_user_activity(user_id, is_group=False):
16 | try:
17 | now = datetime.utcnow()
18 | user = user_activity_collection.find_one({"user_id": user_id})
19 | if not user:
20 | user_activity_collection.insert_one({
21 | "user_id": user_id,
22 | "is_group": is_group,
23 | "last_activity": now,
24 | "daily": 0,
25 | "weekly": 0,
26 | "monthly": 0,
27 | "yearly": 0
28 | })
29 | LOGGER.info(f"Inserted new user activity for user_id {user_id}, is_group={is_group}")
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 | LOGGER.debug(f"Updated activity for user_id {user_id}, is_group={is_group}")
41 | except Exception as e:
42 | LOGGER.error(f"Error updating user activity for user_id {user_id}: {e}")
43 |
44 | def is_admin(user_id):
45 | auth_admins_data = auth_admins.find({}, {"user_id": 1, "_id": 0})
46 | AUTH_ADMIN_IDS = [admin["user_id"] for admin in auth_admins_data]
47 | return user_id == OWNER_ID or user_id in AUTH_ADMIN_IDS
48 |
49 | async def broadcast_handler(client: Client, message: Message):
50 | user_id = message.from_user.id
51 | if not is_admin(user_id):
52 | LOGGER.info(f"Unauthorized broadcast attempt by user_id {user_id}")
53 | await client.send_message(
54 | chat_id=message.chat.id,
55 | text="**✘Kids Not Allowed To Do This↯**",
56 | parse_mode=ParseMode.MARKDOWN
57 | )
58 | return
59 |
60 | is_broadcast = message.command[0].lower() in ["broadcast", "b"]
61 | LOGGER.info(f"{'Broadcast' if is_broadcast else 'Send'} command initiated by user_id {user_id}")
62 |
63 | if message.reply_to_message:
64 | # Admin replies to a message with the command
65 | await process_broadcast(client, message.reply_to_message, is_broadcast, message.chat.id)
66 | elif is_broadcast and len(message.command) > 1:
67 | # Admin uses command with text to broadcast directly
68 | broadcast_text = " ".join(message.command[1:])
69 | await process_broadcast(client, broadcast_text, is_broadcast, message.chat.id)
70 | else:
71 | # Admin sends just the command; bot waits for a message
72 | action_type = "broadcast" if is_broadcast else "send"
73 | await client.send_message(
74 | chat_id=message.chat.id,
75 | text=f"**Please send the message you want to {action_type}.**",
76 | parse_mode=ParseMode.MARKDOWN
77 | )
78 |
79 | # Define the callback to handle the next message
80 | async def broadcast_message_callback(client: Client, broadcast_msg: Message):
81 | if broadcast_msg.from_user.id == message.from_user.id and broadcast_msg.chat.id == message.chat.id:
82 | await process_broadcast(client, broadcast_msg, is_broadcast, message.chat.id)
83 | client.remove_handler(broadcast_message_handler, group=1)
84 |
85 | # Add a temporary handler for the admin's next message
86 | broadcast_message_handler = MessageHandler(
87 | broadcast_message_callback,
88 | filters.user(message.from_user.id) & filters.chat(message.chat.id)
89 | )
90 | client.add_handler(broadcast_message_handler, group=1)
91 |
92 | async def process_broadcast(client: Client, content, is_broadcast=True, chat_id=None):
93 | try:
94 | if isinstance(content, str):
95 | broadcast_text = content
96 | broadcast_msg = None
97 | elif isinstance(content, Message):
98 | broadcast_msg = content
99 | broadcast_text = None
100 | else:
101 | raise ValueError("Invalid content type")
102 |
103 | LOGGER.info(f"Processing {'broadcast' if is_broadcast else 'forward'} to users and groups")
104 | processing_message = await client.send_message(
105 | chat_id=chat_id,
106 | text=f'**💫 {"Broadcasting" if is_broadcast else "Sending"} Message In Progress 💫**',
107 | parse_mode=ParseMode.MARKDOWN
108 | )
109 |
110 | user_ids = [user["user_id"] for user in user_activity_collection.find({"is_group": False})]
111 | group_ids = [group["user_id"] for group in user_activity_collection.find({"is_group": True})]
112 |
113 | successful_users = 0
114 | failed_users = 0
115 | successful_groups = 0
116 | failed_groups = 0
117 | broadcast_start_time = datetime.now()
118 |
119 | keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("💥 Bot Updates 💥", url=UPDATE_CHANNEL_URL)]])
120 |
121 | for target_chat_id in user_ids + group_ids:
122 | try:
123 | if broadcast_text:
124 | await client.send_message(
125 | chat_id=target_chat_id,
126 | text=broadcast_text,
127 | reply_markup=keyboard
128 | )
129 | elif broadcast_msg:
130 | if is_broadcast:
131 | # Broadcast (copy) method
132 | if broadcast_msg.text:
133 | await client.send_message(
134 | chat_id=target_chat_id,
135 | text=broadcast_msg.text,
136 | reply_markup=keyboard
137 | )
138 | elif broadcast_msg.photo:
139 | await client.send_photo(
140 | chat_id=target_chat_id,
141 | photo=broadcast_msg.photo.file_id,
142 | caption=broadcast_msg.caption or "",
143 | reply_markup=keyboard
144 | )
145 | elif broadcast_msg.video:
146 | await client.send_video(
147 | chat_id=target_chat_id,
148 | video=broadcast_msg.video.file_id,
149 | caption=broadcast_msg.caption or "",
150 | reply_markup=keyboard
151 | )
152 | elif broadcast_msg.audio:
153 | await client.send_audio(
154 | chat_id=target_chat_id,
155 | audio=broadcast_msg.audio.file_id,
156 | caption=broadcast_msg.caption or "",
157 | reply_markup=keyboard
158 | )
159 | elif broadcast_msg.document:
160 | await client.send_document(
161 | chat_id=target_chat_id,
162 | document=broadcast_msg.document.file_id,
163 | caption=broadcast_msg.caption or "",
164 | reply_markup=keyboard
165 | )
166 | else:
167 | await client.copy_message(
168 | chat_id=target_chat_id,
169 | from_chat_id=broadcast_msg.chat.id,
170 | message_id=broadcast_msg.id
171 | )
172 | else:
173 | # Forward method
174 | await client.forward_messages(
175 | chat_id=target_chat_id,
176 | from_chat_id=broadcast_msg.chat.id,
177 | message_ids=broadcast_msg.id
178 | )
179 | if target_chat_id in user_ids:
180 | successful_users += 1
181 | else:
182 | successful_groups += 1
183 | except Exception as e:
184 | LOGGER.error(f"Failed to {'broadcast' if is_broadcast else 'forward'} to chat_id {target_chat_id}: {e}")
185 | if target_chat_id in user_ids:
186 | failed_users += 1
187 | else:
188 | failed_groups += 1
189 |
190 | broadcast_end_time = datetime.now()
191 | time_diff = broadcast_end_time - broadcast_start_time
192 | hours, remainder = divmod(time_diff.seconds, 3600)
193 | minutes, seconds = divmod(remainder, 60)
194 | time_taken = f"{hours}h {minutes}m {seconds}s" if hours > 0 else f"{minutes}m {seconds}s" if minutes > 0 else f"{seconds}s"
195 |
196 | await processing_message.delete()
197 |
198 | action_success = "Broadcast" if is_broadcast else "Forward"
199 | LOGGER.info(f"{action_success} completed: {successful_users} users, {successful_groups} groups, {failed_users} failed users, {failed_groups} failed groups")
200 |
201 | await client.send_message(
202 | chat_id=chat_id,
203 | text=f"**💥 Hey Bro! {action_success} Successful ! 💥**\n"
204 | "**✘━━━━━━━━━━━✘**\n"
205 | f"**👀 To Users:** `{successful_users}` ✨\n"
206 | f"**✘ Blocked Users** `{failed_users}` ❄️\n"
207 | "**✘━━━━━━━━━━━✘**\n"
208 | f"**🌐 To Groups** `{successful_groups}` 🌟\n"
209 | f"**✘ Blocked Groups** `{failed_groups}` 💫\n"
210 | "**✘━━━━━━━━━━━✘**\n"
211 | f"**↯ Time Taken** `{time_taken}` 🇧🇩",
212 | parse_mode=ParseMode.MARKDOWN,
213 | reply_markup=InlineKeyboardMarkup(
214 | [[InlineKeyboardButton("💥 Bot Updates 💥", url=UPDATE_CHANNEL_URL)]]
215 | )
216 | )
217 | except Exception as e:
218 | LOGGER.error(f"Error processing {'broadcast' if is_broadcast else 'forward'}: {e}")
219 | await client.send_message(
220 | chat_id=chat_id,
221 | text="**✘ Error Processing Broadcast/Forward!**",
222 | parse_mode=ParseMode.MARKDOWN
223 | )
224 |
225 | async def stats_handler(client: Client, message: Message):
226 | user_id = message.from_user.id
227 | if not is_admin(user_id):
228 | LOGGER.info(f"Unauthorized stats attempt by user_id {user_id}")
229 | await client.send_message(
230 | chat_id=message.chat.id,
231 | text="**✘Kids Not Allowed To Do This↯**",
232 | parse_mode=ParseMode.MARKDOWN
233 | )
234 | return
235 |
236 | LOGGER.info(f"Stats command initiated by user_id {user_id}")
237 | try:
238 | now = datetime.utcnow()
239 | daily_users = user_activity_collection.count_documents({"is_group": False, "last_activity": {"$gt": now - timedelta(days=1)}})
240 | weekly_users = user_activity_collection.count_documents({"is_group": False, "last_activity": {"$gt": now - timedelta(weeks=1)}})
241 | monthly_users = user_activity_collection.count_documents({"is_group": False, "last_activity": {"$gt": now - timedelta(days=30)}})
242 | yearly_users = user_activity_collection.count_documents({"is_group": False, "last_activity": {"$gt": now - timedelta(days=365)}})
243 |
244 | total_users = user_activity_collection.count_documents({"is_group": False})
245 | total_groups = user_activity_collection.count_documents({"is_group": True})
246 |
247 | stats_text = (
248 | "**💥 Bot's Full Database Info 💥**\n"
249 | "**✘━━━━━━━━━━━✘**\n"
250 | "**✨ Registered Users Activity: ✨**\n"
251 | f"- 💫 Daily Active: {daily_users} 🔥\n"
252 | f"- 🌟 Weekly Active: {weekly_users} ⚡\n"
253 | f"- ❄️ Monthly Active: {monthly_users} 🌈\n"
254 | f"- 👀 Annual Active: {yearly_users} 🎯\n"
255 | "**✘━━━━━━━━━━━✘**\n"
256 | "**✘ Total Metrics: ✘**\n"
257 | f"- 👥 Total Users: {total_users} 💫\n"
258 | f"- 🌐 Total Groups: {total_groups} 🌟\n"
259 | f"- ↯ Database Size: {total_users + total_groups} ✨\n"
260 | )
261 |
262 | keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("💥 Bot Updates 💥", url=UPDATE_CHANNEL_URL)]])
263 | await client.send_message(
264 | chat_id=message.chat.id,
265 | text=stats_text,
266 | parse_mode=ParseMode.MARKDOWN,
267 | reply_markup=keyboard,
268 | disable_web_page_preview=True
269 | )
270 | LOGGER.info("Stats command completed successfully")
271 | except Exception as e:
272 | LOGGER.error(f"Error processing stats: {e}")
273 | await client.send_message(
274 | chat_id=message.chat.id,
275 | text="**✘ Error Processing Stats!**",
276 | parse_mode=ParseMode.MARKDOWN
277 | )
278 |
279 | async def group_added_handler(client: Client, message: Message):
280 | try:
281 | for new_member in message.new_chat_members:
282 | if new_member.is_self:
283 | chat_id = message.chat.id
284 | update_user_activity(chat_id, is_group=True)
285 | await client.send_message(
286 | chat_id=chat_id,
287 | text="**💥 Thank You For Adding Me In This Group! 💫**\n"
288 | "**✘ ━━━━━━━━━━━━━━━━━━ ✘**\n"
289 | "**✨ I'm here to assist you with various tasks and make your group experience better.\n"
290 | "↯ Feel free to explore my features and let me know if you need any help! 🌟**\n"
291 | "**✘ ━━━━━━━━━━━━━━━━━━ ✘**",
292 | parse_mode=ParseMode.MARKDOWN,
293 | reply_markup=InlineKeyboardMarkup([
294 | [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"),
295 | InlineKeyboardButton("My Dev 💫", user_id=DEVELOPER_USER_ID)]
296 | ])
297 | )
298 | LOGGER.info(f"Bot added to group {chat_id}")
299 | except Exception as e:
300 | LOGGER.error(f"Error in group_added_handler for chat_id {message.chat.id}: {e}")
301 |
302 | def setup_admin_handler(app: Client):
303 | """
304 | Set up command handlers for the Pyrogram bot.
305 | This includes specific commands like /broadcast and /stats, as well as general activity tracking.
306 | """
307 | app.add_handler(
308 | MessageHandler(
309 | broadcast_handler,
310 | (filters.command(["broadcast", "b"]) | filters.command(["broadcast", "b"], prefixes=COMMAND_PREFIX) |
311 | filters.command(["send", "s"]) | filters.command(["send", "s"], prefixes=COMMAND_PREFIX)) &
312 | (filters.private | filters.group)
313 | ),
314 | group=1,
315 | )
316 |
317 | app.add_handler(
318 | MessageHandler(
319 | stats_handler,
320 | (filters.command(["stats", "report", "status"]) |
321 | filters.command(["stats", "report", "status"], prefixes=COMMAND_PREFIX)) &
322 | (filters.private | filters.group)
323 | ),
324 | group=1,
325 | )
326 |
327 | app.add_handler(
328 | MessageHandler(
329 | lambda client, message: update_user_activity(message.from_user.id) if message.from_user else None,
330 | filters.all
331 | ),
332 | group=2,
333 | )
334 |
335 | app.add_handler(
336 | MessageHandler(
337 | group_added_handler,
338 | filters.group & filters.new_chat_members
339 | ),
340 | group=1
341 | )
342 |
--------------------------------------------------------------------------------
/modules/infoxutils/info.py:
--------------------------------------------------------------------------------
1 | # Copyright @ISmartDevs
2 | # Channel t.me/TheSmartDev
3 |
4 | from datetime import datetime, timedelta
5 | from dateutil.relativedelta import relativedelta
6 | from pyrogram import filters, Client
7 | from pyrogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup
8 | from pyrogram.enums import ParseMode, ChatType, UserStatus
9 | from pyrogram.errors import PeerIdInvalid, UsernameNotOccupied, ChannelInvalid
10 | from config import COMMAND_PREFIX
11 | from utils import LOGGER, get_dc_locations # Use LOGGER from utils
12 | from core import banned_users # Check if user is banned
13 |
14 | # Use the imported LOGGER
15 | logger = LOGGER
16 |
17 | # Function to calculate account age accurately
18 | def calculate_account_age(creation_date):
19 | today = datetime.now()
20 | delta = relativedelta(today, creation_date)
21 | years = delta.years
22 | months = delta.months
23 | days = delta.days
24 | return f"{years} years, {months} months, {days} days"
25 |
26 | # Function to estimate account creation date based on user ID
27 | def estimate_account_creation_date(user_id):
28 | # Known reference points for user IDs and their creation dates
29 | reference_points = [
30 | (100000000, datetime(2013, 8, 1)), # Telegram's launch date
31 | (1273841502, datetime(2020, 8, 13)), # Example reference point
32 | (1500000000, datetime(2021, 5, 1)), # Another reference point
33 | (2000000000, datetime(2022, 12, 1)), # Another reference point
34 | ]
35 |
36 | # Find the closest reference point
37 | closest_point = min(reference_points, key=lambda x: abs(x[0] - user_id))
38 | closest_user_id, closest_date = closest_point
39 |
40 | # Calculate the difference in user IDs
41 | id_difference = user_id - closest_user_id
42 |
43 | # Estimate the creation date based on the difference
44 | # Assuming 20,000,000 user IDs are created per day (adjusted for estimation)
45 | days_difference = id_difference / 20000000
46 | creation_date = closest_date + timedelta(days=days_difference)
47 |
48 | return creation_date
49 |
50 | def setup_info_handler(app):
51 | @app.on_message(filters.command(["info", "id"], prefixes=COMMAND_PREFIX) & (filters.private | filters.group))
52 | async def handle_info_command(client: Client, message: Message):
53 | # Check if user is banned
54 | user_id = message.from_user.id if message.from_user else None
55 | if user_id and banned_users.find_one({"user_id": user_id}):
56 | await client.send_message(message.chat.id, "**✘Sorry You're Banned From Using Me↯**")
57 | return
58 |
59 | logger.info("Received /info or /id command")
60 | try:
61 | # Get DC locations data from imported function
62 | DC_LOCATIONS = get_dc_locations()
63 |
64 | progress_message = await client.send_message(message.chat.id, "**✨ Smart Tools Fetching Info From Database 💥**")
65 | try:
66 | if not message.command or (len(message.command) == 1 and not message.reply_to_message):
67 | logger.info("Fetching current user info")
68 | user = message.from_user
69 | chat = message.chat
70 | premium_status = "Yes" if user.is_premium else "No"
71 | dc_location = DC_LOCATIONS.get(user.dc_id, "Unknown")
72 | account_created = estimate_account_creation_date(user.id)
73 | account_created_str = account_created.strftime("%B %d, %Y")
74 | account_age = calculate_account_age(account_created)
75 |
76 | # Added verification and status
77 | verified_status = "Yes" if getattr(user, 'is_verified', False) else "No"
78 |
79 | status = "⚪️ Unknown"
80 | if user.status:
81 | if user.status == UserStatus.ONLINE:
82 | status = "Online"
83 | elif user.status == UserStatus.OFFLINE:
84 | status = "Offline"
85 | elif user.status == UserStatus.RECENTLY:
86 | status = "Recently online"
87 | elif user.status == UserStatus.LAST_WEEK:
88 | status = "Last seen within week"
89 | elif user.status == UserStatus.LAST_MONTH:
90 | status = "Last seen within month"
91 |
92 | response = (
93 | "✘《 **User Information** ↯ 》\n"
94 | f"↯ **Full Name:** {user.first_name} {user.last_name or ''}\n"
95 | f"↯ **User ID:** `{user.id}`\n"
96 | f"↯ **Username:** @{user.username if user.username else 'None'}\n"
97 | f"↯ **Chat Id:** `{chat.id}`\n"
98 | f"↯ **Data Center:** {user.dc_id} ({dc_location})\n"
99 | f"↯ **Premium User:** {premium_status}\n"
100 | f"↯ **Verified:** {verified_status}\n"
101 | f"↯ **Flags:** {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else 'Clean'}\n"
102 | f"↯ **Status:** {status}\n"
103 | f"↯ **Account Created On:** {account_created_str}\n"
104 | f"↯ **Account Age:** {account_age}"
105 | )
106 | buttons = [
107 | [InlineKeyboardButton("✘ Android Link ↯", url=f"tg://openmessage?user_id={user.id}"), InlineKeyboardButton("✘ iOS Link ↯", url=f"tg://user?id={user.id}")],
108 | [InlineKeyboardButton("✘ Permanent Link ↯", url=f"tg://user?id={user.id}")],
109 | ]
110 | await client.send_message(chat_id=message.chat.id, text=response, parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(buttons))
111 | logger.info("User info fetched successfully with buttons")
112 | elif message.reply_to_message:
113 | # Show info of the replied user or bot
114 | logger.info("Fetching info of the replied user or bot")
115 | user = message.reply_to_message.from_user
116 | chat = message.chat
117 | premium_status = "Yes" if user.is_premium else "No"
118 | dc_location = DC_LOCATIONS.get(user.dc_id, "Unknown")
119 | account_created = estimate_account_creation_date(user.id)
120 | account_created_str = account_created.strftime("%B %d, %Y")
121 | account_age = calculate_account_age(account_created)
122 |
123 | # Added verification and status
124 | verified_status = "Yes" if getattr(user, 'is_verified', False) else "No"
125 |
126 | status = "⚪️ Unknown"
127 | if user.status:
128 | if user.status == UserStatus.ONLINE:
129 | status = "Online"
130 | elif user.status == UserStatus.OFFLINE:
131 | status = "Offline"
132 | elif user.status == UserStatus.RECENTLY:
133 | status = "Recently online"
134 | elif user.status == UserStatus.LAST_WEEK:
135 | status = "Last seen within week"
136 | elif user.status == UserStatus.LAST_MONTH:
137 | status = "Last seen within month"
138 |
139 | response = (
140 | "✘《 **User Information** ↯ 》\n"
141 | f"↯ **Full Name:** {user.first_name} {user.last_name or ''}\n"
142 | f"↯ **User ID:** `{user.id}`\n"
143 | f"↯ **Username:** @{user.username if user.username else 'None'}\n"
144 | f"↯ **Chat Id:** `{chat.id}`\n"
145 | f"↯ **Data Center:** {user.dc_id} ({dc_location})\n"
146 | f"↯ **Premium User:** {premium_status}\n"
147 | f"↯ **Verified:** {verified_status}\n"
148 | f"↯ **Flags:** {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else 'Clean'}\n"
149 | f"↯ **Status:** {status}\n"
150 | f"↯ **Account Created On:** {account_created_str}\n"
151 | f"↯ **Account Age:** {account_age}"
152 | )
153 | if user.is_bot:
154 | response = (
155 | "✘《 **Bot Information** ↯ 》\n"
156 | f"↯ **Bot Name:** {user.first_name} {user.last_name or ''}\n"
157 | f"↯ **Bot ID:** `{user.id}`\n"
158 | f"↯ **Username:** @{user.username if user.username else 'None'}\n"
159 | f"↯ **Data Center:** {user.dc_id} ({dc_location})\n"
160 | f"↯ **Premium User:** {premium_status}\n"
161 | f"↯ **Verified:** {verified_status}\n"
162 | f"↯ **Flags:** {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else 'Clean'}\n"
163 | f"↯ **Account Created On:** {account_created_str}\n"
164 | f"↯ **Account Age:** {account_age}"
165 | )
166 | buttons = [
167 | [InlineKeyboardButton("✘ Android Link ↯", url=f"tg://openmessage?user_id={user.id}"), InlineKeyboardButton("✘ iOS Link ↯", url=f"tg://user?id={user.id}")],
168 | [InlineKeyboardButton("✘ Permanent Link ↯", url=f"tg://user?id={user.id}")],
169 | ]
170 | await client.send_message(chat_id=message.chat.id, text=response, parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(buttons))
171 | logger.info("Replied user info fetched successfully")
172 | elif len(message.command) > 1:
173 | # Extract username from the command
174 | logger.info("Extracting username from the command")
175 | username = message.command[1].strip('@').replace('https://', '').replace('http://', '').replace('t.me/', '').replace('/', '').replace(':', '')
176 |
177 | try:
178 | # First, attempt to get user or bot info
179 | logger.info(f"Fetching info for user or bot: {username}")
180 | user = await client.get_users(username)
181 | premium_status = "Yes" if user.is_premium else "No"
182 | dc_location = DC_LOCATIONS.get(user.dc_id, "Unknown")
183 | account_created = estimate_account_creation_date(user.id)
184 | account_created_str = account_created.strftime("%B %d, %Y")
185 | account_age = calculate_account_age(account_created)
186 |
187 | # Added verification and status
188 | verified_status = "Verified" if user.is_verified else "Not Verified"
189 |
190 | status = "⚪️ Unknown"
191 | if user.status:
192 | if user.status == UserStatus.ONLINE:
193 | status = "Online"
194 | elif user.status == UserStatus.OFFLINE:
195 | status = "Offline"
196 | elif user.status == UserStatus.RECENTLY:
197 | status = "Online"
198 | elif user.status == UserStatus.LAST_WEEK:
199 | status = "Last seen within a week"
200 | elif user.status == UserStatus.LAST_MONTH:
201 | status = "Last seen within a month"
202 |
203 | response = (
204 | "✘《 **User Information** ↯ 》\n"
205 | f"✓ **Full Name:** {user.first_name} {user.last_name or ''}\n"
206 | f"✓ **ID:** `{user.id}`\n"
207 | f"✓ **Username:** @{user.username if user.username else 'None'}\n"
208 | f"✓ **Context ID:** `{user.id}`\n"
209 | f"✓ **Data Center:** {user.dc_id} ({dc_location})\n"
210 | f"✓ **Premium:** {premium_status}\n"
211 | f"✓ **Verification:** {verified_status}\n"
212 | f"✓ **Flags:** {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else '✓ Clean'}\n"
213 | f"✓ **Status:** {status}\n"
214 | f"✓ **Account Created On:** {account_created_str}\n"
215 | f"✓ **Account Age:** {account_age}"
216 | )
217 | if user.is_bot:
218 | response = (
219 | "✘《 **Bot Information** ↯ 》\n"
220 | f"✓ **Bot Name:** {user.first_name} {user.last_name or ''}\n"
221 | f"✓ **Bot ID:** `{user.id}`\n"
222 | f"✓ **Username:** @{user.username if user.username else 'None'}\n"
223 | f"✓ **Data Center:** {user.dc_id} ({dc_location})\n"
224 | f"✓ **Premium:** {premium_status}\n"
225 | f"✓ **Verification:** {verified_status}\n"
226 | f"✓ **Flags:** {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else '✓ Clean'}\n"
227 | f"✓ **Account Created On:** {account_created_str}\n"
228 | f"✓ **Account Age:** {account_age}"
229 | )
230 | buttons = [
231 | [InlineKeyboardButton("✘ Android Link ↯", url=f"tg://openmessage?user_id={user.id}"), InlineKeyboardButton("✘ iOS Link ↯", url=f"tg://user?id={user.id}")],
232 | [InlineKeyboardButton("✘ Permanent Link ↯", url=f"tg://user?id={user.id}")],
233 | ]
234 | await client.send_message(chat_id=message.chat.id, text=response, parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(buttons))
235 | logger.info("User/bot info fetched successfully with buttons")
236 | except (PeerIdInvalid, UsernameNotOccupied, IndexError):
237 | logger.info(f"Username '{username}' not found as a user/bot. Checking for chat...")
238 | try:
239 | chat = await client.get_chat(username)
240 | dc_location = DC_LOCATIONS.get(chat.dc_id, "Unknown")
241 | response = (
242 | f"✘《 **Chat Information** ↯ 》\n"
243 | f"✓ **{chat.title}**\n"
244 | f"✓ **ID:** `{chat.id}`\n"
245 | f"✓ **Type:** {'Supergroup' if chat.type == ChatType.SUPERGROUP else 'Group' if chat.type == ChatType.GROUP else 'Channel'}\n"
246 | f"✓ **Member count:** {chat.members_count if chat.members_count else 'Unknown'}"
247 | )
248 | buttons = [
249 | [InlineKeyboardButton("✘ Joining Link ↯", url=f"t.me/c/{str(chat.id).replace('-100', '')}/100"), InlineKeyboardButton("✘ Permanent Link ↯", url=f"t.me/c/{str(chat.id).replace('-100', '')}/100")],
250 | ]
251 | await client.send_message(chat_id=message.chat.id, text=response, parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup(buttons))
252 | logger.info("Chat info fetched successfully with buttons")
253 | except (ChannelInvalid, PeerIdInvalid):
254 | error_message = (
255 | "**Looks Like I Don't Have Control Over The Channel**"
256 | if chat.type == ChatType.CHANNEL
257 | else "**Looks Like I Don't Have Control Over The Group**"
258 | )
259 | await client.send_message(chat_id=message.chat.id, text=error_message, parse_mode=ParseMode.MARKDOWN)
260 | logger.error(f"Permission error: {error_message}")
261 | except Exception as e:
262 | logger.error(f"Error fetching chat info: {str(e)}")
263 | await client.send_message(chat_id=message.chat.id, text="**Looks Like I Don't Have Control Over The Group**", parse_mode=ParseMode.MARKDOWN)
264 | except Exception as e:
265 | logger.error(f"Error fetching user or bot info: {str(e)}")
266 | await client.send_message(chat_id=message.chat.id, text="**Looks Like I Don't Have Control Over The User**", parse_mode=ParseMode.MARKDOWN)
267 | except Exception as e:
268 | logger.error(f"Unhandled exception: {str(e)}")
269 | await client.send_message(chat_id=message.chat.id, text="**Sorry User Info Database API Error❌**", parse_mode=ParseMode.MARKDOWN)
270 | finally:
271 | await client.delete_messages(chat_id=message.chat.id, message_ids=progress_message.id)
272 | logger.info("Progress message deleted")
273 | except Exception as e:
274 | logger.error(f"Unhandled exception: {str(e)}")
275 | await client.send_message(chat_id=message.chat.id, text="**Sorry User Info Database API Error❌**", parse_mode=ParseMode.MARKDOWN)
--------------------------------------------------------------------------------