selfapi-v2 / free-Chatgpt-api /api_bridge.go
akashyadav758
Harden monitor (gate all routes), swap ChatGPT backend to free-Chatgpt-api, clean repo
5c7f4b5
Raw
History Blame Contribute Delete
4.82 kB
package main
import (
"log"
"os"
"regexp"
"strings"
"time"
)
var fileIDRegexp = regexp.MustCompile(`(file_[0-9a-fA-F]{32}|file-[a-zA-Z0-9_-]{24,36})`)
func sendChat(prompt string) (string, string, error) {
return sendChatWithConversation(prompt, "", cfg.DefaultModel, cfg.DefaultThinkingEffort)
}
func sendChatWithConversation(prompt, conversationID, model, thinkingEffort string) (string, string, error) {
return sendChatWithConversationAndAttachments(prompt, conversationID, model, thinkingEffort, nil)
}
func sendChatWithConversationAndAttachments(prompt, conversationID, model, thinkingEffort string, attachments []FileAttachment) (string, string, error) {
var response, rawText, newConvID string
var err error
// ─── Try 1: Direct Go HTTP call using synced cookies (fastest, no extension needed) ───
if HasCookies() && len(attachments) == 0 {
log.Println("[chat] Attempting direct API call using synced cookies...")
response, rawText, newConvID, err = SendChatDirect(prompt, conversationID, model, thinkingEffort)
if err == nil {
log.Println("[chat] βœ… Direct API call succeeded!")
return processResponse(response, rawText, newConvID, prompt, attachments)
}
log.Printf("[chat] ⚠️ Direct API call failed: %v β€” falling back to extension", err)
}
// ─── Try 2: Extension full_conversation method (browser-based) ───
log.Println("[chat] Using extension full_conversation method...")
response, rawText, newConvID, err = sendChatViaFullConversation(prompt, conversationID, model, thinkingEffort, attachments...)
if err != nil {
return "", "", err
}
return processResponse(response, rawText, newConvID, prompt, attachments)
}
// processResponse handles post-processing: auto-download images, poll for async images
func processResponse(response, rawText, newConvID, prompt string, attachments []FileAttachment) (string, string, error) {
// Auto-download any files found in the rawText or response
var rawFileIDs []string
rawFileIDs = append(rawFileIDs, fileIDRegexp.FindAllString(rawText, -1)...)
rawFileIDs = append(rawFileIDs, fileIDRegexp.FindAllString(response, -1)...)
// Dedup and filter file IDs (excluding any attachment IDs we uploaded)
uploadedIDs := make(map[string]bool)
for _, att := range attachments {
uploadedIDs[att.ID] = true
}
seen := make(map[string]bool)
var fileIDs []string
for _, id := range rawFileIDs {
if !seen[id] && !uploadedIDs[id] {
seen[id] = true
fileIDs = append(fileIDs, id)
}
}
log.Printf("[chat] Found %d new file ID(s) to download: %v", len(fileIDs), fileIDs)
if len(fileIDs) > 0 {
for _, id := range fileIDs {
registerFilePrompt(id, prompt)
name := getPromptFilename(prompt, id)
log.Printf("[auto-download] Detected image %s (%s) in response, downloading...", id, name)
// Retry download up to 10 times because the image might still be generating/saving on OpenAI backend
var data []byte
var err error
for attempt := 1; attempt <= 10; attempt++ {
data, err = downloadChatGPTFile(id)
if err == nil {
break
}
log.Printf("[auto-download] Image %s not ready yet, retrying in 2 seconds... (attempt %d/10)", id, attempt)
time.Sleep(2 * time.Second)
}
if err == nil {
_ = os.MkdirAll("output", 0755)
localPath := "output/" + name + ".png"
err = os.WriteFile(localPath, data, 0644)
if err != nil {
log.Printf("[auto-download] Error saving file %s: %v", localPath, err)
} else {
log.Printf("[auto-download] Successfully saved image to %s", localPath)
}
} else {
log.Printf("[auto-download] Error downloading image %s: %v", id, err)
}
}
}
shouldPoll := newConvID != "" && (strings.Contains(response, "Processing image") ||
strings.Contains(response, "creating images") ||
strings.Contains(response, "generating your image") ||
len(attachments) > 0 ||
isPromptForImage(prompt))
if shouldPoll {
log.Printf("[chat] detected async image generation/edit in conversation %s, starting poll...", newConvID)
var excludeIDs []string
for _, att := range attachments {
excludeIDs = append(excludeIDs, att.ID)
}
polledResponse, pollErr := pollForImage(prompt, newConvID, 150*time.Second, excludeIDs...)
if pollErr == nil {
return polledResponse, newConvID, nil
}
log.Printf("[chat] image poll failed: %v, returning original response", pollErr)
}
return response, newConvID, nil
}
func isPromptForImage(prompt string) bool {
p := strings.ToLower(prompt)
keywords := []string{
"generate", "create", "draw", "make", "edit", "add", "remove",
"change", "modify", "paint", "cartoon", "3d", "render",
"picture", "photo", "illustration", "dall-e", "dalle",
}
for _, kw := range keywords {
if strings.Contains(p, kw) {
return true
}
}
return false
}