Skip to content

Commit

Permalink
合并拉取请求 #14
Browse files Browse the repository at this point in the history
fix 500 error
  • Loading branch information
aurorax-neo committed Apr 20, 2024
2 parents 8d1a768 + d51fef1 commit a1d09e0
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 73 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,10 @@ curl --location --request POST 'http://127.0.0.1:9846/v1/chat/completions' \
LOG_LEVEL=info # debug, info, warn, error
BIND=0.0.0.0 # 127.0.0.1
PORT=3040
PROXY= # http://127.0.0.1:7890
PROXY= # http://127.0.0.1:7890
AUTHORIZATIONS= # abc,bac (英文 , 分隔)
POOL_MAX_COUNT=20 # max number of connections to keep in the pool 默认:10
POOL_MAX_COUNT=64 # max number of connections to keep in the pool 默认:64
AUTH_ED=600 # expiration time for the authorization in seconds 默认:600
AUTH_USE_COUNT=70 # number of times an authorization can be used 默认:75
```

###### 也可使用与程序同目录下 `.env` 文件配置上述字段
Expand Down
37 changes: 23 additions & 14 deletions chat/gpt35.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ type Gpt35 struct {
Client tlsClient.HttpClient
MaxUseCount int
ExpiresIn int64
IsLapse bool
Session *session
Ua string
Language string
}

type session struct {
OaiDeviceId string `json:"-"`
Persona string `json:"persona"`
Arkose arkose `json:"arkose"`
Turnstile turnstile `json:"turnstile"`
Token string `json:"token"`
OaiDeviceId string `json:"-"`
Persona string `json:"persona"`
Arkose arkose `json:"arkose"`
Turnstile turnstile `json:"turnstile"`
ProofWork common.ProofWork `json:"proofofwork"`
Token string `json:"token"`
}

type arkose struct {
Expand All @@ -58,9 +60,8 @@ func NewGpt35() *Gpt35 {
}
instance := &Gpt35{
Client: client,
MaxUseCount: config.CONFIG.AuthUseCount,
MaxUseCount: 1,
ExpiresIn: common.GetTimestampSecond(config.CONFIG.AuthED),
IsLapse: false,
Session: &session{},
}
// 获取新的 session
Expand All @@ -72,6 +73,10 @@ func NewGpt35() *Gpt35 {
}

func (G *Gpt35) getNewSession() error {
// 随机生成语言
G.Language = common.RandomLanguage()
// 随机生成 User-Agent
G.Ua = browser.Random()
// 生成新的设备 ID
G.Session.OaiDeviceId = uuid.New().String()
// 设置请求体
Expand All @@ -83,6 +88,8 @@ func (G *Gpt35) getNewSession() error {
}
// 设置请求头
request.Header.Set("oai-device-id", G.Session.OaiDeviceId)
request.Header.Set("accept-language", G.Language)
request.Header.Set("oai-language", G.Language)
// 发送 POST 请求
response, err := G.Client.Do(request)
if err != nil {
Expand All @@ -97,6 +104,9 @@ func (G *Gpt35) getNewSession() error {
if err := json.NewDecoder(response.Body).Decode(&G.Session); err != nil {
return err
}
if G.Session.ProofWork.Required {
G.Session.ProofWork.Ospt = common.CalcProofToken(G.Session.ProofWork.Seed, G.Session.ProofWork.Difficulty, request.Header.Get("User-Agent"))
}
return nil
}

Expand All @@ -105,19 +115,18 @@ func (G *Gpt35) NewRequest(method, url string, body io.Reader) (*fhttp.Request,
if err != nil {
return nil, err
}
request.Header.Set("origin", BaseUrl)
request.Header.Set("referer", BaseUrl)
request.Header.Set("origin", common.GetOrigin(BaseUrl))
request.Header.Set("referer", common.GetOrigin(BaseUrl))
request.Header.Set("accept", "*/*")
language := common.RandomLanguage()
request.Header.Set("accept-language", language)
request.Header.Set("oai-language", language)
request.Header.Set("cache-control", "no-cache")
request.Header.Set("content-type", "application/json")
request.Header.Set("pragma", "no-cache")
request.Header.Set("sec-ch-ua-mobile", "?0")
request.Header.Set("sec-fetch-dest", "empty")
request.Header.Set("sec-fetch-mode", "cors")
request.Header.Set("sec-fetch-site", "same-origin")
request.Header.Set("User-Agent", browser.Random())
request.Header.Set("accept-language", G.Language)
request.Header.Set("oai-language", G.Language)
request.Header.Set("User-Agent", G.Ua)
return request, nil
}
58 changes: 58 additions & 0 deletions common/proofwork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package common

import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"golang.org/x/crypto/sha3"
"math/rand"
"time"
)

var (
numberCollisions = 100000
cores = []int{8, 12, 16, 24}
screens = []int{3000, 4000, 6000}
timeLocation, _ = time.LoadLocation("Asia/Shanghai")
timeLayout = "Mon Jan 2 2006 15:04:05"
)

type ProofWork struct {
Difficulty string `json:"difficulty,omitempty"`
Required bool `json:"required"`
Seed string `json:"seed,omitempty"`
Ospt string `json:"-"`
}

func getParseTime() string {
now := time.Now()
now = now.In(timeLocation)
return now.Format(timeLayout) + " GMT+0800 (中国标准时间)"
}

func getConfig(userAgent string) []interface{} {
rand.New(rand.NewSource(time.Now().UnixNano()))
core := cores[rand.Intn(4)]
rand.New(rand.NewSource(time.Now().UnixNano()))
screen := screens[rand.Intn(3)]
return []interface{}{core + screen, getParseTime(), int64(4294705152), 0, userAgent}

}

func CalcProofToken(seed string, diff string, userAgent string) string {
config := getConfig(userAgent)
diffLen := len(diff) / 2
hasher := sha3.New512()
for i := 0; i < numberCollisions; i++ {
config[3] = i
jsonStr, _ := json.Marshal(config)
base := base64.StdEncoding.EncodeToString(jsonStr)
hasher.Write([]byte(seed + base))
hash := hasher.Sum(nil)
hasher.Reset()
if hex.EncodeToString(hash[:diffLen]) <= diff {
return "gAAAAAB" + base
}
}
return "gAAAAABwQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" + base64.StdEncoding.EncodeToString([]byte(`"`+seed+`"`))
}
13 changes: 11 additions & 2 deletions common/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ func ParseUrl(link string) *url.URL {
return u
}

func GetOrigin(link string) string {
u := ParseUrl(link)
if u == nil {
return ""
}
return u.Scheme + "://" + u.Host
}

func Struct2BytesBuffer(v interface{}) (*bytes.Buffer, error) {
data := new(bytes.Buffer)
err := json.NewEncoder(data).Encode(v)
Expand Down Expand Up @@ -74,11 +82,12 @@ func SplitAndAddBearer(authTokens string) []string {

func RandomLanguage() string {
// 初始化随机数生成器
rand.Seed(time.Now().UnixNano())
seed := time.Now().UnixNano()
rng := rand.New(rand.NewSource(seed))
// 语言列表
languages := []string{"af", "am", "ar-sa", "as", "az-Latn", "be", "bg", "bn-BD", "bn-IN", "bs", "ca", "ca-ES-valencia", "cs", "cy", "da", "de", "de-de", "el", "en-GB", "en-US", "es", "es-ES", "es-US", "es-MX", "et", "eu", "fa", "fi", "fil-Latn", "fr", "fr-FR", "fr-CA", "ga", "gd-Latn", "gl", "gu", "ha-Latn", "he", "hi", "hr", "hu", "hy", "id", "ig-Latn", "is", "it", "it-it", "ja", "ka", "kk", "km", "kn", "ko", "kok", "ku-Arab", "ky-Cyrl", "lb", "lt", "lv", "mi-Latn", "mk", "ml", "mn-Cyrl", "mr", "ms", "mt", "nb", "ne", "nl", "nl-BE", "nn", "nso", "or", "pa", "pa-Arab", "pl", "prs-Arab", "pt-BR", "pt-PT", "qut-Latn", "quz", "ro", "ru", "rw", "sd-Arab", "si", "sk", "sl", "sq", "sr-Cyrl-BA", "sr-Cyrl-RS", "sr-Latn-RS", "sv", "sw", "ta", "te", "tg-Cyrl", "th", "ti", "tk-Latn", "tn", "tr", "tt-Cyrl", "ug-Arab", "uk", "ur", "uz-Latn", "vi", "wo", "xh", "yo-Latn", "zh-Hans", "zh-Hant", "zu"}
// 随机选择一个语言
randomIndex := rand.Intn(len(languages))
randomIndex := rng.Intn(len(languages))
return languages[randomIndex]
}

Expand Down
15 changes: 2 additions & 13 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ type config struct {
AUTHORIZATIONS []string
PoolMaxCount int
AuthED int
AuthUseCount int
}

var CONFIG *config
Expand Down Expand Up @@ -53,11 +52,11 @@ func init() {
poolMaxCount := os.Getenv("POOL_MAX_COUNT")
var err error
if poolMaxCount == "" {
CONFIG.PoolMaxCount = 10
CONFIG.PoolMaxCount = 64
} else {
CONFIG.PoolMaxCount, err = strconv.Atoi(poolMaxCount)
if err != nil {
CONFIG.PoolMaxCount = 10
CONFIG.PoolMaxCount = 64
}
}
// AUTH_ED
Expand All @@ -70,14 +69,4 @@ func init() {
CONFIG.AuthED = 600
}
}
// AUTH_USE_COUNT
authUseCount := os.Getenv("AUTH_USE_COUNT")
if authUseCount == "" {
CONFIG.AuthUseCount = 75
} else {
CONFIG.AuthUseCount, err = strconv.Atoi(authUseCount)
if err != nil {
CONFIG.AuthUseCount = 75
}
}
}
85 changes: 52 additions & 33 deletions pool/gpt35pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Gpt35Pool struct {
Gpt35s []*chat.Gpt35
Index int
MaxCount int
Lock sync.Mutex
}

func GetGpt35PoolInstance() *Gpt35Pool {
Expand All @@ -32,69 +33,87 @@ func GetGpt35PoolInstance() *Gpt35Pool {
Index: -1,
MaxCount: config.CONFIG.PoolMaxCount,
}
logger.Logger.Info(fmt.Sprint("PoolMaxCount: ", config.CONFIG.PoolMaxCount, ", AuthUseCount: ", config.CONFIG.AuthUseCount, ", AuthExpirationDate: ", config.CONFIG.AuthED, ", Init Gpt35Pool..."))
logger.Logger.Info(fmt.Sprint("PoolMaxCount: ", config.CONFIG.PoolMaxCount, ", AuthExpirationDate: ", config.CONFIG.AuthED, ", Init Gpt35Pool..."))
// 定时刷新 Gpt35Pool
go gpt35PoolInstance.timingFlushGpt35Pool(60)
go gpt35PoolInstance.timingUpdateGpt35Pool(60)
})
return gpt35PoolInstance
}

func (G *Gpt35Pool) GetGpt35(retry int) *chat.Gpt35 {
// 加锁
G.Lock.Lock()
defer G.Lock.Unlock()
// 索引加 1,采用取模运算实现循环
G.Index = (G.Index + 1) % G.MaxCount

// 处理索引为负数的情况
if G.Index < 0 {
G.Index = G.MaxCount - 1
}

// 返回索引对应的 Gpt35 实例
if G.IsLive(G.Index) {
if G.IsLiveGpt35(G.Index) {
// 获取 Gpt35 实例
gpt35 := G.Gpt35s[G.Index]
// 可用次数减 1
gpt35.MaxUseCount--
return gpt35
} else if retry > 0 { // 如果 Gpt35 实例为空且重试次数大于 0,则重新获取 Gpt35 实例
G.raGpt35AtIndex(G.Index)
retry--
return G.GetGpt35(retry)
// 更新 index 的 Gpt35 实例
G.updateGpt35AtIndex(G.Index)
// 返回 深拷贝的 Gpt35 实例
gpt35_ := chat.Gpt35{
Client: gpt35.Client,
MaxUseCount: gpt35.MaxUseCount,
ExpiresIn: gpt35.ExpiresIn,
Session: gpt35.Session,
Ua: gpt35.Ua,
Language: gpt35.Language,
}
return &gpt35_
} else if retry > 0 {
// 释放锁 防止死锁
G.Lock.Unlock()
defer G.Lock.Lock()
// 更新 index 的 Gpt35 实例
G.updateGpt35AtIndex(G.Index)
// 保证重试获取是刚刚更新的 Gpt35 实例
G.Index--
// 递归获取 Gpt35 实例
return G.GetGpt35(retry - 1)
}
return nil
}

func (G *Gpt35Pool) timingFlushGpt35Pool(sec int) {
func (G *Gpt35Pool) timingUpdateGpt35Pool(sec int) {
ticker := time.NewTicker(time.Duration(sec) * time.Second)
defer ticker.Stop()
G._flushGpt35Pool()
G.updateGpt35Pool()
for {
select {
case <-ticker.C:
G._flushGpt35Pool()
G.updateGpt35Pool()
}
}
}

func (G *Gpt35Pool) raGpt35AtIndex(index int) {
if index < 0 || index >= len(G.Gpt35s) {
return
func (G *Gpt35Pool) IsLiveGpt35(index int) bool {
//判断是否为空
if G.Gpt35s[index] == nil || //空的
G.Gpt35s[index].MaxUseCount <= 0 || //无可用次数
G.Gpt35s[index].ExpiresIn <= common.GetTimestampSecond(0) {
return false
}
G.Gpt35s[index] = chat.NewGpt35()
return true
}

func (G *Gpt35Pool) _flushGpt35Pool() {
for i := 0; i < G.MaxCount; i++ {
if !G.IsLive(i) { //过期
G.raGpt35AtIndex(i)
}
func (G *Gpt35Pool) updateGpt35AtIndex(index int) bool {
if index < 0 || index >= len(G.Gpt35s) {
return false
}
if !G.IsLiveGpt35(index) {
G.Gpt35s[index] = chat.NewGpt35()
return true
}
return false
}

func (G *Gpt35Pool) IsLive(index int) bool {
//判断是否为空
if G.Gpt35s[index] == nil || //空的
G.Gpt35s[index].MaxUseCount <= 0 || //可使用次数为0
G.Gpt35s[index].IsLapse || //失效
G.Gpt35s[index].ExpiresIn <= common.GetTimestampSecond(0) {
return false
func (G *Gpt35Pool) updateGpt35Pool() {
for i := 0; i < G.MaxCount; i++ {
G.updateGpt35AtIndex(i)
}
return true
}
Loading

0 comments on commit a1d09e0

Please sign in to comment.