├── 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 | 
6 | 
7 | 
8 | 
9 | 
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 | 
21 | 
22 | 
23 | 
24 | 
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 | [](https://t.me/xPOURY4)
184 | [](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 |
--------------------------------------------------------------------------------