├── .python-version ├── Procfile ├── core ├── __init__.py └── mongo.py ├── heroku.yml ├── start.sh ├── requirements.txt ├── utils ├── __init__.py ├── dc_locations.py └── logging_setup.py ├── docker-compose.yml ├── Dockerfile ├── app.py ├── sample.env ├── main.py ├── app.json ├── config.py ├── plugins ├── start.py ├── help.py ├── get.py ├── auth.py ├── login.py ├── info.py └── buy.py └── README.md /.python-version: -------------------------------------------------------------------------------- 1 | 3.11 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 main.py -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | from .mongo import numbersdb_collection, logindb_collection, authusers_collection -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | web: Dockerfile 4 | 5 | run: 6 | worker: python3 main.py -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | python3 main.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrofork 2 | tgcrypto 3 | asyncio 4 | aiohttp 5 | python-dateutil 6 | motor 7 | twilio -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | from .logging_setup import LOGGER 4 | from .dc_locations import get_dc_locations -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | smarttwiliobot: 3 | build: . 4 | container_name: smarttwiliobot 5 | ports: 6 | - "8000:8000" 7 | env_file: 8 | - .env 9 | volumes: 10 | - .:/app -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | FROM python:3.9-slim-buster 4 | 5 | RUN apt update && apt install -y git curl && apt clean 6 | 7 | WORKDIR /app 8 | 9 | COPY requirements.txt . 10 | RUN pip install --no-cache-dir -r requirements.txt 11 | 12 | COPY . . 13 | 14 | RUN chmod +x start.sh 15 | 16 | EXPOSE 8000 17 | 18 | CMD ["bash", "start.sh"] 19 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client 2 | from utils import LOGGER 3 | from config import API_ID, API_HASH, BOT_TOKEN 4 | 5 | LOGGER.info("Creating Bot Client From BOT_TOKEN") 6 | 7 | app = Client( 8 | "SmartTwilio", 9 | api_id=API_ID, 10 | api_hash=API_HASH, 11 | bot_token=BOT_TOKEN, 12 | workers=10000, 13 | plugins=dict(root="plugins") 14 | ) 15 | 16 | LOGGER.info("Bot Client Created Successfully!") -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | 4 | #Telegram API Credentials 5 | API_ID=12345678 6 | API_HASH=abc123yourhashhere 7 | BOT_TOKEN=123456:ABC-Your-Bot-Token 8 | #Bot Settings Dev 9 | DEVELOPER_ID=123456789 10 | UPDATE_CHANNEL_URL=https://t.me/TheSmartDev 11 | ADMIN_IDS=123456789,987654321 12 | COMMAND_PREFIX=.,/,!,# 13 | #Bot Database Connection 14 | MONGO_URL=mongodb+srv://yourmongo.url 15 | -------------------------------------------------------------------------------- /utils/dc_locations.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | 4 | def get_dc_locations(): 5 | return { 6 | 1: "MIA, Miami, USA, US", 7 | 2: "AMS, Amsterdam, Netherlands, NL", 8 | 3: "MBA, Mumbai, India, IN", 9 | 4: "STO, Stockholm, Sweden, SE", 10 | 5: "SIN, Singapore, SG", 11 | 6: "LHR, London, United Kingdom, GB", 12 | 7: "FRA, Frankfurt, Germany, DE", 13 | 8: "JFK, New York, USA, US", 14 | 9: "HKG, Hong Kong, HK", 15 | 10: "TYO, Tokyo, Japan, JP", 16 | 11: "SYD, Sydney, Australia, AU", 17 | 12: "GRU, São Paulo, Brazil, BR", 18 | 13: "DXB, Dubai, UAE, AE", 19 | 14: "CDG, Paris, France, FR", 20 | 15: "ICN, Seoul, South Korea, KR", 21 | } -------------------------------------------------------------------------------- /utils/logging_setup.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | 4 | import logging 5 | import logging.handlers 6 | 7 | LOGGER = logging.getLogger() 8 | LOGGER.setLevel(logging.INFO) 9 | 10 | console_handler = logging.StreamHandler() 11 | console_handler.setLevel(logging.INFO) 12 | 13 | file_handler = logging.handlers.RotatingFileHandler( 14 | 'botlog.txt', maxBytes=5*1024*1024, backupCount=5, encoding='utf-8' 15 | ) 16 | file_handler.setLevel(logging.INFO) 17 | 18 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 19 | console_handler.setFormatter(formatter) 20 | file_handler.setFormatter(formatter) 21 | 22 | LOGGER.addHandler(console_handler) 23 | LOGGER.addHandler(file_handler) 24 | 25 | logging.getLogger("aiohttp").setLevel(logging.WARNING) 26 | logging.getLogger("apscheduler").setLevel(logging.WARNING) 27 | 28 | LOGGER.info("LOGGER Module Successfully Intailized") -------------------------------------------------------------------------------- /core/mongo.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartDevs 2 | #Channel t.me/TheSmartDev 3 | 4 | from motor.motor_asyncio import AsyncIOMotorClient 5 | from urllib.parse import urlparse, parse_qs 6 | from utils import LOGGER 7 | from config import MONGO_URL 8 | 9 | LOGGER.info("Creating MONGO_CLIENT From MONGO_URL") 10 | 11 | try: 12 | parsed = urlparse(MONGO_URL) 13 | query_params = parse_qs(parsed.query) 14 | db_name = query_params.get("appName", [None])[0] 15 | 16 | if not db_name: 17 | raise ValueError("No database name found in MONGO_URL (missing 'appName' query param)") 18 | 19 | MONGO_CLIENT = AsyncIOMotorClient(MONGO_URL) 20 | db = MONGO_CLIENT.get_database(db_name) 21 | authusers_collection = db["authusers"] 22 | logindb_collection = db["logindb"] 23 | numbersdb_collection = db["numbersdb"] 24 | 25 | LOGGER.info(f"MONGO_CLIENT Created Successfully!") 26 | except Exception as e: 27 | LOGGER.error(f"Failed to create MONGO_CLIENT: {e}") 28 | raise -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | from app import app 4 | from utils import LOGGER 5 | from config import DEVELOPER_ID 6 | from pyrogram import idle 7 | import asyncio 8 | 9 | async def main(): 10 | await app.start() 11 | LOGGER.info("Bot Successfully Started💥") 12 | if DEVELOPER_ID: 13 | try: 14 | await app.send_message( 15 | int(DEVELOPER_ID), 16 | "**Bot Successfully Started💥**" 17 | ) 18 | LOGGER.info(f"Sent startup confirmation to DEVELOPER_ID: {DEVELOPER_ID}") 19 | except Exception as e: 20 | LOGGER.error(f"Could not send startup message to DEVELOPER_ID: {e}") 21 | LOGGER.error("Please ensure DEVELOPER_ID is a valid user ID and the bot can message them.") 22 | await idle() 23 | await app.stop() 24 | LOGGER.info("Bot Stopped Successfully.") 25 | 26 | if __name__ == "__main__": 27 | loop = asyncio.get_event_loop() 28 | loop.run_until_complete(main()) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TwilioSMSBot", 3 | "description": "Best Twilio SMS Bot For All Type Features", 4 | "repository": "https://github.com/YourUsername/SmartToolsBot", 5 | "env": { 6 | "API_ID": { 7 | "description": "Your Telegram API ID", 8 | "required": true 9 | }, 10 | "API_HASH": { 11 | "description": "Your Telegram API Hash", 12 | "required": true 13 | }, 14 | "BOT_TOKEN": { 15 | "description": "Your Bot Token", 16 | "required": true 17 | }, 18 | "DEVELOPER_ID": { 19 | "description": "Your Telegram User ID", 20 | "required": true 21 | }, 22 | "UPDATE_CHANNEL_URL": { 23 | "description": "Updates Channel URL", 24 | "required": true 25 | }, 26 | "ADMIN_IDS": { 27 | "description": "Comma-separated list of admin user IDs", 28 | "required": true 29 | }, 30 | "COMMAND_PREFIX": { 31 | "description": "Command prefixes, separated by commas", 32 | "required": false 33 | }, 34 | "MONGO_URL": { 35 | "description": "Your MongoDB connection URL", 36 | "required": true 37 | } 38 | }, 39 | "buildpacks": [ 40 | { 41 | "url": "heroku/python" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | import os 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | def get_env_or_default(key, default=None, cast_func=str): 9 | value = os.getenv(key) 10 | if value is not None and value.strip() != "": 11 | try: 12 | return cast_func(value) 13 | except (ValueError, TypeError) as e: 14 | print(f"Error casting {key} with value '{value}' to {cast_func.__name__}: {e}") 15 | return default 16 | return default 17 | 18 | API_ID = get_env_or_default("API_ID", default=None, cast_func=int) 19 | API_HASH = get_env_or_default("API_HASH") 20 | BOT_TOKEN = get_env_or_default("BOT_TOKEN") 21 | DEVELOPER_ID = get_env_or_default("DEVELOPER_ID", default=None, cast_func=int) 22 | UPDATE_CHANNEL_URL = get_env_or_default("UPDATE_CHANNEL_URL") 23 | ADMIN_IDS = get_env_or_default("ADMIN_IDS", default="", cast_func=str) 24 | COMMAND_PREFIX = get_env_or_default("COMMAND_PREFIX", default=".,/,!,#", cast_func=str).split(",") 25 | MONGO_URL = get_env_or_default("MONGO_URL") 26 | 27 | ADMIN_IDS = [int(x.strip()) for x in ADMIN_IDS.split(",") if x.strip().isdigit()] 28 | 29 | required_vars = { 30 | "API_ID": API_ID, 31 | "API_HASH": API_HASH, 32 | "BOT_TOKEN": BOT_TOKEN, 33 | "DEVELOPER_ID": DEVELOPER_ID, 34 | "UPDATE_CHANNEL_URL": UPDATE_CHANNEL_URL, 35 | "MONGO_URL": MONGO_URL, 36 | } 37 | 38 | for var_name, var_value in required_vars.items(): 39 | if var_value is None or var_value == f"Your_{var_name}_Here" or (isinstance(var_value, str) and var_value.strip() == ""): 40 | raise ValueError(f"Required variable {var_name} is missing or invalid. Set it in .env (VPS), config.py (VPS), or Heroku config vars.") 41 | 42 | print("Loaded COMMAND_PREFIX:", COMMAND_PREFIX) 43 | if not COMMAND_PREFIX: 44 | raise ValueError("No command prefixes found. Set COMMAND_PREFIX in .env, config.py, or Heroku config vars.") 45 | -------------------------------------------------------------------------------- /plugins/start.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel: https://t.me/TheSmartDev 3 | from pyrogram import Client, filters 4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup 5 | from pyrogram.enums import ParseMode 6 | from core import authusers_collection 7 | from config import ADMIN_IDS, COMMAND_PREFIX, DEVELOPER_ID, UPDATE_CHANNEL_URL 8 | 9 | 10 | @Client.on_message(filters.command("start", prefixes=COMMAND_PREFIX) & filters.private) 11 | async def start_handler(client, message): 12 | user = message.from_user 13 | user_id = user.id 14 | 15 | # Check if user is authorized (ADMIN or in DB) 16 | is_admin = user_id in ADMIN_IDS 17 | is_authorized = await authusers_collection.find_one({"user_id": user_id}) 18 | 19 | if not is_admin and not is_authorized: 20 | return await client.send_message( 21 | chat_id=user_id, 22 | text="**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**", 23 | reply_markup=InlineKeyboardMarkup([ 24 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 25 | ]), 26 | parse_mode=ParseMode.MARKDOWN 27 | ) 28 | 29 | fullname = f"{user.first_name or ''} {user.last_name or ''}".strip() 30 | 31 | # Send welcome message with non-inline ReplyKeyboardMarkup 32 | await client.send_message( 33 | chat_id=user_id, 34 | text=( 35 | f"**Hi {fullname}! I Am TwilioSMSBot💸 The ultimate toolkit on Telegram, offering auto otp system, supreme features, lightning faster server otp.**\n\n" 36 | f"🛒 Choose An Option Below:" 37 | ), 38 | reply_markup=ReplyKeyboardMarkup( 39 | [ 40 | ["🔐 Login", "🔍 Search Number"], 41 | ["👤 Account", "💸 Auth Users"], 42 | ["💰 Custom Area Code", "Get OTP💸"], 43 | ["Delete Number 🗑"] 44 | ], 45 | resize_keyboard=True 46 | ), 47 | parse_mode=ParseMode.MARKDOWN, 48 | disable_web_page_preview=True 49 | ) -------------------------------------------------------------------------------- /plugins/help.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel: https://t.me/TheSmartDev 3 | from pyrogram import Client, filters 4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 5 | from pyrogram.enums import ParseMode 6 | from config import ADMIN_IDS, COMMAND_PREFIX, DEVELOPER_ID, UPDATE_CHANNEL_URL 7 | from core import authusers_collection 8 | from utils import LOGGER 9 | 10 | # Helper to check if user is authorized 11 | async def is_authorized_user(user_id): 12 | if user_id in ADMIN_IDS: 13 | return True 14 | user = await authusers_collection.find_one({"user_id": user_id}) 15 | return user is not None 16 | 17 | # Filter for authorized users 18 | def authorized_only(): 19 | async def func(flt, client, message): 20 | return await is_authorized_user(message.from_user.id) 21 | return filters.create(func) 22 | 23 | @Client.on_message(filters.command("help", prefixes=COMMAND_PREFIX) & filters.private & authorized_only()) 24 | async def help_command(client, message): 25 | help_text = ( 26 | "Showing Help Menu Of Smart Twilio:👇\n" 27 | "━━━━━━━━━━━━━━━━━━━\n" 28 | "/start - Start Smart Twilio Bot\n" 29 | "/my - Get All Purchased Numbers\n" 30 | "/list - Get List Of All Auth Users\n" 31 | "/auth - Auth A User In DB\n" 32 | "/unauth - Remove A User From DB\n" 33 | "/del - Delete Purchased Numbers\n" 34 | "/get - Get OTP Manually From Server\n" 35 | "/login - Login To Twilio Account\n" 36 | "/logout - Logout From Twilio Account\n" 37 | "/info - Get User Info From Smart DB\n" 38 | "━━━━━━━━━━━━━━━━━━━\n" 39 | "Smart Twilio Bot By Smart Dev 🇧🇩" 40 | ) 41 | 42 | await message.reply( 43 | help_text, 44 | parse_mode=ParseMode.HTML, 45 | reply_markup=InlineKeyboardMarkup([ 46 | [InlineKeyboardButton("Updates Channel", url=UPDATE_CHANNEL_URL)] 47 | ]) 48 | ) 49 | 50 | @Client.on_message(filters.command("help", prefixes=COMMAND_PREFIX) & filters.private & ~authorized_only()) 51 | async def unauthorized_access(client, message): 52 | await message.reply( 53 | "Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth", 54 | parse_mode=ParseMode.HTML, 55 | reply_markup=InlineKeyboardMarkup([ 56 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 57 | ]) 58 | ) -------------------------------------------------------------------------------- /plugins/get.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | 4 | from pyrogram import Client, filters 5 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 6 | from pyrogram.enums import ParseMode 7 | from config import ADMIN_IDS, COMMAND_PREFIX, DEVELOPER_ID 8 | from core import authusers_collection, logindb_collection 9 | from utils import LOGGER 10 | import aiohttp 11 | import base64 12 | from datetime import datetime 13 | 14 | async def is_authorized_user(user_id): 15 | if user_id in ADMIN_IDS: 16 | return True 17 | user = await authusers_collection.find_one({"user_id": user_id}) 18 | return user is not None 19 | 20 | def authorized_only(): 21 | async def func(flt, client, message): 22 | return await is_authorized_user(message.from_user.id) 23 | return filters.create(func) 24 | 25 | async def is_logged_in(user_id): 26 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 27 | return user_data is not None 28 | 29 | def get_auth_header(sid, auth_token): 30 | auth_str = f"{sid}:{auth_token}" 31 | auth_b64 = base64.b64encode(auth_str.encode()).decode() 32 | return f"Basic {auth_b64}" 33 | 34 | @Client.on_message((filters.command("get", prefixes=COMMAND_PREFIX) | filters.regex(r"^Get OTP💸$")) & filters.private & authorized_only()) 35 | async def get_command(client, message): 36 | user_id = message.from_user.id 37 | 38 | if not await is_logged_in(user_id): 39 | await message.reply( 40 | "Sorry, Please First Login Using /login ❌", 41 | parse_mode=ParseMode.HTML, 42 | reply_markup=InlineKeyboardMarkup([ 43 | [InlineKeyboardButton("Developer", user_id=DEVELOPER_ID)] 44 | ]) 45 | ) 46 | return 47 | 48 | loading_message = await message.reply( 49 | "Getting All Latest OTP Messages", 50 | parse_mode=ParseMode.HTML 51 | ) 52 | 53 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 54 | if not user_data: 55 | await loading_message.edit( 56 | "Please First Login Using /login", 57 | parse_mode=ParseMode.HTML 58 | ) 59 | return 60 | 61 | sid = user_data["sid"] 62 | auth_token = user_data["auth_token"] 63 | 64 | url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/Messages.json?PageSize=10" 65 | headers = {"Authorization": get_auth_header(sid, auth_token)} 66 | 67 | async with aiohttp.ClientSession() as session: 68 | try: 69 | async with session.get(url, headers=headers) as resp: 70 | response_text = await resp.text() 71 | LOGGER.info(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Fetch OTP messages API response for user_id {user_id}: Status {resp.status}, Response {response_text}") 72 | if resp.status != 200: 73 | error_message = f"** Failed to fetch messages.**\nDetails: HTTP {resp.status}" 74 | try: 75 | error_data = await resp.json() 76 | error_message += f" - {error_data.get('message', 'Unknown error')}" 77 | except ValueError: 78 | error_message += f" - {response_text}" 79 | await loading_message.edit(error_message, parse_mode=ParseMode.MARKDOWN) 80 | return 81 | 82 | data = await resp.json() 83 | messages = data.get("messages", []) 84 | if not messages: 85 | await loading_message.edit( 86 | "**Sorry No messages found**", 87 | parse_mode=ParseMode.MARKDOWN 88 | ) 89 | return 90 | 91 | text = "**Latest OTP Messages:**\n\n" 92 | for msg in messages: 93 | if msg.get("direction") == "inbound": 94 | text += f"{msg.get('from')} -> {msg.get('body')}\n" 95 | 96 | await loading_message.edit( 97 | text or "**Sorry No OTPs found**", 98 | parse_mode=ParseMode.MARKDOWN 99 | ) 100 | except aiohttp.ClientError as e: 101 | LOGGER.error(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Network error during get_otp for user_id {user_id}: {str(e)}") 102 | await loading_message.edit( 103 | "** Network error while fetching OTPs. Please try again.**", 104 | parse_mode=ParseMode.MARKDOWN 105 | ) 106 | 107 | @Client.on_message((filters.command("get", prefixes=COMMAND_PREFIX) | filters.regex(r"^Get OTP💸$")) & filters.private & ~authorized_only()) 108 | async def unauthorized_access(client, message): 109 | await message.reply( 110 | "Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth", 111 | parse_mode=ParseMode.HTML, 112 | reply_markup=InlineKeyboardMarkup([ 113 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 114 | ]) 115 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌟 TᴡɪʟɪᴏSMSBᴏᴛ 💥 2 | 3 | A modern Telegram bot for managing Twilio phone numbers and retrieving OTPs, built with **Pʏᴛʜᴏɴ 3.8+** and **Pʏʀᴏɢʀᴀᴍ**. This bot allows users to purchase phone numbers, receive OTP messages, and manage their Twilio accounts directly through Telegram with a sleek and intuitive interface. ❄️ 4 | 5 | ![Pʏᴛʜᴏɴ](https://img.shields.io/badge/Python-3.8%2B-blue) ![Pʏʀᴏɢʀᴀᴍ](https://img.shields.io/badge/Pyrogram-2.0.106-orange) 6 | 7 | ## ✨《 Fᴇᴀᴛᴜʀᴇs 👀 》 8 | 9 | - **Uꜱᴇʀ Aᴜᴛʜᴇɴᴛɪᴄᴀᴛɪᴏɴ**: Secure login and logout with Twilio SID and Token. ⭐️ 10 | - **Nᴜᴍʙᴇʀ Mᴀɴᴀɢᴇᴍᴇɴᴛ**: Purchase, list, and delete phone numbers (Puerto Rico numbers supported). 💫 11 | - **OTP Rᴇᴛʀɪᴇᴠᴀʟ**: Fetch and display the latest OTP messages. 🌐 12 | - **Aᴅᴍɪɴ Cᴏɴᴛʀᴏʟꜱ**: Authorize/unauthorize users via commands. ✅ 13 | - **Cᴜꜱᴛᴏᴍ Nᴜᴍʙᴇʀ Sᴇʟᴇᴄᴛɪᴏɴ**: Choose numbers by area code for personalized purchases. 💀 14 | - **MᴏɴɢᴏDB Iɴᴛᴇɢʀᴀᴛɪᴏɴ**: Persistent storage for user data and numbers. ☠️ 15 | - **Mᴏᴅᴇʀɴ UI**: Markdown-based messages with inline keyboards for a seamless user experience. ⭐️ 16 | - **Eʀʀᴏʀ Hᴀɴᴅʟɪɴɢ**: Robust logging and user-friendly error messages. 💫 17 | - **Aꜱʏɴᴄʜʀᴏɴᴏᴜꜱ Dᴇꜱɪɢɴ**: Built with Pyrogram and aiohttp for high performance. ❄️ 18 | 19 | ## 🌟《 PʀᴇʀᴇQᴜɪꜱɪᴛᴇꜱ ✨ 》 20 | 21 | - **Pʏᴛʜᴏɴ 3.8+**: Ensure Python is installed. 22 | - **MᴏɴɢᴏDB**: A running MongoDB instance (local or cloud-based, e.g., MongoDB Atlas). 23 | - **Tᴇʟᴇɢʀᴀᴍ Bᴏᴛ**: A bot token from [BᴏᴛFᴀᴛʜᴇʀ](https://t.me/BotFather). 24 | - **Tᴡɪʟɪᴏ Aᴄᴄᴏᴜɴᴛ**: Twilio SID and Token for number purchasing and OTP retrieval. 25 | - **VPS (Oᴘᴛɪᴏɴᴀʟ)**: For deployment (e.g., Ubuntu 20.04+). 26 | 27 | ## 💥《 Sᴇᴛᴜᴘ Tᴜᴛᴏʀɪᴀʟ ❄️ 》 28 | 29 | ### 1. Cʟᴏɴᴇ ᴛʜᴇ Rᴇᴘᴏꜱɪᴛᴏʀʏ 30 | 31 | ```bash 32 | git clone https://github.com/TheSmartDevs/TwilioBestBot.git 33 | cd TwilioBestBot 34 | ``` 35 | 36 | ### 2. Iɴꜱᴛᴀʟʟ Dᴇᴘᴇɴᴅᴇɴᴄɪᴇꜱ 37 | 38 | Create a virtual environment and install the required packages: 39 | 40 | ```bash 41 | python3 -m venv venv 42 | source venv/bin/activate 43 | pip install -r requirements.txt 44 | ``` 45 | 46 | ### 3. Cᴏɴꜰɪɢᴜʀᴇ Eɴᴠɪʀᴏɴᴍᴇɴᴛ 47 | 48 | Edit `config.py` with your credentials: 49 | 50 | ```python 51 | API_ID = YourAPIID # From https://my.telegram.org 52 | API_HASH = "YourAPIHash" # From https://my.telegram.org 53 | BOT_TOKEN = "YourBotToken" # From BotFather 54 | ADMIN_IDS = [7303810912, 5991909954, 6249257243] # Your Telegram user IDs 55 | MONGO_URI = "Your_Mongo_Url" # MongoDB connection string 56 | ``` 57 | 58 | - **API_ID** and **API_HASH**: Obtain from [Tᴇʟᴇɢʀᴀᴍ'ꜱ API ᴘᴀɢᴇ](https://my.telegram.org). 59 | - **BOT_TOKEN**: Create a bot via [BᴏᴛFᴀᴛʜᴇʀ](https://t.me/BotFather). 60 | - **MONGO_URI**: Use MongoDB Atlas or a local MongoDB instance (`mongodb://localhost:27017`). 61 | - **ADMIN_IDS**: Add Telegram user IDs of admins who can authorize users. 62 | 63 | ### 4. Rᴜɴ Lᴏᴄᴀʟʟʏ 64 | 65 | Start the bot: 66 | 67 | ```bash 68 | python3 bot.py 69 | ``` 70 | 71 | The bot will log its status and respond to commands in Telegram. ✅ 72 | 73 | ## 💫《 Dᴇᴘʟᴏʏɪɴɢ ᴛᴏ ᴀ VPS ⭐️ 》 74 | 75 | ### 1. Sᴇᴛ Uᴘ VPS 76 | 77 | - Use a provider like DigitalOcean, AWS, or Linode. 78 | - Recommended: Ubuntu 20.04+ with at least 1GB RAM. 79 | 80 | ### 2. Iɴꜱᴛᴀʟʟ Dᴇᴘᴇɴᴅᴇɴᴄɪᴇꜱ ᴏɴ VPS 81 | 82 | ```bash 83 | sudo apt update 84 | sudo apt install python3 python3-venv python3-pip git screen mongodb -y 85 | ``` 86 | 87 | If using MongoDB Atlas, skip local MongoDB installation. 88 | 89 | ### 3. Cʟᴏɴᴇ ᴀɴᴅ Cᴏɴꜰɪɢᴜʀᴇ 90 | 91 | ```bash 92 | git clone https://github.com/TheSmartDevs/TwilioBestBot.git 93 | cd TwilioBestBot 94 | python3 -m venv venv 95 | source venv/bin/activate 96 | pip install -r requirements.txt 97 | ``` 98 | 99 | Copy and edit `config.py` as described in the setup section. 100 | 101 | ### 4. Rᴜɴ ᴡɪᴛʜ Sᴄʀᴇᴇɴ 102 | 103 | Use `screen` to keep the bot running in the background: 104 | 105 | ```bash 106 | screen -S twilio_bot 107 | source venv/bin/activate 108 | python3 bot.py 109 | ``` 110 | 111 | Detach from the screen session by pressing `Ctrl+A` then `D`. To reattach: 112 | 113 | ```bash 114 | screen -r twilio_bot 115 | ``` 116 | 117 | ## 🌟《 Uꜱᴀɢᴇ 💥 》 118 | 119 | ### Cᴏᴍᴍᴀɴᴅꜱ 120 | 121 | - `/start`: Welcome message with bot instructions. 122 | - `/login `: Log in to your Twilio account. 123 | - `/buy`: Purchase a phone number (supports custom area codes). 124 | - `/get`: Retrieve OTP messages. 125 | - `/del `: Delete a purchased number. 126 | - `/my`: List your active numbers. 127 | - `/logout`: Log out from your Twilio account. 128 | - `/auth `: (Admin only) Authorize a user. 129 | - `/unauth `: (Admin only) Unauthorize a user. 130 | 131 | ### ⭐️ Eᴜᴀᴍᴘʟᴇ Wᴏʀᴋꜰʟᴏᴡ 132 | 133 | 1. Start the bot: `/start` 134 | 2. Log in: `/login ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx YourTwilioToken` 135 | 3. Buy a number: `/buy` (follow prompts to select a number). 136 | 4. Get OTPs: `/get` 137 | 5. Delete a number: `/del ` 138 | 139 | 140 | ## 🌐《 Cᴏɴᴛʀɪʙᴜᴛɪɴɢ ✨ 》 141 | 142 | Contributions are welcome! Please: 143 | 144 | 1. Fork the repository. 145 | 2. Create a new branch (`git checkout -b feature/your-feature`). 146 | 3. Commit your changes (`git commit -m 'Add your feature'`). 147 | 4. Push to the branch (`git push origin feature/your-feature`). 148 | 5. Open a Pull Request. 149 | 150 | ## ⭐️《 Sᴜᴘᴘᴏʀᴛ 🌟 》 151 | 152 | For issues or questions, contact the developer: 153 | 154 | - Tᴇʟᴇɢʀᴀᴍ: [@TʜᴇSᴍᴀʀᴛDᴇᴠ](https://t.me/TheSmartDev) 155 | - GɪᴛHᴜʙ: [TʜᴇSᴍᴀʀᴛDᴇᴠꜱ](https://github.com/TheSmartDevs) 156 | 157 | Rᴇᴘᴏꜱɪᴛᴏʀʏ: [https://github.com/TheSmartDevs/TwilioBestBot](https://github.com/TheSmartDevs/TwilioBestBot) -------------------------------------------------------------------------------- /plugins/auth.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel: https://t.me/TheSmartDev 3 | from pyrogram import Client, filters 4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 5 | from pyrogram.errors import UserIdInvalid, UsernameInvalid, PeerIdInvalid 6 | from config import ADMIN_IDS, COMMAND_PREFIX 7 | from core import authusers_collection 8 | from utils import LOGGER 9 | import asyncio 10 | from datetime import datetime 11 | import re 12 | 13 | def sanitize(text): 14 | return re.sub(r'[^\w\s@.-]', '', text) 15 | 16 | PAGE_SIZE = 5 17 | 18 | def get_profile_link(user_id): 19 | return f"tg://user?id={user_id}" 20 | 21 | @Client.on_message(filters.command("auth", prefixes=COMMAND_PREFIX) & filters.private) 22 | async def auth_user(client, message): 23 | if message.from_user.id not in ADMIN_IDS: 24 | return await message.reply("**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**") 25 | 26 | args = message.text.split(maxsplit=1) 27 | if len(args) < 2: 28 | return await message.reply("**Please Provide A Username Or Userid To Auth ❌**") 29 | 30 | user_identifier = args[1].strip() 31 | try: 32 | user = await client.get_users(user_identifier) 33 | full_name = sanitize(f"{user.first_name or ''} {user.last_name or ''}").strip() 34 | username = user.username or "N/A" 35 | 36 | now = datetime.now() 37 | user_data = { 38 | "user_id": user.id, 39 | "first_name": user.first_name, 40 | "last_name": user.last_name, 41 | "username": username, 42 | "auth_time": now.strftime("%H:%M:%S"), 43 | "auth_date": now.strftime("%Y-%m-%d"), 44 | "auth_by_id": message.from_user.id, 45 | "auth_by_name": message.from_user.first_name 46 | } 47 | 48 | sent = await message.reply("**Authorizing User To Auth List✅**") 49 | await authusers_collection.update_one({"user_id": user.id}, {"$set": user_data}, upsert=True) 50 | await asyncio.sleep(1) 51 | await sent.edit(f"**Successfully Authorized User [{full_name}](tg://user?id={user.id}) ✅**") 52 | 53 | except (UserIdInvalid, UsernameInvalid, PeerIdInvalid, Exception) as e: 54 | LOGGER.error(f"Auth failed: {e}") 55 | await message.reply("** Sorry Failed To Authorize User ❌**") 56 | 57 | 58 | @Client.on_message(filters.command("unauth", prefixes=COMMAND_PREFIX) & filters.private) 59 | async def unauth_user(client, message): 60 | if message.from_user.id not in ADMIN_IDS: 61 | return await message.reply("**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**") 62 | 63 | args = message.text.split(maxsplit=1) 64 | if len(args) < 2: 65 | return await message.reply("**Please Provide A Username Or Userid To Unauth ❌**") 66 | 67 | user_identifier = args[1].strip() 68 | try: 69 | user = await client.get_users(user_identifier) 70 | full_name = sanitize(f"{user.first_name or ''} {user.last_name or ''}").strip() 71 | sent = await message.reply("**Demoting User To Auth List✅**") 72 | await authusers_collection.delete_one({"user_id": user.id}) 73 | await asyncio.sleep(1) 74 | await sent.edit(f"**Successfully Demoted User [{full_name}](tg://user?id={user.id}) ✅**") 75 | 76 | except (UserIdInvalid, UsernameInvalid, PeerIdInvalid, Exception) as e: 77 | LOGGER.error(f"Unauth failed: {e}") 78 | await message.reply("** Sorry Failed To Unauthorize User ❌**") 79 | 80 | 81 | @Client.on_message(filters.command("list", prefixes=COMMAND_PREFIX) & filters.private) 82 | async def list_auth_users(client, message): 83 | if message.from_user.id not in ADMIN_IDS: 84 | return await message.reply("**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**") 85 | 86 | page = 1 87 | await send_auth_list_page(client, message, page) 88 | 89 | 90 | @Client.on_message(filters.text & filters.regex(r"💸 Auth Users") & filters.private) 91 | async def list_auth_users_text(client, message): 92 | if message.from_user.id not in ADMIN_IDS: 93 | return await message.reply("**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**") 94 | 95 | page = 1 96 | await send_auth_list_page(client, message, page) 97 | 98 | 99 | async def send_auth_list_page(client, message, page): 100 | skip = (page - 1) * PAGE_SIZE 101 | users = await authusers_collection.find().skip(skip).limit(PAGE_SIZE).to_list(PAGE_SIZE) 102 | total_users = await authusers_collection.count_documents({}) 103 | 104 | if not users: 105 | return await message.reply("**No Authorized Users Found ❌**") 106 | 107 | text = "**Twilio Authorized Users List Below ✅**\n**━━━━━━━━━━━━━━━━━━━━**\n" 108 | for user in users: 109 | full_name = sanitize(f"{user.get('first_name', '')} {user.get('last_name', '')}").strip() 110 | text += f"**⊗ Full Name:** **{full_name}**\n" 111 | text += f"**⊗ Username:** @{user.get('username', 'N/A')}\n" 112 | text += f"**⊗ UserID:** `{user['user_id']}`\n" 113 | text += f"**⊗ Auth Time:** **{user.get('auth_time', 'N/A')}**\n" 114 | text += f"**⊗ Auth Date:** **{user.get('auth_date', 'N/A')}**\n" 115 | text += f"**⊗ Auth By:** [{user.get('auth_by_name')}](tg://user?id={user.get('auth_by_id')})\n" 116 | text += "**━━━━━━━━━━━━━━━━━━━━**\n" 117 | 118 | keyboard = [] 119 | if page > 1: 120 | keyboard.append(InlineKeyboardButton("Previous", callback_data=f"auth_prev_{page - 1}")) 121 | if skip + PAGE_SIZE < total_users: 122 | keyboard.append(InlineKeyboardButton("Next", callback_data=f"auth_next_{page + 1}")) 123 | 124 | reply_markup = InlineKeyboardMarkup([keyboard]) if keyboard else None 125 | await message.reply(text, reply_markup=reply_markup, disable_web_page_preview=True) 126 | 127 | 128 | @Client.on_callback_query(filters.regex(r"auth_(prev|next)_(\d+)") & filters.user(ADMIN_IDS)) 129 | async def paginate_auth_users(client, callback_query): 130 | _, direction, page = callback_query.data.split("_") 131 | page = int(page) 132 | try: 133 | await callback_query.message.edit("Loading Page...") 134 | await send_auth_list_page(client, callback_query.message, page) 135 | except Exception as e: 136 | LOGGER.error(f"Pagination failed: {e}") 137 | await callback_query.answer("Error loading page", show_alert=True) -------------------------------------------------------------------------------- /plugins/login.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel: https://t.me/TheSmartDev 3 | from pyrogram import Client, filters 4 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton 5 | from pyrogram.enums import ParseMode 6 | from config import ADMIN_IDS, COMMAND_PREFIX, DEVELOPER_ID 7 | from core import authusers_collection, logindb_collection 8 | from utils import LOGGER 9 | import asyncio 10 | import re 11 | from twilio.rest import Client as TwilioClient 12 | 13 | def clean_text(text): 14 | return re.sub(r"[^\w\s@.-]", "", text or "") 15 | 16 | # Store conversation state for each user 17 | user_states = {} 18 | 19 | async def is_authorized(user_id): 20 | if user_id in ADMIN_IDS: 21 | return True 22 | return await authusers_collection.find_one({"user_id": user_id}) is not None 23 | 24 | @Client.on_message(filters.command(["login"], prefixes=COMMAND_PREFIX) & filters.private) 25 | async def login_command(client: Client, message: Message): 26 | user_id = message.from_user.id 27 | if not await is_authorized(user_id): 28 | return await message.reply( 29 | "**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**", 30 | parse_mode=ParseMode.MARKDOWN, 31 | reply_markup=InlineKeyboardMarkup([ 32 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 33 | ]) 34 | ) 35 | 36 | # Split command arguments 37 | args = message.text.split(maxsplit=2)[1:] # Get arguments after /login 38 | if len(args) != 2: 39 | return await message.reply( 40 | "**Usage: /login {SID} {Auth Token} ❌**", 41 | parse_mode=ParseMode.MARKDOWN 42 | ) 43 | 44 | sid, auth_token = args 45 | sent = await message.reply( 46 | "**Logging To Twilio Account...**", 47 | parse_mode=ParseMode.MARKDOWN 48 | ) 49 | 50 | try: 51 | # Check if user is already logged in and log them out 52 | existing_user = await logindb_collection.find_one({"user_id": user_id}) 53 | if existing_user and existing_user.get("logged_in", False): 54 | await logindb_collection.update_one( 55 | {"user_id": user_id}, 56 | {"$set": {"logged_in": False}} 57 | ) 58 | LOGGER.info(f"User {user_id} automatically logged out from previous account.") 59 | 60 | # Attempt to log in with Twilio 61 | twilio_client = TwilioClient(sid, auth_token) 62 | # Verify credentials by fetching account details 63 | account = twilio_client.api.accounts(sid).fetch() 64 | 65 | # Get user info properly 66 | user_info = message.from_user 67 | full_name = clean_text(f"{user_info.first_name} {user_info.last_name or ''}".strip()) 68 | 69 | # Save login data to MongoDB 70 | await logindb_collection.update_one( 71 | {"user_id": user_id}, 72 | {"$set": { 73 | "user_id": user_id, 74 | "full_name": full_name, 75 | "sid": sid, 76 | "auth_token": auth_token, 77 | "logged_in": True 78 | }}, 79 | upsert=True 80 | ) 81 | 82 | LOGGER.info(f"User {user_id} logged in successfully.") 83 | await sent.edit( 84 | "**Successfully Logged In To Twilio Account ✅**", 85 | parse_mode=ParseMode.MARKDOWN 86 | ) 87 | 88 | except Exception as e: 89 | LOGGER.error(f"Login error for user {user_id}: {e}") 90 | await sent.edit( 91 | "**❌ Sorry Invalid Credentials Provided**", 92 | parse_mode=ParseMode.MARKDOWN 93 | ) 94 | 95 | @Client.on_message( 96 | filters.text & 97 | filters.private & 98 | filters.regex(r"^\🔐 Login$") # Only match exact text "🔐 Login" 99 | ) 100 | async def login_text_command(client: Client, message: Message): 101 | user_id = message.from_user.id 102 | if not await is_authorized(user_id): 103 | return await message.reply( 104 | "**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**", 105 | parse_mode=ParseMode.MARKDOWN, 106 | reply_markup=InlineKeyboardMarkup([ 107 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 108 | ]) 109 | ) 110 | 111 | user_states[user_id] = {"step": "awaiting_credentials"} 112 | return await message.reply( 113 | "**Now Please Provide Your SID and AUTH Token**", 114 | parse_mode=ParseMode.MARKDOWN, 115 | reply_markup=InlineKeyboardMarkup([ 116 | [InlineKeyboardButton("Cancel ❌", callback_data=f"cancel_login_{user_id}")] 117 | ]) 118 | ) 119 | 120 | @Client.on_message( 121 | filters.text & 122 | filters.private & 123 | ~filters.command([], prefixes=COMMAND_PREFIX) & # Exclude all commands 124 | ~filters.regex(r"^\🔐 Login$") & # Exclude "🔐 Login" text 125 | filters.create(lambda _, __, message: message.from_user.id in user_states) # Only for users in login state 126 | ) 127 | async def handle_text_input(client: Client, message: Message): 128 | user_id = message.from_user.id 129 | state = user_states[user_id] 130 | text = message.text.strip() 131 | 132 | if state["step"] == "awaiting_credentials": 133 | args = text.split(maxsplit=1) 134 | if len(args) != 2: 135 | return await message.reply( 136 | "**Please provide both SID and Auth Token separated by a space ❌**", 137 | parse_mode=ParseMode.MARKDOWN, 138 | reply_markup=InlineKeyboardMarkup([ 139 | [InlineKeyboardButton("Cancel ❌", callback_data=f"cancel_login_{user_id}")] 140 | ]) 141 | ) 142 | 143 | sid, auth_token = args 144 | sent = await message.reply( 145 | "**Logging To Twilio Account...**", 146 | parse_mode=ParseMode.MARKDOWN 147 | ) 148 | 149 | try: 150 | # Check if user is already logged in and log them out 151 | existing_user = await logindb_collection.find_one({"user_id": user_id}) 152 | if existing_user and existing_user.get("logged_in", False): 153 | await logindb_collection.update_one( 154 | {"user_id": user_id}, 155 | {"$set": {"logged_in": False}} 156 | ) 157 | LOGGER.info(f"User {user_id} automatically logged out from previous account.") 158 | 159 | # Attempt to log in with Twilio 160 | twilio_client = TwilioClient(sid, auth_token) 161 | # Verify credentials by fetching account details 162 | account = twilio_client.api.accounts(sid).fetch() 163 | 164 | # Get user info properly 165 | user_info = message.from_user 166 | full_name = clean_text(f"{user_info.first_name} {user_info.last_name or ''}".strip()) 167 | 168 | # Save login data to MongoDB 169 | await logindb_collection.update_one( 170 | {"user_id": user_id}, 171 | {"$set": { 172 | "user_id": user_id, 173 | "full_name": full_name, 174 | "sid": sid, 175 | "auth_token": auth_token, 176 | "logged_in": True 177 | }}, 178 | upsert=True 179 | ) 180 | 181 | LOGGER.info(f"User {user_id} logged in successfully.") 182 | # Clean up user state 183 | del user_states[user_id] 184 | 185 | await sent.edit( 186 | "**Successfully Logged In To Twilio Account ✅**", 187 | parse_mode=ParseMode.MARKDOWN 188 | ) 189 | 190 | except Exception as e: 191 | LOGGER.error(f"Login error for user {user_id}: {e}") 192 | # Clean up user state on error 193 | if user_id in user_states: 194 | del user_states[user_id] 195 | 196 | await sent.edit( 197 | "**❌ Sorry Invalid Credentials Provided**", 198 | parse_mode=ParseMode.MARKDOWN 199 | ) 200 | 201 | @Client.on_callback_query(filters.regex(r"cancel_login_\d+")) 202 | async def cancel_login(client: Client, query): 203 | user_id = query.from_user.id 204 | 205 | # Extract user_id from callback data for verification 206 | try: 207 | callback_user_id = int(query.data.split("_")[-1]) 208 | if user_id != callback_user_id: 209 | return await query.answer("Not authorized to cancel this process.", show_alert=True) 210 | except (ValueError, IndexError): 211 | return await query.answer("Invalid callback data.", show_alert=True) 212 | 213 | # Clean up user state 214 | if user_id in user_states: 215 | del user_states[user_id] 216 | 217 | await query.message.edit_text( 218 | "**❌ Process Cancelled! You Can Start Again With /login**", 219 | parse_mode=ParseMode.MARKDOWN 220 | ) 221 | await query.answer("Login process cancelled.") 222 | 223 | @Client.on_message(filters.command(["logout"], prefixes=COMMAND_PREFIX) & filters.private) 224 | async def logout_command(client: Client, message: Message): 225 | user_id = message.from_user.id 226 | 227 | if not await is_authorized(user_id): 228 | return await message.reply( 229 | "**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**", 230 | parse_mode=ParseMode.MARKDOWN, 231 | reply_markup=InlineKeyboardMarkup([ 232 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 233 | ]) 234 | ) 235 | 236 | try: 237 | # Check if user is logged in 238 | user_doc = await logindb_collection.find_one({"user_id": user_id}) 239 | if not user_doc or not user_doc.get("logged_in", False): 240 | LOGGER.info(f"Logout attempt for user {user_id}: No active login found.") 241 | return await message.reply( 242 | "**You are not logged in! Use /login to log in first. ❌**", 243 | parse_mode=ParseMode.MARKDOWN 244 | ) 245 | 246 | # Update the document to set logged_in to False 247 | result = await logindb_collection.update_one( 248 | {"user_id": user_id}, 249 | {"$set": {"logged_in": False}} 250 | ) 251 | 252 | LOGGER.info(f"Logout attempt for user {user_id}: modified_count={result.modified_count}") 253 | 254 | if result.modified_count > 0: 255 | LOGGER.info(f"User {user_id} logged out successfully.") 256 | 257 | # Also clean up any active login state 258 | if user_id in user_states: 259 | del user_states[user_id] 260 | 261 | return await message.reply( 262 | "**Successfully Logged Out From Account ✅**", 263 | parse_mode=ParseMode.MARKDOWN 264 | ) 265 | else: 266 | LOGGER.warning(f"Logout for user {user_id}: No document was modified.") 267 | return await message.reply( 268 | "**You were already logged out! ⚠️**", 269 | parse_mode=ParseMode.MARKDOWN 270 | ) 271 | 272 | except Exception as e: 273 | LOGGER.error(f"Logout error for user {user_id}: {e}") 274 | return await message.reply( 275 | "**Sorry Failed To Logout Due To Database Error ❌**", 276 | parse_mode=ParseMode.MARKDOWN 277 | ) -------------------------------------------------------------------------------- /plugins/info.py: -------------------------------------------------------------------------------- 1 | # Copyright @ISmartDevs 2 | # Channel t.me/TheSmartDev 3 | from datetime import datetime, timedelta 4 | from dateutil.relativedelta import relativedelta 5 | from pyrogram import Client, filters 6 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 7 | from pyrogram.enums import ParseMode, ChatType, UserStatus 8 | from pyrogram.errors import PeerIdInvalid, UsernameNotOccupied, ChannelInvalid 9 | from config import ADMIN_IDS, COMMAND_PREFIX, DEVELOPER_ID 10 | from core import authusers_collection 11 | from utils import LOGGER, get_dc_locations 12 | 13 | # Helper to check if user is authorized 14 | async def is_authorized_user(user_id): 15 | if user_id in ADMIN_IDS: 16 | return True 17 | user = await authusers_collection.find_one({"user_id": user_id}) 18 | return user is not None 19 | 20 | # Filter for authorized users 21 | def authorized_only(): 22 | async def func(flt, client, message): 23 | return await is_authorized_user(message.from_user.id) 24 | return filters.create(func) 25 | 26 | # Function to calculate account age accurately 27 | def calculate_account_age(creation_date): 28 | today = datetime.now() 29 | delta = relativedelta(today, creation_date) 30 | years = delta.years 31 | months = delta.months 32 | days = delta.days 33 | return f"{years} years, {months} months, {days} days" 34 | 35 | # Function to estimate account creation date based on user ID 36 | def estimate_account_creation_date(user_id): 37 | reference_points = [ 38 | (100000000, datetime(2013, 8, 1)), # Telegram's launch date 39 | (1273841502, datetime(2020, 8, 13)), # Example reference point 40 | (1500000000, datetime(2021, 5, 1)), # Another reference point 41 | (2000000000, datetime(2022, 12, 1)), # Another reference point 42 | ] 43 | 44 | closest_point = min(reference_points, key=lambda x: abs(x[0] - user_id)) 45 | closest_user_id, closest_date = closest_point 46 | id_difference = user_id - closest_user_id 47 | days_difference = id_difference / 20000000 # Assuming 20M user IDs per day 48 | creation_date = closest_date + timedelta(days=days_difference) 49 | return creation_date 50 | 51 | @Client.on_message(filters.command(["info", "id"], prefixes=COMMAND_PREFIX) & filters.private & authorized_only()) 52 | async def handle_info_command(client, message): 53 | LOGGER.info("Received /info or /id command") 54 | 55 | try: 56 | DC_LOCATIONS = get_dc_locations() 57 | progress_message = await client.send_message( 58 | message.chat.id, 59 | "Fetching Info From Database...", 60 | parse_mode=ParseMode.HTML 61 | ) 62 | 63 | # Handle different cases: current user, replied user/bot, or provided username 64 | if not message.command or (len(message.command) == 1 and not message.reply_to_message): 65 | LOGGER.info("Fetching current user info") 66 | user = message.from_user 67 | chat = message.chat 68 | await process_user_info(client, message, progress_message, user, chat, DC_LOCATIONS) 69 | 70 | elif message.reply_to_message: 71 | LOGGER.info("Fetching info of the replied user or bot") 72 | user = message.reply_to_message.from_user 73 | chat = message.chat 74 | await process_user_info(client, message, progress_message, user, chat, DC_LOCATIONS) 75 | 76 | elif len(message.command) > 1: 77 | username = message.command[1].strip('@').replace('https://', '').replace('http://', '').replace('t.me/', '').replace('/', '').replace(':', '') 78 | LOGGER.info(f"Fetching info for user, bot, or chat: {username}") 79 | 80 | try: 81 | # Try user or bot info 82 | user = await client.get_users(username) 83 | await process_user_info(client, message, progress_message, user, message.chat, DC_LOCATIONS) 84 | except (PeerIdInvalid, UsernameNotOccupied): 85 | LOGGER.info(f"Username '{username}' not found as a user/bot. Checking for chat...") 86 | try: 87 | chat = await client.get_chat(username) 88 | await process_chat_info(client, message, progress_message, chat, DC_LOCATIONS) 89 | except (ChannelInvalid, PeerIdInvalid): 90 | await client.edit_message_text( 91 | chat_id=message.chat.id, 92 | message_id=progress_message.id, 93 | text="Sorry Bro User Invalid❌", 94 | parse_mode=ParseMode.HTML 95 | ) 96 | LOGGER.error(f"Permission error for chat: {username}") 97 | except Exception as e: 98 | await client.edit_message_text( 99 | chat_id=message.chat.id, 100 | message_id=progress_message.id, 101 | text="Sorry Bro User Invalid❌", 102 | parse_mode=ParseMode.HTML 103 | ) 104 | LOGGER.error(f"Error fetching chat info: {str(e)}") 105 | except Exception as e: 106 | await client.edit_message_text( 107 | chat_id=message.chat.id, 108 | message_id=progress_message.id, 109 | text="Sorry Bro User Invalid❌", 110 | parse_mode=ParseMode.HTML 111 | ) 112 | LOGGER.error(f"Error fetching user/bot info: {str(e)}") 113 | 114 | except Exception as e: 115 | LOGGER.error(f"Unhandled exception: {str(e)}") 116 | await client.send_message( 117 | message.chat.id, 118 | "Sorry Bro User Invalid❌", 119 | parse_mode=ParseMode.HTML 120 | ) 121 | 122 | async def process_user_info(client, message, progress_message, user, chat, DC_LOCATIONS): 123 | premium_status = "Yes" if user.is_premium else "No" 124 | dc_location = DC_LOCATIONS.get(user.dc_id, "Unknown") 125 | account_created = estimate_account_creation_date(user.id) 126 | account_created_str = account_created.strftime("%B %d, %Y") 127 | account_age = calculate_account_age(account_created) 128 | verified_status = "Yes" if getattr(user, 'is_verified', False) else "No" 129 | 130 | # Check authorization status 131 | authorized_status = "Yes" if await is_authorized_user(user.id) else "No" 132 | 133 | status = "Unknown" 134 | if user.status: 135 | if user.status == UserStatus.ONLINE: 136 | status = "Online" 137 | elif user.status == UserStatus.OFFLINE: 138 | status = "Offline" 139 | elif user.status == UserStatus.RECENTLY: 140 | status = "Recently online" 141 | elif user.status == UserStatus.LAST_WEEK: 142 | status = "Last seen within week" 143 | elif user.status == UserStatus.LAST_MONTH: 144 | status = "Last seen within month" 145 | 146 | response = ( 147 | "Smart Twilio User Info From Database✅\n" 148 | "━━━━━━━━━━━━━━━━━━━\n" 149 | f"⊗ Full Name: {user.first_name} {user.last_name or ''}\n" 150 | f"⊗ ID: {user.id}\n" 151 | f"⊗ Chat ID: {chat.id}\n" 152 | f"⊗ Data Center: {user.dc_id} ({dc_location})\n" 153 | f"⊗ Premium: {premium_status}\n" 154 | f"⊗ Verification: {verified_status}\n" 155 | f"⊗ Authorized: {authorized_status}\n" 156 | f"⊗ Flags: {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else 'Clean'}\n" 157 | f"⊗ Status: {status}\n" 158 | f"⊗ Account Created On: {account_created_str}\n" 159 | f"⊗ Account Age: {account_age}\n" 160 | "━━━━━━━━━━━━━━━━━━━\n" 161 | "Smart Twilio User Info Check ➺ Successful✅" 162 | ) 163 | 164 | buttons = [[InlineKeyboardButton("Profile Info", user_id=user.id)]] 165 | if user.is_bot: 166 | response = ( 167 | "Smart Twilio Bot Info From Database✅\n" 168 | "━━━━━━━━━━━━━━━━━━━\n" 169 | f"⊗ Bot Name: {user.first_name} {user.last_name or ''}\n" 170 | f"⊗ ID: {user.id}\n" 171 | f"⊗ Chat ID: {chat.id}\n" 172 | f"⊗ Data Center: {user.dc_id} ({dc_location})\n" 173 | f"⊗ Premium: {premium_status}\n" 174 | f"⊗ Verification: {verified_status}\n" 175 | f"⊗ Authorized: {authorized_status}\n" 176 | f"⊗ Flags: {'Scam' if getattr(user, 'is_scam', False) else 'Fake' if getattr(user, 'is_fake', False) else 'Clean'}\n" 177 | f"⊗ Status: Bot\n" 178 | f"⊗ Account Created On: {account_created_str}\n" 179 | f"⊗ Account Age: {account_age}\n" 180 | "━━━━━━━━━━━━━━━━━━━\n" 181 | "Smart Twilio Bot Info Check ➺ Successful✅" 182 | ) 183 | 184 | await client.edit_message_text( 185 | chat_id=message.chat.id, 186 | message_id=progress_message.id, 187 | text=response, 188 | parse_mode=ParseMode.HTML, 189 | reply_markup=InlineKeyboardMarkup(buttons) 190 | ) 191 | LOGGER.info("User/bot info fetched successfully") 192 | 193 | async def process_chat_info(client, message, progress_message, chat, DC_LOCATIONS): 194 | chat_type = "Supergroup" if chat.type == ChatType.SUPERGROUP else "Group" if chat.type == ChatType.GROUP else "Channel" 195 | response = ( 196 | "Smart Twilio Chat Info From Database✅\n" 197 | "━━━━━━━━━━━━━━━━━━━\n" 198 | f"⊗ Chat Name: {chat.title}\n" 199 | f"⊗ ID: {chat.id}\n" 200 | f"⊗ Type: {chat_type}\n" 201 | f"⊗ Member count: {chat.members_count if chat.members_count else 'Unknown'}\n" 202 | "━━━━━━━━━━━━━━━━━━━\n" 203 | "Smart Twilio Chat Info Check ➺ Successful✅" 204 | ) 205 | 206 | buttons = [[InlineKeyboardButton("Joining Link", url=f"https://t.me/{chat.username}" if chat.username else f"https://t.me/c/{str(chat.id).replace('-100', '')}/1")]] 207 | await client.edit_message_text( 208 | chat_id=message.chat.id, 209 | message_id=progress_message.id, 210 | text=response, 211 | parse_mode=ParseMode.HTML, 212 | reply_markup=InlineKeyboardMarkup(buttons) 213 | ) 214 | LOGGER.info("Chat info fetched successfully") 215 | 216 | @Client.on_message(filters.command(["info", "id"], prefixes=COMMAND_PREFIX) & filters.private & ~authorized_only()) 217 | async def unauthorized_access(client, message): 218 | await message.reply( 219 | "Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth", 220 | parse_mode=ParseMode.HTML, 221 | reply_markup=InlineKeyboardMarkup([ 222 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 223 | ]) 224 | ) 225 | LOGGER.info("Unauthorized access attempt for /info command") 226 | 227 | @Client.on_message(filters.text & filters.private & filters.regex("^👤 Account$") & authorized_only()) 228 | async def handle_account_text(client, message): 229 | LOGGER.info("Received '👤 Account' text from authorized user") 230 | 231 | try: 232 | DC_LOCATIONS = get_dc_locations() 233 | progress_message = await client.send_message( 234 | message.chat.id, 235 | "Fetching Info From Database...", 236 | parse_mode=ParseMode.HTML 237 | ) 238 | 239 | user = message.from_user 240 | chat = message.chat 241 | await process_user_info(client, message, progress_message, user, chat, DC_LOCATIONS) 242 | 243 | except Exception as e: 244 | LOGGER.error(f"Error in handle_account_text: {str(e)}") 245 | await client.send_message( 246 | message.chat.id, 247 | "Sorry Bro User Invalid❌", 248 | parse_mode=ParseMode.HTML 249 | ) 250 | 251 | @Client.on_message(filters.text & filters.private & filters.regex("^👤 Account$") & ~authorized_only()) 252 | async def handle_account_text_unauthorized(client, message): 253 | LOGGER.info("Received '👤 Account' text from unauthorized user") 254 | 255 | await message.reply( 256 | "Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth", 257 | parse_mode=ParseMode.HTML, 258 | reply_markup=InlineKeyboardMarkup([ 259 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 260 | ]) 261 | ) -------------------------------------------------------------------------------- /plugins/buy.py: -------------------------------------------------------------------------------- 1 | #Copyright @ISmartCoder 2 | #Updates Channel https://t.me/TheSmartDev 3 | 4 | from pyrogram import Client, filters 5 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 6 | from pyrogram.enums import ParseMode 7 | from config import ADMIN_IDS, COMMAND_PREFIX, DEVELOPER_ID 8 | from core import authusers_collection, logindb_collection, numbersdb_collection 9 | from utils import LOGGER 10 | import aiohttp 11 | import base64 12 | from urllib.parse import quote 13 | import asyncio 14 | from datetime import datetime 15 | import re 16 | 17 | custom_area_states = {} 18 | 19 | async def is_authorized_user(user_id): 20 | if user_id in ADMIN_IDS: 21 | return True 22 | user = await authusers_collection.find_one({"user_id": user_id}) 23 | return user is not None 24 | 25 | def authorized_only(): 26 | async def func(flt, client, message): 27 | return await is_authorized_user(message.from_user.id) 28 | return filters.create(func) 29 | 30 | async def is_logged_in(user_id): 31 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 32 | return user_data is not None 33 | 34 | def get_auth_header(sid, auth_token): 35 | auth_str = f"{sid}:{auth_token}" 36 | auth_b64 = base64.b64encode(auth_str.encode()).decode() 37 | return f"Basic {auth_b64}" 38 | 39 | async def cleanup_old_account_numbers(user_id): 40 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 41 | if not user_data: 42 | return 43 | 44 | current_sid = user_data["sid"] 45 | 46 | result = await numbersdb_collection.delete_many({ 47 | "user_id": user_id, 48 | "account_sid": {"$ne": current_sid} 49 | }) 50 | if result.deleted_count > 0: 51 | LOGGER.info(f"Removed {result.deleted_count} numbers from old accounts for user {user_id}") 52 | 53 | @Client.on_message(filters.command("buy", prefixes=COMMAND_PREFIX) & filters.private & authorized_only()) 54 | async def buy_command(client, message): 55 | user_id = message.from_user.id 56 | 57 | if not await is_logged_in(user_id): 58 | await message.reply( 59 | "**Please First Login Using /login**", 60 | reply_markup=InlineKeyboardMarkup([ 61 | [InlineKeyboardButton("Developer", user_id=DEVELOPER_ID)] 62 | ]) 63 | ) 64 | return 65 | 66 | await cleanup_old_account_numbers(user_id) 67 | 68 | await message.reply( 69 | "🛒 **Choose A Country Below:**", 70 | parse_mode=ParseMode.MARKDOWN, 71 | reply_markup=InlineKeyboardMarkup([ 72 | [ 73 | InlineKeyboardButton("🇺🇸 United States", callback_data="country_us"), 74 | InlineKeyboardButton("🇵🇷 Puerto Rico", callback_data="country_pr") 75 | ], 76 | [ 77 | InlineKeyboardButton("🇨🇦 Canada", callback_data="country_ca"), 78 | InlineKeyboardButton("❌ Close", callback_data="close_menu") 79 | ], 80 | [ 81 | InlineKeyboardButton("Custom Area Code 💸", callback_data="custom_area_code") 82 | ] 83 | ]) 84 | ) 85 | 86 | @Client.on_message(filters.text & filters.regex(r"🔍 Search Number") & filters.private & authorized_only()) 87 | async def search_number_text(client, message): 88 | user_id = message.from_user.id 89 | 90 | if not await is_logged_in(user_id): 91 | await message.reply( 92 | "**Please First Login Using /login**", 93 | reply_markup=InlineKeyboardMarkup([ 94 | [InlineKeyboardButton("Developer", user_id=DEVELOPER_ID)] 95 | ]) 96 | ) 97 | return 98 | 99 | await cleanup_old_account_numbers(user_id) 100 | 101 | await message.reply( 102 | "🛒 **Choose A Country Below:**", 103 | parse_mode=ParseMode.MARKDOWN, 104 | reply_markup=InlineKeyboardMarkup([ 105 | [ 106 | InlineKeyboardButton("🇺🇸 United States", callback_data="country_us"), 107 | InlineKeyboardButton("🇵🇷 Puerto Rico", callback_data="country_pr") 108 | ], 109 | [ 110 | InlineKeyboardButton("🇨🇦 Canada", callback_data="country_ca"), 111 | InlineKeyboardButton("❌ Close", callback_data="close_menu") 112 | ], 113 | [ 114 | InlineKeyboardButton("Custom Area Code 💸", callback_data="custom_area_code") 115 | ] 116 | ]) 117 | ) 118 | 119 | @Client.on_message(filters.text & filters.regex(r"💰 Custom Area Code") & filters.private & authorized_only()) 120 | async def custom_area_code_text(client, message): 121 | user_id = message.from_user.id 122 | 123 | # Check if user is logged in 124 | if not await is_logged_in(user_id): 125 | await message.reply( 126 | "**Please First Login Using /login**", 127 | reply_markup=InlineKeyboardMarkup([ 128 | [InlineKeyboardButton("Developer", user_id=DEVELOPER_ID)] 129 | ]) 130 | ) 131 | return 132 | 133 | # Clean up numbers from old accounts 134 | await cleanup_old_account_numbers(user_id) 135 | 136 | await message.reply( 137 | "**Please Choose The Country For Custom Numbers**", 138 | parse_mode=ParseMode.MARKDOWN, 139 | reply_markup=InlineKeyboardMarkup([ 140 | [ 141 | InlineKeyboardButton("🇺🇸 United States", callback_data="custom_country_us"), 142 | InlineKeyboardButton("🇵🇷 Puerto Rico", callback_data="custom_country_pr") 143 | ], 144 | [ 145 | InlineKeyboardButton("🇨🇦 Canada", callback_data="custom_country_ca"), 146 | InlineKeyboardButton("❌ Close", callback_data="close_menu") 147 | ] 148 | ]) 149 | ) 150 | 151 | @Client.on_message(filters.text & filters.regex(r"Delete Number 🗑") & filters.private & authorized_only()) 152 | async def delete_number_text(client, message): 153 | user_id = message.from_user.id 154 | 155 | if not await is_logged_in(user_id): 156 | await message.reply( 157 | "**Please First Login Using /login**", 158 | reply_markup=InlineKeyboardMarkup([ 159 | [InlineKeyboardButton("Developer", user_id=DEVELOPER_ID)] 160 | ]) 161 | ) 162 | return 163 | 164 | await cleanup_old_account_numbers(user_id) 165 | 166 | numbers = await numbersdb_collection.find({"user_id": user_id}).to_list(None) 167 | 168 | if not numbers: 169 | await message.reply("**Sorry Bro No Number Purchased**", parse_mode=ParseMode.MARKDOWN) 170 | return 171 | 172 | buttons = [] 173 | for i, number in enumerate(numbers): 174 | if i % 2 == 0: 175 | buttons.append([]) 176 | buttons[-1].append(InlineKeyboardButton( 177 | number["phone_number"], 178 | callback_data=f"del_{quote(number['phone_number'])}" 179 | )) 180 | 181 | await message.reply( 182 | "**Please Select A Number To Delete 👇**", 183 | parse_mode=ParseMode.MARKDOWN, 184 | reply_markup=InlineKeyboardMarkup(buttons) 185 | ) 186 | 187 | @Client.on_message(filters.text & filters.regex(r"🔍 Search Number|💰 Custom Area Code|Delete Number 🗑") & filters.private & ~authorized_only()) 188 | async def unauthorized_text_access(client, message): 189 | await message.reply( 190 | "**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**", 191 | parse_mode=ParseMode.MARKDOWN, 192 | reply_markup=InlineKeyboardMarkup([ 193 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 194 | ]) 195 | ) 196 | 197 | @Client.on_callback_query(filters.regex("custom_area_code")) 198 | async def custom_area_code_handler(client, callback_query): 199 | user_id = callback_query.from_user.id 200 | 201 | if not await is_logged_in(user_id): 202 | await callback_query.message.edit("**Please First Login Using /login**") 203 | return 204 | 205 | await cleanup_old_account_numbers(user_id) 206 | 207 | await callback_query.message.edit( 208 | "**Please Choose The Country For Custom Numbers**", 209 | parse_mode=ParseMode.MARKDOWN, 210 | reply_markup=InlineKeyboardMarkup([ 211 | [ 212 | InlineKeyboardButton("🇺🇸 United States", callback_data="custom_country_us"), 213 | InlineKeyboardButton("🇵🇷 Puerto Rico", callback_data="custom_country_pr") 214 | ], 215 | [ 216 | InlineKeyboardButton("🇨🇦 Canada", callback_data="custom_country_ca"), 217 | InlineKeyboardButton("❌ Close", callback_data="close_menu") 218 | ] 219 | ]) 220 | ) 221 | 222 | @Client.on_callback_query(filters.regex(r"custom_country_(us|pr|ca)")) 223 | async def custom_country_handler(client, callback_query): 224 | user_id = callback_query.from_user.id 225 | country_code = callback_query.data.split("_")[2].upper() 226 | country_map = {"US": "United States 🇺🇸", "PR": "Puerto Rico 🇵🇷", "CA": "Canada 🇨🇦"} 227 | country_name = country_map[country_code] 228 | 229 | custom_area_states[user_id] = { 230 | "step": "awaiting_digit_selection", 231 | "country_code": country_code, 232 | "country_name": country_name 233 | } 234 | 235 | await callback_query.message.edit( 236 | "**Please Select The Number Of Area Code Digits**", 237 | parse_mode=ParseMode.MARKDOWN, 238 | reply_markup=InlineKeyboardMarkup([ 239 | [ 240 | InlineKeyboardButton("3 Digit", callback_data="digit_3"), 241 | InlineKeyboardButton("6 Digit", callback_data="digit_6") 242 | ], 243 | [ 244 | InlineKeyboardButton("❌ Cancel", callback_data=f"cancel_custom_{user_id}") 245 | ] 246 | ]) 247 | ) 248 | 249 | @Client.on_callback_query(filters.regex(r"digit_(3|6)")) 250 | async def digit_selection_handler(client, callback_query): 251 | user_id = callback_query.from_user.id 252 | digit_count = callback_query.data.split("_")[1] 253 | 254 | if user_id not in custom_area_states: 255 | await callback_query.message.edit("**Session expired. Please start again.**") 256 | return 257 | 258 | custom_area_states[user_id]["step"] = "awaiting_area_code" 259 | custom_area_states[user_id]["digit_count"] = int(digit_count) 260 | 261 | await callback_query.message.edit( 262 | f"**Please Send The {digit_count} Digit Area Code**", 263 | parse_mode=ParseMode.MARKDOWN, 264 | reply_markup=InlineKeyboardMarkup([ 265 | [InlineKeyboardButton("❌ Cancel", callback_data=f"cancel_custom_{user_id}")] 266 | ]) 267 | ) 268 | 269 | @Client.on_callback_query(filters.regex(r"cancel_custom_\d+")) 270 | async def cancel_custom_area_code(client, callback_query): 271 | user_id = callback_query.from_user.id 272 | 273 | try: 274 | callback_user_id = int(callback_query.data.split("_")[-1]) 275 | if user_id != callback_user_id: 276 | return await callback_query.answer("Not authorized to cancel this process.", show_alert=True) 277 | except (ValueError, IndexError): 278 | return await callback_query.answer("Invalid callback data.", show_alert=True) 279 | 280 | # Clean up user state 281 | if user_id in custom_area_states: 282 | del custom_area_states[user_id] 283 | 284 | await callback_query.message.edit( 285 | "**Custom Area Code Number Purchase Cancelled**", 286 | parse_mode=ParseMode.MARKDOWN 287 | ) 288 | await callback_query.answer("Process cancelled.") 289 | 290 | @Client.on_message( 291 | filters.text & 292 | filters.private & 293 | ~filters.command([], prefixes=COMMAND_PREFIX) & 294 | filters.create(lambda _, __, message: message.from_user.id in custom_area_states) 295 | ) 296 | async def handle_area_code_input(client, message): 297 | user_id = message.from_user.id 298 | state = custom_area_states[user_id] 299 | text = message.text.strip() 300 | 301 | if state["step"] == "awaiting_area_code": 302 | digit_count = state["digit_count"] 303 | 304 | if not re.match(rf'^\d{{{digit_count}}}$', text): 305 | await message.reply( 306 | f"**Please Provide A Valid {digit_count}-Digit Integer**", 307 | parse_mode=ParseMode.MARKDOWN, 308 | reply_markup=InlineKeyboardMarkup([ 309 | [InlineKeyboardButton("❌ Cancel", callback_data=f"cancel_custom_{user_id}")] 310 | ]) 311 | ) 312 | return 313 | 314 | area_code = text 315 | country_code = state["country_code"] 316 | country_name = state["country_name"] 317 | 318 | 319 | del custom_area_states[user_id] 320 | 321 | 322 | sent = await message.reply( 323 | f"**Fetching Numbers With {digit_count}-Digit Area Code {area_code} For {country_name}**", 324 | parse_mode=ParseMode.MARKDOWN 325 | ) 326 | 327 | 328 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 329 | if not user_data: 330 | await sent.edit("**Please First Login Using /login**") 331 | return 332 | 333 | 334 | sid = user_data["sid"] 335 | auth_token = user_data["auth_token"] 336 | headers = {"Authorization": get_auth_header(sid, auth_token)} 337 | base_url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/AvailablePhoneNumbers/{country_code}/Local.json?PageSize=50" 338 | 339 | 340 | if digit_count == 3 and country_code in ["US", "CA"]: 341 | url = f"{base_url}&AreaCode={area_code}" 342 | else: 343 | url = f"{base_url}&Contains={area_code}*" 344 | 345 | try: 346 | async with aiohttp.ClientSession() as session: 347 | async with session.get(url, headers=headers) as response: 348 | if response.status != 200: 349 | 350 | if digit_count == 3 and country_code in ["US", "CA"]: 351 | url = f"{base_url}&Contains={area_code}*" 352 | async with session.get(url, headers=headers) as fallback_response: 353 | if fallback_response.status != 200: 354 | raise Exception(f"API error: {fallback_response.status}") 355 | data = await fallback_response.json() 356 | else: 357 | raise Exception(f"API error: {response.status}") 358 | else: 359 | data = await response.json() 360 | 361 | matching_numbers = data.get("available_phone_numbers", []) 362 | 363 | if not matching_numbers: 364 | await sent.edit( 365 | f"**No Available {country_name} Numbers Found With {digit_count}-Digit Area Code {area_code} ❌**", 366 | parse_mode=ParseMode.MARKDOWN 367 | ) 368 | return 369 | 370 | 371 | text = f"**Available {country_name} Numbers With {digit_count}-Digit Area Code {area_code} ✅**\n**━━━━━━━━━━━━━━━━━━━━**\n" 372 | for i, number in enumerate(matching_numbers, 1): 373 | text += f"{number['phone_number']}\n" 374 | text += "**━━━━━━━━━━━━━━━━━━━━**\n**Select a number to purchase:👇**" 375 | 376 | buttons = [] 377 | for i, number in enumerate(matching_numbers): 378 | if i % 2 == 0: 379 | buttons.append([]) 380 | buttons[-1].append(InlineKeyboardButton( 381 | number["phone_number"], 382 | callback_data=f"buy_{country_code}_{quote(number['phone_number'])}" 383 | )) 384 | 385 | await sent.edit( 386 | text, 387 | parse_mode=ParseMode.MARKDOWN, 388 | reply_markup=InlineKeyboardMarkup(buttons) 389 | ) 390 | 391 | except Exception as e: 392 | LOGGER.error(f"Failed to fetch custom area code numbers for {user_id}: {e}") 393 | await sent.edit( 394 | f"**Failed to Fetch {country_name} Numbers With {digit_count}-Digit Area Code {area_code} ❌**", 395 | parse_mode=ParseMode.MARKDOWN 396 | ) 397 | 398 | @Client.on_callback_query(filters.regex(r"country_(us|pr|ca)")) 399 | async def fetch_numbers(client, callback_query): 400 | user_id = callback_query.from_user.id 401 | country_code = callback_query.data.split("_")[1].upper() 402 | country_map = {"US": "United States 🇺🇸", "PR": "Puerto Rico 🇵🇷", "CA": "Canada 🇨🇦"} 403 | country_name = country_map[country_code] 404 | 405 | 406 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 407 | if not user_data: 408 | await callback_query.message.edit("**Please First Login Using /login**") 409 | return 410 | 411 | 412 | await cleanup_old_account_numbers(user_id) 413 | 414 | 415 | await callback_query.message.edit(f"**Fetching Numbers For Country {country_name}**", parse_mode=ParseMode.MARKDOWN) 416 | 417 | 418 | sid = user_data["sid"] 419 | auth_token = user_data["auth_token"] 420 | url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/AvailablePhoneNumbers/{country_code}/Local.json?PageSize=50" 421 | headers = {"Authorization": get_auth_header(sid, auth_token)} 422 | 423 | try: 424 | async with aiohttp.ClientSession() as session: 425 | async with session.get(url, headers=headers) as response: 426 | if response.status != 200: 427 | raise Exception(f"API error: {response.status}") 428 | data = await response.json() 429 | numbers = data.get("available_phone_numbers", []) 430 | 431 | if not numbers: 432 | await callback_query.message.edit(f"**No Available {country_name} Numbers Found ❌**", parse_mode=ParseMode.MARKDOWN) 433 | return 434 | 435 | 436 | text = f"**Available {country_name} Numbers ✅**\n**━━━━━━━━━━━━━━━━━━━━**\n" 437 | for i, number in enumerate(numbers, 1): 438 | text += f"{number['phone_number']}\n" 439 | text += "**━━━━━━━━━━━━━━━━━━━━**\n**Select a number to purchase:👇**" 440 | 441 | buttons = [] 442 | for i, number in enumerate(numbers): 443 | if i % 2 == 0: 444 | buttons.append([]) 445 | buttons[-1].append(InlineKeyboardButton( 446 | number["phone_number"], 447 | callback_data=f"buy_{country_code}_{quote(number['phone_number'])}" 448 | )) 449 | 450 | await callback_query.message.edit( 451 | text, 452 | parse_mode=ParseMode.MARKDOWN, 453 | reply_markup=InlineKeyboardMarkup(buttons) 454 | ) 455 | 456 | except Exception as e: 457 | LOGGER.error(f"Failed to fetch numbers for {user_id}: {e}") 458 | await callback_query.message.edit(f"**Failed to Fetch {country_name} Numbers ❌**", parse_mode=ParseMode.MARKDOWN) 459 | 460 | @Client.on_callback_query(filters.regex(r"buy_")) 461 | async def purchase_number(client, callback_query): 462 | user_id = callback_query.from_user.id 463 | _, country_code, phone_number = callback_query.data.split("_", 2) 464 | phone_number = phone_number.replace("%2B", "+") 465 | 466 | 467 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 468 | if not user_data: 469 | await callback_query.message.reply("**Please First Login Using /login**", parse_mode=ParseMode.MARKDOWN) 470 | return 471 | 472 | 473 | sid = user_data["sid"] 474 | auth_token = user_data["auth_token"] 475 | url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/IncomingPhoneNumbers.json" 476 | headers = { 477 | "Authorization": get_auth_header(sid, auth_token), 478 | "Content-Type": "application/x-www-form-urlencoded" 479 | } 480 | data = { 481 | "PhoneNumber": phone_number, 482 | "SmsEnabled": "true" 483 | } 484 | 485 | try: 486 | async with aiohttp.ClientSession() as session: 487 | async with session.post(url, headers=headers, data=data) as response: 488 | if response.status != 201: 489 | raise Exception(f"Purchase failed: {response.status}") 490 | purchase_data = await response.json() 491 | phone_sid = purchase_data.get("sid") 492 | 493 | 494 | await numbersdb_collection.insert_one({ 495 | "user_id": user_id, 496 | "phone_number": phone_number, 497 | "phone_sid": phone_sid, 498 | "account_sid": sid, 499 | "country_code": country_code, 500 | "purchase_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S") 501 | }) 502 | 503 | 504 | await callback_query.message.reply( 505 | f"**Successfully Purchased Number {phone_number} ✅**", 506 | parse_mode=ParseMode.MARKDOWN 507 | ) 508 | 509 | except Exception as e: 510 | LOGGER.error(f"Failed to purchase number {phone_number} for {user_id}: {e}") 511 | await callback_query.message.reply( 512 | "**Failed To Purchase Number ❌**", 513 | parse_mode=ParseMode.MARKDOWN 514 | ) 515 | 516 | @Client.on_callback_query(filters.regex("close_menu")) 517 | async def close_menu(client, callback_query): 518 | await callback_query.message.delete() 519 | 520 | @Client.on_message(filters.command("my", prefixes=COMMAND_PREFIX) & filters.private & authorized_only()) 521 | async def my_numbers(client, message): 522 | user_id = message.from_user.id 523 | 524 | 525 | await cleanup_old_account_numbers(user_id) 526 | 527 | 528 | numbers = await numbersdb_collection.find({"user_id": user_id}).to_list(None) 529 | 530 | if not numbers: 531 | await message.reply("**Sorry Bro No Number Purchased**", parse_mode=ParseMode.MARKDOWN) 532 | return 533 | 534 | text = "**Your Purchased Numbers ✅**\n**━━━━━━━━━━━━━━━━━━━━**\n" 535 | for i, number in enumerate(numbers, 1): 536 | text += f"**{i}.** {number['phone_number']} ({number['country_code']})\n" 537 | text += f"**Purchased:** {number['purchase_time']}\n" 538 | text += "**━━━━━━━━━━━━━━━━━━━━**\n" 539 | 540 | await message.reply(text, parse_mode=ParseMode.MARKDOWN) 541 | 542 | @Client.on_message(filters.command("del", prefixes=COMMAND_PREFIX) & filters.private & authorized_only()) 543 | async def delete_number_command(client, message): 544 | user_id = message.from_user.id 545 | 546 | 547 | await cleanup_old_account_numbers(user_id) 548 | 549 | 550 | numbers = await numbersdb_collection.find({"user_id": user_id}).to_list(None) 551 | 552 | if not numbers: 553 | await message.reply("**Sorry Bro No Number Purchased**", parse_mode=ParseMode.MARKDOWN) 554 | return 555 | 556 | 557 | buttons = [] 558 | for i, number in enumerate(numbers): 559 | if i % 2 == 0: 560 | buttons.append([]) 561 | buttons[-1].append(InlineKeyboardButton( 562 | number["phone_number"], 563 | callback_data=f"del_{quote(number['phone_number'])}" 564 | )) 565 | 566 | await message.reply( 567 | "**Please Select A Number To Delete 👇**", 568 | parse_mode=ParseMode.MARKDOWN, 569 | reply_markup=InlineKeyboardMarkup(buttons) 570 | ) 571 | 572 | @Client.on_callback_query(filters.regex(r"del_")) 573 | async def delete_number(client, callback_query): 574 | user_id = callback_query.from_user.id 575 | phone_number = callback_query.data.split("_", 1)[1].replace("%2B", "+") 576 | 577 | 578 | user_data = await logindb_collection.find_one({"user_id": user_id, "logged_in": True}) 579 | if not user_data: 580 | await callback_query.message.edit("**Please First Login Using /login**") 581 | return 582 | 583 | 584 | number_data = await numbersdb_collection.find_one({"user_id": user_id, "phone_number": phone_number}) 585 | if not number_data: 586 | await callback_query.message.edit("**Number Not Found ❌**", parse_mode=ParseMode.MARKDOWN) 587 | return 588 | 589 | 590 | sent = await callback_query.message.edit(f"**Deleting Number {phone_number}**", parse_mode=ParseMode.MARKDOWN) 591 | 592 | 593 | sid = user_data["sid"] 594 | auth_token = user_data["auth_token"] 595 | url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/IncomingPhoneNumbers.json" 596 | headers = {"Authorization": get_auth_header(sid, auth_token)} 597 | 598 | try: 599 | async with aiohttp.ClientSession() as session: 600 | async with session.get(url, headers=headers) as response: 601 | if response.status != 200: 602 | raise Exception(f"Failed to fetch numbers: {response.status}") 603 | data = await response.json() 604 | phone_sid = None 605 | for record in data.get("incoming_phone_numbers", []): 606 | if record["phone_number"] == phone_number: 607 | phone_sid = record["sid"] 608 | break 609 | 610 | if not phone_sid: 611 | raise Exception("Phone SID not found") 612 | 613 | 614 | delete_url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/IncomingPhoneNumbers/{phone_sid}.json" 615 | async with session.delete(delete_url, headers=headers) as delete_response: 616 | if delete_response.status != 204: 617 | raise Exception(f"Failed to delete number: {delete_response.status}") 618 | 619 | await numbersdb_collection.delete_one({"user_id": user_id, "phone_number": phone_number}) 620 | 621 | await sent.edit("**Successfully Deleted Number ✅**", parse_mode=ParseMode.MARKDOWN) 622 | 623 | except Exception as e: 624 | LOGGER.error(f"Failed to delete number {phone_number} for {user_id}: {e}") 625 | await sent.edit("**Sorry Unable To Delete Number ❌**", parse_mode=ParseMode.MARKDOWN) 626 | 627 | @Client.on_message(filters.command(["buy", "my", "del"], prefixes=COMMAND_PREFIX) & filters.private & ~authorized_only()) 628 | async def unauthorized_access(client, message): 629 | await message.reply( 630 | "**Sorry Bro You Are Not Authorized ❌ DM @Saddam_XD For Auth**", 631 | parse_mode=ParseMode.MARKDOWN, 632 | reply_markup=InlineKeyboardMarkup([ 633 | [InlineKeyboardButton("Contact Owner", user_id=DEVELOPER_ID)] 634 | ]) 635 | ) --------------------------------------------------------------------------------