package duckgo import ( "aurora/httpclient" officialtypes "aurora/typings/official" "bufio" "bytes" "encoding/json" "errors" "github.com/gin-gonic/gin" "io" "net/http" "strings" "sync" "time" ) type ApiRequest struct { Model string `json:"model"` Messages []struct { Role string `json:"role"` Content string `json:"content"` } `json:"messages"` } func (a *ApiRequest) AddMessage(role string, content string) { a.Messages = append(a.Messages, struct { Role string `json:"role"` Content string `json:"content"` }{ Role: role, Content: content, }) } func NewApiRequest(model string) ApiRequest { return ApiRequest{ Model: model, } } 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 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 == 400 { c.JSON(400, gin.H{"error": gin.H{ "message": "Request must be proper JSON", "type": "invalid_request_error", "param": nil, "code": response.Status, }}) 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, 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 originalResponse ApiResponse var previousText = "" 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]") { 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 += 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", originalResponse.Model) c.Writer.WriteString("data: " + final_line.String() + "\n\n") } } } return previousText }