a / duckgo /request.go
yetey's picture
Upload 45 files
5757554 verified
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
}