Skip to content

Commit 46dfa46

Browse files
authored
Dev (#268)
* feat: 更新 ConvertToString 函数以支持动态模型,并添加相关测试用例 * feat: 更新 HandlerTTS 函数以支持流式数据解析,并添加相关单元测试 * fix: 移除不再需要的 funcaptcha 依赖 * refactor: 替换固定UA为随机浏览器UA,优化指纹多样性 - 新增util包的RandomUserAgent函数,生成主流桌面浏览器随机UA - 替换所有硬编码userAgent为defaultUserAgent()调用 - 为客户端状态加入UserAgent字段并初始化随机UA - 重构prooftoken模块,适配新的随机UA和POW逻辑 * refactor(util/useragent): 限定UA模板仅为Edge Windows一族 移除其他浏览器的UA模板,仅保留Edge Windows版本,匹配硬编码的sec-ch-ua头以通过Cloudflare校验,同时保留版本随机化保证指纹多样性 * chore: release v2.3.1 and implement various feature improvements - bump version to 2.3.1 - update .gitignore to add new ignore patterns - update chatgpt request converter to pass client parameter - optimize chatgpt request struct tags to omit empty fields - add support for image_url message part and related file handling - add automatic continuation when max tokens reached - rewrite file upload logic with better mime detection and error handling - rewrite turnstile solver with full VM implementation - update proof token logic to align with latest ChatGPT requirements
1 parent 48c827a commit 46dfa46

9 files changed

Lines changed: 592 additions & 58 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ tools/authenticator/access_tokens.txt
66
*.txt
77
aurora
88
chatgpttoapi
9+
chatgpt2api
910
tools/authenticator/.proxies.txt.swp
1011
.env
1112
*.har
@@ -16,4 +17,6 @@ tools/authenticator/.proxies.txt.swp
1617
.claude
1718
.gocache
1819
.atomcode
19-
CLAUDE.md
20+
CLAUDE.md
21+
MIGRATION_PLAN.md
22+
_scratch/

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.3.0
1+
2.3.1

conversion/requests/chatgpt/convert.go

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import (
55
"aurora/internal/tokens"
66
chatgpt_types "aurora/typings/chatgpt"
77
official_types "aurora/typings/official"
8+
"aurora/httpclient"
9+
"encoding/base64"
10+
"io"
11+
"net/http"
812
"strings"
913
)
1014

11-
func ConvertAPIRequest(api_request official_types.APIRequest, secret *tokens.Secret, proxy string) chatgpt_types.ChatGPTRequest {
15+
func ConvertAPIRequest(api_request official_types.APIRequest, secret *tokens.Secret, proxy string, client httpclient.AuroraHttpClient) chatgpt_types.ChatGPTRequest {
1216
chatgpt_request := chatgpt_types.NewChatGPTRequest()
1317

1418
// Model is passed directly to upstream; default to "auto" if not provided
@@ -22,7 +26,7 @@ func ConvertAPIRequest(api_request official_types.APIRequest, secret *tokens.Sec
2226
if apiMessage.Role == "system" {
2327
apiMessage.Role = "critic"
2428
}
25-
parts, metadata := buildMessageParts(apiMessage)
29+
parts, metadata := buildMessageParts(apiMessage, client, secret, proxy)
2630
if len(metadata) > 0 {
2731
chatgpt_request.AddMultimodalMessage(apiMessage.Role, parts, metadata)
2832
continue
@@ -39,9 +43,9 @@ func ConvertTTSAPIRequest(input string) chatgpt_types.ChatGPTRequest {
3943
return chatgpt_request
4044
}
4145

42-
func buildMessageParts(message official_types.APIMessage) ([]interface{}, map[string]interface{}) {
46+
func buildMessageParts(message official_types.APIMessage, client httpclient.AuroraHttpClient, secret *tokens.Secret, proxy string) ([]interface{}, map[string]interface{}) {
4347
text := message.Text()
44-
files := enrichFiles(message.Files())
48+
files := enrichFiles(message.Files(), client, secret, proxy)
4549
if len(files) == 0 {
4650
return []interface{}{text}, nil
4751
}
@@ -106,15 +110,26 @@ func buildMessageParts(message official_types.APIMessage) ([]interface{}, map[st
106110
}
107111
}
108112

109-
func enrichFiles(files []official_types.FileAttachment) []official_types.FileAttachment {
113+
func enrichFiles(files []official_types.FileAttachment, client httpclient.AuroraHttpClient, secret *tokens.Secret, proxy string) []official_types.FileAttachment {
110114
enriched := make([]official_types.FileAttachment, 0, len(files))
111115
seen := make(map[string]bool)
112116
for _, file := range files {
113117
id := fileID(file)
114118
if id == "" || seen[id] {
115119
continue
116120
}
117-
if uploaded, ok := backendchatgpt.LookupUploadedFile(id); ok {
121+
122+
// 处理 image_url 的 inline 数据(data: URL 或 http URL)
123+
if file.Source != "" && client != nil && secret != nil {
124+
if uploaded, ok := uploadInlineImage(file, client, secret, proxy); ok {
125+
file = uploaded
126+
} else {
127+
// 免费账号或上传失败:丢弃图片,只保留文本
128+
continue
129+
}
130+
}
131+
132+
if uploaded, ok := backendchatgpt.LookupUploadedFile(fileID(file)); ok {
118133
if file.ID == "" {
119134
file.ID = uploaded.ID
120135
}
@@ -137,12 +152,109 @@ func enrichFiles(files []official_types.FileAttachment) []official_types.FileAtt
137152
file.LibraryFileID = uploaded.LibraryFileID
138153
}
139154
}
140-
seen[id] = true
155+
seen[fileID(file)] = true
141156
enriched = append(enriched, file)
142157
}
143158
return enriched
144159
}
145160

161+
// uploadInlineImage 将 data: URL 或 http URL 图片上传到 ChatGPT 文件服务。
162+
func uploadInlineImage(file official_types.FileAttachment, client httpclient.AuroraHttpClient, secret *tokens.Secret, proxy string) (official_types.FileAttachment, bool) {
163+
src := file.Source
164+
var data []byte
165+
var filename string
166+
var contentType string
167+
168+
if strings.HasPrefix(src, "data:") {
169+
// data:image/png;base64,iVBOR...
170+
commaIdx := strings.Index(src, ",")
171+
if commaIdx < 0 {
172+
return file, false
173+
}
174+
meta := src[:commaIdx]
175+
b64data := src[commaIdx+1:]
176+
// 提取 mime type
177+
if semiIdx := strings.Index(meta, ";"); semiIdx > 5 {
178+
contentType = meta[5:semiIdx]
179+
}
180+
var err error
181+
data, err = base64.StdEncoding.DecodeString(b64data)
182+
if err != nil {
183+
// 尝试 raw base64
184+
data, err = base64.RawStdEncoding.DecodeString(b64data)
185+
if err != nil {
186+
return file, false
187+
}
188+
}
189+
filename = "image.png"
190+
if contentType == "" {
191+
contentType = "image/png"
192+
}
193+
} else if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
194+
// 下载远程图片
195+
resp, err := http.Get(src)
196+
if err != nil {
197+
return file, false
198+
}
199+
defer resp.Body.Close()
200+
if resp.StatusCode != 200 {
201+
return file, false
202+
}
203+
data, err = io.ReadAll(resp.Body)
204+
if err != nil {
205+
return file, false
206+
}
207+
contentType = resp.Header.Get("Content-Type")
208+
filename = guessFilenameFromURL(src)
209+
} else {
210+
return file, false
211+
}
212+
213+
if len(data) == 0 {
214+
return file, false
215+
}
216+
if contentType == "" {
217+
contentType = http.DetectContentType(data)
218+
}
219+
if filename == "" {
220+
filename = "image.png"
221+
}
222+
223+
uploaded, _, err := backendchatgpt.UploadFile(client, secret, proxy, filename, contentType, data)
224+
if err != nil {
225+
// 免费 token 无法上传文件,回退:把 data URL 原样传递
226+
return file, false
227+
}
228+
229+
return official_types.FileAttachment{
230+
ID: uploaded.FileID,
231+
FileID: uploaded.FileID,
232+
Name: uploaded.Filename,
233+
FileName: uploaded.Filename,
234+
Filename: uploaded.Filename,
235+
MimeType: uploaded.MimeType,
236+
MIMEType: uploaded.MimeType,
237+
Size: uploaded.Bytes,
238+
Width: uploaded.Width,
239+
Height: uploaded.Height,
240+
LibraryFileID: uploaded.LibraryFileID,
241+
}, true
242+
}
243+
244+
func guessFilenameFromURL(url string) string {
245+
idx := strings.LastIndex(url, "/")
246+
if idx >= 0 && idx < len(url)-1 {
247+
name := url[idx+1:]
248+
if q := strings.Index(name, "?"); q >= 0 {
249+
name = name[:q]
250+
}
251+
if name != "" {
252+
return name
253+
}
254+
}
255+
return "image.png"
256+
}
257+
146258
func fileID(file official_types.FileAttachment) string {
147259
if strings.TrimSpace(file.FileID) != "" {
148260
return strings.TrimSpace(file.FileID)

initialize/handlers.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func (h *Handler) nightmare(c *gin.Context) {
286286
client := bogdanfinn.NewStdClient()
287287

288288
// Convert the chat request to a ChatGPT request
289-
translated_request := chatgptrequestconverter.ConvertAPIRequest(original_request, secret, proxyUrl)
289+
translated_request := chatgptrequestconverter.ConvertAPIRequest(original_request, secret, proxyUrl, client)
290290
clientState := chatgpt.NewChatClientState()
291291
clientState.ConversationID = translated_request.ConversationID
292292
clientState.ParentMessageID = translated_request.ParentMessageID
@@ -438,7 +438,7 @@ func (h *Handler) responses(c *gin.Context) {
438438
uid := uuid.NewString()
439439
client := bogdanfinn.NewStdClient()
440440

441-
translated_request := chatgptrequestconverter.ConvertAPIRequest(original_request, secret, proxyUrl)
441+
translated_request := chatgptrequestconverter.ConvertAPIRequest(original_request, secret, proxyUrl, client)
442442
clientState := chatgpt.NewChatClientState()
443443
clientState.ConversationID = translated_request.ConversationID
444444
clientState.ParentMessageID = translated_request.ParentMessageID
@@ -726,7 +726,7 @@ func (h *Handler) files(c *gin.Context) {
726726
contentType := formFile.Header.Get("Content-Type")
727727
client := bogdanfinn.NewStdClient()
728728
client.SetCookies("https://chatgpt.com", chatgpt.BasicCookies)
729-
uploaded, status, err := chatgpt.UploadFile(client, secret, h.proxy.GetProxyIP(), formFile.Filename, contentType, c.PostForm("purpose"), data)
729+
uploaded, status, err := chatgpt.UploadFile(client, secret, h.proxy.GetProxyIP(), formFile.Filename, contentType, data)
730730
if err != nil {
731731
c.JSON(status, gin.H{"error": gin.H{
732732
"message": err.Error(),

0 commit comments

Comments
 (0)