Hansimov commited on
Commit
44c5e78
0 Parent(s):

:gem: [Feature] Enable chat with Bing from unauthenticated users

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ __pycache__
README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ ## Bing-Chat-API
2
+
3
+ A successor of [EdgeGPT](https://github.com/acheong08/EdgeGPT) by [acheong08](https://github.com/acheong08).
chathub_request_constructor.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import uuid
3
+ from datetime import datetime
4
+
5
+
6
+ def generate_random_hex_str(length: int = 32) -> str:
7
+ return "".join(random.choice("0123456789abcdef") for _ in range(length))
8
+
9
+
10
+ def generate_random_uuid():
11
+ return str(uuid.uuid4())
12
+
13
+
14
+ def get_locale():
15
+ return "en-US"
16
+
17
+
18
+ def get_timestamp_str():
19
+ now = datetime.now()
20
+ now_utc = datetime.utcnow()
21
+ timezone_offset = now - now_utc
22
+ offset_seconds = timezone_offset.total_seconds()
23
+ offset_hours = int(offset_seconds // 3600)
24
+ offset_minutes = int((offset_seconds % 3600) // 60)
25
+ offset_string = f"{offset_hours:+03d}:{offset_minutes:02d}"
26
+ timestamp_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + offset_string
27
+ # print(timestamp_str)
28
+ return timestamp_str
29
+
30
+
31
+ def get_prompt():
32
+ return "Hello, who are you?"
33
+
34
+
35
+ class ChathubRequestConstructor:
36
+ def __init__(
37
+ self,
38
+ conversation_style: str,
39
+ client_id: str,
40
+ conversation_id: str,
41
+ invocation_id: int = 0,
42
+ ):
43
+ self.client_id = client_id
44
+ self.conversation_id = conversation_id
45
+ self.message_id = generate_random_uuid()
46
+ self.invocation_id = invocation_id
47
+ self.conversation_style = conversation_style
48
+ self.construct()
49
+
50
+ def construct(self):
51
+ self.request_message = {
52
+ "arguments": [
53
+ {
54
+ "source": "cib",
55
+ "optionsSets": [
56
+ "nlu_direct_response_filter",
57
+ "deepleo",
58
+ "disable_emoji_spoken_text",
59
+ "responsible_ai_policy_235",
60
+ "enablemm",
61
+ "dv3sugg",
62
+ "autosave",
63
+ "uquopt",
64
+ "enelecintl",
65
+ "gndeleccf",
66
+ "gndlogcf",
67
+ "logprobsc",
68
+ "fluxprod",
69
+ "eredirecturl",
70
+ ],
71
+ "allowedMessageTypes": [
72
+ "ActionRequest",
73
+ "Chat",
74
+ "ConfirmationCard",
75
+ "Context",
76
+ "InternalSearchQuery",
77
+ "InternalSearchResult",
78
+ "Disengaged",
79
+ "InternalLoaderMessage",
80
+ "InvokeAction",
81
+ "Progress",
82
+ "RenderCardRequest",
83
+ "RenderContentRequest",
84
+ "AdsQuery",
85
+ "SemanticSerp",
86
+ "GenerateContentQuery",
87
+ "SearchQuery",
88
+ ],
89
+ "sliceIds": [
90
+ "cruisecf",
91
+ "adssqovr",
92
+ "gbacf",
93
+ "bggrey",
94
+ "1366cf",
95
+ "vnextvoice",
96
+ "caccnctat3",
97
+ "specedgecf",
98
+ "inosanewsmob",
99
+ "wrapnoins",
100
+ "readaloud",
101
+ "autotts",
102
+ "styleoffall",
103
+ "rwt2",
104
+ "dismmaslp",
105
+ "1117gndelecs0",
106
+ "713logprobsc",
107
+ "1118wcpdcl",
108
+ "1119backos",
109
+ "1103gndlog",
110
+ "1107reviewss0",
111
+ "fluxnosearch",
112
+ "727nrprdrt3",
113
+ "codecreator1",
114
+ "kchero50cf",
115
+ "cacmuidarb",
116
+ ],
117
+ "verbosity": "verbose",
118
+ "scenario": "SERP",
119
+ "plugins": [
120
+ {"id": "c310c353-b9f0-4d76-ab0d-1dd5e979cf68"},
121
+ ],
122
+ "traceId": generate_random_hex_str(),
123
+ "conversationHistoryOptionsSets": [
124
+ "autosave",
125
+ "savemem",
126
+ "uprofupd",
127
+ "uprofgen",
128
+ ],
129
+ "isStartOfSession": self.invocation_id == 0,
130
+ "requestId": self.message_id,
131
+ "message": {
132
+ "locale": get_locale(), # "en-US"
133
+ "market": get_locale(), # "en-US"
134
+ "region": get_locale()[-2:], # "US"
135
+ "location": "lat:47.639557;long:-122.128159;re=1000m;",
136
+ "locationHints": [
137
+ {
138
+ "SourceType": 1,
139
+ "RegionType": 2,
140
+ "Center": {
141
+ "Latitude": 38.668399810791016,
142
+ "Longitude": -121.14900207519531,
143
+ },
144
+ "Radius": 24902,
145
+ "Name": "Folsom, California",
146
+ "Accuracy": 24902,
147
+ "FDConfidence": 0.5,
148
+ "CountryName": "United States",
149
+ "CountryConfidence": 8,
150
+ "Admin1Name": "California",
151
+ "PopulatedPlaceName": "Folsom",
152
+ "PopulatedPlaceConfidence": 5,
153
+ "PostCodeName": "95630",
154
+ "UtcOffset": -8,
155
+ "Dma": 862,
156
+ }
157
+ ],
158
+ "userIpAddress": "192.55.55.51",
159
+ "timestamp": get_timestamp_str(), # "2023-11-20T12:50:17+08:00",
160
+ "author": "user",
161
+ "inputMethod": "Keyboard",
162
+ "text": get_prompt(),
163
+ "messageType": "Chat",
164
+ "requestId": self.message_id, # "a6ecd3aa-1007-6959-52fb-9e23f34e86be",
165
+ "messageId": self.message_id, # "a6ecd3aa-1007-6959-52fb-9e23f34e86be",
166
+ },
167
+ "tone": self.conversation_style.capitalize(),
168
+ "spokenTextMode": "None",
169
+ "conversationId": self.conversation_id, # "51D|BingProd|30FA137663F2BDBA514A0F31EE0A99E082B5AF8C0DA05696D2A5C6B56C10CF99",
170
+ "participant": {
171
+ "id": self.client_id, # "1055519195774559",
172
+ },
173
+ }
174
+ ],
175
+ "invocationId": str(self.invocation_id),
176
+ "target": "chat",
177
+ "type": 4,
178
+ }
conversation_creater.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import aiohttp
2
+ import asyncio
3
+ import certifi
4
+ import httpx
5
+ import json
6
+ import pprint
7
+ import ssl
8
+ import urllib
9
+
10
+ from chathub_request_constructor import ChathubRequestConstructor
11
+ from cookies_constructor import CookiesConstructor
12
+
13
+ ssl_context = ssl.create_default_context()
14
+ ssl_context.load_verify_locations(certifi.where())
15
+
16
+ http_proxy = "http://localhost:11111"
17
+
18
+
19
+ class ConversationCreator:
20
+ conversation_create_url = "https://www.bing.com/turing/conversation/create"
21
+
22
+ def __init__(self, cookies={}):
23
+ self.cookies = cookies
24
+ self.construct_cookies()
25
+
26
+ def construct_cookies(self):
27
+ self.httpx_cookies = httpx.Cookies()
28
+ for key, val in self.cookies.items():
29
+ self.httpx_cookies.set(key, val)
30
+
31
+ def create(self, proxy=None):
32
+ self.response = httpx.get(
33
+ self.conversation_create_url,
34
+ proxies=http_proxy if proxy is None else proxy,
35
+ cookies=self.httpx_cookies,
36
+ )
37
+ self.response_content = json.loads(self.response.content.decode("utf-8"))
38
+ self.response_headers = dict(self.response.headers)
39
+ pprint.pprint(self.response_content)
40
+ # pprint.pprint(self.response_headers)
41
+
42
+
43
+ def serialize_websockets_message(msg: dict) -> str:
44
+ return json.dumps(msg, ensure_ascii=False) + "\x1e"
45
+
46
+
47
+ class ConversationChatter:
48
+ def __init__(
49
+ self,
50
+ sec_access_token=None,
51
+ client_id=None,
52
+ conversation_id=None,
53
+ invocation_id=0,
54
+ cookies={},
55
+ ):
56
+ self.sec_access_token = sec_access_token
57
+ self.client_id = client_id
58
+ self.conversation_id = conversation_id
59
+ self.invocation_id = invocation_id
60
+ self.cookies = cookies
61
+ self.ws_url = (
62
+ "wss://sydney.bing.com/sydney/ChatHub"
63
+ + f"?sec_access_token={urllib.parse.quote(self.sec_access_token)}"
64
+ )
65
+ # print(f"sec_access_token: {self.sec_access_token}")
66
+
67
+ async def init_handshake(self, wss):
68
+ await wss.send_str(
69
+ serialize_websockets_message({"protocol": "json", "version": 1})
70
+ )
71
+ await wss.receive_str()
72
+ await wss.send_str(serialize_websockets_message({"type": 6}))
73
+
74
+ async def stream_chat(self, prompt=""):
75
+ self.aio_session = aiohttp.ClientSession(cookies=self.cookies)
76
+ request_headers = {
77
+ "Accept-Encoding": " gzip, deflate, br",
78
+ "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
79
+ "Cache-Control": "no-cache",
80
+ "Connection": "Upgrade",
81
+ "Host": "sydney.bing.com",
82
+ "Origin": "https://www.bing.com",
83
+ "Pragma": "no-cache",
84
+ "Sec-Websocket-Extensions": "permessage-deflate; client_max_window_bits",
85
+ # "Sec-Websocket-Key": "**********************==",
86
+ "Sec-Websocket-Version": "13",
87
+ "Upgrade": "websocket",
88
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
89
+ }
90
+ wss = await self.aio_session.ws_connect(
91
+ self.ws_url,
92
+ headers=request_headers,
93
+ ssl=ssl_context,
94
+ proxy=http_proxy,
95
+ )
96
+
97
+ await self.init_handshake(wss)
98
+ chathub_request_construtor = ChathubRequestConstructor(
99
+ conversation_style="precise",
100
+ client_id=self.client_id,
101
+ conversation_id=self.conversation_id,
102
+ invocation_id=self.invocation_id,
103
+ )
104
+ chathub_request_construtor.construct()
105
+
106
+ await wss.send_str(
107
+ serialize_websockets_message(chathub_request_construtor.request_message)
108
+ )
109
+ while not wss.closed:
110
+ response_lines_str = await wss.receive_str()
111
+ if isinstance(response_lines_str, str):
112
+ response_lines = response_lines_str.split("\x1e")
113
+ else:
114
+ continue
115
+ for line in response_lines:
116
+ if not line:
117
+ continue
118
+ data = json.loads(line)
119
+ if data.get("type") == 1:
120
+ arguments = data["arguments"][0]
121
+ if arguments.get("throttling"):
122
+ throttling = arguments.get("throttling")
123
+ pprint.pprint(throttling)
124
+ if arguments.get("messages"):
125
+ messages = arguments.get("messages")[0]
126
+ html_str = messages["adaptiveCards"][0]["body"][0]["text"]
127
+ # pprint.pprint(html_str)
128
+ elif data.get("type") == 2:
129
+ if data.get("item"):
130
+ item = data.get("item")
131
+ for message in item.get("messages"):
132
+ author = message["author"]
133
+ message_text = message["text"]
134
+ print(f"[{author}]: {message_text}")
135
+ elif data.get("type") == 3:
136
+ print("[Finished]")
137
+ await wss.close()
138
+ break
139
+ else:
140
+ # pprint.pprint(data)
141
+ continue
142
+
143
+
144
+ if __name__ == "__main__":
145
+ # cookies_constructor = CookiesConstructor()
146
+ # cookies_constructor.construct()
147
+
148
+ creator = ConversationCreator()
149
+ creator.create()
150
+
151
+ chatter = ConversationChatter(
152
+ sec_access_token=creator.response_headers[
153
+ "x-sydney-encryptedconversationsignature"
154
+ ],
155
+ client_id=creator.response_content["clientId"],
156
+ conversation_id=creator.response_content["conversationId"],
157
+ )
158
+ loop = asyncio.get_event_loop()
159
+ loop.run_until_complete(chatter.stream_chat())
160
+ loop.close()
cookies_constructor.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ class CookiesConstructor:
2
+ def __init__(self) -> None:
3
+ self.cookies_list = [
4
+ ]
5
+
6
+ def construct(self):
7
+ self.cookies = {}
8
+ for cookie in self.cookies_list:
9
+ self.cookies[cookie["name"]] = cookie["value"]