Skip to content

Commit 8a5074e

Browse files
authored
Merge pull request #212 from AmarnathCJD/revert-210-master
Revert "Refactor: Optimize Auth and Scrape Logic for Better Readability and Robustness"
2 parents 6dc3929 + f59e967 commit 8a5074e

File tree

1 file changed

+98
-130
lines changed

1 file changed

+98
-130
lines changed

telegram/auth.go

Lines changed: 98 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
package telegram
44

55
import (
6-
"bufio"
7-
"context"
86
"crypto/rand"
97
"encoding/base64"
108
"encoding/json"
@@ -13,7 +11,6 @@ import (
1311
"math"
1412
"net/http"
1513
"net/url"
16-
"os"
1714
"regexp"
1815
"strconv"
1916
"strings"
@@ -31,8 +28,6 @@ func (c *Client) ConnectBot(botToken string) error {
3128
return c.LoginBot(botToken)
3229
}
3330

34-
const maxRetries = 3
35-
3631
var (
3732
botTokenRegex = regexp.MustCompile(`^\d+:[\w\d_-]+$`)
3833
phoneRegex = regexp.MustCompile(`^\+?\d+$`)
@@ -43,30 +38,34 @@ func (c *Client) AuthPrompt() error {
4338
if au, _ := c.IsAuthorized(); au {
4439
return nil
4540
}
46-
47-
reader := bufio.NewReader(os.Stdin)
48-
for i := range maxRetries {
49-
fmt.Print("Enter phone number (with country code [+42xxx]) or bot token: ")
50-
input, err := reader.ReadString('\n')
51-
if err != nil {
52-
return fmt.Errorf("failed to read input: %w", err)
53-
}
54-
input = strings.TrimSpace(input)
55-
if input == "" {
56-
fmt.Printf("Invalid response, try again [%d/%d]\n", i+1, maxRetries)
57-
continue
58-
}
59-
60-
if botTokenRegex.MatchString(input) {
61-
return c.LoginBot(input)
62-
}
63-
if phoneRegex.MatchString(input) {
64-
_, err := c.Login(input)
65-
return err
41+
var input string
42+
maxRetries := 3
43+
for i := 0; i < maxRetries; i++ {
44+
fmt.Printf("Enter phone number (with country code [+42xxx]) or bot token: ")
45+
fmt.Scanln(&input)
46+
if input != "" {
47+
if botTokenRegex.MatchString(input) {
48+
err := c.LoginBot(input)
49+
if err != nil {
50+
return err
51+
}
52+
} else {
53+
if phoneRegex.MatchString(strings.TrimSpace(input)) {
54+
_, err := c.Login(input)
55+
if err != nil {
56+
return err
57+
}
58+
} else {
59+
fmt.Println("The input is not a valid phone number or bot token, try again [", i+1, "/", maxRetries, "]")
60+
continue
61+
}
62+
}
63+
break
64+
} else {
65+
fmt.Println("Invalid response, try again")
6666
}
67-
fmt.Printf("The input is not a valid phone number or bot token, try again [%d/%d]\n", i+1, maxRetries)
6867
}
69-
return fmt.Errorf("max retries exceeded for authentication")
68+
return nil
7069
}
7170

7271
// Authorize client with bot token
@@ -289,163 +288,139 @@ type ScrapeConfig struct {
289288
}
290289

291290
func (c *Client) ScrapeAppConfig(config ...*ScrapeConfig) (int32, string, bool, error) {
292-
conf := getVariadic(config, &ScrapeConfig{
291+
var conf = getVariadic(config, &ScrapeConfig{
293292
CreateIfNotExists: true,
294293
})
295294

296295
if conf.PhoneNum == "" {
297-
reader := bufio.NewReader(os.Stdin)
298-
fmt.Print("Enter phone number (with country code [+1xxx]): ")
299-
input, err := reader.ReadString('\n')
300-
if err != nil {
301-
return 0, "", false, fmt.Errorf("failed to read phone number: %w", err)
302-
}
303-
conf.PhoneNum = strings.TrimSpace(input)
296+
fmt.Printf("Enter phone number (with country code [+1xxx]): ")
297+
fmt.Scanln(&conf.PhoneNum)
304298
}
305299

306300
if conf.WebCodeCallback == nil {
307301
conf.WebCodeCallback = func() (string, error) {
308-
reader := bufio.NewReader(os.Stdin)
309-
fmt.Print("Enter received web login code: ")
310-
311-
code, err := reader.ReadString('\n')
312-
if err != nil {
313-
return "", fmt.Errorf("failed to read web code: %w", err)
314-
}
315-
return strings.TrimSpace(code), nil
302+
fmt.Printf("Enter received web login code: ")
303+
var password string
304+
fmt.Scanln(&password)
305+
return password, nil
316306
}
317307
}
318308

319309
customClient := &http.Client{
320310
Timeout: time.Second * 10,
321311
}
322312

323-
// Send code request
324313
reqCode, err := http.NewRequest("POST", "https://my.telegram.org/auth/send_password", strings.NewReader("phone="+url.QueryEscape(conf.PhoneNum)))
325314
if err != nil {
326-
return 0, "", false, fmt.Errorf("failed to create code request: %w", err)
315+
return 0, "", false, err
327316
}
317+
328318
reqCode.Header.Set("Content-Type", "application/x-www-form-urlencoded")
329319

330320
respCode, err := customClient.Do(reqCode)
331-
if err != nil {
332-
return 0, "", false, fmt.Errorf("failed to send code request: %w", err)
333-
}
334-
defer respCode.Body.Close()
335-
if respCode.StatusCode != 200 {
336-
return 0, "", false, fmt.Errorf("code request failed with status: %d", respCode.StatusCode)
321+
if err != nil || respCode.StatusCode != 200 {
322+
return 0, "", false, err
337323
}
338324

339325
var result struct {
340326
RandomHash string `json:"random_hash"`
341327
}
328+
342329
if err := json.NewDecoder(respCode.Body).Decode(&result); err != nil {
343-
return 0, "", false, errors.Wrap(err, "failed to decode response, too many requests?")
330+
return 0, "", false, errors.Wrap(err, "Too many requests, try again later")
344331
}
345332

346333
code, err := conf.WebCodeCallback()
347334
if err != nil {
348-
return 0, "", false, fmt.Errorf("failed to get web code: %w", err)
335+
return 0, "", false, err
349336
}
350337

351-
// Login request
352338
reqLogin, err := http.NewRequest("POST", "https://my.telegram.org/auth/login", strings.NewReader("phone="+url.QueryEscape(conf.PhoneNum)+"&random_hash="+result.RandomHash+"&password="+url.QueryEscape(code)))
353339
if err != nil {
354-
return 0, "", false, fmt.Errorf("failed to create login request: %w", err)
340+
return 0, "", false, err
355341
}
342+
356343
reqLogin.Header.Set("Content-Type", "application/x-www-form-urlencoded")
357344

358345
respLogin, err := customClient.Do(reqLogin)
359-
if err != nil {
360-
return 0, "", false, fmt.Errorf("failed to send login request: %w", err)
361-
}
362-
defer respLogin.Body.Close()
363-
if respLogin.StatusCode != 200 {
364-
return 0, "", false, fmt.Errorf("login request failed with status: %d", respLogin.StatusCode)
365-
}
366-
367-
cookies := respLogin.Cookies()
368-
appID, appHash, err := c.scrapeAppDetails(customClient, cookies, conf.CreateIfNotExists)
369-
if err != nil {
346+
if err != nil || respLogin.StatusCode != 200 {
370347
return 0, "", false, err
371348
}
372349

373-
return appID, appHash, true, nil
374-
}
375-
376-
func (c *Client) scrapeAppDetails(client *http.Client, cookies []*http.Cookie, createIfNotExists bool) (int32, string, error) {
377-
appIDRegex := regexp.MustCompile(`<label for="app_id".*?>.*?</label>\s*<div.*?>\s*<span.*?><strong>(\d+)</strong></span>`)
378-
appHashRegex := regexp.MustCompile(`<label for="app_hash".*?>.*?</label>\s*<div.*?>\s*<span.*?>([a-fA-F0-9]+)</span>`)
350+
cookies := respLogin.Cookies()
351+
ALREDY_TRIED_CREATION := false
379352

353+
BackToAppsPage:
380354
reqScrape, err := http.NewRequest("GET", "https://my.telegram.org/apps", nil)
381355
if err != nil {
382-
return 0, "", fmt.Errorf("failed to create scrape request: %w", err)
356+
return 0, "", false, err
383357
}
358+
384359
for _, cookie := range cookies {
385360
reqScrape.AddCookie(cookie)
386361
}
387362

388-
respScrape, err := client.Do(reqScrape)
389-
if err != nil {
390-
return 0, "", fmt.Errorf("failed to send scrape request: %w", err)
391-
}
392-
defer respScrape.Body.Close()
393-
if respScrape.StatusCode != 200 {
394-
return 0, "", fmt.Errorf("scrape request failed with status: %d", respScrape.StatusCode)
363+
respScrape, err := customClient.Do(reqScrape)
364+
if err != nil || respScrape.StatusCode != 200 {
365+
return 0, "", false, err
395366
}
396367

397368
body, err := io.ReadAll(respScrape.Body)
398369
if err != nil {
399-
return 0, "", fmt.Errorf("failed to read scrape response: %w", err)
370+
return 0, "", false, err
400371
}
401372

373+
appIDRegex := regexp.MustCompile(`<label for="app_id".*?>.*?</label>\s*<div.*?>\s*<span.*?><strong>(\d+)</strong></span>`)
374+
appHashRegex := regexp.MustCompile(`<label for="app_hash".*?>.*?</label>\s*<div.*?>\s*<span.*?>([a-fA-F0-9]+)</span>`)
375+
402376
appID := appIDRegex.FindStringSubmatch(string(body))
403377
appHash := appHashRegex.FindStringSubmatch(string(body))
404378

405-
if len(appID) < 2 || len(appHash) < 2 {
406-
if !createIfNotExists || strings.Contains(string(body), "Create new application") {
407-
hiddenHashRegex := regexp.MustCompile(`<input type="hidden" name="hash" value="([a-fA-F0-9]+)"\/>`)
408-
hiddenHash := hiddenHashRegex.FindStringSubmatch(string(body))
409-
if len(hiddenHash) < 2 {
410-
return 0, "", errors.New("creation hash not found, try manual creation")
411-
}
379+
if len(appID) < 2 || len(appHash) < 2 || strings.Contains(string(body), "Create new application") && !ALREDY_TRIED_CREATION {
380+
ALREDY_TRIED_CREATION = true
381+
// assume app is not created, create app
382+
hiddenHashRegex := regexp.MustCompile(`<input type="hidden" name="hash" value="([a-fA-F0-9]+)"\/>`)
383+
hiddenHash := hiddenHashRegex.FindStringSubmatch(string(body))
384+
if len(hiddenHash) < 2 {
385+
return 0, "", false, errors.New("creation hash not found, try manual creation")
386+
}
412387

413-
appRandomSuffix := make([]byte, 8)
414-
if _, err := rand.Read(appRandomSuffix); err != nil {
415-
return 0, "", fmt.Errorf("failed to generate random suffix: %w", err)
416-
}
388+
appRandomSuffix := make([]byte, 8)
389+
_, err := rand.Read(appRandomSuffix)
417390

418-
reqCreateApp, err := http.NewRequest("POST", "https://my.telegram.org/apps/create", strings.NewReader("hash="+hiddenHash[1]+"&app_title=AppForGogram"+string(appRandomSuffix)+"&app_shortname=gogramapp"+string(appRandomSuffix)+"&app_platform=android&app_url=https%3A%2F%2Fgogram.vercel.app&app_desc=ForGoGram"+string(appRandomSuffix)))
419-
if err != nil {
420-
return 0, "", fmt.Errorf("failed to create app creation request: %w", err)
421-
}
422-
reqCreateApp.Header.Set("Content-Type", "application/x-www-form-urlencoded")
423-
for _, cookie := range cookies {
424-
reqCreateApp.AddCookie(cookie)
425-
}
391+
if err != nil {
392+
return 0, "", false, err
393+
}
426394

427-
respCreateApp, err := client.Do(reqCreateApp)
428-
if err != nil {
429-
return 0, "", fmt.Errorf("failed to send app creation request: %w", err)
430-
}
431-
defer respCreateApp.Body.Close()
432-
if respCreateApp.StatusCode != 200 {
433-
return 0, "", fmt.Errorf("app creation request failed with status: %d", respCreateApp.StatusCode)
434-
}
395+
reqCreateApp, err := http.NewRequest("POST", "https://my.telegram.org/apps/create", strings.NewReader("hash="+hiddenHash[1]+"&app_title=AppForGogram"+string(appRandomSuffix)+"&app_shortname=gogramapp"+string(appRandomSuffix)+"&app_platform=android&app_url=https%3A%2F%2Fgogram.vercel.app&app_desc=ForGoGram"+string(appRandomSuffix)))
396+
if err != nil {
397+
return 0, "", false, err
398+
}
435399

436-
return c.scrapeAppDetails(client, cookies, false)
400+
reqCreateApp.Header.Set("Content-Type", "application/x-www-form-urlencoded")
401+
402+
for _, cookie := range cookies {
403+
reqCreateApp.AddCookie(cookie)
404+
}
405+
406+
respCreateApp, err := customClient.Do(reqCreateApp)
407+
if err != nil || respCreateApp.StatusCode != 200 {
408+
return 0, "", false, err
437409
}
438-
return 0, "", errors.New("failed to scrape app ID or hash")
410+
411+
goto BackToAppsPage
439412
}
440413

441414
appIdNum, err := strconv.Atoi(appID[1])
442415
if err != nil {
443-
return 0, "", fmt.Errorf("failed to parse app ID: %w", err)
444-
} else if appIdNum > math.MaxInt32 || appIdNum < math.MinInt32 {
445-
return 0, "", errors.New("app ID is out of range")
416+
return 0, "", false, err
446417
}
447418

448-
return int32(appIdNum), appHash[1], nil
419+
if appIdNum > math.MaxInt32 || appIdNum < math.MinInt32 {
420+
return 0, "", false, errors.New("app id is out of range")
421+
}
422+
423+
return int32(appIdNum), appHash[1], true, nil
449424
}
450425

451426
func (c *Client) AcceptTOS() (bool, error) {
@@ -457,13 +432,11 @@ func (c *Client) AcceptTOS() (bool, error) {
457432
case *HelpTermsOfServiceUpdateObj:
458433
fmt.Println(tos.TermsOfService.Text)
459434
fmt.Println("Do you accept the TOS? (y/n)")
460-
461435
var input string
462436
fmt.Scanln(&input)
463437
if input != "y" {
464438
return false, nil
465439
}
466-
467440
return c.HelpAcceptTermsOfService(tos.TermsOfService.ID)
468441
default:
469442
return false, nil
@@ -584,19 +557,14 @@ func (q *QrToken) Recreate() (*QrToken, error) {
584557
return q, err
585558
}
586559

587-
func (q *QrToken) Wait(ctx context.Context, timeout ...int32) error {
588-
const defaultTimeout int32 = 600 // 10 minutes
589-
q.Timeout = getVariadic(timeout, defaultTimeout)
590-
591-
ctx, cancel := context.WithTimeout(ctx, time.Duration(q.Timeout)*time.Second)
592-
defer cancel()
593-
560+
func (q *QrToken) Wait(timeout ...int32) error {
561+
const def int32 = 600 // 10 minutes
562+
q.Timeout = getVariadic(timeout, def)
594563
ch := make(chan int)
595564
ev := q.client.AddRawHandler(&UpdateLoginToken{}, func(update Update, client *Client) error {
596565
ch <- 1
597566
return nil
598567
})
599-
600568
select {
601569
case <-ch:
602570
go q.client.removeHandle(ev)
@@ -625,19 +593,20 @@ func (q *QrToken) Wait(ctx context.Context, timeout ...int32) error {
625593
}
626594
}
627595
return nil
628-
case <-ctx.Done():
596+
case <-time.After(time.Duration(q.Timeout) * time.Second):
629597
go q.client.removeHandle(ev)
630598
return errors.New("qr login timed out")
631599
}
632600
}
633601

634602
func (c *Client) QRLogin(IgnoreIDs ...int64) (*QrToken, error) {
635603
// Get QR code
636-
qr, err := c.AuthExportLoginToken(c.AppID(), c.AppHash(), IgnoreIDs)
604+
var ignoreIDs []int64
605+
ignoreIDs = append(ignoreIDs, IgnoreIDs...)
606+
qr, err := c.AuthExportLoginToken(c.AppID(), c.AppHash(), ignoreIDs)
637607
if err != nil {
638-
return nil, fmt.Errorf("failed to export QR token: %v", err)
608+
return nil, err
639609
}
640-
641610
var (
642611
qrToken []byte
643612
expiresIn int32 = 60
@@ -650,15 +619,14 @@ func (c *Client) QRLogin(IgnoreIDs ...int64) (*QrToken, error) {
650619
qrToken = qr.Token
651620
expiresIn = qr.Expires
652621
}
653-
654622
// Get QR code URL
655623
qrURL := base64.RawURLEncoding.EncodeToString(qrToken)
656624
return &QrToken{
657625
Token: qrToken,
658626
Url: fmt.Sprintf("tg://login?token=%s", qrURL),
659627
ExpiresIn: expiresIn,
660628
client: c,
661-
IgnoredIDs: IgnoreIDs,
629+
IgnoredIDs: ignoreIDs,
662630
}, nil
663631
}
664632

0 commit comments

Comments
 (0)