ffreemt
Add route prefix /hf
931bd01
raw
history blame
4.97 kB
package duckgo
import (
"aurora/httpclient"
duckgotypes "aurora/typings/duckgo"
officialtypes "aurora/typings/official"
"bufio"
"bytes"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"io"
"net/http"
"strings"
"sync"
"time"
)
var (
Token *XqdgToken
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
type XqdgToken struct {
Token string `json:"token"`
M sync.Mutex `json:"-"`
ExpireAt time.Time `json:"expire"`
}
func InitXVQD(client httpclient.AuroraHttpClient, proxyUrl string) (string, error) {
if Token == nil {
Token = &XqdgToken{
Token: "",
M: sync.Mutex{},
}
}
Token.M.Lock()
defer Token.M.Unlock()
if Token.Token == "" || Token.ExpireAt.Before(time.Now()) {
status, err := postStatus(client, proxyUrl)
if err != nil {
return "", err
}
defer status.Body.Close()
token := status.Header.Get("x-vqd-4")
if token == "" {
return "", errors.New("no x-vqd-4 token")
}
Token.Token = token
Token.ExpireAt = time.Now().Add(time.Minute * 5)
}
return Token.Token, nil
}
func postStatus(client httpclient.AuroraHttpClient, proxyUrl string) (*http.Response, error) {
if proxyUrl != "" {
client.SetProxy(proxyUrl)
}
header := createHeader()
header.Set("accept", "*/*")
header.Set("x-vqd-accept", "1")
response, err := client.Request(httpclient.GET, "https://duckduckgo.com/duckchat/v1/status", header, nil, nil)
if err != nil {
return nil, err
}
return response, nil
}
func POSTconversation(client httpclient.AuroraHttpClient, request duckgotypes.ApiRequest, token string, proxyUrl string) (*http.Response, error) {
if proxyUrl != "" {
client.SetProxy(proxyUrl)
}
body_json, err := json.Marshal(request)
if err != nil {
return &http.Response{}, err
}
header := createHeader()
header.Set("accept", "text/event-stream")
header.Set("x-vqd-4", token)
response, err := client.Request(httpclient.POST, "https://duckduckgo.com/duckchat/v1/chat", header, nil, bytes.NewBuffer(body_json))
if err != nil {
return nil, err
}
return response, nil
}
func Handle_request_error(c *gin.Context, response *http.Response) bool {
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(response.StatusCode, gin.H{"error": gin.H{
"message": "Unknown error",
"type": "internal_server_error",
"param": nil,
"code": "500",
"details": string(body),
}})
return true
}
c.JSON(response.StatusCode, gin.H{"error": gin.H{
"message": error_response["detail"],
"type": response.Status,
"param": nil,
"code": "error",
}})
return true
}
return false
}
func createHeader() httpclient.AuroraHeaders {
header := make(httpclient.AuroraHeaders)
header.Set("accept-language", "zh-CN,zh;q=0.9")
header.Set("content-type", "application/json")
header.Set("origin", "https://duckduckgo.com")
header.Set("referer", "https://duckduckgo.com/")
header.Set("sec-ch-ua", `"Chromium";v="120", "Google Chrome";v="120", "Not-A.Brand";v="99"`)
header.Set("sec-ch-ua-mobile", "?0")
header.Set("sec-ch-ua-platform", `"Windows"`)
header.Set("user-agent", UA)
return header
}
func Handler(c *gin.Context, response *http.Response, oldRequest duckgotypes.ApiRequest, stream bool) string {
reader := bufio.NewReader(response.Body)
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")
}
var previousText strings.Builder
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return ""
}
if len(line) < 6 {
continue
}
line = line[6:]
if !strings.HasPrefix(line, "[DONE]") {
var originalResponse duckgotypes.ApiResponse
err = json.Unmarshal([]byte(line), &originalResponse)
if err != nil {
continue
}
if originalResponse.Action != "success" {
c.JSON(500, gin.H{"error": "Error"})
return ""
}
responseString := ""
if originalResponse.Message != "" {
previousText.WriteString(originalResponse.Message)
translatedResponse := officialtypes.NewChatCompletionChunkWithModel(originalResponse.Message, originalResponse.Model)
responseString = "data: " + translatedResponse.String() + "\n\n"
}
if responseString == "" {
continue
}
if stream {
_, err = c.Writer.WriteString(responseString)
if err != nil {
return ""
}
c.Writer.Flush()
}
} else {
if stream {
final_line := officialtypes.StopChunkWithModel("stop", oldRequest.Model)
c.Writer.WriteString("data: " + final_line.String() + "\n\n")
}
}
}
return previousText.String()
}