1
0

Merge remote-tracking branch 'upgrade/master'

This commit is contained in:
root 2023-07-17 09:10:40 +08:00
commit 4813ac39e3
10 changed files with 26 additions and 436 deletions

View File

@ -17,9 +17,7 @@
### 获取PUID
PUID就是Personal User ID。这是这个项目中一个特色其他项目没遇到需要这个的不过还是弄一下吧。可能直接访问官网才要使用或搭建的绕过WAF的代理不需要目前第三方代理源已经可以自带绕过WAF
获取链接是 https://chat.openai.com/api/auth/session 打开这个URL会得到一个JSON最前面的 ```{"user":{"id":"user-XXXX","name":"XXXX","email":"XXX",``` 这里面的 user.id 就是我要的PUID至少我的实践是这个我并没有找到作者具体的说明(有可能需要PLUS用户权限作者的说明是用于绕过CloudFlare的速率限制)
`_puid` cookie.
### 获取Access Token
目前有多种方法和原理,这部分内容可以参考 [TOKEN中文手册](docs/TOKEN_CN.md)

View File

@ -15,7 +15,7 @@ func ConvertAPIRequest(api_request official_types.APIRequest) chatgpt_types.Chat
chatgpt_request.Model = "text-davinci-002-render-sha"
}
if strings.HasPrefix(api_request.Model, "gpt-4") {
token, err := arkose.GetOpenAIToken()
token, _, err := arkose.GetOpenAIToken()
if err == nil {
chatgpt_request.ArkoseToken = token
} else {

View File

@ -11,8 +11,5 @@ services:
SERVER_HOST: 0.0.0.0
SERVER_PORT: 8080
ADMIN_PASSWORD: TotallySecurePassword
# Reverse Proxy - Available on accessToken
API_REVERSE_PROXY: https://bypass.churchless.tech/api/conversation
# If the parameter API_REVERSE_PROXY is empty, the default request URL is https://chat.openai.com/backend-api/conversation, and the PUID is required.
# You can get your PUID for Plus account from the following link: https://chat.openai.com/api/auth/session.
# If the parameter API_REVERSE_PROXY is empty, the default request URL is https://chat.openai.com/backend-api/conversation, and the PUID is <NOT> equired.
PUID: xxx

View File

@ -69,7 +69,7 @@ GO-ChatGPT-API项目 https://github.com/linweiyuan/go-chatgpt-api
```
curl http://127.0.0.1:8080/chatgpt/conversation \
curl http://127.0.0.1:8080/chatgpt/backend-api/conversation \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
@ -122,4 +122,4 @@ curl -X PATCH \
http://127.0.0.1:10080/admin/tokens
```
要清理Token直接停用删除Docker容器后重新构建运行容器即可
要清理Token直接停用删除Docker容器后重新构建运行容器即可

4
go.mod
View File

@ -3,9 +3,9 @@ module freechatgpt
go 1.20
require (
github.com/acheong08/OpenAIAuth v0.0.0-20230609193408-55a0f33f1057
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507
github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd
github.com/acheong08/funcaptcha v0.2.1-0.20230629044031-084e7dfaffef
github.com/acheong08/funcaptcha v1.2.2-0.20230702093045-02d1fbf1b0e8
github.com/bogdanfinn/fhttp v0.5.23
github.com/bogdanfinn/tls-client v1.4.0
github.com/gin-gonic/gin v1.9.1

4
go.sum
View File

@ -1,5 +1,7 @@
github.com/acheong08/OpenAIAuth v0.0.0-20230609193408-55a0f33f1057 h1:AmqKpClFTUHREYekSfSy3Yn7MR/oc6WTfKtEyzB/J7o=
github.com/acheong08/OpenAIAuth v0.0.0-20230609193408-55a0f33f1057/go.mod h1:ES3Dh9hnbR2mDPlNTagj5e3b4nXECd4tbAjVgxggXEE=
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507 h1:1kZEmE1DeQEeIMHhQUqn+NqOdApF9BIv835ox09E2k8=
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507/go.mod h1:bkiXtklBFVpWHyWTys6Zhqb521i/gtT8cIUKWVx2m/M=
github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd h1:oIpfrRhD7Jus41dotbK+SQjWSFRnf1cLZUYCZpF/o/4=
github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd/go.mod h1:0yO7neMeJLvKk/B/fq5votDY8rByrOPDubpvU+6saKo=
github.com/acheong08/funcaptcha v0.2.1-0.20230628085018-57a8c9b81bc8 h1:COt2vPM8gz+PiUjeWH1enYPfMM3FiM/HFELqU6nO2PQ=
@ -8,6 +10,8 @@ github.com/acheong08/funcaptcha v0.2.1-0.20230628154248-28b05efd8e52 h1:cZ9RUUz+
github.com/acheong08/funcaptcha v0.2.1-0.20230628154248-28b05efd8e52/go.mod h1:VupbjtVAODvgyAB3Zo86fOA53G+UAmaV/Rk9jUCGuTU=
github.com/acheong08/funcaptcha v0.2.1-0.20230629044031-084e7dfaffef h1:B5fq4j+Qiu/6vay/70BG9mBuBgF28CnA4MTx1+J2V+o=
github.com/acheong08/funcaptcha v0.2.1-0.20230629044031-084e7dfaffef/go.mod h1:VupbjtVAODvgyAB3Zo86fOA53G+UAmaV/Rk9jUCGuTU=
github.com/acheong08/funcaptcha v1.2.2-0.20230702093045-02d1fbf1b0e8 h1:6VDA9GYZeDBc5xB6mmG9M/U2scvKCxd8izGxk6qHWmY=
github.com/acheong08/funcaptcha v1.2.2-0.20230702093045-02d1fbf1b0e8/go.mod h1:azmXv2Mfbw5eBOnMw5e1yTMNVyku17klWhGtkHwm7PY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bogdanfinn/fhttp v0.5.23 h1:4Xb5OjYArB8GpnUw4A4r5jmt8UW0/Cvey3R9nS2dC9U=

View File

@ -1,417 +0,0 @@
package auth
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/url"
"regexp"
"strings"
"crypto/rand"
http "github.com/bogdanfinn/fhttp"
tls_client "github.com/bogdanfinn/tls-client"
pkce "github.com/nirasan/go-oauth-pkce-code-verifier"
)
type Error struct {
Location string
StatusCode int
Details string
Error error
}
func NewError(location string, statusCode int, details string, err error) *Error {
return &Error{
Location: location,
StatusCode: statusCode,
Details: details,
Error: err,
}
}
type Authenticator struct {
EmailAddress string
Password string
Proxy string
Session tls_client.HttpClient
UserAgent string
State string
URL string
Verifier_code string
Verifier_challenge string
AuthRequest AuthRequest
AuthResult AuthResult
}
type AuthRequest struct {
ClientID string `json:"client_id"`
Scope string `json:"scope"`
ResponseType string `json:"response_type"`
RedirectURL string `json:"redirect_url"`
Audience string `json:"audience"`
Prompt string `json:"prompt"`
State string `json:"state"`
CodeChallenge string `json:"code_challenge"`
CodeChallengeMethod string `json:"code_challenge_method"`
}
type AuthResult struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
PUID string `json:"puid"`
}
func NewAuthDetails(challenge string) AuthRequest {
// Generate state (secrets.token_urlsafe(32))
b := make([]byte, 32)
rand.Read(b)
state := base64.URLEncoding.EncodeToString(b)
return AuthRequest{
ClientID: "pdlLIX2Y72MIl2rhLhTE9VV9bN905kBh",
Scope: "openid email profile offline_access model.request model.read organization.read",
ResponseType: "code",
RedirectURL: "com.openai.chat://auth0.openai.com/ios/com.openai.chat/callback",
Audience: "https://api.openai.com/v1",
Prompt: "login",
State: state,
CodeChallenge: challenge,
CodeChallengeMethod: "S256",
}
}
func NewAuthenticator(emailAddress, password, proxy string) *Authenticator {
auth := &Authenticator{
EmailAddress: emailAddress,
Password: password,
Proxy: proxy,
UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
}
jar := tls_client.NewCookieJar()
options := []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(20),
tls_client.WithClientProfile(tls_client.Firefox_102),
tls_client.WithNotFollowRedirects(),
tls_client.WithCookieJar(jar), // create cookieJar instance and pass it as argument
// Proxy
tls_client.WithProxyUrl(proxy),
}
auth.Session, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
// PKCE
verifier, _ := pkce.CreateCodeVerifier()
auth.Verifier_code = verifier.String()
auth.Verifier_challenge = verifier.CodeChallengeS256()
auth.AuthRequest = NewAuthDetails(auth.Verifier_challenge)
return auth
}
func (auth *Authenticator) URLEncode(str string) string {
return url.QueryEscape(str)
}
func (auth *Authenticator) Begin() *Error {
// Just realized that the client id is hardcoded in the JS file
return auth.partOne()
}
func (auth *Authenticator) partOne() *Error {
auth_url := "https://auth0.openai.com/authorize"
headers := map[string]string{
"User-Agent": auth.UserAgent,
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "*/*",
"Sec-Gpc": "1",
"Accept-Language": "en-US,en;q=0.8",
"Origin": "https://chat.openai.com",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Referer": "https://chat.openai.com/auth/login",
"Accept-Encoding": "gzip, deflate",
}
// Construct payload
payload := url.Values{
"client_id": {auth.AuthRequest.ClientID},
"scope": {auth.AuthRequest.Scope},
"response_type": {auth.AuthRequest.ResponseType},
"redirect_uri": {auth.AuthRequest.RedirectURL},
"audience": {auth.AuthRequest.Audience},
"prompt": {auth.AuthRequest.Prompt},
"state": {auth.AuthRequest.State},
"code_challenge": {auth.AuthRequest.CodeChallenge},
"code_challenge_method": {auth.AuthRequest.CodeChallengeMethod},
}
auth_url = auth_url + "?" + payload.Encode()
req, _ := http.NewRequest("GET", auth_url, nil)
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := auth.Session.Do(req)
if err != nil {
return NewError("part_one", 0, "Failed to send request", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return NewError("part_one", 0, "Failed to read body", err)
}
if resp.StatusCode == 302 {
return auth.partTwo("https://auth0.openai.com" + resp.Header.Get("Location"))
} else {
return NewError("part_one", resp.StatusCode, string(body), fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partTwo(url string) *Error {
headers := map[string]string{
"Host": "auth0.openai.com",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Connection": "keep-alive",
"User-Agent": auth.UserAgent,
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://ios.chat.openai.com/",
}
req, _ := http.NewRequest("GET", url, nil)
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := auth.Session.Do(req)
if err != nil {
return NewError("part_two", 0, "Failed to make request", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 302 || resp.StatusCode == 200 {
stateRegex := regexp.MustCompile(`state=(.*)`)
stateMatch := stateRegex.FindStringSubmatch(string(body))
if len(stateMatch) < 2 {
return NewError("part_two", 0, "Could not find state in response", fmt.Errorf("error: Check details"))
}
state := strings.Split(stateMatch[1], `"`)[0]
return auth.partThree(state)
} else {
return NewError("__part_two", resp.StatusCode, string(body), fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partThree(state string) *Error {
url := fmt.Sprintf("https://auth0.openai.com/u/login/identifier?state=%s", state)
emailURLEncoded := auth.URLEncode(auth.EmailAddress)
payload := fmt.Sprintf(
"state=%s&username=%s&js-available=false&webauthn-available=true&is-brave=false&webauthn-platform-available=true&action=default",
state, emailURLEncoded,
)
headers := map[string]string{
"Host": "auth0.openai.com",
"Origin": "https://auth0.openai.com",
"Connection": "keep-alive",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"User-Agent": auth.UserAgent,
"Referer": fmt.Sprintf("https://auth0.openai.com/u/login/identifier?state=%s", state),
"Accept-Language": "en-US,en;q=0.9",
"Content-Type": "application/x-www-form-urlencoded",
}
req, _ := http.NewRequest("POST", url, strings.NewReader(payload))
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := auth.Session.Do(req)
if err != nil {
return NewError("part_four", 0, "Failed to send request", err)
}
defer resp.Body.Close()
if resp.StatusCode == 302 || resp.StatusCode == 200 {
return auth.partFour(state)
} else {
return NewError("__part_four", resp.StatusCode, "Your email address is invalid.", fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partFour(state string) *Error {
url := fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", state)
emailURLEncoded := auth.URLEncode(auth.EmailAddress)
passwordURLEncoded := auth.URLEncode(auth.Password)
payload := fmt.Sprintf("state=%s&username=%s&password=%s&action=default", state, emailURLEncoded, passwordURLEncoded)
headers := map[string]string{
"Host": "auth0.openai.com",
"Origin": "https://auth0.openai.com",
"Connection": "keep-alive",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"User-Agent": auth.UserAgent,
"Referer": fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", state),
"Accept-Language": "en-US,en;q=0.9",
"Content-Type": "application/x-www-form-urlencoded",
}
req, _ := http.NewRequest("POST", url, strings.NewReader(payload))
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := auth.Session.Do(req)
if err != nil {
return NewError("part_five", 0, "Failed to send request", err)
}
defer resp.Body.Close()
if resp.StatusCode == 302 {
redirectURL := resp.Header.Get("Location")
return auth.partFive(state, redirectURL)
} else {
body := bytes.NewBuffer(nil)
body.ReadFrom(resp.Body)
return NewError("__part_five", resp.StatusCode, body.String(), fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partFive(oldState string, redirectURL string) *Error {
url := "https://auth0.openai.com" + redirectURL
headers := map[string]string{
"Host": "auth0.openai.com",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Connection": "keep-alive",
"User-Agent": auth.UserAgent,
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
"Referer": fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", oldState),
}
req, _ := http.NewRequest("GET", url, nil)
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := auth.Session.Do(req)
if err != nil {
return NewError("part_six", 0, "Failed to send request", err)
}
defer resp.Body.Close()
if resp.StatusCode == 302 {
auth.URL = resp.Header.Get("Location")
return auth.partSix()
} else {
return NewError("__part_six", resp.StatusCode, resp.Status, fmt.Errorf("error: Check details"))
}
}
func (auth *Authenticator) partSix() *Error {
code := regexp.MustCompile(`code=(.*)&`).FindStringSubmatch(auth.URL)
if len(code) == 0 {
return NewError("__get_access_token", 0, auth.URL, fmt.Errorf("error: Check details"))
}
payload, _ := json.Marshal(map[string]string{
"redirect_uri": "com.openai.chat://auth0.openai.com/ios/com.openai.chat/callback",
"grant_type": "authorization_code",
"client_id": "pdlLIX2Y72MIl2rhLhTE9VV9bN905kBh",
"code": code[1],
"code_verifier": auth.Verifier_code,
"state": auth.State,
})
req, _ := http.NewRequest("POST", "https://auth0.openai.com/oauth/token", strings.NewReader(string(payload)))
for k, v := range map[string]string{
"User-Agent": auth.UserAgent,
"content-type": "application/json",
} {
req.Header.Set(k, v)
}
resp, err := auth.Session.Do(req)
if err != nil {
return NewError("get_access_token", 0, "Failed to send request", err)
}
defer resp.Body.Close()
// Parse response
body, _ := io.ReadAll(resp.Body)
// Parse as JSON
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
return NewError("get_access_token", 0, "Response was not JSON", err)
}
// Check if access token in data
if _, ok := data["access_token"]; !ok {
return NewError("get_access_token", 0, "Missing access token", fmt.Errorf("error: Check details"))
}
auth.AuthResult.AccessToken = data["access_token"].(string)
auth.AuthResult.RefreshToken = data["refresh_token"].(string)
return nil
}
func (auth *Authenticator) GetAccessToken() string {
return auth.AuthResult.AccessToken
}
func (auth *Authenticator) GetPUID() (string, *Error) {
// Check if user has access token
if auth.AuthResult.AccessToken == "" {
return "", NewError("get_puid", 0, "Missing access token", fmt.Errorf("error: Check details"))
}
// Make request to https://chat.openai.com/backend-api/models
req, _ := http.NewRequest("GET", "https://chat.openai.com/backend-api/models", nil)
// Add headers
req.Header.Add("Authorization", "Bearer "+auth.AuthResult.AccessToken)
req.Header.Add("User-Agent", auth.UserAgent)
req.Header.Add("Accept", "application/json")
req.Header.Add("Accept-Language", "en-US,en;q=0.9")
req.Header.Add("Referer", "https://chat.openai.com/")
req.Header.Add("Origin", "https://chat.openai.com")
req.Header.Add("Connection", "keep-alive")
resp, err := auth.Session.Do(req)
if err != nil {
return "", NewError("get_puid", 0, "Failed to make request", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return "", NewError("get_puid", resp.StatusCode, "Failed to make request", fmt.Errorf("error: Check details"))
}
// Find `_puid` cookie in response
for _, cookie := range resp.Cookies() {
if cookie.Name == "_puid" {
auth.AuthResult.PUID = cookie.Value
return cookie.Value, nil
}
}
// If cookie not found, return error
return "", NewError("get_puid", 0, "PUID cookie not found", fmt.Errorf("error: Check details"))
}
func (auth *Authenticator) GetAuthResult() AuthResult {
return auth.AuthResult
}

View File

@ -2,16 +2,16 @@ module authenticator
go 1.20
require (
github.com/bogdanfinn/fhttp v0.5.22
github.com/bogdanfinn/tls-client v1.3.12
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c
)
require github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/bogdanfinn/fhttp v0.5.22 // indirect
github.com/bogdanfinn/tls-client v1.3.12 // indirect
github.com/bogdanfinn/utls v1.5.16 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c // indirect
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect

View File

@ -1,3 +1,7 @@
github.com/acheong08/OpenAIAuth v0.0.0-20230716132312-c206753c0819 h1:LE8tbbgfCcsUJZfQlpCF95eA+b2M6Q30ipzFjcpWp/I=
github.com/acheong08/OpenAIAuth v0.0.0-20230716132312-c206753c0819/go.mod h1:ES3Dh9hnbR2mDPlNTagj5e3b4nXECd4tbAjVgxggXEE=
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507 h1:1kZEmE1DeQEeIMHhQUqn+NqOdApF9BIv835ox09E2k8=
github.com/acheong08/OpenAIAuth v0.0.0-20230716134840-dbf5ba4f9507/go.mod h1:bkiXtklBFVpWHyWTys6Zhqb521i/gtT8cIUKWVx2m/M=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bogdanfinn/fhttp v0.5.22 h1:U1jhZRtuaOanWWcm1WdMFnwMvSxUQgvO6berqAVTc5o=
@ -6,6 +10,8 @@ github.com/bogdanfinn/tls-client v1.3.12 h1:jpNj7owMY/oULUQyAhAv6tRFkliFGLyr8Qx1
github.com/bogdanfinn/tls-client v1.3.12/go.mod h1:Q46nwIm0wPCweDM3XZcupxEIsTOWo3HVYSSsDj02/Qo=
github.com/bogdanfinn/utls v1.5.16 h1:NhhWkegEcYETBMj9nvgO4lwvc6NcLH+znrXzO3gnw4M=
github.com/bogdanfinn/utls v1.5.16/go.mod h1:mHeRCi69cUiEyVBkKONB1cAbLjRcZnlJbGzttmiuK4o=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20220510032225-4f9f17eaec4c h1:4RYnE0ISVwRxm9Dfo7utw1dh0kdRDEmVYq2MFVLy5zI=

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
"authenticator/auth"
auth "github.com/acheong08/OpenAIAuth/auth"
)
type Account struct {
@ -126,7 +126,9 @@ func main() {
println("Status code: " + fmt.Sprint(err.StatusCode))
println("Details: " + err.Details)
println("Embedded error: " + err.Error.Error())
return
// Sleep for 10 seconds
time.Sleep(10 * time.Second)
continue
}
access_token := authenticator.GetAccessToken()
// Append access token to access_tokens.txt