├── utils ├── __init__.py └── LOGGING.py ├── requirements.txt ├── core ├── __init__.py └── mongo.py ├── config.py ├── app.py ├── README.md └── bot.py /utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .LOGGING import LOGGER -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram 2 | tgcrypto 3 | aiohttp 4 | pymongo -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | from .mongo import users_collection, numbers_collection, authorized_users_collection -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | API_ID = YourAPIID 2 | API_HASH = "YOURAPIHASH" 3 | BOT_TOKEN = "BOT_TKEN" 4 | ADMIN_IDS = [7303810912, 5991909954, 6249257243] 5 | MONGO_URI = "Your_Mongo_Url" -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client 2 | from config import API_ID, API_HASH, BOT_TOKEN 3 | from utils import LOGGER 4 | import datetime 5 | 6 | # Bot client setup 7 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Creating Bot Client From BOT_TOKEN") 8 | try: 9 | bot = Client("twilio_async_bot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN) 10 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Bot Client Created Successfully!") 11 | except Exception as e: 12 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Failed to create bot client: {e}") 13 | raise -------------------------------------------------------------------------------- /utils/LOGGING.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from logging.handlers import RotatingFileHandler 3 | 4 | logging.basicConfig( 5 | level=logging.INFO, 6 | format="%(asctime)s - %(levelname)s - %(message)s", 7 | datefmt='%Y-%m-%d %H:%M:%S', 8 | handlers=[ 9 | RotatingFileHandler( 10 | "botlog.txt", 11 | maxBytes=50000000, 12 | backupCount=10 13 | ), 14 | logging.StreamHandler() 15 | ] 16 | ) 17 | 18 | # Set Logging Levels 19 | logging.getLogger("pyrogram").setLevel(logging.ERROR) 20 | logging.getLogger("aiohttp").setLevel(logging.ERROR) 21 | logging.getLogger("apscheduler").setLevel(logging.ERROR) 22 | 23 | LOGGER = logging.getLogger(__name__) -------------------------------------------------------------------------------- /core/mongo.py: -------------------------------------------------------------------------------- 1 | from pymongo import MongoClient 2 | from pymongo.errors import ConnectionFailure 3 | from config import MONGO_URI 4 | from utils import LOGGER 5 | import datetime 6 | 7 | # MongoDB setup 8 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Creating MONGO_CLIENT From MONGO_URL") 9 | try: 10 | mongo_client = MongoClient(MONGO_URI) 11 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - MONGO_CLIENT Successfully Created!") 12 | except ConnectionFailure as e: 13 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Failed to connect to MongoDB: {e}") 14 | raise 15 | 16 | db = mongo_client["ItsSmartToolBot"] 17 | users_collection = db["users"] 18 | numbers_collection = db["numbers"] 19 | authorized_users_collection = db["authorized_users"] -------------------------------------------------------------------------------- /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/TwilioSMSBot.git 33 | cd TwilioSMSBot 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/TwilioSMSBot.git 93 | cd TwilioSMSBot 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 +1234567890` 138 | 139 | ## 💀《 Pʀᴏᴊᴇᴄᴛ Sᴛʀᴜᴄᴛᴜʀᴇ ☠️ 》 140 | 141 | ``` 142 | TwilioSMSBot/ 143 | ├── bot.py # Main bot logic 144 | ├── app.py # Bot client setup 145 | ├── config.py # Configuration file 146 | ├── core/ # MongoDB collections 147 | │ ├── __init__.py 148 | │ └── mongo.py 149 | ├── utils/ # Logger and utilities 150 | │ ├── __init__.py 151 | │ └── LOGGING.py 152 | ├── requirements.txt # Dependencies 153 | └── README.md # This file 154 | ``` 155 | 156 | ## 🌐《 Cᴏɴᴛʀɪʙᴜᴛɪɴɢ ✨ 》 157 | 158 | Contributions are welcome! Please: 159 | 160 | 1. Fork the repository. 161 | 2. Create a new branch (`git checkout -b feature/your-feature`). 162 | 3. Commit your changes (`git commit -m 'Add your feature'`). 163 | 4. Push to the branch (`git push origin feature/your-feature`). 164 | 5. Open a Pull Request. 165 | 166 | ## ⭐️《 Sᴜᴘᴘᴏʀᴛ 🌟 》 167 | 168 | For issues or questions, contact the developer: 169 | 170 | - Tᴇʟᴇɢʀᴀᴍ: [@TʜᴇSᴍᴀʀᴛDᴇᴠ](https://t.me/TheSmartDev) 171 | - GɪᴛHᴜʙ: [TʜᴇSᴍᴀʀᴛDᴇᴠꜱ](https://github.com/TheSmartDevs) 172 | 173 | Rᴇᴘᴏꜱɪᴛᴏʀʏ: [https://github.com/TheSmartDevs/TwilioSMSBot](https://github.com/TheSmartDevs/TwilioSMSBot) -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import base64 3 | import aiohttp 4 | import datetime 5 | from functools import wraps 6 | from pyrogram import filters 7 | from pyrogram.types import ( 8 | Message, 9 | InlineKeyboardMarkup, 10 | InlineKeyboardButton, 11 | CallbackQuery 12 | ) 13 | from pyrogram.enums import ParseMode 14 | from pyrogram.errors import ( 15 | UserIdInvalid, 16 | UsernameNotOccupied, 17 | MessageNotModified 18 | ) 19 | from pymongo.errors import ConnectionFailure 20 | from config import ADMIN_IDS 21 | from core import ( 22 | users_collection, 23 | numbers_collection, 24 | authorized_users_collection 25 | ) 26 | from utils import LOGGER 27 | from app import bot 28 | 29 | # Load authorized users from MongoDB 30 | def load_authorized_users(): 31 | authorized_users = set() 32 | try: 33 | for user in authorized_users_collection.find(): 34 | if "user_id" in user: 35 | authorized_users.add(user["user_id"]) 36 | else: 37 | LOGGER.warning(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - WARNING - Skipping invalid authorized user document: {user}") 38 | except ConnectionFailure: 39 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Failed to connect to MongoDB") 40 | return authorized_users 41 | 42 | # Save authorized user to MongoDB 43 | def auth_user(user_id): 44 | authorized_users_collection.update_one( 45 | {"user_id": user_id}, 46 | {"$set": {"user_id": user_id}}, 47 | upsert=True 48 | ) 49 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Authorized user_id {user_id}") 50 | 51 | # Remove authorized user from MongoDB 52 | def unauth_user(user_id): 53 | authorized_users_collection.delete_one({"user_id": user_id}) 54 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Unauthorized user_id {user_id}") 55 | 56 | # Load data from MongoDB 57 | def load_data(): 58 | twilio_users = {} 59 | twilio_numbers = {} 60 | try: 61 | for user in users_collection.find(): 62 | if "user_id" in user and "sid" in user and "token" in user: 63 | twilio_users[user["user_id"]] = (user["sid"], user["token"]) 64 | else: 65 | LOGGER.warning(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - WARNING - Skipping invalid user document: {user}") 66 | for number in numbers_collection.find(): 67 | if "user_id" in number and "numbers" in number: 68 | twilio_numbers[number["user_id"]] = number["numbers"] 69 | else: 70 | LOGGER.warning(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - WARNING - Skipping invalid numbers document: {number}") 71 | except ConnectionFailure: 72 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Failed to connect to MongoDB") 73 | return twilio_users, twilio_numbers 74 | 75 | # Save data to MongoDB 76 | def save_user(user_id, sid, token): 77 | if not sid or not token: 78 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Attempted to save invalid SID or token for user_id {user_id}") 79 | return 80 | users_collection.update_one( 81 | {"user_id": user_id}, 82 | {"$set": {"user_id": user_id, "sid": sid, "token": token}}, 83 | upsert=True 84 | ) 85 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Saved user data for user_id {user_id}") 86 | 87 | def save_numbers(user_id, numbers): 88 | numbers_collection.update_one( 89 | {"user_id": user_id}, 90 | {"$set": {"user_id": user_id, "numbers": numbers}}, 91 | upsert=True 92 | ) 93 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Saved numbers for user_id {user_id}") 94 | 95 | def delete_user_data(user_id): 96 | users_collection.delete_one({"user_id": user_id}) 97 | numbers_collection.delete_one({"user_id": user_id}) 98 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Deleted data for user_id {user_id}") 99 | 100 | twilio_users, twilio_numbers = load_data() 101 | authorized_users = load_authorized_users() 102 | 103 | # Helper function to resolve username or user ID to user ID 104 | async def resolve_identifier(client, identifier): 105 | try: 106 | if identifier.startswith("@"): 107 | username = identifier[1:] # Remove "@" 108 | user = await client.get_users(username) 109 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Resolved username {identifier} to user_id {user.id}") 110 | return user.id 111 | else: 112 | user_id = int(identifier) 113 | return user_id 114 | except (UserIdInvalid, UsernameNotOccupied, ValueError) as e: 115 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Failed to resolve identifier {identifier}: {str(e)}") 116 | return None 117 | 118 | # Decorator to restrict commands to authorized users or admins 119 | def restrict_to_authorized(func): 120 | @wraps(func) 121 | async def wrapper(client, message: Message, *args, **kwargs): 122 | user_id = message.from_user.id 123 | if user_id in ADMIN_IDS or user_id in authorized_users: 124 | return await func(client, message, *args, **kwargs) 125 | else: 126 | buttons = [[InlineKeyboardButton("Contact Owner", user_id=5991909954)]] 127 | await client.send_message( 128 | message.chat.id, 129 | "**Sorry Bro Unauthorized User Kindly Contact @Ruhulxr For Auth**", 130 | reply_markup=InlineKeyboardMarkup(buttons), 131 | parse_mode=ParseMode.MARKDOWN 132 | ) 133 | return wrapper 134 | 135 | @bot.on_message(filters.command("auth") & filters.user(ADMIN_IDS)) 136 | async def auth_command(client, message: Message): 137 | try: 138 | _, identifier = message.text.split(maxsplit=1) 139 | except ValueError: 140 | await client.send_message( 141 | message.chat.id, 142 | "**✘《 Error ↯ 》 Usage: /auth **", 143 | parse_mode=ParseMode.MARKDOWN 144 | ) 145 | return 146 | 147 | user_id = await resolve_identifier(client, identifier) 148 | if user_id is None: 149 | await client.send_message( 150 | message.chat.id, 151 | "**✘《 Error ↯ 》 Invalid user ID or username**", 152 | parse_mode=ParseMode.MARKDOWN 153 | ) 154 | return 155 | 156 | if user_id in authorized_users: 157 | await client.send_message( 158 | message.chat.id, 159 | f"**✘《 Error ↯ 》 User ID {user_id} is already authorized**", 160 | parse_mode=ParseMode.MARKDOWN 161 | ) 162 | return 163 | 164 | authorized_users.add(user_id) 165 | auth_user(user_id) 166 | await client.send_message( 167 | message.chat.id, 168 | f"**✘《 Success ↯ 》 User ID {user_id} has been authorized**", 169 | parse_mode=ParseMode.MARKDOWN 170 | ) 171 | 172 | @bot.on_message(filters.command("unauth") & filters.user(ADMIN_IDS)) 173 | async def unauth_command(client, message: Message): 174 | try: 175 | _, identifier = message.text.split(maxsplit=1) 176 | except ValueError: 177 | await client.send_message( 178 | message.chat.id, 179 | "**✘《 Error ↯ 》 Usage: /unauth **", 180 | parse_mode=ParseMode.MARKDOWN 181 | ) 182 | return 183 | 184 | user_id = await resolve_identifier(client, identifier) 185 | if user_id is None: 186 | await client.send_message( 187 | message.chat.id, 188 | "**✘《 Error ↯ 》 Invalid user ID or username**", 189 | parse_mode=ParseMode.MARKDOWN 190 | ) 191 | return 192 | 193 | if user_id not in authorized_users: 194 | await client.send_message( 195 | message.chat.id, 196 | f"**✘《 Error ↯ 》 User ID {user_id} is not authorized**", 197 | parse_mode=ParseMode.MARKDOWN 198 | ) 199 | return 200 | 201 | authorized_users.remove(user_id) 202 | unauth_user(user_id) 203 | await client.send_message( 204 | message.chat.id, 205 | f"**✘《 Success ↯ 》 User ID {user_id} has been unauthorized**", 206 | parse_mode=ParseMode.MARKDOWN 207 | ) 208 | 209 | @bot.on_message(filters.command("start")) 210 | @restrict_to_authorized 211 | async def start(client, message: Message): 212 | full_name = message.from_user.first_name 213 | if message.from_user.last_name: 214 | full_name += f" {message.from_user.last_name}" 215 | text = ( 216 | f"**Hello, {full_name}! Welcome to the Twilio Bot!**\n\n" 217 | "Here, you can easily purchase numbers and retrieve OTPs to create WhatsApp or Telegram accounts. Follow the commands below to get started:\n\n" 218 | "**/login ** - Log in to your Twilio account\n" 219 | "**/buy** - Purchase available numbers\n" 220 | "**/get** - Retrieve OTP messages\n" 221 | "**/del ** - Delete a purchased number\n" 222 | "**/my** - List your active numbers\n" 223 | "**/logout** - Log out from your Twilio account\n\n" 224 | "**Support: @TheSmartDev**" 225 | ) 226 | buttons = [ 227 | [ 228 | InlineKeyboardButton("✘《 Updates ↯ 》", url="t.me/TheSmartDev"), 229 | InlineKeyboardButton("✘《 Dev ↯ 》", user_id=7303810912) 230 | ] 231 | ] 232 | await client.send_message( 233 | message.chat.id, 234 | text, 235 | reply_markup=InlineKeyboardMarkup(buttons), 236 | parse_mode=ParseMode.MARKDOWN 237 | ) 238 | 239 | @bot.on_message(filters.command("login")) 240 | @restrict_to_authorized 241 | async def login(client, message: Message): 242 | loading_message = await client.send_message(message.chat.id, "**✘《 Loading ↯ 》 Processing login...**", parse_mode=ParseMode.MARKDOWN) 243 | try: 244 | parts = message.text.split() 245 | if len(parts) != 3: 246 | raise ValueError("Invalid format") 247 | _, sid, token = parts 248 | if not sid.startswith("AC") or len(token) < 32: 249 | raise ValueError("Invalid SID or token format") 250 | except ValueError: 251 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 Usage: /login **", parse_mode=ParseMode.MARKDOWN) 252 | return 253 | 254 | headers = { 255 | "Authorization": "Basic " + base64.b64encode(f"{sid}:{token}".encode()).decode() 256 | } 257 | 258 | async with aiohttp.ClientSession() as session: 259 | try: 260 | async with session.get(f"https://api.twilio.com/2010-04-01/Accounts/{sid}.json", headers=headers) as resp: 261 | response_text = await resp.text() 262 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Login API response for user_id {message.from_user.id}: Status {resp.status}, Response {response_text}") 263 | if resp.status == 200: 264 | twilio_users[message.from_user.id] = (sid, token) 265 | twilio_numbers.setdefault(message.from_user.id, []) 266 | save_user(message.from_user.id, sid, token) 267 | save_numbers(message.from_user.id, twilio_numbers[message.from_user.id]) 268 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Success ↯ 》 Login successful**", parse_mode=ParseMode.MARKDOWN) 269 | else: 270 | error_message = f"**✘《 Error ↯ 》 Login failed. Check your SID/TOKEN.**\nDetails: HTTP {resp.status}" 271 | try: 272 | error_data = await resp.json() 273 | error_message += f" - {error_data.get('message', 'Unknown error')}" 274 | except ValueError: 275 | error_message += f" - {response_text}" 276 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 277 | except aiohttp.ClientError as e: 278 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Network error during login for user_id {message.from_user.id}: {str(e)}") 279 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 Network error during login. Please try again.**", parse_mode=ParseMode.MARKDOWN) 280 | 281 | @bot.on_message(filters.command("logout")) 282 | @restrict_to_authorized 283 | async def logout(client, message: Message): 284 | user_id = message.from_user.id 285 | loading_message = await client.send_message(message.chat.id, "**✘《 Loading ↯ 》 Processing logout...**", parse_mode=ParseMode.MARKDOWN) 286 | if user_id in twilio_users: 287 | del twilio_users[user_id] 288 | if user_id in twilio_numbers: 289 | del twilio_numbers[user_id] 290 | delete_user_data(user_id) 291 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Success ↯ 》 Logout successful**", parse_mode=ParseMode.MARKDOWN) 292 | else: 293 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 You are not logged in**", parse_mode=ParseMode.MARKDOWN) 294 | 295 | @bot.on_message(filters.command("buy")) 296 | @restrict_to_authorized 297 | async def buy_numbers(client, message: Message): 298 | user_id = message.from_user.id 299 | if user_id not in twilio_users: 300 | await client.send_message(message.chat.id, "**✘《 Error ↯ 》 Log in first using /login**", parse_mode=ParseMode.MARKDOWN) 301 | return 302 | 303 | buttons = [ 304 | [ 305 | InlineKeyboardButton("US 🇺🇸", callback_data="country_US"), 306 | InlineKeyboardButton("CA 🇨🇦", callback_data="country_CA") 307 | ], 308 | [ 309 | InlineKeyboardButton("PR 🇵🇷", callback_data="country_PR") 310 | ] 311 | ] 312 | await client.send_message( 313 | message.chat.id, 314 | "**✘《 Kindly Choose The Country You Prefer ↯ 》**", 315 | reply_markup=InlineKeyboardMarkup(buttons), 316 | parse_mode=ParseMode.MARKDOWN 317 | ) 318 | 319 | async def fetch_numbers(client, message: Message, user_id, country_code, custom_prefix=None): 320 | loading_message = await client.send_message(message.chat.id, f"**✘《 Loading ↯ 》 Fetching {country_code} numbers...**", parse_mode=ParseMode.MARKDOWN) 321 | 322 | sid, token = twilio_users[user_id] 323 | headers = { 324 | "Authorization": "Basic " + base64.b64encode(f"{sid}:{token}".encode()).decode() 325 | } 326 | 327 | url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/AvailablePhoneNumbers/{country_code}/Local.json?PageSize=10" 328 | if custom_prefix: 329 | if country_code == "PR": 330 | url += f"&AreaCode=787" 331 | else: 332 | url += f"&AreaCode={custom_prefix}" 333 | 334 | async with aiohttp.ClientSession() as session: 335 | try: 336 | async with session.get(url, headers=headers) as resp: 337 | response_text = await resp.text() 338 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Fetch numbers API response for user_id {user_id}: Status {resp.status}, Response {response_text}") 339 | if resp.status != 200: 340 | error_message = f"**✘《 Error ↯ 》 Failed to fetch {country_code} numbers.**\nDetails: HTTP {resp.status}" 341 | try: 342 | error_data = await resp.json() 343 | error_message += f" - {error_data.get('message', 'Unknown error')}" 344 | except ValueError: 345 | error_message += f" - {response_text}" 346 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 347 | return 348 | 349 | data = await resp.json() 350 | numbers = data.get("available_phone_numbers", []) 351 | if country_code == "CA": 352 | numbers = [num for num in numbers if num['phone_number'].startswith('+1')] 353 | 354 | if not numbers: 355 | error_message = f"**✘《 Error ↯ 》 No available {country_code} numbers{' with area code ' + custom_prefix if custom_prefix else ''}**" 356 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 357 | return 358 | 359 | # Filter numbers by custom prefix if provided 360 | if custom_prefix and country_code != "PR": 361 | numbers = [num for num in numbers if num['phone_number'].startswith(f'+1{custom_prefix}')] 362 | elif custom_prefix and country_code == "PR": 363 | numbers = [num for num in numbers if num['phone_number'].startswith('+1787')] 364 | 365 | if not numbers: 366 | error_message = f"**✘《 Error ↯ 》 No available {country_code} numbers with area code {custom_prefix}**" 367 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 368 | return 369 | 370 | # Filter out already owned numbers 371 | owned_numbers = twilio_numbers.get(user_id, []) 372 | numbers = [num for num in numbers if num['phone_number'] not in owned_numbers] 373 | 374 | if not numbers: 375 | error_message = f"**✘《 Error ↯ 》 All available {country_code} numbers{' with area code ' + custom_prefix if custom_prefix else ''} are already owned**" 376 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 377 | return 378 | 379 | numbers_list = "\n".join(num['phone_number'] for num in numbers) 380 | message_text = f"**Available {country_code} Numbers{' with area code ' + custom_prefix if custom_prefix else ''}:**\n{numbers_list}\n\n**Select a number to purchase:**" 381 | 382 | buttons = [] 383 | row = [] 384 | for i, num in enumerate(numbers): 385 | phone = num['phone_number'] 386 | row.append(InlineKeyboardButton(phone, callback_data=f"buy_{phone}")) 387 | if len(row) == 2: 388 | buttons.append(row) 389 | row = [] 390 | 391 | if row: 392 | buttons.append(row) 393 | 394 | try: 395 | await client.edit_message_text( 396 | message.chat.id, 397 | loading_message.id, 398 | message_text, 399 | reply_markup=InlineKeyboardMarkup(buttons), 400 | parse_mode=ParseMode.MARKDOWN 401 | ) 402 | except MessageNotModified: 403 | LOGGER.debug(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - DEBUG - Message not modified for fetch_numbers, user_id {user_id}") 404 | except aiohttp.ClientError as e: 405 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Network error during fetch_numbers for user_id {user_id}: {str(e)}") 406 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 Network error while fetching numbers. Please try again.**", parse_mode=ParseMode.MARKDOWN) 407 | 408 | @bot.on_message(filters.regex(r"^[0-9]{3}$") & filters.reply) 409 | @restrict_to_authorized 410 | async def handle_custom_area_code(client, message: Message): 411 | user_id = message.from_user.id 412 | if user_id not in twilio_users: 413 | await client.send_message(message.chat.id, "**✘《 Error ↯ 》 Log in first using /login**", parse_mode=ParseMode.MARKDOWN) 414 | return 415 | 416 | area_code = message.text.strip() 417 | if message.reply_to_message and message.reply_to_message.text: 418 | if "Enter your preferred 3-digit area code for" in message.reply_to_message.text: 419 | country_code = message.reply_to_message.text.split("for ")[-1].split(" ")[0] 420 | await fetch_numbers(client, message, user_id, country_code, custom_prefix=area_code) 421 | return 422 | await client.send_message(message.chat.id, "**✘《 Error ↯ 》 Please select a country first and reply to the area code prompt**", parse_mode=ParseMode.MARKDOWN) 423 | 424 | @bot.on_callback_query() 425 | async def handle_callbacks(client, callback_query: CallbackQuery): 426 | user_id = callback_query.from_user.id 427 | if user_id not in twilio_users and user_id not in ADMIN_IDS and user_id not in authorized_users: 428 | await callback_query.answer("Please log in first and ensure you are authorized.", show_alert=True) 429 | return 430 | 431 | data = callback_query.data 432 | if data.startswith("country_"): 433 | country_code = data.split("_")[1] 434 | buttons = [ 435 | [ 436 | InlineKeyboardButton("✘《 Yes ↯ 》", callback_data=f"custom_{country_code}"), 437 | InlineKeyboardButton("✘《 No ↯ 》", callback_data=f"all_{country_code}") 438 | ] 439 | ] 440 | try: 441 | await client.edit_message_text( 442 | callback_query.message.chat.id, 443 | callback_query.message.id, 444 | "**✘《 Do You Prefer Custom Area Code ↯ 》**", 445 | reply_markup=InlineKeyboardMarkup(buttons), 446 | parse_mode=ParseMode.MARKDOWN 447 | ) 448 | except MessageNotModified: 449 | LOGGER.debug(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - DEBUG - Message not modified for country selection, user_id {user_id}") 450 | await callback_query.answer() 451 | elif data.startswith("all_"): 452 | country_code = data.split("_")[1] 453 | await fetch_numbers(client, callback_query.message, user_id, country_code) 454 | await callback_query.answer() 455 | elif data.startswith("custom_"): 456 | country_code = data.split("_")[1] 457 | try: 458 | await callback_query.message.reply(f"**Enter your preferred 3-digit area code for {country_code} (e.g., 592):**", parse_mode=ParseMode.MARKDOWN) 459 | except Exception as e: 460 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Failed to send area code prompt for user_id {user_id}: {str(e)}") 461 | await client.send_message(callback_query.message.chat.id, "**✘《 Error ↯ 》 Failed to prompt for area code. Please try again.**", parse_mode=ParseMode.MARKDOWN) 462 | await callback_query.answer() 463 | elif data.startswith("buy_"): 464 | phone = data.replace("buy_", "") 465 | if phone in twilio_numbers.get(user_id, []): 466 | await callback_query.answer(f"Number {phone} is already owned by you.", show_alert=True) 467 | return 468 | 469 | sid, token = twilio_users[user_id] 470 | loading_message = await client.send_message( 471 | callback_query.message.chat.id, 472 | f"**✘《 Loading ↯ 》 Purchasing number `{phone}`...**", 473 | parse_mode=ParseMode.MARKDOWN 474 | ) 475 | 476 | headers = { 477 | "Authorization": "Basic " + base64.b64encode(f"{sid}:{token}".encode()).decode(), 478 | "Content-Type": "application/x-www-form-urlencoded" 479 | } 480 | 481 | data = { 482 | "PhoneNumber": phone, 483 | "SmsEnabled": "true" # Ensure SMS capability for OTP 484 | } 485 | 486 | async with aiohttp.ClientSession() as session: 487 | try: 488 | async with session.post( 489 | f"https://api.twilio.com/2010-04-01/Accounts/{sid}/IncomingPhoneNumbers.json", 490 | headers=headers, 491 | data=data 492 | ) as resp: 493 | response_text = await resp.text() 494 | LOGGER.info( 495 | f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - " 496 | f"Purchase number API response for user_id {user_id}: Status {resp.status}, Response {response_text}" 497 | ) 498 | if resp.status == 201: 499 | twilio_numbers.setdefault(user_id, []).append(phone) 500 | save_numbers(user_id, twilio_numbers[user_id]) 501 | await client.edit_message_text( 502 | callback_query.message.chat.id, 503 | loading_message.id, 504 | f"**✘《 Success ↯ 》 Number purchased: `{phone}`**", 505 | parse_mode=ParseMode.MARKDOWN 506 | ) 507 | else: 508 | error_message = f"**✘《 Error ↯ 》 Failed to purchase number: `{phone}`**" 509 | try: 510 | error_data = await resp.json() 511 | error_message += f"\nDetails: {error_data.get('message', 'Unknown error')}" 512 | except ValueError: 513 | error_message += f"\nDetails: HTTP {resp.status} - {response_text}" 514 | await client.edit_message_text( 515 | callback_query.message.chat.id, 516 | loading_message.id, 517 | error_message, 518 | parse_mode=ParseMode.MARKDOWN 519 | ) 520 | except aiohttp.ClientError as e: 521 | LOGGER.error( 522 | f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - " 523 | f"Network error during number purchase for user_id {user_id}: {str(e)}" 524 | ) 525 | await client.edit_message_text( 526 | callback_query.message.chat.id, 527 | loading_message.id, 528 | f"**✘《 Error ↯ 》 Network error while purchasing `{phone}`. Please try again.**", 529 | parse_mode=ParseMode.MARKDOWN 530 | ) 531 | await callback_query.answer() 532 | 533 | @bot.on_message(filters.command("my")) 534 | @restrict_to_authorized 535 | async def my_numbers(client, message: Message): 536 | user_id = message.from_user.id 537 | loading_message = await client.send_message(message.chat.id, "**✘《 Loading ↯ 》 Fetching your numbers...**", parse_mode=ParseMode.MARKDOWN) 538 | nums = twilio_numbers.get(user_id, []) 539 | if not nums: 540 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 No numbers found**", parse_mode=ParseMode.MARKDOWN) 541 | return 542 | text = "**Your Active Numbers:**\n\n" + "\n".join(num for num in nums) 543 | try: 544 | await client.edit_message_text(message.chat.id, loading_message.id, text, parse_mode=ParseMode.MARKDOWN) 545 | except MessageNotModified: 546 | LOGGER.debug(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - DEBUG - Message not modified for my_numbers, user_id {user_id}") 547 | 548 | @bot.on_message(filters.command("del")) 549 | @restrict_to_authorized 550 | async def delete_number(client, message: Message): 551 | user_id = message.from_user.id 552 | if user_id not in twilio_users: 553 | await client.send_message(message.chat.id, "**✘《 Error ↯ 》 Log in first using /login**", parse_mode=ParseMode.MARKDOWN) 554 | return 555 | try: 556 | _, number = message.text.split() 557 | except ValueError: 558 | await client.send_message(message.chat.id, "**✘《 Error ↯ 》 Usage: /del **", parse_mode=ParseMode.MARKDOWN) 559 | return 560 | 561 | loading_message = await client.send_message(message.chat.id, f"**✘《 Loading ↯ 》 Deleting number `{number}`...**", parse_mode=ParseMode.MARKDOWN) 562 | sid, token = twilio_users[user_id] 563 | headers = { 564 | "Authorization": "Basic " + base64.b64encode(f"{sid}:{token}".encode()).decode() 565 | } 566 | 567 | async with aiohttp.ClientSession() as session: 568 | try: 569 | async with session.get(f"https://api.twilio.com/2010-04-01/Accounts/{sid}/IncomingPhoneNumbers.json", headers=headers) as resp: 570 | response_text = await resp.text() 571 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Fetch incoming numbers API response for user_id {user_id}: Status {resp.status}, Response {response_text}") 572 | if resp.status != 200: 573 | error_message = f"**✘《 Error ↯ 》 Failed to fetch numbers.**\nDetails: HTTP {resp.status}" 574 | try: 575 | error_data = await resp.json() 576 | error_message += f" - {error_data.get('message', 'Unknown error')}" 577 | except ValueError: 578 | error_message += f" - {response_text}" 579 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 580 | return 581 | 582 | data = await resp.json() 583 | for record in data.get("incoming_phone_numbers", []): 584 | if record.get("phone_number") == number: 585 | sid_to_delete = record.get("sid") 586 | del_url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/IncomingPhoneNumbers/{sid_to_delete}.json" 587 | async with session.delete(del_url, headers=headers) as del_resp: 588 | del_response_text = await del_resp.text() 589 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Delete number API response for user_id {user_id}: Status {del_resp.status}, Response {del_response_text}") 590 | if del_resp.status == 204: 591 | twilio_numbers[user_id].remove(number) 592 | save_numbers(user_id, twilio_numbers[user_id]) 593 | await client.edit_message_text(message.chat.id, loading_message.id, f"**✘《 Success ↯ 》 Number deleted: `{number}`**", parse_mode=ParseMode.MARKDOWN) 594 | return 595 | else: 596 | error_message = f"**✘《 Error ↯ 》 Failed to delete number: `{number}`**" 597 | try: 598 | error_data = await del_resp.json() 599 | error_message += f"\nDetails: {error_data.get('message', 'Unknown error')}" 600 | except ValueError: 601 | error_message += f"\nDetails: HTTP {del_resp.status} - {del_response_text}" 602 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 603 | return 604 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 Number not found in your account**", parse_mode=ParseMode.MARKDOWN) 605 | except aiohttp.ClientError as e: 606 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Network error during delete_number for user_id {user_id}: {str(e)}") 607 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 Network error while deleting number. Please try again.**", parse_mode=ParseMode.MARKDOWN) 608 | 609 | @bot.on_message(filters.command("get")) 610 | @restrict_to_authorized 611 | async def get_otp(client, message: Message): 612 | user_id = message.from_user.id 613 | if user_id not in twilio_users: 614 | await client.send_message(message.chat.id, "**✘《 Error ↯ 》 Login required. Use /login**", parse_mode=ParseMode.MARKDOWN) 615 | return 616 | 617 | loading_message = await client.send_message(message.chat.id, "**✘《 Loading ↯ 》 Fetching OTP messages...**", parse_mode=ParseMode.MARKDOWN) 618 | sid, token = twilio_users[user_id] 619 | headers = { 620 | "Authorization": "Basic " + base64.b64encode(f"{sid}:{token}".encode()).decode() 621 | } 622 | 623 | url = f"https://api.twilio.com/2010-04-01/Accounts/{sid}/Messages.json?PageSize=10" 624 | 625 | async with aiohttp.ClientSession() as session: 626 | try: 627 | async with session.get(url, headers=headers) as resp: 628 | response_text = await resp.text() 629 | LOGGER.info(f"{datetime.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}") 630 | if resp.status != 200: 631 | error_message = f"**✘《 Error ↯ 》 Failed to fetch messages.**\nDetails: HTTP {resp.status}" 632 | try: 633 | error_data = await resp.json() 634 | error_message += f" - {error_data.get('message', 'Unknown error')}" 635 | except ValueError: 636 | error_message += f" - {response_text}" 637 | await client.edit_message_text(message.chat.id, loading_message.id, error_message, parse_mode=ParseMode.MARKDOWN) 638 | return 639 | 640 | data = await resp.json() 641 | messages = data.get("messages", []) 642 | if not messages: 643 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 No messages found**", parse_mode=ParseMode.MARKDOWN) 644 | return 645 | 646 | text = "**Latest OTP Messages:**\n\n" 647 | for msg in messages: 648 | if msg.get("direction") == "inbound": 649 | text += f"{msg.get('from')} -> {msg.get('body')}\n" 650 | 651 | await client.edit_message_text(message.chat.id, loading_message.id, text or "**✘《 Error ↯ 》 No OTPs found**", parse_mode=ParseMode.MARKDOWN) 652 | except aiohttp.ClientError as e: 653 | LOGGER.error(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - ERROR - Network error during get_otp for user_id {user_id}: {str(e)}") 654 | await client.edit_message_text(message.chat.id, loading_message.id, "**✘《 Error ↯ 》 Network error while fetching OTPs. Please try again.**", parse_mode=ParseMode.MARKDOWN) 655 | 656 | LOGGER.info(f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - INFO - Bot Successfully Started! 💥") 657 | bot.run() 658 | --------------------------------------------------------------------------------