1
0
This repository has been archived on 2024-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
chatgpt-to-api/internal/chatgpt/request.go
2023-06-10 03:30:45 +08:00

203 lines
5.5 KiB
Go

package chatgpt
import (
"bufio"
"bytes"
"encoding/json"
chatgpt_types "freechatgpt/typings/chatgpt"
"io"
"math/rand"
"os"
"strings"
http "github.com/bogdanfinn/fhttp"
tls_client "github.com/bogdanfinn/tls-client"
"github.com/gin-gonic/gin"
chatgpt_response_converter "freechatgpt/conversion/response/chatgpt"
// chatgpt "freechatgpt/internal/chatgpt"
official_types "freechatgpt/typings/official"
)
var proxies []string
var (
jar = tls_client.NewCookieJar()
options = []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(360),
tls_client.WithClientProfile(tls_client.Firefox_110),
tls_client.WithNotFollowRedirects(),
tls_client.WithCookieJar(jar), // create cookieJar instance and pass it as argument
// Disable SSL verification
tls_client.WithInsecureSkipVerify(),
}
client, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
http_proxy = os.Getenv("http_proxy")
API_REVERSE_PROXY = os.Getenv("API_REVERSE_PROXY")
)
func init() {
// Check for proxies.txt
if _, err := os.Stat("proxies.txt"); err == nil {
// Each line is a proxy, put in proxies array
file, _ := os.Open("proxies.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// Split line by :
proxy := scanner.Text()
proxy_parts := strings.Split(proxy, ":")
if len(proxy_parts) == 2 {
proxy = "socks5://" + proxy
} else if len(proxy_parts) == 4 {
proxy = "socks5://" + proxy_parts[2] + ":" + proxy_parts[3] + "@" + proxy_parts[0] + ":" + proxy_parts[1]
} else {
continue
}
proxies = append(proxies, proxy)
}
}
}
func random_int(min int, max int) int {
return min + rand.Intn(max-min)
}
func send_request(message chatgpt_types.ChatGPTRequest, access_token string) (*http.Response, error) {
if http_proxy != "" && len(proxies) == 0 {
client.SetProxy(http_proxy)
}
// Take random proxy from proxies.txt
if len(proxies) > 0 {
client.SetProxy(proxies[random_int(0, len(proxies))])
}
apiUrl := "https://chat.openai.com/backend-api/conversation"
if API_REVERSE_PROXY != "" {
apiUrl = API_REVERSE_PROXY
}
// JSONify the body and add it to the request
body_json, err := json.Marshal(message)
if err != nil {
return &http.Response{}, err
}
request, err := http.NewRequest(http.MethodPost, apiUrl, bytes.NewBuffer(body_json))
if err != nil {
return &http.Response{}, err
}
// Clear cookies
if os.Getenv("PUID") != "" {
request.Header.Set("Cookie", "_puid="+os.Getenv("PUID")+";")
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
request.Header.Set("Accept", "*/*")
if access_token != "" {
request.Header.Set("Authorization", "Bearer "+access_token)
}
if err != nil {
return &http.Response{}, err
}
response, err := client.Do(request)
return response, err
}
func Handler(c *gin.Context, token string, translated_request chatgpt_types.ChatGPTRequest, stream bool) (string, bool) {
response, err := send_request(translated_request, token)
if err != nil {
c.JSON(response.StatusCode, gin.H{
"error": "error sending request",
"message": response.Status,
})
return "", false
}
defer response.Body.Close()
if response.StatusCode != 200 {
// Try read response body as JSON
var error_response map[string]interface{}
err = json.NewDecoder(response.Body).Decode(&error_response)
if err != nil {
// Read response body
body, _ := io.ReadAll(response.Body)
c.JSON(500, gin.H{"error": gin.H{
"message": "Unknown error",
"type": "internal_server_error",
"param": nil,
"code": "500",
"details": string(body),
}})
return "", false
}
c.JSON(response.StatusCode, gin.H{"error": gin.H{
"message": error_response["detail"],
"type": response.Status,
"param": nil,
"code": "error",
}})
return "", false
}
// Create a bufio.Reader from the response body
reader := bufio.NewReader(response.Body)
// Read the response byte by byte until a newline character is encountered
if stream {
// Response content type is text/event-stream
c.Header("Content-Type", "text/event-stream")
} else {
// Response content type is application/json
c.Header("Content-Type", "application/json")
}
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return "", false
}
if len(line) < 6 {
continue
}
// Remove "data: " from the beginning of the line
line = line[6:]
// Check if line starts with [DONE]
if !strings.HasPrefix(line, "[DONE]") {
// Parse the line as JSON
var original_response chatgpt_types.ChatGPTResponse
err = json.Unmarshal([]byte(line), &original_response)
if err != nil {
continue
}
if original_response.Error != nil {
return "", false
}
if original_response.Message.Author.Role != "assistant" || original_response.Message.Content.Parts == nil {
continue
}
if stream {
_, err = c.Writer.WriteString(chatgpt_response_converter.ConvertToString(&original_response))
if err != nil {
return "", false
}
}
// Flush the response writer buffer to ensure that the client receives each line as it's written
c.Writer.Flush()
} else {
if stream {
final_line := official_types.StopChunk()
c.Writer.WriteString("data: " + final_line.String() + "\n\n")
c.String(200, "data: [DONE]\n\n")
return "", false
}
}
}
return chatgpt_response_converter.Previous_text, false
}