├── .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 |  
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 | )
--------------------------------------------------------------------------------