Skip to content

Commit

Permalink
add bing's file upload to sydney package
Browse files Browse the repository at this point in the history
  • Loading branch information
juzeon committed Mar 3, 2024
1 parent 8652712 commit 8d4f3b7
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 32 deletions.
3 changes: 3 additions & 0 deletions sydney/conversation.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ func (o *Sydney) createConversation() (CreateConversationResponse, error) {
if value := resp.Header.Get("X-Sydney-Encryptedconversationsignature"); value != "" {
response.SecAccessToken = value
}
if value := resp.Header.Get("X-Sydney-Conversationsignature"); value != "" {
response.BearerToken = value
}
var cookieFields []string
for _, field := range resp.Header.Values("set-cookie") {
cookieFields = append(cookieFields, strings.Split(field, ";")[0])
Expand Down
58 changes: 46 additions & 12 deletions sydney/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ func (o *Sydney) AskStream(options AskStreamOptions) (<-chan Message, error) {
})
}
case "InternalLoaderMessage":
if message.Get("contentOrigin").String() == "retrieve-shortdoc-progress" {
out <- Message{
Type: MessageTypeLoading,
Text: messageText + " " + messageHiddenText,
}
continue
}
if message.Get("hiddenText").Exists() {
out <- Message{
Type: MessageTypeLoading,
Expand Down Expand Up @@ -319,6 +326,34 @@ func (o *Sydney) AskStreamRaw(options AskStreamOptions) (CreateConversationRespo
return conversation, nil, options.StopCtx.Err()
default:
}
previousMessages := []PreviousMessage{
{
Author: "user",
Description: options.WebpageContext,
ContextType: "WebPage",
MessageType: "Context",
},
}
var uploadFileResult UploadFileResult
if options.UploadFilePath != "" {
uploadFileResult, err = o.uploadFile(options.UploadFilePath, conversation)
if err != nil {
return CreateConversationResponse{}, nil, err
}
select {
case <-options.StopCtx.Done():
return conversation, nil, options.StopCtx.Err()
default:
}
previousMessages = append(previousMessages, PreviousMessage{
Author: "user",
Description: "User has uploaded one or more files with the following metadata in Json format. " +
"I will use them as the main source of context when I answer questions from user.",
ContextType: "ClientApp",
MessageType: "Context",
HiddenText: uploadFileResult.FileHiddenText,
})
}
msgChan := make(chan RawMessage)
go func(msgChan chan RawMessage) {
defer func(msgChan chan RawMessage) {
Expand Down Expand Up @@ -415,6 +450,12 @@ func (o *Sydney) AskStreamRaw(options AskStreamOptions) (CreateConversationRespo
LocationHints: []LocationHint{
o.locationHint,
},
AttachedFilesInfos: lo.Ternary(uploadFileResult.Valid, []ArgumentAttachedFilesInfo{
{
FileName: uploadFileResult.Response.FileName,
FileType: uploadFileResult.RealFileType,
},
}, nil),
Author: "user",
InputMethod: "Keyboard",
Text: options.Prompt,
Expand All @@ -426,18 +467,11 @@ func (o *Sydney) AskStreamRaw(options AskStreamOptions) (CreateConversationRespo
Tone: o.conversationStyle,
ConversationSignature: util.Ternary[any](conversation.ConversationSignature == "",
nil, conversation.ConversationSignature),
Participant: Participant{Id: conversation.ClientId},
SpokenTextMode: "None",
ConversationId: conversation.ConversationId,
PreviousMessages: []PreviousMessage{
{
Author: "user",
Description: options.WebpageContext,
ContextType: "WebPage",
MessageType: "Context",
},
},
GptId: o.gptID,
Participant: Participant{Id: conversation.ClientId},
SpokenTextMode: "None",
ConversationId: conversation.ConversationId,
PreviousMessages: previousMessages,
GptId: o.gptID,
},
},
InvocationId: "0",
Expand Down
15 changes: 8 additions & 7 deletions sydney/sydney.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,25 @@ func NewSydney(options Options) *Sydney {
}
optionsSet := []string{
"fluxcopilot",
// no jailbreak filter
"nojbf",
"nojbf", // no jailbreak filter
"iyxapbing",
"iycapbing",
"dgencontentv3",
"nointernalsugg",
"disable_telemetry",
"machine_affinity",
"streamf",
// code interpreter
"codeint",
"langdtwb",
"fdwtlst",
"fluxprod",
"eredirecturl",
// may related to image search
"gptvnodesc",
"gptvnoex",
"gptvnodesc", // may related to image search
"gptvnoex", // may related to image search
"codeintfile", // code interpreter + file uploader
"sdretrieval", // retrieve upload file
"gamaxinvoc", // file reader invocation
"ldsummary", // our guess: long description (context) summary
"ldqa", // our guess: long description (context) quality assurance
}
forwardedIP := "1.0.0." + strconv.Itoa(util.RandIntInclusive(1, 255))
cookies := util.Ternary(options.Cookies == nil, map[string]string{}, options.Cookies)
Expand Down
60 changes: 47 additions & 13 deletions sydney/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type CreateConversationResponse struct {
Result CreateConversationResult `json:"result"`
SecAccessToken string `json:"secAccessToken"`
ConversationSignature string `json:"conversationSignature"`
BearerToken string `json:"bearerToken"`
}
type RawMessage struct {
Data string
Expand Down Expand Up @@ -101,18 +102,23 @@ type ArgumentPlugin struct {
Category int `json:"category"`
}
type ArgumentMessage struct {
Locale string `json:"locale"`
Market string `json:"market"`
Region string `json:"region"`
Location string `json:"location"`
LocationHints []LocationHint `json:"locationHints"`
Author string `json:"author"`
InputMethod string `json:"inputMethod"`
Text string `json:"text"`
MessageType string `json:"messageType"`
RequestId string `json:"requestId"`
MessageId string `json:"messageId"`
ImageUrl any `json:"imageUrl"`
Locale string `json:"locale"`
Market string `json:"market"`
Region string `json:"region"`
Location string `json:"location"`
LocationHints []LocationHint `json:"locationHints"`
AttachedFilesInfos []ArgumentAttachedFilesInfo `json:"attachedFilesInfos"`
Author string `json:"author"`
InputMethod string `json:"inputMethod"`
Text string `json:"text"`
MessageType string `json:"messageType"`
RequestId string `json:"requestId"`
MessageId string `json:"messageId"`
ImageUrl any `json:"imageUrl"`
}
type ArgumentAttachedFilesInfo struct {
FileName string `json:"fileName"`
FileType string `json:"fileType"`
}
type Participant struct {
Id string `json:"id"`
Expand All @@ -122,7 +128,7 @@ type PreviousMessage struct {
Description string `json:"description"`
ContextType string `json:"contextType"`
MessageType string `json:"messageType"`
MessageId string `json:"messageId"`
HiddenText string `json:"hiddenText"`
}
type Options struct {
Debug bool
Expand All @@ -143,6 +149,7 @@ type AskStreamOptions struct {
Prompt string
WebpageContext string
ImageURL string
UploadFilePath string

messageID string // A random uuid. Optional.
disableCaptchaBypass bool
Expand Down Expand Up @@ -212,3 +219,30 @@ type BypassCaptchaResponse struct {
} `json:"result"`
Error string `json:"error"`
}
type UploadFileHiddenText struct {
FileName string `json:"fileName"`
FileType string `json:"fileType"`
DocId string `json:"docId"`
IsLongContext bool `json:"isLongContext"`
UserId string `json:"userId"`
IsBCE bool `json:"isBCE"`
}
type UploadFileResponse struct {
FileName string `json:"fileName"`
FileSize int `json:"fileSize"`
FileType string `json:"fileType"`
IsLongContext bool `json:"isLongContext"`
DocId string `json:"docId"`
UserId string `json:"userId"`
Result struct {
Value string `json:"value"`
Message string `json:"message"`
ServiceVersion string `json:"serviceVersion"`
} `json:"result"`
}
type UploadFileResult struct {
Valid bool
Response UploadFileResponse
FileHiddenText string
RealFileType string
}
155 changes: 155 additions & 0 deletions sydney/upload.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package sydney

import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/imroc/req/v3"
"github.com/samber/lo"
"io"
"log/slog"
"os"
"path/filepath"
"strconv"
"strings"
"sydneyqt/util"
"time"
)
Expand Down Expand Up @@ -51,3 +60,149 @@ func (o *Sydney) UploadImage(jpgImgData []byte) (string, error) {
}
return "https://www.bing.com/images/blob?bcid=" + result.BlobId, nil
}

func (o *Sydney) uploadFile(uploadFilePath string, conversation CreateConversationResponse) (UploadFileResult, error) {
var empty UploadFileResult
_, client, err := util.MakeHTTPClient(o.proxy, 60*time.Second)
if err != nil {
return empty, err
}
if !lo.Contains(allowedFileExtensions, strings.TrimPrefix(filepath.Ext(uploadFilePath), ".")) {
return empty, errors.New("file type " + filepath.Ext(uploadFilePath) + " is not allowed")
}
f, err := os.ReadFile(uploadFilePath)
if err != nil {
return empty, err
}
if len(f) >= 1024*1024 { // 1MB
return empty, errors.New("file to upload must be less than 1MB")
}
var response UploadFileResponse
resp, err := client.R().
SetHeader("Authorization", "Bearer "+conversation.BearerToken).
SetHeader("Referer", "https://www.bing.com/search?q=Bing+AI&showconv=1").
SetHeader("Origin", "https://www.bing.com").
SetFileUpload(req.FileUpload{
ParamName: "file",
FileName: filepath.Base(uploadFilePath),
GetFileContent: func() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(f)), nil
},
FileSize: int64(len(f)),
ContentType: "application/octet-stream",
}).SetFormData(map[string]string{
"conversationId": conversation.ConversationId,
"tone": o.conversationStyle,
"userId": conversation.ClientId,
"enableFileUploadLongContext": "true",
}).SetSuccessResult(&response).Post("https://sydney.bing.com/sydney/UploadFile")
if err != nil {
return empty, err
}
if resp.IsErrorState() {
return empty, errors.New("error status code: " + strconv.Itoa(resp.GetStatusCode()))
}
if response.Result.Value != "Success" {
return empty, errors.New("upload returned failed result: " + response.Result.Message)
}
realFileType := fileExtensionToFileType(filepath.Ext(uploadFilePath))
hiddenText := []UploadFileHiddenText{
{
FileName: response.FileName,
FileType: realFileType,
DocId: response.DocId,
IsLongContext: response.IsLongContext,
UserId: response.UserId,
IsBCE: false,
},
}
v, err := json.Marshal(hiddenText)
if err != nil {
return empty, err
}
hiddenTextString := string(v)
result := UploadFileResult{
Valid: true,
Response: response,
FileHiddenText: hiddenTextString,
RealFileType: realFileType,
}
slog.Info("Uploaded file", "result", result)
return result, nil
}

func fileExtensionToFileType(ext string) string {
ext = strings.TrimPrefix(ext, ".")
switch ext {
case "docx", "rtf":
return "word"
case "xlsx":
return "excel"
case "pptx":
return "powerpoint"
case "pdf":
return "pdf"
default:
return "text"
}
}

var allowedFileExtensions = []string{
"rtf",
"txt",
"py",
"ipynb",
"js",
"jsx",
"html",
"css",
"java",
"cs",
"php",
"c",
"cpp",
"cxx",
"h",
"hpp",
"rs",
"R",
"Rmd",
"swift",
"go",
"rb",
"kt",
"kts",
"ts",
"tsx",
"m",
"scala",
"rs",
"dart",
"lua",
"pl",
"pm",
"t",
"sh",
"bash",
"zsh",
"csv",
"log",
"ini",
"config",
"json",
"yaml",
"yml",
"toml",
"lua",
"sql",
"md",
"coffee",
"tex",
"latex",
"pdf",
"docx",
"xlsx",
"pptx",
"html",
"wav",
}

0 comments on commit 8d4f3b7

Please sign in to comment.