├── .gitignore ├── Makefile ├── README.md ├── bing.go ├── cmd └── main.go ├── const.go ├── go.mod ├── go.sum └── model.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmd/cookies.json 3 | .DS_Store 4 | build/* -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | GOOS=linux go build -o build/bingchat_linux_amd64 cmd/main.go 3 | GOOS=darwin go build -o build/bingchat_drawin_amd64 cmd/main.go 4 | GOOS=windows go build -o build/bingchat_windows_amd64.exe cmd/main.go -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Bing Ai Chat Copilot By Golang 3 | 4 | ## Requirements 5 | A Microsoft Account with early access to https://bing.com/chat (Required) 6 | Required in a supported country with New Bing Or set http_proxy 7 | 8 | ## Install 9 | - Install [Cookie-Editor](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm?hl=en), 10 | - Export `bing.com` cookies to save json file 11 | - Download `bingchat` for your platform 12 | 13 | ## Usage 14 | ```shell 15 | ➜ ./bingchat -c cookies.json 16 | local http proxy is set 17 | Bing Ai Chat Copilot 18 | /reset [styles] 19 | 1 more create 20 | 2 more balance 21 | 3 more precise 22 | /quit 23 | quit 24 | /help 25 | print help 26 | Current Style: Balance 27 | Ask> hello 28 | Hello! How can I help you today? 😊 29 | Prompt suggest 30 | 1: What is the weather like today? 31 | 2: What is the latest news? 32 | 3: Can you tell me a joke? 33 | Ask> 2 34 | Here are some of the latest news sources you can check out: 35 | - [The Guardian](https://www.theguardian.com/world) 36 | - [NBC News](https://www.nbcnews.com/latest-stories) 37 | - [RTÉ News](https://www.rte.ie/news/) 38 | 39 | Which one would you like to check out? 40 | Prompt suggest 41 | 1: What is the latest news on politics? 42 | 2: What is the latest news on sports? 43 | 3: What is the latest news on entertainment? 44 | Ask> /reset 1 45 | Switch Style Create 46 | Ask> 47 | ``` -------------------------------------------------------------------------------- /bing.go: -------------------------------------------------------------------------------- 1 | package bingchat 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "encoding/json" 7 | "fmt" 8 | "log" 9 | "math/rand" 10 | "net/http" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "github.com/google/uuid" 16 | "github.com/gorilla/websocket" 17 | ) 18 | 19 | const conversationSessionHeader = ` "accept": "application/json", 20 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", 21 | "sec-ch-ua": "\"Chromium\";v=\"112\", \"Microsoft Edge\";v=\"112\", \"Not:A-Brand\";v=\"99\"", 22 | "sec-ch-ua-arch": "\"x86\"", 23 | "sec-ch-ua-bitness": "\"64\"", 24 | "sec-ch-ua-full-version": "\"112.0.1722.48\"", 25 | "sec-ch-ua-full-version-list": "\"Chromium\";v=\"112.0.5615.121\", \"Microsoft Edge\";v=\"112.0.1722.48\", \"Not:A-Brand\";v=\"99.0.0.0\"", 26 | "sec-ch-ua-mobile": "?0", 27 | "sec-ch-ua-model": "\"\"", 28 | "sec-ch-ua-platform": "\"Windows\"", 29 | "sec-ch-ua-platform-version": "\"15.0.0\"", 30 | "sec-fetch-dest": "empty", 31 | "sec-fetch-mode": "cors", 32 | "sec-fetch-site": "same-origin", 33 | "sec-ms-gec": "673E82A42CAB0AF8C4F97398D164CA4F1F69BEC0D5E41226FD5375F12B17F341", 34 | "sec-ms-gec-version": "1-112.0.1722.48", 35 | "x-ms-client-request-id": "8f0c8a85-28bb-49eb-9c5d-c35dfc5112dd", 36 | "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32" 37 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48"}` 38 | 39 | const wsHeader = `{ 40 | "authority": "edgeservices.bing.com", 41 | "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", 42 | "accept-language": "en-US,en;q=0.9", 43 | "cache-control": "max-age=0", 44 | "sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"', 45 | "sec-ch-ua-arch": '"x86"', 46 | "sec-ch-ua-bitness": '"64"', 47 | "sec-ch-ua-full-version": '"110.0.1587.69"', 48 | "sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"', 49 | "sec-ch-ua-mobile": "?0", 50 | "sec-ch-ua-model": '""', 51 | "sec-ch-ua-platform": '"Windows"', 52 | "sec-ch-ua-platform-version": '"15.0.0"', 53 | "sec-fetch-dest": "document", 54 | "sec-fetch-mode": "navigate", 55 | "sec-fetch-site": "none", 56 | "sec-fetch-user": "?1", 57 | "upgrade-insecure-requests": "1", 58 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69", 59 | "x-edge-shopping-flag": "1" 60 | }` 61 | 62 | var Timeout = time.Second * 30 63 | 64 | type BingChatHub struct { 65 | sync.Mutex 66 | wsConn *websocket.Conn 67 | cookies []*http.Cookie 68 | client *http.Client 69 | chatSession *ConversationSession 70 | invocationId int 71 | sendMessage *SendMessage 72 | conversationStyle ConversationStyle 73 | } 74 | 75 | func (b *BingChatHub) parseHeader(headerData string) http.Header { 76 | data := map[string]string{} 77 | json.Unmarshal([]byte(headerData), &data) 78 | header := http.Header{} 79 | for key, value := range data { 80 | header.Add(key, value) 81 | } 82 | return header 83 | } 84 | 85 | type IBingChat interface { 86 | Reset(style ...ConversationStyle) 87 | SendMessage(msg string) (*MsgResp, error) 88 | Style() ConversationStyle 89 | } 90 | 91 | func NewBingChat(cookiesJson []byte, style ConversationStyle) (IBingChat, error) { 92 | var cookies []*http.Cookie 93 | json.Unmarshal(cookiesJson, &cookies) 94 | return &BingChatHub{ 95 | cookies: cookies, 96 | client: &http.Client{ 97 | Transport: &http.Transport{ 98 | Proxy: http.ProxyFromEnvironment, 99 | }, 100 | Timeout: Timeout, 101 | }, 102 | conversationStyle: style, 103 | }, nil 104 | } 105 | 106 | func (b *BingChatHub) Reset(style ...ConversationStyle) { 107 | if len(style) > 0 { 108 | fmt.Println("Switch Style", style[0]) 109 | b.conversationStyle = style[0] 110 | } 111 | b.wsConn.Close() 112 | b.chatSession = nil 113 | b.invocationId = 0 114 | b.sendMessage = nil 115 | } 116 | 117 | func (b *BingChatHub) Style() ConversationStyle { 118 | return b.conversationStyle 119 | } 120 | 121 | func (b *BingChatHub) createConversation() error { 122 | req, err := http.NewRequest("GET", "https://www.bing.com/turing/conversation/create", nil) 123 | if err != nil { 124 | return err 125 | } 126 | req.Header = b.parseHeader(conversationSessionHeader) 127 | req.Header.Set("x-ms-client-request-id", uuid.New().String()) 128 | for _, cookie := range b.cookies { 129 | req.AddCookie(cookie) 130 | } 131 | 132 | resp, err := b.client.Do(req) 133 | if err != nil { 134 | return err 135 | } 136 | if resp.StatusCode != http.StatusOK { 137 | return fmt.Errorf("request status code: %d", resp.StatusCode) 138 | } 139 | defer resp.Body.Close() 140 | b.chatSession = &ConversationSession{} 141 | err = json.NewDecoder(resp.Body).Decode(b.chatSession) 142 | if err != nil { 143 | return err 144 | } 145 | return nil 146 | } 147 | 148 | func (b *BingChatHub) initWsConnect() error { 149 | dial := websocket.DefaultDialer 150 | dial.Proxy = http.ProxyFromEnvironment 151 | dial.HandshakeTimeout = Timeout 152 | dial.EnableCompression = true 153 | 154 | dial.TLSClientConfig = &tls.Config{} 155 | conn, resp, err := dial.Dial("wss://sydney.bing.com/sydney/ChatHub", b.parseHeader(wsHeader)) 156 | if err != nil { 157 | return err 158 | } 159 | if resp.StatusCode != http.StatusSwitchingProtocols { 160 | return fmt.Errorf("status code: %d", resp.StatusCode) 161 | } 162 | 163 | b.wsConn = conn 164 | err = conn.WriteMessage(websocket.BinaryMessage, []byte(`{"protocol":"json","version":1}`+DELIMITER)) 165 | if err != nil { 166 | return fmt.Errorf("write json response: %v", err) 167 | } 168 | _, _, err = conn.NextReader() 169 | go func() { 170 | for { 171 | b.Lock() 172 | err := conn.WriteMessage(websocket.BinaryMessage, []byte(`{"type":6}`+DELIMITER)) 173 | b.Unlock() 174 | if err != nil { 175 | break 176 | } 177 | time.Sleep(time.Second * 5) 178 | } 179 | }() 180 | return err 181 | } 182 | 183 | type MsgResp struct { 184 | Suggest []string 185 | Notify chan string 186 | Msg string 187 | } 188 | 189 | func (b *BingChatHub) SendMessage(msg string) (*MsgResp, error) { 190 | if b.chatSession == nil { 191 | err := b.createConversation() 192 | if err != nil { 193 | log.Println("create conversation error: ", err) 194 | return nil, err 195 | } 196 | } 197 | err := b.initWsConnect() 198 | if err != nil { 199 | return nil, err 200 | } 201 | if b.sendMessage == nil { 202 | b.sendMessage = b.conversationStyle.TmpMessage() 203 | b.sendMessage.Arguments[0].ConversationSignature = b.chatSession.ConversationSignature 204 | b.sendMessage.Arguments[0].Participant.Id = b.chatSession.ClientID 205 | b.sendMessage.Arguments[0].ConversationId = b.chatSession.ConversationID 206 | } 207 | b.sendMessage.Arguments[0].TraceId = b.getTraceId() 208 | b.sendMessage.Arguments[0].IsStartOfSession = b.invocationId == 0 209 | b.sendMessage.Arguments[0].Message.Text = msg 210 | b.sendMessage.Arguments[0].Message.Timestamp = time.Now() 211 | b.sendMessage.InvocationId = fmt.Sprint(b.invocationId) 212 | b.invocationId += 1 213 | msgData, _ := json.Marshal(b.sendMessage) 214 | b.Lock() 215 | err = b.wsConn.WriteMessage(websocket.BinaryMessage, append(msgData, []byte(DELIMITER)...)) 216 | b.Unlock() 217 | if err != nil { 218 | return nil, err 219 | } 220 | msgRespChannel := &MsgResp{ 221 | Notify: make(chan string, 1), 222 | } 223 | go func() { 224 | var startRev bool 225 | lastMsg := "" 226 | defer close(msgRespChannel.Notify) 227 | for { 228 | _, data, err := b.wsConn.ReadMessage() 229 | if err != nil { 230 | log.Println(err) 231 | b.Reset() 232 | break 233 | } 234 | if len(data) == 0 { 235 | continue 236 | } 237 | spData := bytes.Split(data, []byte(DELIMITER)) 238 | if len(spData) == 0 { 239 | continue 240 | } 241 | data = spData[0] 242 | resp := MessageResp{} 243 | _ = json.Unmarshal(data, &resp) 244 | 245 | for _, message := range resp.Item.Messages { 246 | if message.MessageType == "Disengaged" { 247 | b.Reset() 248 | 249 | return 250 | } 251 | } 252 | 253 | if resp.Type == 1 && len(resp.Arguments) > 0 && resp.Arguments[0].Cursor.J != "" { 254 | startRev = true 255 | continue 256 | } 257 | if !startRev { 258 | continue 259 | } 260 | if resp.Type == 1 && len(resp.Arguments) > 0 && len(resp.Arguments[0].Messages) > 0 { 261 | if resp.Arguments[0].Messages[0].SuggestedResponses != nil { 262 | var suggests []string 263 | for _, suggest := range resp.Arguments[0].Messages[0].SuggestedResponses { 264 | suggests = append(suggests, suggest.Text) 265 | } 266 | msgRespChannel.Suggest = suggests 267 | } 268 | 269 | if resp.Arguments[0].Messages[0].MessageType == "Disengaged" { 270 | b.Reset() 271 | 272 | break 273 | } 274 | msg := strings.TrimSpace(resp.Arguments[0].Messages[0].Text) 275 | msgRespChannel.Msg = msg 276 | if len(lastMsg) > len(msg) { 277 | continue 278 | } 279 | if msg == "" || msg[len(lastMsg):] == "" { 280 | continue 281 | } 282 | msgRespChannel.Notify <- msg[len(lastMsg):] 283 | lastMsg = msg 284 | } 285 | if resp.Type == 2 { 286 | b.wsConn.Close() 287 | break 288 | } 289 | } 290 | 291 | }() 292 | 293 | return msgRespChannel, nil 294 | } 295 | 296 | func (b *BingChatHub) getTraceId() string { 297 | rand.Seed(time.Now().UnixNano()) 298 | length := 32 299 | bytes := make([]byte, length) 300 | str := "0123456789abcdef" 301 | for i := 0; i < length; i++ { 302 | bytes[i] = byte(str[rand.Intn(len(str))]) 303 | } 304 | return string(bytes) 305 | } 306 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bingchat" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | var spinChars = `|/-\` 15 | 16 | func main() { 17 | cookiesPath := flag.String("c", "cookies.json", "cookies path") 18 | flag.Parse() 19 | if os.Getenv("https_proxy") != "" || os.Getenv("http_proxy") != "" { 20 | fmt.Println("local http proxy is set") 21 | } 22 | data, err := os.ReadFile(*cookiesPath) 23 | if err != nil { 24 | log.Fatalln(err) 25 | } 26 | help := `Bing Ai Chat Copilot 27 | /reset [styles] 28 | 1 more create 29 | 2 more balance 30 | 3 more precise 31 | /quit 32 | quit 33 | /help 34 | print help 35 | ` 36 | 37 | fmt.Printf(help+"Current Style: %s \n", bingchat.ConversationBalanceStyle.String()) 38 | bingChat, err := bingchat.NewBingChat(data, bingchat.ConversationBalanceStyle) 39 | if err != nil { 40 | log.Fatalln(err) 41 | } 42 | var suggest []string 43 | for { 44 | var input string 45 | var style bingchat.ConversationStyle 46 | fmt.Printf("Ask> ") 47 | fmt.Scanln(&input, &style) 48 | if input == "" { 49 | continue 50 | } 51 | if strings.HasPrefix(input, "/reset") { 52 | var styles []bingchat.ConversationStyle 53 | if style >= 1 && style <= 3 { 54 | styles = append(styles, style) 55 | } 56 | bingChat.Reset(styles...) 57 | continue 58 | } else if strings.HasPrefix(input, "/quit") { 59 | fmt.Println("bye") 60 | os.Exit(0) 61 | } else if strings.HasPrefix(input, "/help") { 62 | fmt.Printf(help+"Current Style: %s \n", bingChat.Style()) 63 | continue 64 | } 65 | index, err := strconv.Atoi(input) 66 | if err == nil { 67 | if index-1 >= 0 && index-1 < len(suggest) { 68 | input = suggest[index-1] 69 | } 70 | } 71 | msgFinished := make(chan struct{}) 72 | go func() { 73 | i := 0 74 | for { 75 | select { 76 | case <-msgFinished: 77 | return 78 | default: 79 | } 80 | fmt.Printf("\r%s", string(spinChars[i%len(spinChars)])) 81 | time.Sleep(time.Second / 10) 82 | i++ 83 | } 84 | }() 85 | resp, err := bingChat.SendMessage(input) 86 | close(msgFinished) 87 | fmt.Printf("\r") 88 | if err != nil { 89 | fmt.Println(err) 90 | continue 91 | } 92 | 93 | for { 94 | msg, ok := <-resp.Notify 95 | if !ok { 96 | fmt.Println() 97 | break 98 | } 99 | fmt.Printf("%s", msg) 100 | } 101 | if len(resp.Suggest) > 0 { 102 | fmt.Print("Prompt suggest\n") 103 | for i, suggest := range resp.Suggest { 104 | fmt.Printf("%d: %s\n", i+1, suggest) 105 | } 106 | suggest = resp.Suggest 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /const.go: -------------------------------------------------------------------------------- 1 | package bingchat 2 | 3 | import "encoding/json" 4 | 5 | const balanceStyle = `{"arguments":[{"source":"cib","optionsSets":["nlu_direct_response_filter","deepleo","disable_emoji_spoken_text","responsible_ai_policy_235","enablemm","galileo","rcallowlist","responseos","jb090","jbfv202","dv3sugg"],"allowedMessageTypes":["Chat","InternalSearchQuery","InternalSearchResult","Disengaged","InternalLoaderMessage","RenderCardRequest","AdsQuery","SemanticSerp","GenerateContentQuery","SearchQuery"],"sliceIds":["contctxp2tf","delayglobjscf","0417bicunivs0","ssoverlap50","sspltop5","sswebtop1","audseq","sbsvgopt","nopreloadsstf","winlongmsg2tf","perfimpcomb","sugdivdis","sydnoinputt","wpcssopt","414suggs0","scctl","418glpv6ps0","417rcallow","321slocs0","407pgparsers0","0329resp","asfixescf","udscahrfoncf","414jbfv202"],"verbosity":"verbose","traceId":"6441f4712452428ab53b745af65c5089","isStartOfSession":true,"message":{"locale":"zh-CN","market":"zh-CN","region":"WW","location":"lat:47.639557;long:-122.128159;re=1000m;","locationHints":[{"country":"Singapore","timezoneoffset":8,"countryConfidence":8,"Center":{"Latitude":1.2929,"Longitude":103.8547},"RegionType":2,"SourceType":1}],"timestamp":"2023-04-21T10:27:06+08:00","author":"user","inputMethod":"Keyboard","text":"我需要帮助制定计划","messageType":"Chat"},"conversationSignature":"AdctQf6lU2LbVhUuyTMDCcchrXUbEaX2jyBbeQ2iXuY=","participant":{"id":"914798353003051"},"conversationId":"51D|BingProd|92EBE954F84335CBF36EE4E86BF8A028765E8922A38BC8A1E248457AF7342CA3"}],"invocationId":"","target":"chat","type":4}` 6 | const createStyle = `{"arguments":[{"source":"cib","optionsSets":["nlu_direct_response_filter","deepleo","disable_emoji_spoken_text","responsible_ai_policy_235","enablemm","h3imaginative","rcallowlist","responseos","jb090","jbfv202","dv3sugg","clgalileo","gencontentv3"],"allowedMessageTypes":["Chat","InternalSearchQuery","InternalSearchResult","Disengaged","InternalLoaderMessage","RenderCardRequest","AdsQuery","SemanticSerp","GenerateContentQuery","SearchQuery"],"sliceIds":["contctxp2tf","delayglobjscf","0417bicunivs0","ssoverlap50","sspltop5","sswebtop1","audseq","sbsvgopt","nopreloadsstf","winlongmsg2tf","perfimpcomb","sugdivdis","sydnoinputt","wpcssopt","414suggs0","scctl","418glpv6ps0","417rcallow","321slocs0","407pgparsers0","0329resp","asfixescf","udscahrfoncf","414jbfv202"],"verbosity":"verbose","traceId":"6441f4712452428ab53b745af65c5089","isStartOfSession":true,"message":{"locale":"zh-CN","market":"zh-CN","region":"WW","location":"lat:47.639557;long:-122.128159;re=1000m;","locationHints":[{"country":"Singapore","timezoneoffset":8,"countryConfidence":8,"Center":{"Latitude":1.2929,"Longitude":103.8547},"RegionType":2,"SourceType":1}],"timestamp":"2023-04-21T10:27:06+08:00","author":"user","inputMethod":"Keyboard","text":"告诉我的星座","messageType":"Chat"},"conversationSignature":"uk3kLopdE2Zb8nTXHFx/smV2IWyec3G11B0y8ehSC4k=","participant":{"id":"914798353003051"},"conversationId":"51D|BingProd|29F20DF6A2946BAD80F2B98E87138C4E14A6D8C5D29E95056F41D5BE3539D4B4"}],"invocationId":"","target":"chat","type":4}` 7 | const preciseStyle = `{"arguments":[{"source":"cib","optionsSets":["nlu_direct_response_filter","deepleo","disable_emoji_spoken_text","responsible_ai_policy_235","enablemm","h3precise","rcallowlist","responseos","jb090","jbfv202","dv3sugg","clgalileo"],"allowedMessageTypes":["Chat","InternalSearchQuery","InternalSearchResult","Disengaged","InternalLoaderMessage","RenderCardRequest","AdsQuery","SemanticSerp","GenerateContentQuery","SearchQuery"],"sliceIds":["contctxp2tf","delayglobjscf","0417bicunivs0","ssoverlap50","sspltop5","sswebtop1","audseq","sbsvgopt","nopreloadsstf","winlongmsg2tf","perfimpcomb","sugdivdis","sydnoinputt","wpcssopt","414suggs0","scctl","418glpv6ps0","417rcallow","321slocs0","407pgparsers0","0329resp","asfixescf","udscahrfoncf","414jbfv202"],"verbosity":"verbose","traceId":"6441f4712452428ab53b745af65c5089","isStartOfSession":true,"message":{"locale":"zh-CN","market":"zh-CN","region":"WW","location":"lat:47.639557;long:-122.128159;re=1000m;","locationHints":[{"country":"Singapore","timezoneoffset":8,"countryConfidence":8,"Center":{"Latitude":1.2929,"Longitude":103.8547},"RegionType":2,"SourceType":1}],"timestamp":"2023-04-21T10:27:06+08:00","author":"user","inputMethod":"Keyboard","text":"我需要帮助做研究","messageType":"Chat"},"conversationSignature":"1F8e/oVRPtqkMq+/hrKWphxvXbc5DTQTsItUsoaxedE=","participant":{"id":"914798353003051"},"conversationId":"51D|BingProd|23B9F05272D0D7471D94F332A995F6996B9E192846CB8B7007092B4B6DE6FDEC"}],"invocationId":"14","target":"chat","type":4}` 8 | const DELIMITER = "\x1e" 9 | 10 | type ConversationStyle uint8 11 | 12 | const ( 13 | ConversationCreateStyle ConversationStyle = iota + 1 14 | ConversationBalanceStyle 15 | ConversationPreciseStyle 16 | ) 17 | 18 | func (c ConversationStyle) String() string { 19 | switch c { 20 | case ConversationBalanceStyle: 21 | return "Balance" 22 | case ConversationCreateStyle: 23 | return "Create" 24 | case ConversationPreciseStyle: 25 | return "Precise" 26 | } 27 | return "" 28 | } 29 | 30 | func (c ConversationStyle) TmpMessage() *SendMessage { 31 | var data string 32 | switch c { 33 | case ConversationBalanceStyle: 34 | data = balanceStyle 35 | case ConversationCreateStyle: 36 | data = createStyle 37 | case ConversationPreciseStyle: 38 | data = preciseStyle 39 | } 40 | msg := SendMessage{} 41 | json.Unmarshal([]byte(data), &msg) 42 | return &msg 43 | } 44 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module bingchat 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/gorilla/websocket v1.5.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 2 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 3 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 4 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 5 | -------------------------------------------------------------------------------- /model.go: -------------------------------------------------------------------------------- 1 | package bingchat 2 | 3 | import "time" 4 | 5 | type HandShake struct { 6 | Protocol string `json:"protocol"` 7 | Version int `json:"version"` 8 | } 9 | 10 | type ConversationSession struct { 11 | ConversationID string `json:"conversationId"` 12 | ClientID string `json:"clientId"` 13 | ConversationSignature string `json:"conversationSignature"` 14 | Result struct { 15 | Value string `json:"value"` 16 | Message interface{} `json:"message"` 17 | } `json:"result"` 18 | } 19 | 20 | type SendMessage struct { 21 | Arguments []struct { 22 | Source string `json:"source"` 23 | OptionsSets []string `json:"optionsSets"` 24 | AllowedMessageTypes []string `json:"allowedMessageTypes"` 25 | SliceIds []string `json:"sliceIds"` 26 | Verbosity string `json:"verbosity"` 27 | TraceId string `json:"traceId"` 28 | IsStartOfSession bool `json:"isStartOfSession"` 29 | Message struct { 30 | Locale string `json:"locale"` 31 | Market string `json:"market"` 32 | Region string `json:"region"` 33 | Location string `json:"location"` 34 | LocationHints []struct { 35 | Country string `json:"country"` 36 | Timezoneoffset int `json:"timezoneoffset"` 37 | CountryConfidence int `json:"countryConfidence"` 38 | Center struct { 39 | Latitude float64 `json:"Latitude"` 40 | Longitude float64 `json:"Longitude"` 41 | } `json:"Center"` 42 | RegionType int `json:"RegionType"` 43 | SourceType int `json:"SourceType"` 44 | } `json:"locationHints"` 45 | Timestamp time.Time `json:"timestamp"` 46 | Author string `json:"author"` 47 | InputMethod string `json:"inputMethod"` 48 | Text string `json:"text"` 49 | MessageType string `json:"messageType"` 50 | } `json:"message"` 51 | ConversationSignature string `json:"conversationSignature"` 52 | Participant struct { 53 | Id string `json:"id"` 54 | } `json:"participant"` 55 | ConversationId string `json:"conversationId"` 56 | // PreviousMessages []struct { 57 | // Text string `json:"text"` 58 | // Author string `json:"author"` 59 | // AdaptiveCards []interface{} `json:"adaptiveCards"` 60 | // ContentOrigin string `json:"contentOrigin"` 61 | // SuggestedResponses []struct { 62 | // Text string `json:"text"` 63 | // ContentOrigin string `json:"contentOrigin"` 64 | // MessageType string `json:"messageType"` 65 | // MessageId string `json:"messageId"` 66 | // Offense string `json:"offense"` 67 | // } `json:"suggestedResponses"` 68 | // MessageId string `json:"messageId"` 69 | // MessageType string `json:"messageType"` 70 | // } `json:"previousMessages"` 71 | } `json:"arguments"` 72 | InvocationId string `json:"invocationId"` 73 | Target string `json:"target"` 74 | Type int `json:"type"` 75 | } 76 | 77 | type MessageResp struct { 78 | Type int `json:"type"` 79 | Target string `json:"target"` 80 | Item struct { 81 | Messages []struct { 82 | MessageType string `json:"messageType,omitempty"` 83 | } `json:"messages"` 84 | } `json:"item"` 85 | Arguments []struct { 86 | Cursor struct { 87 | J string `json:"j"` 88 | P int `json:"p"` 89 | } `json:"cursor"` 90 | Messages []struct { 91 | Text string `json:"text"` 92 | Author string `json:"author"` 93 | CreatedAt time.Time `json:"createdAt"` 94 | Timestamp time.Time `json:"timestamp"` 95 | MessageId string `json:"messageId"` 96 | Offense string `json:"offense"` 97 | MessageType string `json:"messageType"` 98 | AdaptiveCards []struct { 99 | Type string `json:"type"` 100 | Version string `json:"version"` 101 | Body []struct { 102 | Type string `json:"type"` 103 | Text string `json:"text"` 104 | Wrap bool `json:"wrap"` 105 | Size string `json:"size"` 106 | } `json:"body"` 107 | } `json:"adaptiveCards"` 108 | SourceAttributions []interface{} `json:"sourceAttributions"` 109 | Feedback struct { 110 | Tag interface{} `json:"tag"` 111 | UpdatedOn interface{} `json:"updatedOn"` 112 | Type string `json:"type"` 113 | } `json:"feedback"` 114 | ContentOrigin string `json:"contentOrigin"` 115 | Privacy interface{} `json:"privacy"` 116 | SuggestedResponses []struct { 117 | Text string `json:"text"` 118 | Author string `json:"author"` 119 | CreatedAt time.Time `json:"createdAt"` 120 | Timestamp time.Time `json:"timestamp"` 121 | MessageId string `json:"messageId"` 122 | MessageType string `json:"messageType"` 123 | Offense string `json:"offense"` 124 | Feedback struct { 125 | Tag interface{} `json:"tag"` 126 | UpdatedOn interface{} `json:"updatedOn"` 127 | Type string `json:"type"` 128 | } `json:"feedback"` 129 | ContentOrigin string `json:"contentOrigin"` 130 | Privacy interface{} `json:"privacy"` 131 | } `json:"suggestedResponses"` 132 | } `json:"messages"` 133 | RequestId string `json:"requestId"` 134 | } `json:"arguments"` 135 | } 136 | --------------------------------------------------------------------------------