diff --git a/Pool/Gpt35Pool.go b/Pool/Gpt35Pool.go index efc5fc6..2ad065b 100644 --- a/Pool/Gpt35Pool.go +++ b/Pool/Gpt35Pool.go @@ -20,40 +20,47 @@ func init() { } type Gpt35Pool struct { - Gpt35s []*chat.Gpt35 + data []*chat.Gpt35 head int // 队头指针 tail int // 队尾指针 size int // 队列当前元素个数 capacity int // 队列容量 } +func newGpt35Pool(capacity int) *Gpt35Pool { + return &Gpt35Pool{ + data: make([]*chat.Gpt35, capacity), + capacity: config.PoolMaxCount, + } +} + func GetGpt35PoolInstance() *Gpt35Pool { once.Do(func() { instance = newGpt35Pool(config.PoolMaxCount) logger.Logger.Info(fmt.Sprint("PoolMaxCount: ", config.PoolMaxCount, ", AuthExpirationDate: ", config.AuthED, ", Init Pool...")) // 定时刷新 Pool - go instance.updateGpt35Pool(200) + instance.updateGpt35Pool(time.Millisecond * 200) }) return instance } -func newGpt35Pool(capacity int) *Gpt35Pool { - return &Gpt35Pool{ - Gpt35s: make([]*chat.Gpt35, capacity), - capacity: config.PoolMaxCount, - } -} - -func (G *Gpt35Pool) updateGpt35Pool(duration time.Duration) { - for { +func (G *Gpt35Pool) updateGpt35Pool(nanosecond time.Duration) { + common.TimingTask(nanosecond, func() { + // 遍历队列中的所有元素 + G.Traverse(func(index int, gpt35 *chat.Gpt35) { + // 判断是否为无效 Gpt35 实例 + if !G.isLiveGpt35(gpt35) { + // 移除无效 Gpt35 实例 + G.RemoveAt(index) + } + }) if !G.IsFull() { - G.Enqueue(chat.NewGpt35()) + G.Enqueue(chat.NewGpt35(1)) } - time.Sleep(time.Millisecond * duration) - } + }) } -func (G *Gpt35Pool) IsLiveGpt35(gpt35 *chat.Gpt35) bool { +func (G *Gpt35Pool) isLiveGpt35(gpt35 *chat.Gpt35) bool { //判断是否为空 if gpt35 == nil || gpt35.MaxUseCount <= 0 || //无可用次数 @@ -85,7 +92,7 @@ func (G *Gpt35Pool) GetGpt35(retry int) *chat.Gpt35 { return G.GetGpt35(retry - 1) } // 缓存内无可用 Gpt35 实例,返回新 Gpt35 实例 - return chat.NewGpt35() + return chat.NewGpt35(0) } // GetSize 获取队列当前元素个数 @@ -104,11 +111,11 @@ func (G *Gpt35Pool) IsFull() bool { } // Enqueue 入队 -func (G *Gpt35Pool) Enqueue(gpt35 *chat.Gpt35) bool { - if G.IsFull() || gpt35 == nil { +func (G *Gpt35Pool) Enqueue(v *chat.Gpt35) bool { + if G.IsFull() || v == nil { return false } - G.Gpt35s[G.tail] = gpt35 + G.data[G.tail] = v G.tail = (G.tail + 1) % G.capacity G.size++ return true @@ -126,12 +133,49 @@ func (G *Gpt35Pool) Dequeue() *chat.Gpt35 { return nil } // 获取 Gpt35 实例 - gpt35 := G.Gpt35s[G.head] + gpt35 := G.data[G.head] // 判断是否为无效 Gpt35 实例 - if !G.IsLiveGpt35(gpt35) { + if !G.isLiveGpt35(gpt35) { G.head = (G.head + 1) % G.capacity G.size-- return nil } return gpt35 } + +// RemoveAt 移除指定位置的元素 +func (G *Gpt35Pool) RemoveAt(index int) (*chat.Gpt35, bool) { + if index < 0 || index >= G.size { + return nil, false + } + // 计算要移除的元素在数组中的索引 + removeIndex := (G.head + index) % G.capacity + removedValue := G.data[removeIndex] + + // 移动队列中被移除元素后面的元素 + for i := index; i < G.size-1; i++ { + currentIndex := (G.head + i) % G.capacity + nextIndex := (currentIndex + 1) % G.capacity + G.data[currentIndex] = G.data[nextIndex] + } + // 将最后一个元素置为空 + emptyIndex := (G.head + G.size - 1) % G.capacity + G.data[emptyIndex] = nil + + // 更新队尾指针和元素个数 + G.tail = (G.tail - 1 + G.capacity) % G.capacity + G.size-- + return removedValue, true +} + +// Traverse 遍历队列中的所有元素,并对每个元素执行指定操作 +func (G *Gpt35Pool) Traverse(callback func(int, *chat.Gpt35)) { + if G.IsEmpty() { + return + } + // 从队头开始遍历到队尾 + for i := 0; i < G.size; i++ { + index := (G.head + i) % G.capacity + callback(index, G.data[index]) + } +} diff --git a/ProxyPool/ProxyPool.go b/ProxyPool/ProxyPool.go index de7a918..b04a7dc 100644 --- a/ProxyPool/ProxyPool.go +++ b/ProxyPool/ProxyPool.go @@ -6,6 +6,7 @@ import ( browser "github.com/EDDYCJY/fake-useragent" "net/url" "sync" + "time" ) func init() { @@ -24,47 +25,65 @@ type ProxyPool struct { type Proxy struct { Link *url.URL + CanUseAt int64 Ua string Language string } func GetProxyPoolInstance() *ProxyPool { Once.Do(func() { - Instance = NewProxyPool() + Instance = NewProxyPool(nil) for _, px := range config.Proxy { - proxy := &Proxy{ + Instance.AddProxy(&Proxy{ Link: common.ParseUrl(px), + CanUseAt: common.GetTimestampSecond(0), Ua: browser.Random(), Language: common.RandomLanguage(), - } - Instance.AddProxy(proxy) + }) } + // 定时刷新代理 + Instance.timingUpdateProxy(time.Duration(config.AuthED) * time.Minute) }) return Instance } -func NewProxyPool() *ProxyPool { +func NewProxyPool(proxies []*Proxy) *ProxyPool { return &ProxyPool{ - Proxies: []*Proxy{}, - Index: 0, + Proxies: append([]*Proxy{ + { + Link: &url.URL{}, + CanUseAt: common.GetTimestampSecond(0), + Ua: browser.Random(), + Language: common.RandomLanguage(), + }, + }, proxies...), + Index: 0, } } func (PP *ProxyPool) GetProxy() *Proxy { // 如果没有代理则返回空代理 if len(PP.Proxies) == 0 { - return &Proxy{ - Link: &url.URL{}, - Ua: browser.Safari(), - Language: common.RandomLanguage(), - } + return PP.Proxies[0] } // 获取代理 proxy := PP.Proxies[PP.Index] PP.Index = (PP.Index + 1) % len(PP.Proxies) + if PP.Index == 0 { + PP.Index = 1 + } return proxy } func (PP *ProxyPool) AddProxy(proxy *Proxy) { PP.Proxies = append(PP.Proxies, proxy) } + +func (PP *ProxyPool) timingUpdateProxy(nanosecond time.Duration) { + common.TimingTask(nanosecond, func() { + for _, px := range PP.Proxies { + px.Ua = browser.Random() + px.Language = common.RandomLanguage() + } + }) +} diff --git a/RequestClient/TlsClient.go b/RequestClient/TlsClient.go index ffa7110..740164c 100644 --- a/RequestClient/TlsClient.go +++ b/RequestClient/TlsClient.go @@ -7,6 +7,8 @@ import ( tlsClient "github.com/bogdanfinn/tls-client" "github.com/bogdanfinn/tls-client/profiles" "io" + "math/rand" + "time" ) type TlsClient struct { @@ -30,6 +32,23 @@ func NewTlsClient(timeoutSeconds int, clientProfile profiles.ClientProfile) *Tls } } +func RandomClientProfile() profiles.ClientProfile { + // 初始化随机数生成器 + seed := time.Now().UnixNano() + rng := rand.New(rand.NewSource(seed)) + clientProfiles := []profiles.ClientProfile{ + profiles.Firefox_102, + profiles.Safari_15_6_1, + profiles.Safari_16_0, + profiles.Chrome_110, + profiles.Okhttp4Android13, + profiles.CloudflareCustom, + profiles.Firefox_117, + } + // 随机选择一个 + randomIndex := rng.Intn(len(clientProfiles)) + return clientProfiles[randomIndex] +} func (T *TlsClient) NewRequest(method, url string, body io.Reader) (*fhttp.Request, error) { request, err := fhttp.NewRequest(method, url, body) if err != nil { diff --git a/chat/Gpt35.go b/chat/Gpt35.go index dca4455..7d32f0d 100644 --- a/chat/Gpt35.go +++ b/chat/Gpt35.go @@ -9,7 +9,6 @@ import ( "free-gpt3.5-2api/config" "github.com/aurorax-neo/go-logger" fhttp "github.com/bogdanfinn/fhttp" - "github.com/bogdanfinn/tls-client/profiles" "github.com/google/uuid" "io" ) @@ -20,6 +19,7 @@ const SessionUrl = BaseUrl + "/backend-anon/sentinel/chat-requirements" type Gpt35 struct { RequestClient RequestClient.RequestClient + Proxy *ProxyPool.Proxy MaxUseCount int ExpiresIn int64 Session *session @@ -45,7 +45,8 @@ type turnstile struct { Required bool `json:"required"` } -func NewGpt35() *Gpt35 { +// NewGpt35 创建 Gpt35 实例 0 获取 1 刷新获取 +func NewGpt35(newType int) *Gpt35 { // 创建 Gpt35 实例 gpt35 := &Gpt35{ MaxUseCount: -1, @@ -53,7 +54,7 @@ func NewGpt35() *Gpt35 { Session: &session{}, } // 获取请求客户端 - err := gpt35.getNewRequestClient() + err := gpt35.getNewRequestClient(newType) if err != nil { return nil } @@ -65,26 +66,36 @@ func NewGpt35() *Gpt35 { return gpt35 } -func (G *Gpt35) getNewRequestClient() error { +func (G *Gpt35) getNewRequestClient(newType int) error { // 获取代理池 ProxyPoolInstance := ProxyPool.GetProxyPoolInstance() // 获取代理 - proxy := ProxyPoolInstance.GetProxy() + G.Proxy = ProxyPoolInstance.GetProxy() + // 判断代理是否可用 + if G.Proxy.CanUseAt > common.GetTimestampSecond(0) && newType == 1 { + errStr := fmt.Sprint(G.Proxy.Link, ": Proxy restricted, Reuse at ", G.Proxy.CanUseAt) + logger.Logger.Debug(errStr) + return fmt.Errorf(errStr) + } // 请求客户端 - G.RequestClient = RequestClient.NewTlsClient(300, profiles.Okhttp4Android13) + G.RequestClient = RequestClient.NewTlsClient(300, RequestClient.RandomClientProfile()) if G.RequestClient == nil { - logger.Logger.Error("RequestClient is nil") - return fmt.Errorf("RequestClient is nil") + errStr := fmt.Sprint("RequestClient is nil") + logger.Logger.Debug(errStr) + return fmt.Errorf(errStr) } // 设置代理 - err := G.RequestClient.SetProxy(proxy.Link.String()) + err := G.RequestClient.SetProxy(G.Proxy.Link.String()) if err != nil { - logger.Logger.Error(fmt.Sprint("SetProxy Error: ", err)) + errStr := fmt.Sprint("SetProxy Error: ", err) + logger.Logger.Debug(errStr) } // 设置 User-Agent - G.Ua = proxy.Ua + G.Ua = G.Proxy.Ua // 设置语言 - G.Language = proxy.Language + G.Language = G.Proxy.Language + // 成功后更新代理的可用时间 + G.Proxy.CanUseAt = common.GetTimestampSecond(0) return nil } @@ -104,6 +115,10 @@ func (G *Gpt35) getNewSession() error { return err } if response.StatusCode != 200 { + if response.StatusCode == 429 { + G.Proxy.CanUseAt = common.GetTimestampSecond(600) + } + logger.Logger.Debug(fmt.Sprint("StatusCode: ", response.StatusCode)) return fmt.Errorf("StatusCode: %d", response.StatusCode) } defer func(Body io.ReadCloser) { diff --git a/common/util.go b/common/util.go index 87b4f26..e5576c6 100644 --- a/common/util.go +++ b/common/util.go @@ -162,3 +162,20 @@ func fileIsExistAndCreat(filePath string, content string) bool { } return true } + +// TimingTask 定时任务 参数含函数 +func TimingTask(nanosecond time.Duration, f func()) { + go func() { + timerChan := time.After(nanosecond) + // 使用for循环阻塞等待定时器的信号 + for { + // 通过select语句监听定时器通道和其他事件 + select { + case <-timerChan: + f() + // 重新设置定时器,以便下一次执行 + timerChan = time.After(nanosecond) + } + } + }() +} diff --git a/service/v1/tokens.go b/service/v1/tokens.go index b7adfc4..73d630c 100644 --- a/service/v1/tokens.go +++ b/service/v1/tokens.go @@ -13,13 +13,7 @@ type TokensResp struct { func Tokens(c *gin.Context) { resp := &TokensResp{ - Count: 0, - } - instance := Pool.GetGpt35PoolInstance() - for i := 0; i < instance.GetCapacity(); i++ { - if instance.IsLiveGpt35(instance.Gpt35s[i]) { - resp.Count++ - } + Count: Pool.GetGpt35PoolInstance().GetSize(), } logger.Logger.Info(fmt.Sprint("Pool Tokens: ", resp.Count)) c.JSON(200, resp)