1
0

Merge pull request #92 from xqdoo00o/master

Chinese doc, auto renew token, uniform proxy
This commit is contained in:
Antonio Cheong 2023-07-20 06:59:13 +00:00 committed by GitHub
commit 5afd148f6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 541 additions and 324 deletions

View File

@ -6,63 +6,66 @@ Create a fake API using ChatGPT's website
**API endpoint: http://127.0.0.1:8080/v1/chat/completions.**
[中文文档Chinese Docs](README_CN.md)
## Help needed
- Documentation.
[中文文档Chinese Docs](https://github.com/xqdoo00o/ChatGPT-to-API/blob/master/README_ZH.md)
## Setup
<details>
<summary>
### Authentication
</summary>
Access token retrieval has been automated:
https://github.com/acheong08/ChatGPT-to-API/tree/master/tools/authenticator
Converting from a newline delimited list of access tokens to `access_tokens.json`
```bash
#!/bin/bash
Access token retrieval has been automated by [OpenAIAuth](https://github.com/acheong08/OpenAIAuth/) with account email & password.
START="["
END="]"
`accounts.txt` - A list of accounts separated by new line
TOKENS=""
while read -r line; do
if [ -z "$TOKENS" ]; then
TOKENS="\"$line\""
else
TOKENS+=",\"$line\""
fi
done < access_tokens.txt
echo "$START$TOKENS$END" > access_tokens.json
Format:
```
email:password
...
```
</details>
All authenticated access tokens will store in `access_tokens.json`
Auto renew access tokens after 14 days
Caution! please use unblocked ip for authentication, first login to `https://chat.openai.com/` to check ip availability if you can.
### API Authentication (Optional)
Custom API keys for this fake API, just like OpenAI api
`api_keys.txt` - A list of API keys separated by new line
Format:
```
sk-123456
88888888
...
```
## Getting set up
`git clone https://github.com/acheong08/ChatGPT-to-API`
`cd ChatGPT-to-API`
`go build`
`./freechatgpt`
```
git clone https://github.com/xqdoo00o/ChatGPT-to-API
cd ChatGPT-to-API
go build
./freechatgpt
```
### Environment variables
- `PUID` - A cookie found on chat.openai.com for Plus users. This gets around Cloudflare rate limits
- `http_proxy` - SOCKS5 or HTTP proxy. `socks5://HOST:PORT`
- `SERVER_HOST` - Set to 127.0.0.1 by default
- `SERVER_PORT` - Set to 8080 by default
- `OPENAI_EMAIL` and `OPENAI_PASSWORD` - It will automatically refresh your PUID if set (requires Plus account)
- `ENABLE_HISTORY` - Set to true by default
### Files (Optional)
- `access_tokens.json` - A JSON array of access tokens for cycling (Alternatively, send a PATCH request to the [correct endpoint](https://github.com/acheong08/ChatGPT-to-API/blob/master/docs/admin.md))
- `proxies.txt` - A list of proxies separated by new line (Format: `USERNAME:PASSWORD:HOST:PORT`)
- `proxies.txt` - A list of proxies separated by new line
```
http://127.0.0.1:8888
...
```
- `access_tokens.json` - A JSON array of access tokens for cycling (Alternatively, send a PATCH request to the [correct endpoint](https://github.com/acheong08/ChatGPT-to-API/blob/master/docs/admin.md))
```
["access_token1", "access_token2"...]
```
## Admin API docs
https://github.com/acheong08/ChatGPT-to-API/blob/master/docs/admin.md

70
README_ZH.md Normal file
View File

@ -0,0 +1,70 @@
# ChatGPT-to-API
从ChatGPT网站模拟使用API
**模拟API地址: http://127.0.0.1:8080/v1/chat/completions.**
## 使用
### 设置
配置账户邮箱和密码自动生成和更新Access tokens使用[OpenAIAuth](https://github.com/acheong08/OpenAIAuth/)
`accounts.txt` - 存放OpenAI账号邮箱和密码的文件
格式:
```
邮箱:密码
邮箱:密码
...
```
所有登录后的Access tokens会存放在`access_tokens.json`
每14天自动更新Access tokens
注意! 请使用未封锁的ip登录账号请先打开浏览器登录`https://chat.openai.com/`以检查ip是否可用
### API 密钥(可选)
如OpenAI的官方API一样可给模拟的API添加API密钥认证
`api_keys.txt` - 存放API密钥的文件
格式:
```
sk-123456
88888888
...
```
## 开始
```
git clone https://github.com/xqdoo00o/ChatGPT-to-API
cd ChatGPT-to-API
go build
./freechatgpt
```
### 环境变量
- `PUID` - Plus账户可在`chat.openai.com`的cookies里找到用于绕过cf的频率限制
- `SERVER_HOST` - 默认127.0.0.1
- `SERVER_PORT` - 默认8080
- `ENABLE_HISTORY` - 默认true允许网页端历史记录
### 可选文件配置
- `proxies.txt` - 存放代理地址的文件
```
http://127.0.0.1:8888
socks5://127.0.0.1:9999
...
```
- `access_tokens.json` - 一个存放Access tokens JSON数组的文件 (可使用 PATCH请求更新Access tokens [correct endpoint](https://github.com/acheong08/ChatGPT-to-API/blob/master/docs/admin.md))
```
["access_token1", "access_token2"...]
```
## 用户管理文档
https://github.com/acheong08/ChatGPT-to-API/blob/master/docs/admin.md
## API使用说明
https://platform.openai.com/docs/api-reference/chat

151
auth.go Normal file
View File

@ -0,0 +1,151 @@
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
"time"
"freechatgpt/internal/tokens"
"github.com/acheong08/OpenAIAuth/auth"
)
var accounts []Account
type Account struct {
Email string `json:"username"`
Password string `json:"password"`
}
// Read accounts.txt and create a list of accounts
func readAccounts() {
accounts = []Account{}
// Read accounts.txt and create a list of accounts
if _, err := os.Stat("accounts.txt"); err == nil {
// Each line is a proxy, put in proxies array
file, _ := os.Open("accounts.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// Split by :
line := strings.Split(scanner.Text(), ":")
// Create an account
account := Account{
Email: line[0],
Password: line[1],
}
// Append to accounts
accounts = append(accounts, account)
}
}
}
func scheduleToken() {
// Check if access_tokens.json exists
if stat, err := os.Stat("access_tokens.json"); os.IsNotExist(err) {
// Create the file
file, err := os.Create("access_tokens.json")
if err != nil {
panic(err)
}
defer file.Close()
updateToken()
} else {
nowTime := time.Now()
usedTime := nowTime.Sub(stat.ModTime())
// update access token 14 days after last modify token file
toExpire := 1.2096e15 - usedTime
if toExpire > 0 {
file, err := os.Open("access_tokens.json")
if err != nil {
panic(err)
}
defer file.Close()
decoder := json.NewDecoder(file)
var token_list []string
err = decoder.Decode(&token_list)
if err != nil {
updateToken()
return
}
if len(token_list) == 0 {
updateToken()
} else {
ACCESS_TOKENS = tokens.NewAccessToken(token_list, false)
time.AfterFunc(toExpire, updateToken)
}
} else {
updateToken()
}
}
}
func updateToken() {
token_list := []string{}
// Loop through each account
for _, account := range accounts {
if os.Getenv("CF_PROXY") != "" {
// exec warp-cli disconnect and connect
exec.Command("warp-cli", "disconnect").Run()
exec.Command("warp-cli", "connect").Run()
time.Sleep(5 * time.Second)
}
println("Updating access token for " + account.Email)
var proxy_url string
if len(proxies) == 0 {
proxy_url = ""
} else {
proxy_url = proxies[0]
// Push used proxy to the back of the list
proxies = append(proxies[1:], proxies[0])
}
authenticator := auth.NewAuthenticator(account.Email, account.Password, proxy_url)
err := authenticator.Begin()
if err != nil {
// println("Error: " + err.Details)
println("Location: " + err.Location)
println("Status code: " + fmt.Sprint(err.StatusCode))
println("Details: " + err.Details)
println("Embedded error: " + err.Error.Error())
return
}
access_token := authenticator.GetAccessToken()
token_list = append(token_list, access_token)
println("Success!")
// Write authenticated account to authenticated_accounts.txt
f, go_err := os.OpenFile("authenticated_accounts.txt", os.O_APPEND|os.O_WRONLY, 0600)
if go_err != nil {
continue
}
defer f.Close()
if _, go_err = f.WriteString(account.Email + ":" + account.Password + "\n"); go_err != nil {
continue
}
// Remove accounts.txt
os.Remove("accounts.txt")
// Create accounts.txt
f, go_err = os.Create("accounts.txt")
if go_err != nil {
continue
}
defer f.Close()
// Remove account from accounts
accounts = accounts[1:]
// Write unauthenticated accounts to accounts.txt
for _, acc := range accounts {
// Check if account is authenticated
if acc.Email == account.Email {
continue
}
if _, go_err = f.WriteString(acc.Email + ":" + acc.Password + "\n"); go_err != nil {
continue
}
}
}
// Append access token to access_tokens.json
ACCESS_TOKENS = tokens.NewAccessToken(token_list, true)
time.AfterFunc(1.2096e15, updateToken)
}

View File

@ -22,12 +22,15 @@ func ConvertAPIRequest(api_request official_types.APIRequest) chatgpt_types.Chat
fmt.Println("Error getting Arkose token: ", err)
}
chatgpt_request.Model = api_request.Model
// Cover some models like gpt-4-32k
if len(api_request.Model) >= 7 && api_request.Model[6] >= 48 && api_request.Model[6] <= 57 {
chatgpt_request.Model = "gpt-4"
}
}
if api_request.PluginIDs != nil {
chatgpt_request.PluginIDs = api_request.PluginIDs
chatgpt_request.Model = "gpt-4-plugins"
}
for _, api_message := range api_request.Messages {
if api_message.Role == "system" {
api_message.Role = "critic"

View File

@ -1,170 +1,179 @@
package main
import (
chatgpt_request_converter "freechatgpt/conversion/requests/chatgpt"
chatgpt "freechatgpt/internal/chatgpt"
"freechatgpt/internal/tokens"
official_types "freechatgpt/typings/official"
"os"
"strings"
"github.com/gin-gonic/gin"
)
func openaiHandler(c *gin.Context) {
var authorizations struct {
OpenAI_Email string `json:"openai_email"`
OpenAI_Password string `json:"openai_password"`
Official_API_Key string `json:"official_api_key"`
}
err := c.BindJSON(&authorizations)
if err != nil {
c.JSON(400, gin.H{"error": "JSON invalid"})
}
if authorizations.OpenAI_Email != "" && authorizations.OpenAI_Password != "" {
os.Setenv("OPENAI_EMAIL", authorizations.OpenAI_Email)
os.Setenv("OPENAI_PASSWORD", authorizations.OpenAI_Password)
}
if authorizations.Official_API_Key != "" {
os.Setenv("OFFICIAL_API_KEY", authorizations.Official_API_Key)
}
if authorizations.OpenAI_Email == "" && authorizations.OpenAI_Password == "" && authorizations.Official_API_Key == "" {
c.JSON(400, gin.H{"error": "JSON invalid"})
return
}
c.String(200, "OpenAI credentials updated")
}
func passwordHandler(c *gin.Context) {
// Get the password from the request (json) and update the password
type password_struct struct {
Password string `json:"password"`
}
var password password_struct
err := c.BindJSON(&password)
if err != nil {
c.String(400, "password not provided")
return
}
ADMIN_PASSWORD = password.Password
// Set environment variable
os.Setenv("ADMIN_PASSWORD", ADMIN_PASSWORD)
c.String(200, "password updated")
}
func puidHandler(c *gin.Context) {
// Get the password from the request (json) and update the password
type puid_struct struct {
PUID string `json:"puid"`
}
var puid puid_struct
err := c.BindJSON(&puid)
if err != nil {
c.String(400, "puid not provided")
return
}
// Set environment variable
os.Setenv("PUID", puid.PUID)
c.String(200, "puid updated")
}
func tokensHandler(c *gin.Context) {
// Get the request_tokens from the request (json) and update the request_tokens
var request_tokens []string
err := c.BindJSON(&request_tokens)
if err != nil {
c.String(400, "tokens not provided")
return
}
ACCESS_TOKENS = tokens.NewAccessToken(request_tokens)
c.String(200, "tokens updated")
}
func optionsHandler(c *gin.Context) {
// Set headers for CORS
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST")
c.Header("Access-Control-Allow-Headers", "*")
c.JSON(200, gin.H{
"message": "pong",
})
}
func nightmare(c *gin.Context) {
var original_request official_types.APIRequest
err := c.BindJSON(&original_request)
if err != nil {
c.JSON(400, gin.H{"error": gin.H{
"message": "Request must be proper JSON",
"type": "invalid_request_error",
"param": nil,
"code": err.Error(),
}})
return
}
authHeader := c.GetHeader("Authorization")
token := ACCESS_TOKENS.GetToken()
if authHeader != "" {
customAccessToken := strings.Replace(authHeader, "Bearer ", "", 1)
// Check if customAccessToken starts with sk-
if strings.HasPrefix(customAccessToken, "eyJhbGciOiJSUzI1NiI") {
token = customAccessToken
}
}
// Convert the chat request to a ChatGPT request
translated_request := chatgpt_request_converter.ConvertAPIRequest(original_request)
response, err := chatgpt.POSTconversation(translated_request, token)
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
defer response.Body.Close()
if chatgpt.Handle_request_error(c, response) {
return
}
var full_response string
for i := 3; i > 0; i-- {
var continue_info *chatgpt.ContinueInfo
var response_part string
response_part, continue_info = chatgpt.Handler(c, response, token, translated_request, original_request.Stream)
full_response += response_part
if continue_info == nil {
break
}
println("Continuing conversation")
translated_request.Messages = nil
translated_request.Action = "continue"
translated_request.ConversationID = continue_info.ConversationID
translated_request.ParentMessageID = continue_info.ParentID
response, err = chatgpt.POSTconversation(translated_request, token)
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
defer response.Body.Close()
if chatgpt.Handle_request_error(c, response) {
return
}
}
if !original_request.Stream {
c.JSON(200, official_types.NewChatCompletion(full_response))
} else {
c.String(200, "data: [DONE]\n\n")
}
}
func engines_handler(c *gin.Context) {
resp, status, err := chatgpt.GETengines()
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
c.JSON(status, resp)
}
package main
import (
chatgpt_request_converter "freechatgpt/conversion/requests/chatgpt"
chatgpt "freechatgpt/internal/chatgpt"
"freechatgpt/internal/tokens"
official_types "freechatgpt/typings/official"
"os"
"strings"
"github.com/gin-gonic/gin"
)
func openaiHandler(c *gin.Context) {
var authorizations struct {
OpenAI_Email string `json:"openai_email"`
OpenAI_Password string `json:"openai_password"`
Official_API_Key string `json:"official_api_key"`
}
err := c.BindJSON(&authorizations)
if err != nil {
c.JSON(400, gin.H{"error": "JSON invalid"})
}
if authorizations.OpenAI_Email != "" && authorizations.OpenAI_Password != "" {
os.Setenv("OPENAI_EMAIL", authorizations.OpenAI_Email)
os.Setenv("OPENAI_PASSWORD", authorizations.OpenAI_Password)
}
if authorizations.Official_API_Key != "" {
os.Setenv("OFFICIAL_API_KEY", authorizations.Official_API_Key)
}
if authorizations.OpenAI_Email == "" && authorizations.OpenAI_Password == "" && authorizations.Official_API_Key == "" {
c.JSON(400, gin.H{"error": "JSON invalid"})
return
}
c.String(200, "OpenAI credentials updated")
}
func passwordHandler(c *gin.Context) {
// Get the password from the request (json) and update the password
type password_struct struct {
Password string `json:"password"`
}
var password password_struct
err := c.BindJSON(&password)
if err != nil {
c.String(400, "password not provided")
return
}
ADMIN_PASSWORD = password.Password
// Set environment variable
os.Setenv("ADMIN_PASSWORD", ADMIN_PASSWORD)
c.String(200, "password updated")
}
func puidHandler(c *gin.Context) {
// Get the password from the request (json) and update the password
type puid_struct struct {
PUID string `json:"puid"`
}
var puid puid_struct
err := c.BindJSON(&puid)
if err != nil {
c.String(400, "puid not provided")
return
}
// Set environment variable
os.Setenv("PUID", puid.PUID)
c.String(200, "puid updated")
}
func tokensHandler(c *gin.Context) {
// Get the request_tokens from the request (json) and update the request_tokens
var request_tokens []string
err := c.BindJSON(&request_tokens)
if err != nil {
c.String(400, "tokens not provided")
return
}
ACCESS_TOKENS = tokens.NewAccessToken(request_tokens, true)
c.String(200, "tokens updated")
}
func optionsHandler(c *gin.Context) {
// Set headers for CORS
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST")
c.Header("Access-Control-Allow-Headers", "*")
c.JSON(200, gin.H{
"message": "pong",
})
}
func nightmare(c *gin.Context) {
var original_request official_types.APIRequest
err := c.BindJSON(&original_request)
if err != nil {
c.JSON(400, gin.H{"error": gin.H{
"message": "Request must be proper JSON",
"type": "invalid_request_error",
"param": nil,
"code": err.Error(),
}})
return
}
authHeader := c.GetHeader("Authorization")
token := ACCESS_TOKENS.GetToken()
if authHeader != "" {
customAccessToken := strings.Replace(authHeader, "Bearer ", "", 1)
// Check if customAccessToken starts with sk-
if strings.HasPrefix(customAccessToken, "eyJhbGciOiJSUzI1NiI") {
token = customAccessToken
}
}
var proxy_url string
if len(proxies) == 0 {
proxy_url = ""
} else {
proxy_url = proxies[0]
// Push used proxy to the back of the list
proxies = append(proxies[1:], proxies[0])
}
// Convert the chat request to a ChatGPT request
translated_request := chatgpt_request_converter.ConvertAPIRequest(original_request)
response, err := chatgpt.POSTconversation(translated_request, token, proxy_url)
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
defer response.Body.Close()
if chatgpt.Handle_request_error(c, response) {
return
}
var full_response string
for i := 3; i > 0; i-- {
var continue_info *chatgpt.ContinueInfo
var response_part string
response_part, continue_info = chatgpt.Handler(c, response, token, translated_request, original_request.Stream)
full_response += response_part
if continue_info == nil {
break
}
println("Continuing conversation")
translated_request.Messages = nil
translated_request.Action = "continue"
translated_request.ConversationID = continue_info.ConversationID
translated_request.ParentMessageID = continue_info.ParentID
response, err = chatgpt.POSTconversation(translated_request, token, proxy_url)
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
defer response.Body.Close()
if chatgpt.Handle_request_error(c, response) {
return
}
}
if !original_request.Stream {
c.JSON(200, official_types.NewChatCompletion(full_response))
} else {
c.String(200, "data: [DONE]\n\n")
}
}
func engines_handler(c *gin.Context) {
resp, status, err := chatgpt.GETengines()
if err != nil {
c.JSON(500, gin.H{
"error": "error sending request",
})
return
}
c.JSON(status, resp)
}

View File

@ -7,7 +7,6 @@ import (
"freechatgpt/typings"
chatgpt_types "freechatgpt/typings/chatgpt"
"io"
"math/rand"
"os"
"strings"
@ -23,8 +22,6 @@ import (
official_types "freechatgpt/typings/official"
)
var proxies []string
var (
jar = tls_client.NewCookieJar()
options = []tls_client.HttpClientOption{
@ -36,46 +33,16 @@ var (
tls_client.WithInsecureSkipVerify(),
}
client, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
http_proxy = os.Getenv("http_proxy")
API_REVERSE_PROXY = os.Getenv("API_REVERSE_PROXY")
)
func init() {
// Check for proxies.txt
if _, err := os.Stat("proxies.txt"); err == nil {
// Each line is a proxy, put in proxies array
file, _ := os.Open("proxies.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// Split line by :
proxy := scanner.Text()
proxy_parts := strings.Split(proxy, ":")
if len(proxy_parts) == 2 {
proxy = "socks5://" + proxy
} else if len(proxy_parts) == 4 {
proxy = "socks5://" + proxy_parts[2] + ":" + proxy_parts[3] + "@" + proxy_parts[0] + ":" + proxy_parts[1]
} else {
continue
}
proxies = append(proxies, proxy)
}
}
arkose.SetTLSClient(&client)
}
func random_int(min int, max int) int {
return min + rand.Intn(max-min)
}
func POSTconversation(message chatgpt_types.ChatGPTRequest, access_token string) (*http.Response, error) {
if http_proxy != "" && len(proxies) == 0 {
client.SetProxy(http_proxy)
}
// Take random proxy from proxies.txt
if len(proxies) > 0 {
client.SetProxy(proxies[random_int(0, len(proxies))])
func POSTconversation(message chatgpt_types.ChatGPTRequest, access_token string, proxy string) (*http.Response, error) {
if proxy != "" {
client.SetProxy(proxy)
}
apiUrl := "https://chat.openai.com/backend-api/conversation"

View File

@ -1,50 +1,60 @@
package tokens
import (
"encoding/json"
"os"
"sync"
)
type AccessToken struct {
tokens []string
lock sync.Mutex
}
func NewAccessToken(tokens []string) AccessToken {
// Save the tokens to a file
if _, err := os.Stat("access_tokens.json"); os.IsNotExist(err) {
// Create the file
file, err := os.Create("access_tokens.json")
if err != nil {
return AccessToken{}
}
defer file.Close()
}
file, err := os.OpenFile("access_tokens.json", os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return AccessToken{}
}
defer file.Close()
encoder := json.NewEncoder(file)
err = encoder.Encode(tokens)
if err != nil {
return AccessToken{}
}
return AccessToken{
tokens: tokens,
}
}
func (a *AccessToken) GetToken() string {
a.lock.Lock()
defer a.lock.Unlock()
if len(a.tokens) == 0 {
return ""
}
token := a.tokens[0]
a.tokens = append(a.tokens[1:], token)
return token
}
package tokens
import (
"encoding/json"
"os"
"sync"
)
type AccessToken struct {
tokens []string
lock sync.Mutex
}
func NewAccessToken(tokens []string, save bool) AccessToken {
// Save the tokens to a file
if _, err := os.Stat("access_tokens.json"); os.IsNotExist(err) {
// Create the file
file, err := os.Create("access_tokens.json")
if err != nil {
return AccessToken{}
}
defer file.Close()
}
if save {
saved := Save(tokens)
if saved == false {
return AccessToken{}
}
}
return AccessToken{
tokens: tokens,
}
}
func Save(tokens []string) bool {
file, err := os.OpenFile("access_tokens.json", os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return false
}
defer file.Close()
encoder := json.NewEncoder(file)
err = encoder.Encode(tokens)
if err != nil {
return false
}
return true
}
func (a *AccessToken) GetToken() string {
a.lock.Lock()
defer a.lock.Unlock()
if len(a.tokens) == 0 {
return ""
}
token := a.tokens[0]
a.tokens = append(a.tokens[1:], token)
return token
}

62
main.go
View File

@ -1,7 +1,7 @@
package main
import (
"encoding/json"
"bufio"
"freechatgpt/internal/tokens"
"log"
"os"
@ -17,6 +17,35 @@ import (
var HOST string
var PORT string
var ACCESS_TOKENS tokens.AccessToken
var proxies []string
func checkProxy() {
// first check for proxies.txt
proxies = []string{}
if _, err := os.Stat("proxies.txt"); err == nil {
// Each line is a proxy, put in proxies array
file, _ := os.Open("proxies.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// Split line by :
proxy := scanner.Text()
proxy_parts := strings.Split(proxy, ":")
if len(proxy_parts) > 1 {
proxies = append(proxies, proxy)
} else {
continue
}
}
}
// if no proxies, then check env http_proxy
if len(proxies) == 0 {
proxy := os.Getenv("http_proxy")
if proxy != "" {
proxies = append(proxies, proxy)
}
}
}
func init() {
_ = godotenv.Load(".env")
@ -53,34 +82,9 @@ func init() {
if PORT == "" {
PORT = "8080"
}
accessToken := os.Getenv("ACCESS_TOKENS")
if accessToken != "" {
accessTokens := strings.Split(accessToken, ",")
ACCESS_TOKENS = tokens.NewAccessToken(accessTokens)
}
// Check if access_tokens.json exists
if _, err := os.Stat("access_tokens.json"); os.IsNotExist(err) {
// Create the file
file, err := os.Create("access_tokens.json")
if err != nil {
panic(err)
}
defer file.Close()
} else {
// Load the tokens
file, err := os.Open("access_tokens.json")
if err != nil {
panic(err)
}
defer file.Close()
decoder := json.NewDecoder(file)
var token_list []string
err = decoder.Decode(&token_list)
if err != nil {
return
}
ACCESS_TOKENS = tokens.NewAccessToken(token_list)
}
checkProxy()
readAccounts()
scheduleToken()
}
func main() {
router := gin.Default()