├── usernames.txt ├── config.json ├── go.mod ├── README.md └── main.go /usernames.txt: -------------------------------------------------------------------------------- 1 | user1 2 | user2 3 | user3 4 | . 5 | . 6 | . 7 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "YOUR_API_ID", 3 | "api_hash": "YOUR_API_HASH", 4 | "phone": "YOUR_PHONE_NUMBER", 5 | "base_delay_seconds": 3, 6 | "max_retries": 3, 7 | "workers": 1, 8 | "use_proxy": false, 9 | "proxy_url": "" 10 | } 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module telegram-username-checker 2 | 3 | go 1.23.1 4 | 5 | require ( 6 | github.com/gotd/td v0.111.2 7 | github.com/schollz/progressbar/v3 v3.14.1 8 | ) 9 | 10 | require ( 11 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 12 | github.com/go-faster/errors v0.7.1 // indirect 13 | github.com/go-faster/jx v1.1.0 // indirect 14 | github.com/go-faster/xor v1.0.0 // indirect 15 | github.com/gotd/ige v0.2.2 // indirect 16 | github.com/gotd/neo v0.1.5 // indirect 17 | github.com/klauspost/compress v1.17.10 // indirect 18 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 19 | github.com/rivo/uniseg v0.4.7 // indirect 20 | github.com/segmentio/asm v1.2.0 // indirect 21 | go.opentelemetry.io/otel v1.29.0 // indirect 22 | go.opentelemetry.io/otel/trace v1.29.0 // indirect 23 | go.uber.org/atomic v1.11.0 // indirect 24 | go.uber.org/multierr v1.11.0 // indirect 25 | go.uber.org/zap v1.27.0 // indirect 26 | golang.org/x/crypto v0.27.0 // indirect 27 | golang.org/x/net v0.29.0 // indirect 28 | golang.org/x/sync v0.8.0 // indirect 29 | golang.org/x/sys v0.25.0 // indirect 30 | golang.org/x/term v0.15.0 // indirect 31 | nhooyr.io/websocket v1.8.11 // indirect 32 | rsc.io/qr v0.2.0 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Username Checker 2 | 3 |
4 | 5 | ![GitHub stars](https://img.shields.io/github/stars/xPOURY4/telegram-username-checker?style=for-the-badge&color=yellow) 6 | ![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge) 7 | ![Go Version](https://img.shields.io/badge/Go-1.16+-00ADD8?style=for-the-badge&logo=go&logoColor=white) 8 | ![GitHub last commit](https://img.shields.io/github/last-commit/xPOURY4/telegram-username-checker?style=for-the-badge) 9 | ![Made with Love](https://img.shields.io/badge/Made%20with-❤️-red?style=for-the-badge) 10 | 11 |
12 | 13 | 14 | This advanced tool allows you to check the availability of Telegram usernames using the Telegram API. It handles rate limiting, provides multiple modes of operation, and offers extensive customization options. 15 | 16 | ## ✨ Features 17 | 18 |
19 | 20 | ![Multi-threaded](https://img.shields.io/badge/Multi--threaded-blue?style=flat-square&logo=gitbook&logoColor=white) 21 | ![Interactive Mode](https://img.shields.io/badge/Interactive%20Mode-blue?style=flat-square&logo=iterm2&logoColor=white) 22 | ![Adaptive Rate Limiting](https://img.shields.io/badge/Adaptive%20Rate%20Limiting-blue?style=flat-square&logo=speedtest&logoColor=white) 23 | ![Session Persistence](https://img.shields.io/badge/Session%20Persistence-blue?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xNyA3aC0xVjZjMC0yLjgtMi4yLTUtNS01UzYgMy4yIDYgNnYxSDVjLTEuMSAwLTIgLjktMiAydjEwYzAgMS4xLjkgMiAyIDJoMTJjMS4xIDAgMi0uOSAyLTJWOWMwLTEuMS0uOS0yLTItMnptLTUgOGMtMS4xIDAtMi0uOS0yLTJzLjktMiAyLTIgMiAuOSAyIDItLjkgMi0yIDJ6bTMuMS04SDguOVY2YzAtMS43IDEuNC0zLjEgMy4xLTMuMSAxLjcgMCAzLjEgMS40IDMuMSAzLjF2MXoiLz48L3N2Zz4=&logoColor=white) 24 | ![Username Generation](https://img.shields.io/badge/Username%20Generation-blue?style=flat-square&logo=database&logoColor=white) 25 | 26 |
27 | 28 | - ✅ Check multiple usernames in bulk or interactive mode 29 | - 🚀 Multi-threaded checking with configurable worker count 30 | - 🛡️ Adaptive rate limiting with automatic backoff 31 | - 💾 Session persistence - resume checking after interruptions 32 | - 🔄 Generate username combinations automatically 33 | - 📊 Detailed progress tracking with visual progress bar 34 | - 📝 Comprehensive results in both text and JSON formats 35 | - 💻 Interactive mode for real-time username checking 36 | - ⚙️ Customizable configuration via config file or command-line arguments 37 | - 🏷️ Categorize usernames as available, taken, invalid, or purchasable 38 | - 🔒 Graceful handling of API rate limits and errors 39 | - 🌐 Proxy support for bypassing restrictions 40 | 41 | ## 📋 Prerequisites 42 | 43 | - Go 1.16 or higher 44 | - Telegram API credentials (API ID and API Hash) 45 | 46 | ## 🔧 Installation 47 | 48 | 1. Clone this repository: 49 | ```bash 50 | git clone https://github.com/xPOURY4/telegram-username-checker.git 51 | cd telegram-username-checker 52 | ``` 53 | 54 | 2. Install dependencies: 55 | ```bash 56 | go mod tidy 57 | ``` 58 | 59 | ## ⚙️ Configuration 60 | 61 | ### Option 1: Config File (Recommended) 62 | 63 | Create a file named `config.json` with the following structure: 64 | 65 | ```json 66 | { 67 | "api_id": "YOUR_API_ID", 68 | "api_hash": "YOUR_API_HASH", 69 | "phone": "YOUR_PHONE_NUMBER", 70 | "base_delay_seconds": 3, 71 | "max_retries": 3, 72 | "workers": 1, 73 | "use_proxy": false, 74 | "proxy_url": "" 75 | } 76 | ``` 77 | 78 | You can obtain your API ID and API Hash from the [Telegram API Development Tools](https://my.telegram.org/apps). 79 | 80 | ### Option 2: Environment Variables 81 | 82 | Coming in future updates. 83 | 84 | ### Username Input 85 | 86 | Create a file named `usernames.txt` in the same directory. Add the usernames you want to check, one per line. 87 | 88 | ## 🚀 Usage 89 | 90 | ### Basic Usage 91 | 92 | Run the program with default settings: 93 | ```bash 94 | go run main.go 95 | ``` 96 | 97 | ### Command-line Arguments 98 | 99 | The application supports various command-line arguments for customization: 100 | 101 | ``` 102 | --config Path to config file (default: "config.json") 103 | --input Path to input file with usernames (default: "usernames.txt") 104 | --output Directory for output files (default: "results") 105 | --state Path to state file (default: "state.json") 106 | --verbose Enable verbose logging 107 | --generate Generate username combinations 108 | --min-length Minimum username length for generation (default: 3) 109 | --max-length Maximum username length for generation (default: 30) 110 | --interactive Run in interactive mode 111 | ``` 112 | 113 | ### Examples 114 | 115 | Check usernames with verbose logging: 116 | ```bash 117 | go run main.go --verbose 118 | ``` 119 | 120 | Generate and check username combinations: 121 | ```bash 122 | go run main.go --generate --min-length 3 --max-length 5 123 | ``` 124 | 125 | Run in interactive mode: 126 | ```bash 127 | go run main.go --interactive 128 | ``` 129 | 130 | Use custom input and output paths: 131 | ```bash 132 | go run main.go --input custom_usernames.txt --output custom_results 133 | ``` 134 | 135 | ## 🔐 Authentication 136 | 137 | When you run the program, you will be prompted to enter the authentication code sent to your Telegram account. After successful authentication, the program will start checking usernames. 138 | 139 | ## 📊 Results 140 | 141 | Results will be saved to the specified output directory (default: "results"): 142 | 143 | - `available_usernames.txt`: Usernames available for registration 144 | - `taken_usernames.txt`: Usernames already in use 145 | - `invalid_usernames.txt`: Usernames invalid according to Telegram's rules 146 | - `purchasable_usernames.txt`: Usernames available for purchase 147 | - `error_usernames.txt`: Usernames that couldn't be checked due to errors 148 | - `detailed_results.json`: Complete results with additional metadata 149 | 150 | ## 🔄 Advanced Features 151 | 152 | ### State Persistence 153 | 154 | The application maintains a state file that allows you to resume checking from where you left off if the program is interrupted. This feature is particularly useful when checking large lists of usernames. 155 | 156 | ### Adaptive Rate Limiting 157 | 158 | The application implements an intelligent adaptive delay system to handle Telegram's rate limiting. If you encounter `FLOOD_WAIT` errors, the program will automatically wait and adjust its request rate. 159 | 160 | ### Username Generation 161 | 162 | The `--generate` flag enables automatic generation of username variations based on the usernames provided in your input file. This can help you discover available variations of desired usernames. 163 | 164 | ## ❓ Troubleshooting 165 | 166 | - If you encounter persistent `FLOOD_WAIT` errors, try increasing the `base_delay_seconds` in your config file 167 | - Ensure your API credentials are correctly configured 168 | - Check that your Telegram account is not limited or banned 169 | - If the application crashes, it should automatically resume from the last saved state when restarted 170 | 171 | ## 🤝 Contributing 172 | 173 | Contributions are welcome! Please feel free to submit a Pull Request. 174 | 175 | ## ⚠️ Disclaimer 176 | 177 | This tool is not officially associated with Telegram. Use it at your own risk and ensure compliance with Telegram's Terms of Service. 178 | 179 | ## 📞 Contact 180 | 181 |
182 | 183 | [![Telegram](https://img.shields.io/badge/Telegram-xPOURY4-blue?style=for-the-badge&logo=telegram)](https://t.me/xPOURY4) 184 | [![GitHub](https://img.shields.io/badge/GitHub-xPOURY4-black?style=for-the-badge&logo=github)](https://github.com/xPOURY4) 185 | 186 |
187 | 188 | --- 189 | 190 | 191 | 192 | این ابزار پیشرفته به شما امکان می‌دهد با استفاده از API تلگرام، در دسترس بودن نام‌های کاربری تلگرام را بررسی کنید. این برنامه محدودیت‌های نرخ درخواست را مدیریت می‌کند، حالت‌های مختلف عملیاتی را ارائه می‌دهد و گزینه‌های سفارشی‌سازی گسترده‌ای را فراهم می‌کند. 193 | 194 | 195 | 196 | - ✅ بررسی چندین نام کاربری به صورت دسته‌ای یا حالت تعاملی 197 | - 🚀 بررسی چندنخی با تعداد قابل تنظیم کارگر 198 | - 🛡️ مدیریت انطباقی محدودیت نرخ با عقب‌نشینی خودکار 199 | - 💾 پایداری نشست - ادامه بررسی پس از وقفه‌ها 200 | - 🔄 تولید خودکار ترکیبات نام کاربری 201 | - 📊 ردیابی پیشرفت دقیق با نوار پیشرفت بصری 202 | - 📝 نتایج جامع در هر دو فرمت متن و JSON 203 | - 💻 حالت تعاملی برای بررسی نام کاربری در زمان واقعی 204 | - ⚙️ پیکربندی قابل تنظیم از طریق فایل پیکربندی یا آرگومان‌های خط فرمان 205 | - 🏷️ دسته‌بندی نام‌های کاربری به عنوان در دسترس، گرفته شده، نامعتبر یا قابل خرید 206 | - 🔒 مدیریت مناسب محدودیت‌های نرخ API و خطاها 207 | - 🌐 پشتیبانی از پراکسی برای دور زدن محدودیت‌ها 208 | 209 | ## 📋 پیش‌نیازها 210 | 211 | - Go 1.16 یا بالاتر 212 | - اعتبارنامه‌های API تلگرام (API ID و API Hash) 213 | 214 | ## 🔧 نصب 215 | 216 | ۱. کلون کردن این مخزن: 217 | ```bash 218 | git clone https://github.com/xPOURY4/telegram-username-checker.git 219 | cd telegram-username-checker 220 | ``` 221 | 222 | ۲. نصب وابستگی‌ها: 223 | ```bash 224 | go mod tidy 225 | ``` 226 | 227 | ## ⚙️ پیکربندی 228 | 229 | ### گزینه ۱: فایل پیکربندی (توصیه شده) 230 | 231 | یک فایل به نام `config.json` با ساختار زیر ایجاد کنید: 232 | 233 | ```json 234 | { 235 | "api_id": "API_ID_شما", 236 | "api_hash": "API_HASH_شما", 237 | "phone": "شماره_تلفن_شما", 238 | "base_delay_seconds": 3, 239 | "max_retries": 3, 240 | "workers": 1, 241 | "use_proxy": false, 242 | "proxy_url": "" 243 | } 244 | ``` 245 | 246 | شما می‌توانید API ID و API Hash خود را از [ابزارهای توسعه API تلگرام](https://my.telegram.org/apps) دریافت کنید. 247 | 248 | ### گزینه ۲: متغیرهای محیطی 249 | 250 | در بروزرسانی‌های آینده اضافه خواهد شد. 251 | 252 | ### ورودی نام کاربری 253 | 254 | یک فایل به نام `usernames.txt` در همان دایرکتوری ایجاد کنید. نام‌های کاربری که می‌خواهید بررسی کنید را، یکی در هر خط، اضافه کنید. 255 | 256 | ## 🚀 استفاده 257 | 258 | ### استفاده پایه 259 | 260 | اجرای برنامه با تنظیمات پیش‌فرض: 261 | ```bash 262 | go run main.go 263 | ``` 264 | 265 | ### آرگومان‌های خط فرمان 266 | 267 | برنامه از آرگومان‌های مختلف خط فرمان برای سفارشی‌سازی پشتیبانی می‌کند: 268 | 269 | ``` 270 | --config مسیر فایل پیکربندی (پیش‌فرض: "config.json") 271 | --input مسیر فایل ورودی با نام‌های کاربری (پیش‌فرض: "usernames.txt") 272 | --output دایرکتوری برای فایل‌های خروجی (پیش‌فرض: "results") 273 | --state مسیر فایل وضعیت (پیش‌فرض: "state.json") 274 | --verbose فعال‌سازی لاگ تفصیلی 275 | --generate تولید ترکیبات نام کاربری 276 | --min-length حداقل طول نام کاربری برای تولید (پیش‌فرض: 3) 277 | --max-length حداکثر طول نام کاربری برای تولید (پیش‌فرض: 30) 278 | --interactive اجرا در حالت تعاملی 279 | ``` 280 | 281 | ### مثال‌ها 282 | 283 | بررسی نام‌های کاربری با لاگ تفصیلی: 284 | ```bash 285 | go run main.go --verbose 286 | ``` 287 | 288 | تولید و بررسی ترکیبات نام کاربری: 289 | ```bash 290 | go run main.go --generate --min-length 3 --max-length 5 291 | ``` 292 | 293 | اجرا در حالت تعاملی: 294 | ```bash 295 | go run main.go --interactive 296 | ``` 297 | 298 | استفاده از مسیرهای ورودی و خروجی سفارشی: 299 | ```bash 300 | go run main.go --input custom_usernames.txt --output custom_results 301 | ``` 302 | 303 | ## 🔐 احراز هویت 304 | 305 | هنگامی که برنامه را اجرا می‌کنید، از شما خواسته می‌شود کد احراز هویت ارسال شده به حساب تلگرام خود را وارد کنید. پس از احراز هویت موفق، برنامه شروع به بررسی نام‌های کاربری خواهد کرد. 306 | 307 | ## 📊 نتایج 308 | 309 | نتایج در دایرکتوری خروجی مشخص شده (پیش‌فرض: "results") ذخیره خواهند شد: 310 | 311 | - `available_usernames.txt`: نام‌های کاربری در دسترس برای ثبت‌نام 312 | - `taken_usernames.txt`: نام‌های کاربری که در حال حاضر استفاده می‌شوند 313 | - `invalid_usernames.txt`: نام‌های کاربری نامعتبر طبق قوانین تلگرام 314 | - `purchasable_usernames.txt`: نام‌های کاربری در دسترس برای خرید 315 | - `error_usernames.txt`: نام‌های کاربری که به دلیل خطا قابل بررسی نبودند 316 | - `detailed_results.json`: نتایج کامل با متادیتای اضافی 317 | 318 | ## 🔄 ویژگی‌های پیشرفته 319 | 320 | ### پایداری وضعیت 321 | 322 | برنامه یک فایل وضعیت را نگهداری می‌کند که به شما امکان می‌دهد در صورت قطع برنامه، بررسی را از جایی که متوقف شده‌اید ادامه دهید. این ویژگی به ویژه هنگام بررسی لیست‌های بزرگ نام‌های کاربری مفید است. 323 | 324 | ### محدودیت نرخ انطباقی 325 | 326 | برنامه یک سیستم تأخیر انطباقی هوشمند را برای مدیریت محدودیت نرخ تلگرام پیاده‌سازی می‌کند. اگر با خطاهای `FLOOD_WAIT` مواجه شوید، برنامه به طور خودکار منتظر می‌ماند و نرخ درخواست خود را تنظیم می‌کند. 327 | 328 | ### تولید نام کاربری 329 | 330 | پرچم `--generate` امکان تولید خودکار نسخه‌های مختلف نام کاربری بر اساس نام‌های کاربری ارائه شده در فایل ورودی شما را فراهم می‌کند. این می‌تواند به شما کمک کند تا نسخه‌های در دسترس از نام‌های کاربری مورد نظر را کشف کنید. 331 | 332 | ## ❓ عیب‌یابی 333 | 334 | - اگر به طور مداوم با خطاهای `FLOOD_WAIT` مواجه می‌شوید، سعی کنید `base_delay_seconds` را در فایل پیکربندی خود افزایش دهید 335 | - اطمینان حاصل کنید که اعتبارنامه‌های API شما به درستی پیکربندی شده‌اند 336 | - بررسی کنید که حساب تلگرام شما محدود یا مسدود نشده باشد 337 | - اگر برنامه خراب شود، هنگام راه‌اندازی مجدد باید به طور خودکار از آخرین وضعیت ذخیره شده ادامه دهد 338 | 339 | ## 🤝 مشارکت 340 | 341 | مشارکت‌ها مورد استقبال قرار می‌گیرند! لطفاً برای ارسال Pull Request تردید نکنید. 342 | 343 | ## ⚠️ سلب مسئولیت 344 | 345 | این ابزار به طور رسمی با تلگرام مرتبط نیست. از آن با مسئولیت خود استفاده کنید و اطمینان حاصل کنید که با شرایط خدمات تلگرام مطابقت دارد. 346 | 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "math" 10 | "os" 11 | "os/signal" 12 | "path/filepath" 13 | "strings" 14 | "sync" 15 | "syscall" 16 | "time" 17 | 18 | "github.com/gotd/td/telegram" 19 | "github.com/gotd/td/telegram/auth" 20 | "github.com/gotd/td/tg" 21 | "github.com/schollz/progressbar/v3" 22 | ) 23 | 24 | // Config represents application configuration 25 | type Config struct { 26 | ApiID string `json:"api_id"` 27 | ApiHash string `json:"api_hash"` 28 | Phone string `json:"phone"` 29 | BaseDelay int `json:"base_delay_seconds"` 30 | MaxRetries int `json:"max_retries"` 31 | Workers int `json:"workers"` 32 | UseProxy bool `json:"use_proxy"` 33 | ProxyURL string `json:"proxy_url,omitempty"` 34 | } 35 | 36 | // CheckResult represents the result of a username check 37 | type CheckResult struct { 38 | Username string `json:"username"` 39 | Status string `json:"status"` 40 | CheckTime time.Time `json:"check_time"` 41 | Error string `json:"error,omitempty"` 42 | } 43 | 44 | // State represents the current state of the application 45 | type State struct { 46 | Checked map[string]CheckResult `json:"checked"` 47 | LastCheckTime time.Time `json:"last_check_time"` 48 | RemainingTasks []string `json:"remaining_tasks"` 49 | FloodWaitUntil time.Time `json:"flood_wait_until"` 50 | CurrentUsername string `json:"current_username"` 51 | } 52 | 53 | var ( 54 | configFile = flag.String("config", "config.json", "Path to config file") 55 | inputFile = flag.String("input", "usernames.txt", "Path to input file") 56 | outputDir = flag.String("output", "results", "Directory for output files") 57 | stateFile = flag.String("state", "state.json", "Path to state file") 58 | verbose = flag.Bool("verbose", false, "Enable verbose logging") 59 | generateCombos = flag.Bool("generate", false, "Generate username combinations") 60 | minLength = flag.Int("min-length", 3, "Minimum username length") 61 | maxLength = flag.Int("max-length", 30, "Maximum username length") 62 | interactive = flag.Bool("interactive", false, "Interactive mode") 63 | ) 64 | 65 | func displayHeader() { 66 | header := ` 67 | ╔════════════════════════════════════════════╗ 68 | ║ Telegram Username Checker ║ 69 | ║ Created by: xPOURY4 ║ 70 | ║ GitHub: github.com/xPOURY4 ║ 71 | ║ Extended Version (v2.0) ║ 72 | ╚════════════════════════════════════════════╝ 73 | ` 74 | fmt.Println(header) 75 | } 76 | 77 | func main() { 78 | flag.Parse() 79 | displayHeader() 80 | 81 | // Create output directory if it doesn't exist 82 | if err := os.MkdirAll(*outputDir, 0755); err != nil { 83 | log.Fatalf("Failed to create output directory: %v", err) 84 | } 85 | 86 | // Load configuration 87 | config, err := loadConfig(*configFile) 88 | if err != nil { 89 | log.Printf("Warning: Could not load config file: %v", err) 90 | log.Println("Using default configuration. Please create a config file for future use.") 91 | config = &Config{ 92 | ApiID: "YOUR_API_ID", 93 | ApiHash: "YOUR_API_HASH", 94 | Phone: "YOUR_PHONE_NUMBER", 95 | BaseDelay: 3, 96 | MaxRetries: 3, 97 | Workers: 1, 98 | } 99 | } 100 | 101 | // Load or initialize state 102 | state, err := loadState(*stateFile) 103 | if err != nil { 104 | log.Printf("Starting from fresh state: %v", err) 105 | state = &State{ 106 | Checked: make(map[string]CheckResult), 107 | LastCheckTime: time.Now(), 108 | RemainingTasks: []string{}, 109 | } 110 | } 111 | 112 | // Handle interactive mode 113 | if *interactive { 114 | runInteractiveMode(config, state) 115 | return 116 | } 117 | 118 | // Load usernames 119 | usernames, err := loadUsernames(*inputFile, state) 120 | if err != nil { 121 | log.Fatalf("Error loading usernames: %v", err) 122 | } 123 | 124 | // If we should generate combinations 125 | if *generateCombos { 126 | usernames = generateCombinations(usernames, *minLength, *maxLength) 127 | log.Printf("Generated %d username combinations", len(usernames)) 128 | } 129 | 130 | // Initialize Telegram client 131 | ctx, cancel := context.WithCancel(context.Background()) 132 | defer cancel() 133 | 134 | // Set up signal handling for graceful shutdown 135 | setupSignalHandler(cancel, state) 136 | 137 | // Create Telegram client 138 | client := createTelegramClient(config) 139 | 140 | // Run the client 141 | if err := client.Run(ctx, func(ctx context.Context) error { 142 | // Authenticate 143 | if err := authenticate(ctx, client, config.Phone); err != nil { 144 | return err 145 | } 146 | 147 | log.Println("Successfully logged in!") 148 | 149 | // Configure worker pool 150 | workerCount := config.Workers 151 | if workerCount < 1 { 152 | workerCount = 1 153 | } 154 | 155 | // Process usernames 156 | results := processUsernames(ctx, client, usernames, state, config, workerCount) 157 | 158 | // Save results 159 | for status, statusResults := range results { 160 | if len(statusResults) > 0 { 161 | filename := filepath.Join(*outputDir, fmt.Sprintf("%s_usernames.txt", status)) 162 | if err := saveResults(filename, statusResults); err != nil { 163 | log.Printf("Error saving %s usernames: %v", status, err) 164 | } else { 165 | log.Printf("%s usernames have been saved to %s", strings.Title(status), filename) 166 | } 167 | } 168 | } 169 | 170 | // Save detailed JSON results 171 | detailedResults := make([]CheckResult, 0, len(state.Checked)) 172 | for _, result := range state.Checked { 173 | detailedResults = append(detailedResults, result) 174 | } 175 | 176 | detailedFilename := filepath.Join(*outputDir, "detailed_results.json") 177 | if err := saveJSONResults(detailedFilename, detailedResults); err != nil { 178 | log.Printf("Error saving detailed results: %v", err) 179 | } else { 180 | log.Printf("Detailed results saved to %s", detailedFilename) 181 | } 182 | 183 | return nil 184 | }); err != nil { 185 | log.Printf("Error: %v", err) 186 | // Save state before exiting 187 | saveState(*stateFile, state) 188 | os.Exit(1) 189 | } 190 | 191 | // Final state save 192 | saveState(*stateFile, state) 193 | } 194 | 195 | func loadConfig(filename string) (*Config, error) { 196 | data, err := os.ReadFile(filename) 197 | if err != nil { 198 | return nil, err 199 | } 200 | 201 | var config Config 202 | if err := json.Unmarshal(data, &config); err != nil { 203 | return nil, err 204 | } 205 | 206 | return &config, nil 207 | } 208 | 209 | func loadState(filename string) (*State, error) { 210 | data, err := os.ReadFile(filename) 211 | if err != nil { 212 | return nil, err 213 | } 214 | 215 | var state State 216 | if err := json.Unmarshal(data, &state); err != nil { 217 | return nil, err 218 | } 219 | 220 | return &state, nil 221 | } 222 | 223 | func saveState(filename string, state *State) error { 224 | state.LastCheckTime = time.Now() 225 | 226 | data, err := json.MarshalIndent(state, "", " ") 227 | if err != nil { 228 | return err 229 | } 230 | 231 | return os.WriteFile(filename, data, 0644) 232 | } 233 | 234 | func loadUsernames(filename string, state *State) ([]string, error) { 235 | content, err := os.ReadFile(filename) 236 | if err != nil { 237 | return nil, err 238 | } 239 | 240 | lines := strings.Split(string(content), "\n") 241 | var usernames []string 242 | 243 | // Filter out already checked usernames unless they had errors 244 | for _, line := range lines { 245 | username := strings.TrimSpace(line) 246 | if username == "" { 247 | continue 248 | } 249 | 250 | if result, checked := state.Checked[username]; checked { 251 | if result.Status == "error" { 252 | // Retry errors 253 | usernames = append(usernames, username) 254 | } 255 | } else { 256 | usernames = append(usernames, username) 257 | } 258 | } 259 | 260 | // Add any remaining tasks from previous run 261 | usernames = append(state.RemainingTasks, usernames...) 262 | 263 | return usernames, nil 264 | } 265 | 266 | func generateCombinations(seeds []string, minLen, maxLen int) []string { 267 | // This is a simplified version. You can expand this to generate more complex combinations. 268 | var results []string 269 | 270 | // Add the seeds 271 | for _, seed := range seeds { 272 | if len(seed) >= minLen && len(seed) <= maxLen { 273 | results = append(results, seed) 274 | } 275 | } 276 | 277 | // Generate simple variations 278 | suffixes := []string{"", "_", ".", "0", "1", "2", "3", "official", "real", "thereal"} 279 | 280 | for _, seed := range seeds { 281 | for _, suffix := range suffixes { 282 | combined := seed + suffix 283 | if len(combined) >= minLen && len(combined) <= maxLen && !contains(results, combined) { 284 | results = append(results, combined) 285 | } 286 | } 287 | } 288 | 289 | return results 290 | } 291 | 292 | func contains(slice []string, item string) bool { 293 | for _, s := range slice { 294 | if s == item { 295 | return true 296 | } 297 | } 298 | return false 299 | } 300 | 301 | func createTelegramClient(config *Config) *telegram.Client { 302 | // Set up client options 303 | options := telegram.Options{} 304 | 305 | // Add more customization as needed 306 | 307 | return telegram.NewClient(config.ApiID, config.ApiHash, options) 308 | } 309 | 310 | func authenticate(ctx context.Context, client *telegram.Client, phone string) error { 311 | flow := auth.NewFlow( 312 | auth.Constant(phone, "", auth.CodeAuthenticatorFunc(func(ctx context.Context, _ *tg.AuthSentCode) (string, error) { 313 | fmt.Print("Enter the code you received: ") 314 | var code string 315 | _, err := fmt.Scan(&code) 316 | return code, err 317 | })), 318 | auth.SendCodeOptions{}, 319 | ) 320 | 321 | if err := flow.Run(ctx, client.Auth()); err != nil { 322 | return fmt.Errorf("auth flow: %w", err) 323 | } 324 | 325 | return nil 326 | } 327 | 328 | func processUsernames(ctx context.Context, client *telegram.Client, usernames []string, state *State, config *Config, workerCount int) map[string][]CheckResult { 329 | results := make(map[string][]CheckResult) 330 | resultsMu := sync.Mutex{} 331 | 332 | // Check if we need to wait due to previous flood wait 333 | if time.Now().Before(state.FloodWaitUntil) { 334 | waitDuration := time.Until(state.FloodWaitUntil) 335 | log.Printf("Waiting %.2f minutes due to previous rate limit...", waitDuration.Minutes()) 336 | time.Sleep(waitDuration) 337 | } 338 | 339 | // Create task channel 340 | tasks := make(chan string, len(usernames)) 341 | for _, username := range usernames { 342 | tasks <- username 343 | } 344 | close(tasks) 345 | 346 | // Set up progress bar 347 | bar := progressbar.Default(int64(len(usernames))) 348 | 349 | // Create wait group for workers 350 | var wg sync.WaitGroup 351 | 352 | // Rate limiter shared across workers 353 | rateLimiter := make(chan struct{}, 1) 354 | go func() { 355 | baseDelay := time.Duration(config.BaseDelay) * time.Second 356 | ticker := time.NewTicker(baseDelay) 357 | defer ticker.Stop() 358 | 359 | for range ticker.C { 360 | select { 361 | case <-ctx.Done(): 362 | return 363 | case rateLimiter <- struct{}{}: 364 | // Token added to limiter 365 | } 366 | } 367 | }() 368 | 369 | // Launch workers 370 | for i := 0; i < workerCount; i++ { 371 | wg.Add(1) 372 | go func() { 373 | defer wg.Done() 374 | 375 | for username := range tasks { 376 | select { 377 | case <-ctx.Done(): 378 | return 379 | case <-rateLimiter: 380 | // Got permission to proceed 381 | } 382 | 383 | state.CurrentUsername = username 384 | state.RemainingTasks = removeUsername(state.RemainingTasks, username) 385 | 386 | // Try with retries 387 | var status string 388 | var checkErr error 389 | var retryCount int 390 | 391 | for retryCount = 0; retryCount < config.MaxRetries; retryCount++ { 392 | status, checkErr = checkUsername(ctx, client.API(), username) 393 | 394 | if checkErr == nil { 395 | break 396 | } 397 | 398 | if strings.Contains(checkErr.Error(), "FLOOD_WAIT") { 399 | // Parse wait time and set global wait 400 | waitTimeSec := parseFloodWaitSeconds(checkErr.Error()) 401 | waitTime := time.Duration(waitTimeSec) * time.Second 402 | 403 | // Set flood wait until time 404 | state.FloodWaitUntil = time.Now().Add(waitTime) 405 | 406 | log.Printf("Rate limit hit. Waiting for %.2f minutes", waitTime.Minutes()) 407 | 408 | // Save state in case of program termination during wait 409 | saveState(*stateFile, state) 410 | 411 | time.Sleep(waitTime) 412 | 413 | // Increase the base delay for future requests 414 | rateLimiter = make(chan struct{}, 1) 415 | baseDelay := time.Duration(config.BaseDelay) * time.Second * time.Duration(math.Pow(2, float64(retryCount+1))) 416 | ticker := time.NewTicker(baseDelay) 417 | go func() { 418 | defer ticker.Stop() 419 | for range ticker.C { 420 | select { 421 | case <-ctx.Done(): 422 | return 423 | case rateLimiter <- struct{}{}: 424 | // Token added 425 | } 426 | } 427 | }() 428 | } else { 429 | // For other errors, just wait a bit and retry 430 | time.Sleep(time.Second * time.Duration(retryCount+1)) 431 | } 432 | } 433 | 434 | // Create result 435 | result := CheckResult{ 436 | Username: username, 437 | Status: status, 438 | CheckTime: time.Now(), 439 | } 440 | 441 | if checkErr != nil { 442 | result.Status = "error" 443 | result.Error = checkErr.Error() 444 | if *verbose { 445 | log.Printf("Error checking %s (retry %d/%d): %v", username, retryCount, config.MaxRetries, checkErr) 446 | } 447 | } else { 448 | if *verbose { 449 | log.Printf("%s is %s", username, status) 450 | } 451 | } 452 | 453 | // Update results 454 | resultsMu.Lock() 455 | results[result.Status] = append(results[result.Status], result) 456 | state.Checked[username] = result 457 | resultsMu.Unlock() 458 | 459 | // Update progress bar 460 | bar.Add(1) 461 | 462 | // Periodically save state (every 10 usernames) 463 | if len(state.Checked)%10 == 0 { 464 | saveState(*stateFile, state) 465 | } 466 | } 467 | }() 468 | } 469 | 470 | // Wait for all workers to finish 471 | wg.Wait() 472 | 473 | return results 474 | } 475 | 476 | func removeUsername(slice []string, username string) []string { 477 | for i, u := range slice { 478 | if u == username { 479 | return append(slice[:i], slice[i+1:]...) 480 | } 481 | } 482 | return slice 483 | } 484 | 485 | func parseFloodWaitSeconds(errMsg string) int { 486 | // Parse error message like "FLOOD_WAIT_X" where X is seconds 487 | parts := strings.Split(errMsg, "_") 488 | if len(parts) >= 3 { 489 | var seconds int 490 | fmt.Sscanf(parts[2], "%d", &seconds) 491 | if seconds > 0 { 492 | return seconds 493 | } 494 | } 495 | 496 | // Default to 60 seconds if we couldn't parse 497 | return 60 498 | } 499 | 500 | func checkUsername(ctx context.Context, api *tg.Client, username string) (string, error) { 501 | _, err := api.AccountUpdateUsername(ctx, username) 502 | if err != nil { 503 | if strings.Contains(err.Error(), "USERNAME_OCCUPIED") { 504 | return "taken", nil 505 | } 506 | if strings.Contains(err.Error(), "USERNAME_INVALID") { 507 | return "invalid", nil 508 | } 509 | if strings.Contains(err.Error(), "USERNAME_PURCHASE_AVAILABLE") { 510 | return "purchasable", nil 511 | } 512 | return "error", err 513 | } 514 | 515 | _, err = api.AccountUpdateUsername(ctx, "") // Reset username 516 | if err != nil { 517 | return "error", err 518 | } 519 | 520 | return "available", nil 521 | } 522 | 523 | func saveResults(filename string, results []CheckResult) error { 524 | var lines []string 525 | for _, result := range results { 526 | lines = append(lines, result.Username) 527 | } 528 | 529 | content := strings.Join(lines, "\n") 530 | return os.WriteFile(filename, []byte(content), 0644) 531 | } 532 | 533 | func saveJSONResults(filename string, results []CheckResult) error { 534 | data, err := json.MarshalIndent(results, "", " ") 535 | if err != nil { 536 | return err 537 | } 538 | 539 | return os.WriteFile(filename, data, 0644) 540 | } 541 | 542 | func setupSignalHandler(cancel context.CancelFunc, state *State) { 543 | c := make(chan os.Signal, 1) 544 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 545 | 546 | go func() { 547 | <-c 548 | log.Println("\nReceived interrupt signal. Saving state and exiting gracefully...") 549 | saveState(*stateFile, state) 550 | cancel() 551 | os.Exit(0) 552 | }() 553 | } 554 | 555 | func runInteractiveMode(config *Config, state *State) { 556 | ctx := context.Background() 557 | client := createTelegramClient(config) 558 | 559 | if err := client.Run(ctx, func(ctx context.Context) error { 560 | if err := authenticate(ctx, client, config.Phone); err != nil { 561 | return err 562 | } 563 | 564 | log.Println("Successfully logged in!") 565 | log.Println("Interactive mode: Type usernames to check (one per line). Type 'exit' to quit.") 566 | 567 | scanner := os.NewScanner(os.Stdin) 568 | for { 569 | fmt.Print("> ") 570 | if !scanner.Scan() { 571 | break 572 | } 573 | 574 | username := strings.TrimSpace(scanner.Text()) 575 | if username == "exit" { 576 | break 577 | } 578 | 579 | if username == "" { 580 | continue 581 | } 582 | 583 | status, err := checkUsername(ctx, client.API(), username) 584 | if err != nil { 585 | log.Printf("Error checking %s: %v", username, err) 586 | continue 587 | } 588 | 589 | log.Printf("%s is %s", username, status) 590 | 591 | // Save to state 592 | state.Checked[username] = CheckResult{ 593 | Username: username, 594 | Status: status, 595 | CheckTime: time.Now(), 596 | } 597 | } 598 | 599 | return nil 600 | }); err != nil { 601 | log.Printf("Error: %v", err) 602 | } 603 | } 604 | --------------------------------------------------------------------------------