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() }