From 41fc0b3733bedac27c94a25b8aa620f1552001c2 Mon Sep 17 00:00:00 2001 From: tioffs Date: Fri, 15 Oct 2021 04:33:20 +0300 Subject: [PATCH] optimization, error checking --- README.md | 50 +++++++++++----- auth.go | 153 ++++++++++++++++++++++++++++++------------------ example/main.go | 29 +++------ 3 files changed, 138 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index b2282f2..779ff80 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,14 @@ ```go package main -import "github.com/tioffs/tgah" +import ( + "context" + "fmt" + "os" + "time" + + "github.com/tioffs/tgah" +) func main() { // phone number no + @@ -28,27 +35,40 @@ func main() { // set setting tgah.Setting(int32(botID), domain) // send push notify user (Auth) - if err := tgah.SendPhoneTelegram(phone); err != nil { - panic(err) + if confirm := tgah.SendPhoneTelegram(context.Background(), phone); + confirm.Error != nil || confirm.Status != tgah.Success { + panic(confirm.Error) } // check accept user is auth you bot - user, err := tgah.ChecksIsAcceptUserAuth(phone) - if err != nil { - panic(err) + for { + <-time.After(3 * time.Second) + confirm := tgah.ChecksIsAcceptUserAuth(context.Background(), phone) + if confirm.Error != nil { + panic(confirm.Error) + } + switch confirm.Status { + case tgah.Success: + fmt.Println(confirm.User) + break + case tgah.Cancel: + panic(confirm.Error) + case tgah.Pending: + println(tgah.Pending) + } } - println(user) } ``` #### User profile ```go -type User struct { - ID int `json:"id"` - FirstName string `json:"first_name"` - Username string `json:"username"` - PhotoURL string `json:"photo_url"` - AuthDate int `json:"auth_date"` - Hash string `json:"hash"` - Phone string `json:"phone"` +type user struct { + ID int `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Username string `json:"username"` + PhotoURL string `json:"photo_url"` + AuthDate int `json:"auth_date"` + Hash string `json:"hash"` + Phone string `json:"phone"` } ``` diff --git a/auth.go b/auth.go index 9ab69a6..d06afd9 100644 --- a/auth.go +++ b/auth.go @@ -7,12 +7,13 @@ import ( "io/ioutil" "net/http" "regexp" + "strconv" "strings" "time" - - "github.com/pkg/errors" ) +type status string + const ( // regxConfirm regx parse hash. regxConfirm = `hash=([a-z0-9]+)` @@ -30,22 +31,26 @@ const ( confirmURL = oauthURL + "auth/push?bot_id=%d&origin=%s&request_access=write" // sendPhoneURL endpoint. sendPhoneURL = oauthURL + "auth/request?bot_id=%d&origin=%s&embed=1&request_access=write" - // phone send params body. - phone = "phone=%s" // cleanupInterval clean cache time second. cleanupInterval = 10 // expires default time second cache delete. expires = 60 // cookiesDELETE const status cookie header. cookiesDELETE = "DELETED" - // declined User cancel auth. - declined = "Declined by the User" + // declined user Cancel auth. + declined = "Declined by the user" // trueStr check string true. trueStr = "true" + // Pending status. + Pending status = "pending" + // Cancel status. + Cancel status = "cancel" + // Success status. + Success status = "success" ) -// User struct telegram oauth info User. -type User struct { +// user struct telegram oauth info user. +type user struct { ID int `json:"id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` @@ -61,11 +66,17 @@ type item struct { Created int64 } -type confirmUser struct { - PhoneNumber string +// Confirm struct status, error checks. +type Confirm struct { + Phone string `json:"-"` + User *user `json:"user,omitempty"` + Status status `json:"status"` + Error *string `json:"error,omitempty"` + hash string + context context.Context } -// store memory auth telegram User. +// store memory auth telegram user. var store = make(map[string]item) // domainUrl site connect bot telegram auth. @@ -74,7 +85,7 @@ var domainURL string // bot id bot telegram. var bot int32 -// statusCache is auto delete cache not auth User. +// statusCache is auto delete cache not auth user. var statusCache = false // cache goroutine checking and delete item cache memory. @@ -121,105 +132,131 @@ func Setting(botID int32, domain string) { bot = botID } -// SendPhoneTelegram send push notify telegram User phone. -func SendPhoneTelegram(userPhone string) (err error) { - confirm := &confirmUser{PhoneNumber: userPhone} - phone := fmt.Sprintf(phone, userPhone) - response, err := confirm.httpClient(fmt.Sprintf(sendPhoneURL, bot, domainURL), http.MethodPost, &phone) +// SendPhoneTelegram send push notify telegram user phone. +func SendPhoneTelegram(ctx context.Context, userPhone string) (confirm *Confirm) { + confirm = &Confirm{Phone: userPhone, context: ctx, Status: Cancel} + response, err := confirm.httpClient(fmt.Sprintf(sendPhoneURL, bot, domainURL), http.MethodPost, confirm.pointerString(fmt.Sprintf("phone=%s", userPhone))) if err != nil { + confirm.Error = confirm.pointerString(err.Error()) + return } + statusSendNotify, err := strconv.ParseBool(string(response)) + if err != nil { + confirm.Error = confirm.pointerString(string(response)) - if string(response) != "true" { - err = errors.New(string(response)) + return + } + if !statusSendNotify { + confirm.Error = confirm.pointerString(string(response)) return } + confirm.Status = Success + return } -// ChecksIsAcceptUserAuth Checks Accept User Authentication. -func ChecksIsAcceptUserAuth(userPhone string) (userProfile *User, err error) { - confirm := &confirmUser{PhoneNumber: userPhone} - status, err := confirm.inAccept() - if err != nil { +// ChecksIsAcceptUserAuth Checks Accept user Authentication. +func ChecksIsAcceptUserAuth(ctx context.Context, userPhone string) (confirm *Confirm) { + confirm = &Confirm{Phone: userPhone, context: ctx} + confirm.inAccept() + if confirm.Status != Success { return } - if !status { + confirm.parseHash() + if confirm.Status == Cancel { return } - hash, err := confirm.parseHash() - if err != nil { - return - } - - if err = confirm.isConfirm(hash); err != nil { + confirm.isConfirm() + if confirm.Status == Cancel { return } - userProfile, err = confirm.parseUserProfile() + confirm.parseUserProfile() return } -func (c *confirmUser) inAccept() (status bool, err error) { - status = false - check, err := c.httpClient(fmt.Sprintf(loginURL, bot, domainURL), http.MethodPost, nil) +func (c *Confirm) inAccept() { + response, err := c.httpClient(fmt.Sprintf(loginURL, bot, domainURL), http.MethodPost, nil) if err != nil { + c.Error = c.pointerString(err.Error()) + c.Status = Cancel + return } - switch string(check) { + switch string(response) { case declined: - err = errors.New(declined) + c.Status = Cancel + c.Error = c.pointerString(string(response)) case trueStr: - status = true + c.Status = Success + default: + c.Status = Pending } - - return } -func (c *confirmUser) parseHash() (hash string, err error) { +func (c *Confirm) parseHash() { parseConfirm, err := c.httpClient(fmt.Sprintf(authURL, bot, domainURL), http.MethodGet, nil) if err != nil { + c.Error = c.pointerString(err.Error()) + c.Status = Cancel + return } confirm := regexp.MustCompile(regxConfirm) - hash = confirm.FindString(string(parseConfirm)) - - return + c.hash = confirm.FindString(string(parseConfirm)) + if c.hash == "" { + c.Status = Cancel + } + c.Status = Success } -func (c *confirmUser) isConfirm(hash string) (err error) { - _, err = c.httpClient(fmt.Sprintf(confirmHash, bot, domainURL, hash), http.MethodGet, nil) - - return +func (c *Confirm) isConfirm() { + _, err := c.httpClient(fmt.Sprintf(confirmHash, bot, domainURL, c.hash), http.MethodGet, nil) + if err != nil { + c.Status = Cancel + c.Error = c.pointerString(err.Error()) + } + c.Status = Success } -func (c *confirmUser) parseUserProfile() (u *User, err error) { +func (c *Confirm) parseUserProfile() { responseUserRaw, err := c.httpClient(fmt.Sprintf(confirmURL, bot, domainURL), http.MethodGet, nil) if err != nil { + c.Error = c.pointerString(err.Error()) + c.Status = Cancel + return } RegxUserRaw := regexp.MustCompile(regxUserJSON) UserRaw := RegxUserRaw.FindString(string(responseUserRaw)) - err = json.Unmarshal([]byte(UserRaw), &u) - if err == nil { - u.Phone = c.PhoneNumber + err = json.Unmarshal([]byte(UserRaw), &c.User) + if err != nil { + c.Error = c.pointerString(err.Error()) + c.Status = Cancel + + return } + c.User.Phone = c.Phone + c.Status = Success +} - return +func (c *Confirm) pointerString(s string) *string { + return &s } -func (c *confirmUser) httpClient(url, method string, body *string) (responseBody []byte, err error) { +func (c *Confirm) httpClient(url, method string, body *string) (responseBody []byte, err error) { //nolint:cyclop client := http.Client{} - request, err := http.NewRequestWithContext(context.Background(), method, url, nil) + request, err := http.NewRequestWithContext(c.context, method, url, nil) if body != nil { request.Body = ioutil.NopCloser(strings.NewReader(*body)) } if err != nil { return } - cookie, is := getCookies(c.PhoneNumber) + cookie, is := getCookies(c.Phone) if is { for key, value := range cookie { request.AddCookie(&http.Cookie{ @@ -232,7 +269,7 @@ func (c *confirmUser) httpClient(url, method string, body *string) (responseBody request.Header.Add("origin", domainURL) request.Header.Add("referer", domainURL) - request.Header.Add("User-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4606.81 Safari/537.36") + request.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4606.81 Safari/537.36") if method == http.MethodPost { request.Header.Add("content-type", "application/x-www-form-urlencoded") } @@ -251,7 +288,7 @@ func (c *confirmUser) httpClient(url, method string, body *string) (responseBody cookie[c.Name] = c.Value } } - setCookies(c.PhoneNumber, cookie) + setCookies(c.Phone, cookie) responseBody, err = ioutil.ReadAll(response.Body) if err != nil { diff --git a/example/main.go b/example/main.go index fec9434..7675c70 100644 --- a/example/main.go +++ b/example/main.go @@ -24,7 +24,7 @@ var input = document.querySelector('input[name="phone"]'); function send(){ phone = input.value fetch("/telegram?act=send&phone="+phone, {}).then(r=> r.json().then(data => { -if (data.status) { +if (data.status == "success") { button.setAttribute("disabled", true) input.setAttribute("disabled", true) check() @@ -35,8 +35,9 @@ if (data.error) {alert(data.error)} function check(){ fetch("/telegram?act=check&phone="+phone, {}).then(r=> r.json().then(data => { if (data.error) {alert(data.error)} -if (data.pending) {setTimeout(check, 3000)} -if (data.id) {document.querySelector('#user').innerHTML = JSON.stringify(data)} +if (data.status == "cancel") {alert(data.error + data.status)} +if (data.status == "pending") {setTimeout(check, 3000)} +if (data.status == "success") {document.querySelector('#user').innerHTML = JSON.stringify(data)} })).catch(error => { alert(error) })} @@ -54,25 +55,11 @@ func main() { http.HandleFunc("/telegram", func(w http.ResponseWriter, r *http.Request) { switch r.URL.Query().Get("act") { case "send": - if err := tgah.SendPhoneTelegram(r.URL.Query().Get("phone")); err != nil { - ResponseJSON(w, err) - - return - } - ResponseJSON(w, map[string]bool{"status": true}, http.StatusOK) + confirm := tgah.SendPhoneTelegram(r.Context(), r.URL.Query().Get("phone")) + ResponseJSON(w, confirm, http.StatusOK) case "check": - user, err := tgah.ChecksIsAcceptUserAuth(r.URL.Query().Get("phone")) - if err != nil { - ResponseJSON(w, err) - - return - } - if user != nil { - ResponseJSON(w, user, http.StatusOK) - - return - } - ResponseJSON(w, map[string]bool{"pending": true}, http.StatusOK) + confirm := tgah.ChecksIsAcceptUserAuth(r.Context(), r.URL.Query().Get("phone")) + ResponseJSON(w, confirm, http.StatusOK) } })