Merge pull request #92 from xqdoo00o/master
Chinese doc, auto renew token, uniform proxy
This commit is contained in:
commit
5afd148f6d
79
README.md
79
README.md
@ -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
70
README_ZH.md
Normal 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
151
auth.go
Normal 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)
|
||||
}
|
@ -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"
|
||||
|
349
handlers.go
349
handlers.go
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
62
main.go
@ -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()
|
||||
|
Reference in New Issue
Block a user