|
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 { |
|
|
|
c.Header("Content-Type", "text/event-stream") |
|
} else { |
|
|
|
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 |
|
} |
|
|