Dhudean commited on
Commit
9460ee9
·
1 Parent(s): 031b030
common/config/config.go CHANGED
@@ -24,6 +24,10 @@ var AutoDelChat = env.Int("AUTO_DEL_CHAT", 0)
24
  var ProxyUrl = env.String("PROXY_URL", "")
25
  var AutoModelChatMapType = env.Int("AUTO_MODEL_CHAT_MAP_TYPE", 1)
26
  var YesCaptchaClientKey = env.String("YES_CAPTCHA_CLIENT_KEY", "")
 
 
 
 
27
  var ModelChatMapStr = env.String("MODEL_CHAT_MAP", "")
28
  var ModelChatMap = make(map[string]string)
29
  var SessionImageChatMapStr = env.String("SESSION_IMAGE_CHAT_MAP", "")
@@ -76,6 +80,39 @@ func NewCookieManager() *CookieManager {
76
  }
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  func (cm *CookieManager) GetNextCookie() (string, error) {
80
  cm.mu.Lock()
81
  defer cm.mu.Unlock()
 
24
  var ProxyUrl = env.String("PROXY_URL", "")
25
  var AutoModelChatMapType = env.Int("AUTO_MODEL_CHAT_MAP_TYPE", 1)
26
  var YesCaptchaClientKey = env.String("YES_CAPTCHA_CLIENT_KEY", "")
27
+ var CheatUrl = env.String("CHEAT_URL", "https://gs-cheat.aytsao.cn/genspark/create/req/body")
28
+
29
+ // 路由前缀
30
+ var RoutePrefix = env.String("ROUTE_PREFIX", "")
31
  var ModelChatMapStr = env.String("MODEL_CHAT_MAP", "")
32
  var ModelChatMap = make(map[string]string)
33
  var SessionImageChatMapStr = env.String("SESSION_IMAGE_CHAT_MAP", "")
 
80
  }
81
  }
82
 
83
+ func (cm *CookieManager) RemoveCookie(cookieToRemove string) error {
84
+ cm.mu.Lock()
85
+ defer cm.mu.Unlock()
86
+
87
+ if len(cm.Cookies) == 0 {
88
+ return errors.New("no cookies available")
89
+ }
90
+
91
+ // 查找要删除的cookie的索引
92
+ index := -1
93
+ for i, cookie := range cm.Cookies {
94
+ if cookie == cookieToRemove {
95
+ index = i
96
+ break
97
+ }
98
+ }
99
+
100
+ // 如果没找到要删除的cookie
101
+ if index == -1 {
102
+ return errors.New("RemoveCookie -> cookie not found")
103
+ }
104
+
105
+ // 从切片中删除cookie
106
+ cm.Cookies = append(cm.Cookies[:index], cm.Cookies[index+1:]...)
107
+
108
+ // 如果当前索引大于或等于删除后的切片长度,重置为0
109
+ if cm.currentIndex >= len(cm.Cookies) {
110
+ cm.currentIndex = 0
111
+ }
112
+
113
+ return nil
114
+ }
115
+
116
  func (cm *CookieManager) GetNextCookie() (string, error) {
117
  cm.mu.Lock()
118
  defer cm.mu.Unlock()
common/constants.go CHANGED
@@ -3,7 +3,7 @@ package common
3
  import "time"
4
 
5
  var StartTime = time.Now().Unix() // unit: second
6
- var Version = "v1.8.13" // this hard coding will be replaced automatically when building, no need to manually change
7
 
8
  var DefaultOpenaiModelList = []string{
9
  "gpt-4o",
 
3
  import "time"
4
 
5
  var StartTime = time.Now().Unix() // unit: second
6
+ var Version = "v1.10.0" // this hard coding will be replaced automatically when building, no need to manually change
7
 
8
  var DefaultOpenaiModelList = []string{
9
  "gpt-4o",
common/utils.go CHANGED
@@ -129,6 +129,16 @@ func IsBase64(s string) bool {
129
  return err == nil
130
  }
131
 
 
 
 
 
 
 
 
 
 
 
132
  func IsCloudflareChallenge(data string) bool {
133
  // 检查基本的 HTML 结构
134
  htmlPattern := `^<!DOCTYPE html><html.*?><head>.*?</head><body.*?>.*?</body></html>$`
@@ -166,6 +176,14 @@ func IsRateLimit(data string) bool {
166
  return false
167
  }
168
 
 
 
 
 
 
 
 
 
169
  func IsServerError(data string) bool {
170
  if data == "Internal Server Error" {
171
  return true
 
129
  return err == nil
130
  }
131
 
132
+ //<h1 data-translate="block_headline">Sorry, you have been blocked</h1>
133
+
134
+ func IsCloudflareBlock(data string) bool {
135
+ if strings.Contains(data, `<h1 data-translate="block_headline">Sorry, you have been blocked</h1>`) {
136
+ return true
137
+ }
138
+
139
+ return false
140
+ }
141
+
142
  func IsCloudflareChallenge(data string) bool {
143
  // 检查基本的 HTML 结构
144
  htmlPattern := `^<!DOCTYPE html><html.*?><head>.*?</head><body.*?>.*?</body></html>$`
 
176
  return false
177
  }
178
 
179
+ func IsNotLogin(data string) bool {
180
+ if strings.Contains(data, `{"status":-5,"message":"not login","data":{}}`) {
181
+ return true
182
+ }
183
+
184
+ return false
185
+ }
186
+
187
  func IsServerError(data string) bool {
188
  if data == "Internal Server Error" {
189
  return true
controller/chat.go CHANGED
@@ -2,6 +2,7 @@ package controller
2
 
3
  import (
4
  "bufio"
 
5
  "encoding/base64"
6
  "encoding/json"
7
  "fmt"
@@ -62,11 +63,8 @@ func ChatForOpenAI(c *gin.Context) {
62
  }
63
 
64
  // 模型映射
65
- if openAIReq.Model == "deepseek-r1" {
66
- openAIReq.Model = "deep-seek-r1"
67
- }
68
- if openAIReq.Model == "deepseek-v3" {
69
- openAIReq.Model = "deep-seek-v3"
70
  }
71
 
72
  // 初始化cookie
@@ -172,6 +170,11 @@ func ChatForOpenAI(c *gin.Context) {
172
  }
173
  }
174
 
 
 
 
 
 
175
  requestBody, err := createRequestBody(c, client, cookie, &openAIReq)
176
 
177
  if err != nil {
@@ -186,9 +189,9 @@ func ChatForOpenAI(c *gin.Context) {
186
  //}
187
 
188
  if openAIReq.Stream {
189
- handleStreamRequest(c, client, cookie, cookieManager, requestBody, openAIReq.Model)
190
  } else {
191
- handleNonStreamRequest(c, client, cookie, cookieManager, requestBody, openAIReq.Model)
192
  }
193
 
194
  }
@@ -346,11 +349,16 @@ func createRequestBody(c *gin.Context, client cycletls.CycleTLS, cookie string,
346
  currentQueryString = fmt.Sprintf("id=%s&type=%s", chatId, chatType)
347
  } else if chatId, ok := config.GlobalSessionManager.GetChatID(cookie, openAIReq.Model); ok {
348
  currentQueryString = fmt.Sprintf("id=%s&type=%s", chatId, chatType)
349
- } else if openAIReq.Model == "deep-seek-r1" {
350
  openAIReq.FilterUserMessage()
351
  }
352
-
353
  models := []string{openAIReq.Model}
 
 
 
 
 
354
  if !lo.Contains(common.TextModelList, openAIReq.Model) {
355
  models = common.MixtureModelList
356
  }
@@ -377,21 +385,50 @@ func createRequestBody(c *gin.Context, client cycletls.CycleTLS, cookie string,
377
  //}
378
 
379
  // 创建请求体
380
- return map[string]interface{}{
381
- "type": chatType,
382
- //"current_query_string": fmt.Sprintf("&type=%s", chatType),
383
  "current_query_string": currentQueryString,
384
  "messages": openAIReq.Messages,
385
- //"user_s_input": "我刚刚问了什么问题?",
386
- "action_params": map[string]interface{}{},
387
  "extra_data": map[string]interface{}{
388
  "models": models,
389
  "run_with_another_model": false,
390
  "writingContent": nil,
 
391
  },
392
  //"g_recaptcha_token": gRecaptchaToken,
393
- }, nil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  }
 
395
  func createImageRequestBody(c *gin.Context, cookie string, openAIReq *model.OpenAIImagesGenerationRequest, chatId string) (map[string]interface{}, error) {
396
  //gRecaptchaToken := ""
397
  //if config.YesCaptchaClientKey != "" {
@@ -541,12 +578,15 @@ func createStreamResponse(responseId, modelName string, jsonData []byte, delta m
541
  // handleMessageFieldDelta 处理消息字段增量
542
  func handleMessageFieldDelta(c *gin.Context, event map[string]interface{}, responseId, modelName string, jsonData []byte) error {
543
  fieldName, ok := event["field_name"].(string)
544
- if !ok || fieldName != "session_state.answer" {
 
 
 
545
  return nil
546
  }
547
 
548
  var delta string
549
- if modelName == "o1" || modelName == "o3-mini-high" {
550
  delta, ok = event["field_value"].(string)
551
  } else {
552
  delta, ok = event["delta"].(string)
@@ -559,11 +599,41 @@ func handleMessageFieldDelta(c *gin.Context, event map[string]interface{}, respo
559
  return sendSSEvent(c, streamResp)
560
  }
561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  // handleMessageResult 处理消息结果
563
- func handleMessageResult(c *gin.Context, responseId, modelName string, jsonData []byte) bool {
564
  finishReason := "stop"
 
 
 
 
 
 
 
 
 
565
 
566
- streamResp := createStreamResponse(responseId, modelName, jsonData, model.OpenAIDelta{}, &finishReason)
567
  if err := sendSSEvent(c, streamResp); err != nil {
568
  logger.Warnf(c.Request.Context(), "sendSSEvent err: %v", err)
569
  return false
@@ -736,10 +806,11 @@ func makeUploadRequest(client cycletls.CycleTLS, uploadUrl string, fileBytes []b
736
  // })
737
  //}
738
 
739
- func handleStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string, cookieManager *config.CookieManager, requestBody map[string]interface{}, modelName string) {
740
  const (
741
  errNoValidCookies = "No valid cookies available"
742
  errCloudflareChallengeMsg = "Detected Cloudflare Challenge Page"
 
743
  errServerErrMsg = "An error occurred with the current request, please try again."
744
  errServiceUnavailable = "Genspark Service Unavailable"
745
  )
@@ -768,10 +839,11 @@ func handleStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string
768
 
769
  var projectId string
770
  isRateLimit := false
771
-
772
  for response := range sseChan {
773
  if response.Done {
774
- break
 
775
  }
776
 
777
  data := response.Data
@@ -786,6 +858,10 @@ func handleStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string
786
  logger.Errorf(ctx, errCloudflareChallengeMsg)
787
  c.JSON(http.StatusInternalServerError, gin.H{"error": errCloudflareChallengeMsg})
788
  return false
 
 
 
 
789
  case common.IsServiceUnavailablePage(data):
790
  logger.Errorf(ctx, errServiceUnavailable)
791
  c.JSON(http.StatusInternalServerError, gin.H{"error": errServiceUnavailable})
@@ -796,12 +872,24 @@ func handleStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string
796
  return false
797
  case common.IsRateLimit(data):
798
  isRateLimit = true
799
- logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d", attempt+1, maxRetries)
800
- break
 
 
 
 
 
 
 
 
 
 
 
 
801
  }
802
 
803
  // 处理事件流数据
804
- if shouldContinue := processStreamData(c, data, &projectId, cookie, responseId, modelName, jsonData); !shouldContinue {
805
  return false
806
  }
807
  }
@@ -833,7 +921,7 @@ func handleStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string
833
  }
834
 
835
  // 处理流式数据的辅助函数,返回bool表示是否继续处理
836
- func processStreamData(c *gin.Context, data string, projectId *string, cookie, responseId, model string, jsonData []byte) bool {
837
  data = strings.TrimSpace(data)
838
  if !strings.HasPrefix(data, "data: ") {
839
  return true
@@ -881,7 +969,7 @@ func processStreamData(c *gin.Context, data string, projectId *string, cookie, r
881
  }
882
  }()
883
 
884
- return handleMessageResult(c, responseId, model, jsonData)
885
  }
886
 
887
  return true
@@ -902,7 +990,7 @@ func makeStreamRequest(c *gin.Context, client cycletls.CycleTLS, jsonData []byte
902
  },
903
  }
904
 
905
- logger.Debug(c.Request.Context(), fmt.Sprintf("options: %v", options))
906
 
907
  sseChan, err := client.DoSSE(apiEndpoint, options, "POST")
908
  if err != nil {
@@ -996,10 +1084,11 @@ func makeStreamRequest(c *gin.Context, client cycletls.CycleTLS, jsonData []byte
996
  //
997
  // c.JSON(200, resp)
998
  // }
999
- func handleNonStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string, cookieManager *config.CookieManager, requestBody map[string]interface{}, modelName string) {
1000
  const (
1001
  errNoValidCookies = "No valid cookies available"
1002
  errCloudflareChallengeMsg = "Detected Cloudflare Challenge Page"
 
1003
  errServerErrMsg = "An error occurred with the current request, please try again."
1004
  errServiceUnavailable = "Genspark Service Unavailable"
1005
  errNoValidResponseContent = "No valid response content"
@@ -1042,9 +1131,25 @@ func handleNonStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie str
1042
  logger.Errorf(ctx, errCloudflareChallengeMsg)
1043
  c.JSON(http.StatusInternalServerError, gin.H{"error": errCloudflareChallengeMsg})
1044
  return
 
 
 
 
1045
  case common.IsRateLimit(line):
1046
  isRateLimit = true
1047
- logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d", attempt+1, maxRetries)
 
 
 
 
 
 
 
 
 
 
 
 
1048
  break
1049
  case common.IsServiceUnavailablePage(line):
1050
  logger.Errorf(ctx, errServiceUnavailable)
@@ -1084,6 +1189,16 @@ func handleNonStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie str
1084
  }
1085
  }
1086
  }()
 
 
 
 
 
 
 
 
 
 
1087
  content = parsedResponse.Content
1088
  break
1089
  }
@@ -1257,7 +1372,7 @@ func ImageProcess(c *gin.Context, client cycletls.CycleTLS, openAIReq model.Open
1257
  // Handle different response cases
1258
  switch {
1259
  case common.IsRateLimit(body):
1260
- logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d", attempt+1, maxRetries)
1261
  if sessionImageChatManager != nil {
1262
  cookie, chatId, err = sessionImageChatManager.GetNextKeyValue()
1263
  if err != nil {
@@ -1276,7 +1391,7 @@ func ImageProcess(c *gin.Context, client cycletls.CycleTLS, openAIReq model.Open
1276
  }
1277
  continue
1278
  case common.IsFreeLimit(body):
1279
- logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d", attempt+1, maxRetries)
1280
  if sessionImageChatManager != nil {
1281
  cookie, chatId, err = sessionImageChatManager.GetNextKeyValue()
1282
  if err != nil {
@@ -1294,6 +1409,31 @@ func ImageProcess(c *gin.Context, client cycletls.CycleTLS, openAIReq model.Open
1294
  }
1295
  }
1296
  continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1297
  case common.IsServerError(body):
1298
  logger.Errorf(ctx, errServerErrMsg)
1299
  return nil, fmt.Errorf(errServerErrMsg)
 
2
 
3
  import (
4
  "bufio"
5
+ "bytes"
6
  "encoding/base64"
7
  "encoding/json"
8
  "fmt"
 
63
  }
64
 
65
  // 模型映射
66
+ if strings.HasPrefix(openAIReq.Model, "deepseek") {
67
+ openAIReq.Model = strings.Replace(openAIReq.Model, "deepseek", "deep-seek", 1)
 
 
 
68
  }
69
 
70
  // 初始化cookie
 
170
  }
171
  }
172
 
173
+ var isSearchModel bool
174
+ if strings.HasSuffix(openAIReq.Model, "-search") {
175
+ isSearchModel = true
176
+ }
177
+
178
  requestBody, err := createRequestBody(c, client, cookie, &openAIReq)
179
 
180
  if err != nil {
 
189
  //}
190
 
191
  if openAIReq.Stream {
192
+ handleStreamRequest(c, client, cookie, cookieManager, requestBody, openAIReq.Model, isSearchModel)
193
  } else {
194
+ handleNonStreamRequest(c, client, cookie, cookieManager, requestBody, openAIReq.Model, isSearchModel)
195
  }
196
 
197
  }
 
349
  currentQueryString = fmt.Sprintf("id=%s&type=%s", chatId, chatType)
350
  } else if chatId, ok := config.GlobalSessionManager.GetChatID(cookie, openAIReq.Model); ok {
351
  currentQueryString = fmt.Sprintf("id=%s&type=%s", chatId, chatType)
352
+ } else {
353
  openAIReq.FilterUserMessage()
354
  }
355
+ requestWebKnowledge := false
356
  models := []string{openAIReq.Model}
357
+ if strings.HasSuffix(openAIReq.Model, "-search") {
358
+ openAIReq.Model = strings.Replace(openAIReq.Model, "-search", "", 1)
359
+ requestWebKnowledge = true
360
+ models = []string{openAIReq.Model}
361
+ }
362
  if !lo.Contains(common.TextModelList, openAIReq.Model) {
363
  models = common.MixtureModelList
364
  }
 
385
  //}
386
 
387
  // 创建请求体
388
+ requestBody := map[string]interface{}{
389
+ "type": chatType,
 
390
  "current_query_string": currentQueryString,
391
  "messages": openAIReq.Messages,
392
+ "action_params": map[string]interface{}{},
 
393
  "extra_data": map[string]interface{}{
394
  "models": models,
395
  "run_with_another_model": false,
396
  "writingContent": nil,
397
+ "request_web_knowledge": requestWebKnowledge,
398
  },
399
  //"g_recaptcha_token": gRecaptchaToken,
400
+ }
401
+
402
+ logger.Debug(c.Request.Context(), fmt.Sprintf("RequestBody: %v", requestBody))
403
+
404
+ if strings.TrimSpace(config.CheatUrl) == "" ||
405
+ (!strings.HasPrefix(config.CheatUrl, "http://") &&
406
+ !strings.HasPrefix(config.CheatUrl, "https://")) {
407
+ return requestBody, nil
408
+ } else {
409
+ // 发送请求到本地测试接口
410
+ jsonData, err := json.Marshal(requestBody)
411
+ if err != nil {
412
+ return nil, fmt.Errorf("marshal request body error: %v", err)
413
+ }
414
+
415
+ resp, err := http.Post(config.CheatUrl, "application/json", bytes.NewBuffer(jsonData))
416
+ if err != nil {
417
+ return nil, fmt.Errorf("send request to test api error: %v", err)
418
+ }
419
+ defer resp.Body.Close()
420
+
421
+ // 读取响应
422
+ var response map[string]interface{}
423
+ if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
424
+ return nil, fmt.Errorf("decode response error: %v", err)
425
+ }
426
+ logger.Debugf(c.Request.Context(), fmt.Sprintf("Cheat success!"))
427
+ return response, nil
428
+ }
429
+
430
  }
431
+
432
  func createImageRequestBody(c *gin.Context, cookie string, openAIReq *model.OpenAIImagesGenerationRequest, chatId string) (map[string]interface{}, error) {
433
  //gRecaptchaToken := ""
434
  //if config.YesCaptchaClientKey != "" {
 
578
  // handleMessageFieldDelta 处理消息字段增量
579
  func handleMessageFieldDelta(c *gin.Context, event map[string]interface{}, responseId, modelName string, jsonData []byte) error {
580
  fieldName, ok := event["field_name"].(string)
581
+ if !ok || (fieldName != "session_state.answer" &&
582
+ !strings.Contains(fieldName, "session_state.streaming_detail_answer") &&
583
+ //fieldName != "session_state.reflection" &&
584
+ fieldName != "session_state.streaming_markmap") {
585
  return nil
586
  }
587
 
588
  var delta string
589
+ if (modelName == "o1" || modelName == "o3-mini-high") && fieldName == "session_state.answer" {
590
  delta, ok = event["field_value"].(string)
591
  } else {
592
  delta, ok = event["delta"].(string)
 
599
  return sendSSEvent(c, streamResp)
600
  }
601
 
602
+ type Content struct {
603
+ DetailAnswer string `json:"detailAnswer"`
604
+ }
605
+
606
+ // 然后这样解析
607
+ func getDetailAnswer(eventMap map[string]interface{}) (string, error) {
608
+ // 获取 content 字段的值
609
+ contentStr, ok := eventMap["content"].(string)
610
+ if !ok {
611
+ return "", fmt.Errorf("content is not a string")
612
+ }
613
+
614
+ // 解析内层的 JSON
615
+ var content Content
616
+ if err := json.Unmarshal([]byte(contentStr), &content); err != nil {
617
+ return "", err
618
+ }
619
+
620
+ return content.DetailAnswer, nil
621
+ }
622
+
623
  // handleMessageResult 处理消息结果
624
+ func handleMessageResult(c *gin.Context, event map[string]interface{}, responseId, modelName string, jsonData []byte, searchModel bool) bool {
625
  finishReason := "stop"
626
+ var delta string
627
+ var err error
628
+ if modelName == "o1" && searchModel {
629
+ delta, err = getDetailAnswer(event)
630
+ if err != nil {
631
+ logger.Errorf(c.Request.Context(), "getDetailAnswer err: %v", err)
632
+ return false
633
+ }
634
+ }
635
 
636
+ streamResp := createStreamResponse(responseId, modelName, jsonData, model.OpenAIDelta{Content: delta, Role: "assistant"}, &finishReason)
637
  if err := sendSSEvent(c, streamResp); err != nil {
638
  logger.Warnf(c.Request.Context(), "sendSSEvent err: %v", err)
639
  return false
 
806
  // })
807
  //}
808
 
809
+ func handleStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string, cookieManager *config.CookieManager, requestBody map[string]interface{}, modelName string, searchModel bool) {
810
  const (
811
  errNoValidCookies = "No valid cookies available"
812
  errCloudflareChallengeMsg = "Detected Cloudflare Challenge Page"
813
+ errCloudflareBlock = "CloudFlare: Sorry, you have been blocked"
814
  errServerErrMsg = "An error occurred with the current request, please try again."
815
  errServiceUnavailable = "Genspark Service Unavailable"
816
  )
 
839
 
840
  var projectId string
841
  isRateLimit := false
842
+ SSELoop:
843
  for response := range sseChan {
844
  if response.Done {
845
+ logger.Warnf(ctx, response.Data)
846
+ return false
847
  }
848
 
849
  data := response.Data
 
858
  logger.Errorf(ctx, errCloudflareChallengeMsg)
859
  c.JSON(http.StatusInternalServerError, gin.H{"error": errCloudflareChallengeMsg})
860
  return false
861
+ case common.IsCloudflareBlock(data):
862
+ logger.Errorf(ctx, errCloudflareBlock)
863
+ c.JSON(http.StatusInternalServerError, gin.H{"error": errCloudflareBlock})
864
+ return false
865
  case common.IsServiceUnavailablePage(data):
866
  logger.Errorf(ctx, errServiceUnavailable)
867
  c.JSON(http.StatusInternalServerError, gin.H{"error": errServiceUnavailable})
 
872
  return false
873
  case common.IsRateLimit(data):
874
  isRateLimit = true
875
+ logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
876
+ break SSELoop // 使用 label 跳出 SSE 循环
877
+ case common.IsFreeLimit(data):
878
+ isRateLimit = true
879
+ logger.Warnf(ctx, "Cookie free rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
880
+ break SSELoop // 使用 label 跳出 SSE 循环
881
+ case common.IsNotLogin(data):
882
+ isRateLimit = true
883
+ logger.Warnf(ctx, "Cookie Not Login, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
884
+ //err := cookieManager.RemoveCookie(cookie)
885
+ //if err != nil {
886
+ // logger.Errorf(ctx, "Failed to remove cookie: %v", err)
887
+ //}
888
+ break SSELoop // 使用 label 跳出 SSE 循环
889
  }
890
 
891
  // 处理事件流数据
892
+ if shouldContinue := processStreamData(c, data, &projectId, cookie, responseId, modelName, jsonData, searchModel); !shouldContinue {
893
  return false
894
  }
895
  }
 
921
  }
922
 
923
  // 处理流式数据的辅助函数,返回bool表示是否继续处理
924
+ func processStreamData(c *gin.Context, data string, projectId *string, cookie, responseId, model string, jsonData []byte, searchModel bool) bool {
925
  data = strings.TrimSpace(data)
926
  if !strings.HasPrefix(data, "data: ") {
927
  return true
 
969
  }
970
  }()
971
 
972
+ return handleMessageResult(c, event, responseId, model, jsonData, searchModel)
973
  }
974
 
975
  return true
 
990
  },
991
  }
992
 
993
+ logger.Debug(c.Request.Context(), fmt.Sprintf("cookie: %v", cookie))
994
 
995
  sseChan, err := client.DoSSE(apiEndpoint, options, "POST")
996
  if err != nil {
 
1084
  //
1085
  // c.JSON(200, resp)
1086
  // }
1087
+ func handleNonStreamRequest(c *gin.Context, client cycletls.CycleTLS, cookie string, cookieManager *config.CookieManager, requestBody map[string]interface{}, modelName string, searchModel bool) {
1088
  const (
1089
  errNoValidCookies = "No valid cookies available"
1090
  errCloudflareChallengeMsg = "Detected Cloudflare Challenge Page"
1091
+ errCloudflareBlock = "CloudFlare: Sorry, you have been blocked"
1092
  errServerErrMsg = "An error occurred with the current request, please try again."
1093
  errServiceUnavailable = "Genspark Service Unavailable"
1094
  errNoValidResponseContent = "No valid response content"
 
1131
  logger.Errorf(ctx, errCloudflareChallengeMsg)
1132
  c.JSON(http.StatusInternalServerError, gin.H{"error": errCloudflareChallengeMsg})
1133
  return
1134
+ case common.IsCloudflareBlock(line):
1135
+ logger.Errorf(ctx, errCloudflareBlock)
1136
+ c.JSON(http.StatusInternalServerError, gin.H{"error": errCloudflareBlock})
1137
+ return
1138
  case common.IsRateLimit(line):
1139
  isRateLimit = true
1140
+ logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
1141
+ break
1142
+ case common.IsFreeLimit(line):
1143
+ isRateLimit = true
1144
+ logger.Warnf(ctx, "Cookie free rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
1145
+ break
1146
+ case common.IsNotLogin(line):
1147
+ isRateLimit = true
1148
+ logger.Warnf(ctx, "Cookie Not Login, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
1149
+ //err := cookieManager.RemoveCookie(cookie)
1150
+ //if err != nil {
1151
+ // logger.Errorf(ctx, "Failed to remove cookie: %v", err)
1152
+ //}
1153
  break
1154
  case common.IsServiceUnavailablePage(line):
1155
  logger.Errorf(ctx, errServiceUnavailable)
 
1189
  }
1190
  }
1191
  }()
1192
+ if modelName == "o1" && searchModel {
1193
+ // 解析内层的 JSON
1194
+ var content Content
1195
+ if err := json.Unmarshal([]byte(parsedResponse.Content), &content); err != nil {
1196
+ logger.Errorf(ctx, "Failed to unmarshal response content: %v err %s", parsedResponse.Content, err.Error())
1197
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to unmarshal response content"})
1198
+ return
1199
+ }
1200
+ parsedResponse.Content = content.DetailAnswer
1201
+ }
1202
  content = parsedResponse.Content
1203
  break
1204
  }
 
1372
  // Handle different response cases
1373
  switch {
1374
  case common.IsRateLimit(body):
1375
+ logger.Warnf(ctx, "Cookie rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
1376
  if sessionImageChatManager != nil {
1377
  cookie, chatId, err = sessionImageChatManager.GetNextKeyValue()
1378
  if err != nil {
 
1391
  }
1392
  continue
1393
  case common.IsFreeLimit(body):
1394
+ logger.Warnf(ctx, "Cookie free rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
1395
  if sessionImageChatManager != nil {
1396
  cookie, chatId, err = sessionImageChatManager.GetNextKeyValue()
1397
  if err != nil {
 
1409
  }
1410
  }
1411
  continue
1412
+ case common.IsNotLogin(body):
1413
+ logger.Warnf(ctx, "Cookie free rate limited, switching to next cookie, attempt %d/%d, COOKIE:%s", attempt+1, maxRetries, cookie)
1414
+ if sessionImageChatManager != nil {
1415
+ //sessionImageChatManager.RemoveKey(cookie)
1416
+ cookie, chatId, err = sessionImageChatManager.GetNextKeyValue()
1417
+ if err != nil {
1418
+ logger.Errorf(ctx, "No more valid cookies available after attempt %d", attempt+1)
1419
+ c.JSON(http.StatusInternalServerError, gin.H{"error": errNoValidCookies})
1420
+ return nil, fmt.Errorf(errNoValidCookies)
1421
+ }
1422
+ } else {
1423
+ //cookieManager := config.NewCookieManager()
1424
+ //err := cookieManager.RemoveCookie(cookie)
1425
+ //if err != nil {
1426
+ // logger.Errorf(ctx, "Failed to remove cookie: %v", err)
1427
+ //}
1428
+ cookie, err = cookieManager.GetNextCookie()
1429
+ if err != nil {
1430
+ logger.Errorf(ctx, "No more valid cookies available after attempt %d", attempt+1)
1431
+ c.JSON(http.StatusInternalServerError, gin.H{"error": errNoValidCookies})
1432
+ return nil, fmt.Errorf(errNoValidCookies)
1433
+ }
1434
+
1435
+ }
1436
+ continue
1437
  case common.IsServerError(body):
1438
  logger.Errorf(ctx, errServerErrMsg)
1439
  return nil, fmt.Errorf(errServerErrMsg)
go.mod CHANGED
@@ -3,7 +3,7 @@ module genspark2api
3
  go 1.23
4
 
5
  require (
6
- github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206063908-bfd1b7750d37
7
  github.com/gin-contrib/cors v1.7.3
8
  github.com/gin-gonic/gin v1.10.0
9
  github.com/google/uuid v1.6.0
@@ -39,11 +39,11 @@ require (
39
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
40
  github.com/ugorji/go/codec v1.2.12 // indirect
41
  golang.org/x/arch v0.14.0 // indirect
42
- golang.org/x/crypto v0.32.0 // indirect
43
  golang.org/x/net v0.34.0 // indirect
44
  golang.org/x/sys v0.30.0 // indirect
45
  golang.org/x/text v0.22.0 // indirect
46
- google.golang.org/protobuf v1.36.4 // indirect
47
  gopkg.in/yaml.v3 v3.0.1 // indirect
48
  h12.io/socks v1.0.3 // indirect
49
  )
 
3
  go 1.23
4
 
5
  require (
6
+ github.com/deanxv/CycleTLS/cycletls v0.0.0-20250208062300-063369d205a8
7
  github.com/gin-contrib/cors v1.7.3
8
  github.com/gin-gonic/gin v1.10.0
9
  github.com/google/uuid v1.6.0
 
39
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
40
  github.com/ugorji/go/codec v1.2.12 // indirect
41
  golang.org/x/arch v0.14.0 // indirect
42
+ golang.org/x/crypto v0.33.0 // indirect
43
  golang.org/x/net v0.34.0 // indirect
44
  golang.org/x/sys v0.30.0 // indirect
45
  golang.org/x/text v0.22.0 // indirect
46
+ google.golang.org/protobuf v1.36.5 // indirect
47
  gopkg.in/yaml.v3 v3.0.1 // indirect
48
  h12.io/socks v1.0.3 // indirect
49
  )
go.sum CHANGED
@@ -37,10 +37,10 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7
37
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
38
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
39
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
40
- github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206060125-b8e4d910abbf h1:nsskm99CacPwCDkwCoIFDAqIHahLJyjht1bICqu1uvc=
41
- github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206060125-b8e4d910abbf/go.mod h1:eAyIp7Lbyq6WnJDGicqf7nYr0bTj5FQ0HXQbIesuuJ8=
42
  github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206063908-bfd1b7750d37 h1:39pwCIyBhsAylwHet8IbjgNinLnqyqSkdgqTcNHbi3E=
43
  github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206063908-bfd1b7750d37/go.mod h1:eAyIp7Lbyq6WnJDGicqf7nYr0bTj5FQ0HXQbIesuuJ8=
 
 
44
  github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
45
  github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
46
  github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -282,8 +282,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw
282
  golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
283
  golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
284
  golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
285
- golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
286
- golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
287
  golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
288
  golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
289
  golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -447,8 +447,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
447
  google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
448
  google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
449
  google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
450
- google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
451
- google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
452
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
453
  gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
454
  gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 
37
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
38
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
39
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
 
40
  github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206063908-bfd1b7750d37 h1:39pwCIyBhsAylwHet8IbjgNinLnqyqSkdgqTcNHbi3E=
41
  github.com/deanxv/CycleTLS/cycletls v0.0.0-20250206063908-bfd1b7750d37/go.mod h1:eAyIp7Lbyq6WnJDGicqf7nYr0bTj5FQ0HXQbIesuuJ8=
42
+ github.com/deanxv/CycleTLS/cycletls v0.0.0-20250208062300-063369d205a8 h1:Xc1Wmbj32PCR8t+SjloLXM/tWkdpfInyxr1FZ6OsVjU=
43
+ github.com/deanxv/CycleTLS/cycletls v0.0.0-20250208062300-063369d205a8/go.mod h1:eAyIp7Lbyq6WnJDGicqf7nYr0bTj5FQ0HXQbIesuuJ8=
44
  github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
45
  github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
46
  github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 
282
  golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
283
  golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
284
  golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
285
+ golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
286
+ golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
287
  golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
288
  golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
289
  golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 
447
  google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
448
  google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
449
  google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
450
+ google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
451
+ google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
452
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
453
  gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
454
  gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
router/api-router.go CHANGED
@@ -1,9 +1,12 @@
1
  package router
2
 
3
  import (
 
 
4
  "genspark2api/controller"
5
  "genspark2api/middleware"
6
  "github.com/gin-gonic/gin"
 
7
  )
8
 
9
  func SetApiRouter(router *gin.Engine) {
@@ -16,9 +19,28 @@ func SetApiRouter(router *gin.Engine) {
16
 
17
  //router.GET("/api/init/model/chat/map", controller.InitModelChatMap)
18
  //https://api.openai.com/v1/images/generations
19
- v1Router := router.Group("/v1")
20
  v1Router.Use(middleware.OpenAIAuth())
21
  v1Router.POST("/chat/completions", controller.ChatForOpenAI)
22
  v1Router.POST("/images/generations", controller.ImagesForOpenAI)
23
  v1Router.GET("/models", controller.OpenaiModels)
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  package router
2
 
3
  import (
4
+ "fmt"
5
+ "genspark2api/common/config"
6
  "genspark2api/controller"
7
  "genspark2api/middleware"
8
  "github.com/gin-gonic/gin"
9
+ "strings"
10
  )
11
 
12
  func SetApiRouter(router *gin.Engine) {
 
19
 
20
  //router.GET("/api/init/model/chat/map", controller.InitModelChatMap)
21
  //https://api.openai.com/v1/images/generations
22
+ v1Router := router.Group(fmt.Sprintf("%s/v1", ProcessPath(config.RoutePrefix)))
23
  v1Router.Use(middleware.OpenAIAuth())
24
  v1Router.POST("/chat/completions", controller.ChatForOpenAI)
25
  v1Router.POST("/images/generations", controller.ImagesForOpenAI)
26
  v1Router.GET("/models", controller.OpenaiModels)
27
  }
28
+
29
+ func ProcessPath(path string) string {
30
+ // 判断字符串是否为空
31
+ if path == "" {
32
+ return ""
33
+ }
34
+
35
+ // 判断开头是否为/,不是则添加
36
+ if !strings.HasPrefix(path, "/") {
37
+ path = "/" + path
38
+ }
39
+
40
+ // 判断结尾是否为/,是则去掉
41
+ if strings.HasSuffix(path, "/") {
42
+ path = path[:len(path)-1]
43
+ }
44
+
45
+ return path
46
+ }