xiaolv commited on
Commit
ebc3abc
1 Parent(s): 91525e6

Delete EdgeGPT

Browse files
EdgeGPT/EdgeGPT.py DELETED
@@ -1,235 +0,0 @@
1
- """
2
- Main.py
3
- """
4
- from __future__ import annotations
5
-
6
- import json
7
- from pathlib import Path
8
- from typing import Generator
9
-
10
- from .chathub import *
11
- from .conversation import *
12
- from .conversation_style import *
13
- from .request import *
14
- from .utilities import *
15
-
16
-
17
- class Chatbot:
18
- """
19
- Combines everything to make it seamless
20
- """
21
-
22
- def __init__(
23
- self,
24
- proxy: str | None = None,
25
- cookies: list[dict] | None = None,
26
- ) -> None:
27
- self.proxy: str | None = proxy
28
- self.chat_hub: ChatHub = ChatHub(
29
- Conversation(self.proxy, cookies=cookies),
30
- proxy=self.proxy,
31
- cookies=cookies,
32
- )
33
-
34
- @staticmethod
35
- async def create(
36
- proxy: str | None = None,
37
- cookies: list[dict] | None = None,
38
- ) -> Chatbot:
39
- self = Chatbot.__new__(Chatbot)
40
- self.proxy = proxy
41
- self.chat_hub = ChatHub(
42
- await Conversation.create(self.proxy, cookies=cookies),
43
- proxy=self.proxy,
44
- cookies=cookies,
45
- )
46
- return self
47
-
48
- async def save_conversation(self, filename: str) -> None:
49
- """
50
- Save the conversation to a file
51
- """
52
- with open(filename, "w") as f:
53
- conversation_id = self.chat_hub.request.conversation_id
54
- conversation_signature = self.chat_hub.request.conversation_signature
55
- client_id = self.chat_hub.request.client_id
56
- invocation_id = self.chat_hub.request.invocation_id
57
- f.write(
58
- json.dumps(
59
- {
60
- "conversation_id": conversation_id,
61
- "conversation_signature": conversation_signature,
62
- "client_id": client_id,
63
- "invocation_id": invocation_id,
64
- },
65
- ),
66
- )
67
-
68
- async def load_conversation(self, filename: str) -> None:
69
- """
70
- Load the conversation from a file
71
- """
72
- with open(filename) as f:
73
- conversation = json.load(f)
74
- self.chat_hub.request = ChatHubRequest(
75
- conversation_signature=conversation["conversation_signature"],
76
- client_id=conversation["client_id"],
77
- conversation_id=conversation["conversation_id"],
78
- invocation_id=conversation["invocation_id"],
79
- )
80
-
81
- async def get_conversation(self) -> dict:
82
- """
83
- Gets the conversation history from conversation_id (requires load_conversation)
84
- """
85
- return await self.chat_hub.get_conversation()
86
-
87
- async def get_activity(self) -> dict:
88
- """
89
- Gets the recent activity (requires cookies)
90
- """
91
- return await self.chat_hub.get_activity()
92
-
93
- async def ask(
94
- self,
95
- prompt: str,
96
- wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
97
- conversation_style: CONVERSATION_STYLE_TYPE = None,
98
- webpage_context: str | None = None,
99
- search_result: bool = False,
100
- locale: str = guess_locale(),
101
- simplify_response: bool = False,
102
- ) -> dict:
103
- """
104
- Ask a question to the bot
105
- Response:
106
- {
107
- item (dict):
108
- messages (list[dict]):
109
- adaptiveCards (list[dict]):
110
- body (list[dict]):
111
- text (str): Response
112
- }
113
- To get the response, you can do:
114
- response["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"]
115
- """
116
- async for final, response in self.chat_hub.ask_stream(
117
- prompt=prompt,
118
- conversation_style=conversation_style,
119
- wss_link=wss_link,
120
- webpage_context=webpage_context,
121
- search_result=search_result,
122
- locale=locale,
123
- ):
124
- if final:
125
- if not simplify_response:
126
- return response
127
- messages_left = response["item"]["throttling"][
128
- "maxNumUserMessagesInConversation"
129
- ] - response["item"]["throttling"].get(
130
- "numUserMessagesInConversation", 0
131
- )
132
- if messages_left == 0:
133
- raise Exception("Max messages reached")
134
- for msg in reversed(response["item"]["messages"]):
135
- if msg.get("adaptiveCards") and msg["adaptiveCards"][0]["body"][
136
- 0
137
- ].get("text"):
138
- message = msg
139
- break
140
- if not message:
141
- raise Exception("No message found")
142
- suggestions = [
143
- suggestion["text"]
144
- for suggestion in message.get("suggestedResponses", [])
145
- ]
146
- adaptive_cards = message.get("adaptiveCards", [])
147
- adaptive_text = (
148
- adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None
149
- )
150
- sources = (
151
- adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None
152
- )
153
- sources_text = (
154
- adaptive_cards[0]["body"][-1].get("text")
155
- if adaptive_cards
156
- else None
157
- )
158
- return {
159
- "text": message["text"],
160
- "author": message["author"],
161
- "sources": sources,
162
- "sources_text": sources_text,
163
- "suggestions": suggestions,
164
- "messages_left": messages_left,
165
- "max_messages": response["item"]["throttling"][
166
- "maxNumUserMessagesInConversation"
167
- ],
168
- "adaptive_text": adaptive_text,
169
- }
170
- return {}
171
-
172
- async def ask_stream(
173
- self,
174
- prompt: str,
175
- wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
176
- conversation_style: CONVERSATION_STYLE_TYPE = None,
177
- raw: bool = False,
178
- webpage_context: str | None = None,
179
- search_result: bool = False,
180
- locale: str = guess_locale(),
181
- ) -> Generator[bool, dict | str, None]:
182
- """
183
- Ask a question to the bot
184
- """
185
- async for response in self.chat_hub.ask_stream(
186
- prompt=prompt,
187
- conversation_style=conversation_style,
188
- wss_link=wss_link,
189
- raw=raw,
190
- webpage_context=webpage_context,
191
- search_result=search_result,
192
- locale=locale,
193
- ):
194
- yield response
195
-
196
- async def close(self) -> None:
197
- """
198
- Close the connection
199
- """
200
- await self.chat_hub.close()
201
-
202
- async def delete_conversation(
203
- self,
204
- conversation_id: str = None,
205
- conversation_signature: str = None,
206
- client_id: str = None,
207
- ) -> None:
208
- """
209
- Delete the chat in the server
210
- """
211
- await self.chat_hub.delete_conversation(
212
- conversation_id=conversation_id,
213
- conversation_signature=conversation_signature,
214
- client_id=client_id,
215
- )
216
-
217
- async def reset(self, delete=False) -> None:
218
- """
219
- Reset the conversation
220
- """
221
- if delete:
222
- await self.remove_and_close()
223
- else:
224
- await self.close()
225
- self.chat_hub = ChatHub(
226
- await Conversation.create(self.proxy, cookies=self.chat_hub.cookies),
227
- proxy=self.proxy,
228
- cookies=self.chat_hub.cookies,
229
- )
230
-
231
-
232
- if __name__ == "__main__":
233
- from .main import main
234
-
235
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/EdgeUtils.py DELETED
@@ -1,376 +0,0 @@
1
- import asyncio
2
- import json
3
- import re
4
- from pathlib import Path
5
-
6
- from .EdgeGPT import Chatbot, ConversationStyle
7
- from .ImageGen import ImageGen
8
- from log2d import Log
9
-
10
- Log("BingChat")
11
- log = Log.BingChat.debug # shortcut to create a log entry
12
-
13
- class Cookie:
14
- """
15
- Convenience class for Bing Cookie files, data, and configuration. This Class
16
- is updated dynamically by the Query class to allow cycling through >1
17
- cookie/credentials file e.g. when daily request limits (current 200 per
18
- account per day) are exceeded.
19
- """
20
- current_file_index = 0
21
- dir_path = Path.home().resolve() / "bing_cookies"
22
- current_file_path = dir_path # Avoid Path errors when no cookie file used
23
- search_pattern = 'bing_cookies_*.json'
24
- ignore_files = set()
25
- request_count = {}
26
- supplied_files = set()
27
- rotate_cookies = True
28
-
29
- @classmethod
30
- def files(cls):
31
- """
32
- Return a sorted list of all cookie files matching .search_pattern in
33
- cls.dir_path, plus any supplied files, minus any ignored files.
34
- """
35
- all_files = set(Path(cls.dir_path).glob(cls.search_pattern))
36
- if hasattr(cls, "supplied_files"):
37
- supplied_files = {x for x in cls.supplied_files if x.is_file()}
38
- all_files.update(supplied_files)
39
- return sorted(list(all_files - cls.ignore_files))
40
-
41
- @classmethod
42
- def import_data(cls):
43
- """
44
- Read the active cookie file and populate the following attributes:
45
-
46
- .current_file_path
47
- .current_data
48
- .image_token
49
- """
50
- if not cls.files():
51
- log(f"No files in Cookie.dir_path")
52
- return
53
- try:
54
- cls.current_file_path = cls.files()[cls.current_file_index]
55
- except IndexError:
56
- log(f"Invalid file index [{cls.current_file_index}]")
57
- log("Files in Cookie.dir_path:")
58
- for file in cls.files():
59
- log(f"{file}")
60
- return
61
- log(f"Importing cookies from: {cls.current_file_path.name}")
62
- with open(cls.current_file_path, encoding="utf-8") as file:
63
- cls.current_data = json.load(file)
64
- cls.image_token = [x for x in cls.current_data if x.get("name").startswith("_U")]
65
- cls.image_token = cls.image_token[0].get("value")
66
-
67
- @classmethod
68
- def import_next(cls, discard=False):
69
- """
70
- Cycle through to the next cookies file then import it.
71
-
72
- discard (bool): True -Mark the previous file to be ignored for the remainder of the current session. Otherwise cycle through all available
73
- cookie files (sharing the workload and 'resting' when not in use).
74
- """
75
- if not hasattr(cls, "current_file_path"):
76
- cls.import_data()
77
- return
78
- try:
79
- if discard:
80
- cls.ignore_files.add(cls.current_file_path)
81
- else:
82
- Cookie.current_file_index += 1
83
- except AttributeError:
84
- # Will fail on first instantiation because no current_file_path
85
- pass
86
- if Cookie.current_file_index >= len(cls.files()):
87
- Cookie.current_file_index = 0
88
- Cookie.import_data()
89
-
90
- class Query:
91
- """
92
- A convenience class that wraps around EdgeGPT.Chatbot to encapsulate input,
93
- config, and output all together. Relies on Cookie class for authentication
94
- unless ignore_cookies=True
95
- """
96
- index = []
97
- image_dir_path = Path.cwd().resolve() / "bing_images"
98
-
99
- def __init__(
100
- self,
101
- prompt,
102
- style="precise",
103
- content_type="text",
104
- cookie_files=None,
105
- ignore_cookies=False,
106
- echo=True,
107
- echo_prompt=False,
108
- locale = "en-GB",
109
- simplify_response = True,
110
- ):
111
- """
112
- Arguments:
113
-
114
- prompt: Text to enter into Bing Chat
115
- style: creative, balanced, or precise
116
- content_type: "text" for Bing Chat; "image" for Dall-e
117
- ignore_cookies (bool): Ignore cookie data altogether
118
- echo: Print something to confirm request made
119
- echo_prompt: Print confirmation of the evaluated prompt
120
- simplify_response: True -> single simplified prompt/response exchange
121
- cookie_files: iterable of Paths or strings of cookie files (json)
122
-
123
- Files in Cookie.dir_path will also be used if they exist. This defaults
124
- to the current working directory, so set Cookie.dir_path before
125
- creating a Query if your cookie files are elsewhere.
126
- """
127
- self.__class__.index += [self]
128
- self.prompt = prompt
129
- self.locale = locale
130
- self.simplify_response = simplify_response
131
- self.ignore_cookies = ignore_cookies
132
- if not ignore_cookies:
133
- if cookie_files:
134
- # Convert singular argument to an iterable:
135
- if isinstance(cookie_files, (str, Path)):
136
- cookie_files = {cookie_files}
137
- # Check all elements exist and are Paths:
138
- cookie_files = {
139
- Path(x).resolve()
140
- for x in cookie_files
141
- if isinstance(x, (str, Path)) and x
142
- }
143
- Cookie.supplied_files = cookie_files
144
- files = Cookie.files() # includes .supplied_files
145
- if Cookie.rotate_cookies:
146
- Cookie.import_next()
147
- else:
148
- Cookie.import_data()
149
- if content_type == "text":
150
- self.style = style
151
- self.log_and_send_query(echo, echo_prompt)
152
- if content_type == "image":
153
- self.create_image()
154
-
155
- def log_and_send_query(self, echo, echo_prompt):
156
- self.response = asyncio.run(self.send_to_bing(echo, echo_prompt))
157
- if not hasattr(Cookie, "current_data"):
158
- name = "<no_cookies>"
159
- else:
160
- name = Cookie.current_file_path.name
161
- if not Cookie.request_count.get(name):
162
- Cookie.request_count[name] = 1
163
- else:
164
- Cookie.request_count[name] += 1
165
-
166
- def create_image(self):
167
- image_generator = ImageGen(Cookie.image_token)
168
- image_generator.save_images(
169
- image_generator.get_images(self.prompt),
170
- output_dir=self.__class__.image_dir_path,
171
- )
172
-
173
- async def send_to_bing(self, echo=True, echo_prompt=False):
174
- """Creat, submit, then close a Chatbot instance. Return the response"""
175
- retries = len(Cookie.files()) or 1
176
- while retries:
177
- if not hasattr(Cookie, "current_data"):
178
- bot = await Chatbot.create()
179
- else:
180
- bot = await Chatbot.create(cookies=Cookie.current_data)
181
- if echo_prompt:
182
- log(f"{self.prompt=}")
183
- if echo:
184
- log("Waiting for response...")
185
- if self.style.lower() not in "creative balanced precise".split():
186
- self.style = "precise"
187
- try:
188
- response = await bot.ask(
189
- prompt=self.prompt,
190
- conversation_style=getattr(ConversationStyle, self.style),simplify_response=self.simplify_response,
191
- locale=self.locale,
192
- )
193
- return response
194
- except Exception as ex:
195
- log(f"Exception: [{Cookie.current_file_path.name} may have exceeded the daily limit]\n{ex}")
196
- Cookie.import_next(discard=True)
197
- retries -= 1
198
- finally:
199
- await bot.close()
200
-
201
- @property
202
- def output(self):
203
- """The response from a completed Chatbot request"""
204
- if self.simplify_response:
205
- try:
206
- return self.response['text']
207
- except TypeError as te:
208
- raise TypeError(f"{te}\n(No response received - probably rate throttled...)")
209
- else:
210
- return [
211
- x.get('text') or x.get('hiddenText')
212
- for x in self.response['item']['messages']
213
- if x['author']=='bot'
214
- ]
215
-
216
- @property
217
- def sources(self):
218
- """The source names and details parsed from a completed Chatbot request"""
219
- if self.simplify_response:
220
- return self.response['sources_text']
221
- else:
222
- return [
223
- x.get('sourceAttributions') or []
224
- for x in self.response['item']['messages']
225
- if x['author']=='bot'
226
- ]
227
-
228
- @property
229
- def sources_dict(self):
230
- """The source names and details as a dictionary"""
231
- if self.simplify_response:
232
- text = self.response['sources_text']
233
- sources = enumerate(re.findall(r'\((http.*?)\)', text))
234
- return {index+1: value for index, value in sources}
235
- else:
236
- all_sources = []
237
- name = 'providerDisplayName'
238
- url = 'seeMoreUrl'
239
- for sources in self.sources:
240
- if not sources:
241
- continue
242
- data = {}
243
- for index, source in enumerate(sources):
244
- if name in source.keys() and url in source.keys():
245
- data[index+1] = source[url]
246
- else:
247
- continue
248
- all_sources += [data]
249
- return all_sources
250
-
251
- @property
252
- def code_block_formats(self):
253
- """
254
- Extract a list of programming languages/formats used in code blocks
255
- """
256
- regex = r"``` *(\b\w+\b\+*) *"
257
- if self.simplify_response:
258
- return re.findall(regex, self.output)
259
- else:
260
- return re.findall(regex, "\n".join(self.output))
261
-
262
- @property
263
- def code_blocks(self):
264
- """
265
- Return a list of code blocks (```) or snippets (`) as strings.
266
-
267
- If the response contains a mix of snippets and code blocks, return the
268
- code blocks only.
269
-
270
- This method is not suitable if the main text response includes either of
271
- the delimiters but not as part of an actual snippet or code block.
272
-
273
- For example:
274
- 'In Markdown, the back-tick (`) is used to denote a code snippet'
275
-
276
- """
277
-
278
- final_blocks = []
279
- if isinstance(self.output, str): # I.e. simplify_response is True
280
- separator = '```' if '```' in self.output else '`'
281
- code_blocks = self.output.split(separator)[1:-1:2]
282
- if separator == '`':
283
- return code_blocks
284
- else:
285
- code_blocks = []
286
- for response in self.output:
287
- separator = '```' if '```' in response else '`'
288
- code_blocks.extend(response.split(separator)[1:-1:2])
289
- code_blocks = [x for x in code_blocks if x]
290
- # Remove language name if present:
291
- for block in code_blocks:
292
- lines = block.splitlines()
293
- code = lines[1:] if re.match(" *\w+ *", lines[0]) else lines
294
- final_blocks += ["\n".join(code).removeprefix(separator)]
295
- return [x for x in final_blocks if x]
296
-
297
- @property
298
- def code(self):
299
- """
300
- Extract and join any snippets of code or formatted data in the response
301
- """
302
- return "\n\n".join(self.code_blocks)
303
-
304
-
305
- @property
306
- def suggestions(self):
307
- """Follow-on questions suggested by the Chatbot"""
308
- if self.simplify_response:
309
- return self.response['suggestions']
310
- else:
311
- try:
312
- return [x['text'] for x in self.response['item']['messages'][1]['suggestedResponses']]
313
- except KeyError:
314
- return
315
-
316
- def __repr__(self):
317
- return f"<EdgeGPT.Query: {self.prompt}>"
318
-
319
- def __str__(self):
320
- if self.simplify_response:
321
- return self.output
322
- else:
323
- return "\n\n".join(self.output)
324
-
325
- class ImageQuery(Query):
326
- def __init__(self, prompt, **kwargs):
327
- kwargs.update({"content_type": "image"})
328
- super().__init__(prompt, **kwargs)
329
-
330
- def __repr__(self):
331
- return f"<EdgeGPT.ImageQuery: {self.prompt}>"
332
-
333
- def test_cookie_rotation():
334
- for i in range(1, 50):
335
- q = Query(f"What is {i} in Roman numerals? Give the answer in JSON", style="precise")
336
- log(f"{i}: {Cookie.current_file_path.name}")
337
- log(q.code)
338
- log(f"Cookie count: {Cookie.request_count.get(Cookie.current_file_path.name)}")
339
-
340
- def test_features():
341
- try:
342
- q = Query(f"What is {i} in Roman numerals? Give the answer in JSON", style="precise")
343
- log(f"{i}: {Cookie.current_file_path.name}")
344
- print(f"{Cookie.current_file_index=}")
345
- print(f"{Cookie.current_file_path=}")
346
- print(f"{Cookie.current_data=}")
347
- print(f"{Cookie.dir_path=}")
348
- print(f"{Cookie.search_pattern=}")
349
- print(f"{Cookie.files()=}")
350
- print(f"{Cookie.image_token=}")
351
- print(f"{Cookie.import_next(discard=True)=}")
352
- print(f"{Cookie.rotate_cookies=}")
353
- print(f"{Cookie.files()=}")
354
- print(f"{Cookie.ignore_files=}")
355
- print(f"{Cookie.supplied_files=}")
356
- print(f"{Cookie.request_count=}") # Keeps a tally of requests made in using each cookie file during this session
357
- print(f"{q=}")
358
- print(f"{q.prompt=}")
359
- print(f"{q.ignore_cookies=}")
360
- print(f"{q.style=}")
361
- print(f"{q.simplify_response=}")
362
- print(f"{q.locale=}")
363
- print(f"{q.output=}")
364
- print(q)
365
- print(f"{q.sources=}")
366
- print(f"{q.sources_dict=}")
367
- print(f"{q.suggestions=}")
368
- print(f"{q.code=}") # All code as a single string
369
- print(f"{q.code_blocks=}") # Individual code blocks
370
- print(f"{q.code_block_formats=}") # The language/format of each code block (if given)
371
- print(f"{Query.index=}") # Keeps an index of Query objects created
372
- print(f"{Query.image_dir_path=}")
373
- except Exception as E:
374
- raise Exception(E)
375
- finally:
376
- return q
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/ImageGen.py DELETED
@@ -1,11 +0,0 @@
1
- # Open pull requests and issues at https://github.com/acheong08/BingImageCreator
2
- import BingImageCreator
3
-
4
- ImageGen = BingImageCreator.ImageGen
5
-
6
- ImageGenAsync = BingImageCreator.ImageGenAsync
7
-
8
- main = BingImageCreator.main
9
-
10
- if __name__ == "__main__":
11
- main()
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/__init__.py DELETED
File without changes
EdgeGPT/chathub.py DELETED
@@ -1,275 +0,0 @@
1
- import asyncio
2
- import json
3
- import os
4
- import ssl
5
- import sys
6
- from time import time
7
- from typing import Generator
8
- from typing import List
9
- from typing import Union
10
-
11
- from websockets.client import connect, WebSocketClientProtocol
12
- import certifi
13
- import httpx
14
- from BingImageCreator import ImageGenAsync
15
-
16
- from .constants import DELIMITER
17
- from .constants import HEADERS
18
- from .constants import HEADERS_INIT_CONVER
19
- from .conversation import Conversation
20
- from .conversation_style import CONVERSATION_STYLE_TYPE
21
- from .request import ChatHubRequest
22
- from .utilities import append_identifier
23
- from .utilities import get_ran_hex
24
- from .utilities import guess_locale
25
-
26
- ssl_context = ssl.create_default_context()
27
- ssl_context.load_verify_locations(certifi.where())
28
-
29
-
30
- class ChatHub:
31
- def __init__(
32
- self,
33
- conversation: Conversation,
34
- proxy: str = None,
35
- cookies: Union[List[dict], None] = None,
36
- ) -> None:
37
- self.request: ChatHubRequest
38
- self.loop: bool
39
- self.task: asyncio.Task
40
- self.request = ChatHubRequest(
41
- conversation_signature=conversation.struct["conversationSignature"],
42
- client_id=conversation.struct["clientId"],
43
- conversation_id=conversation.struct["conversationId"],
44
- )
45
- self.cookies = cookies
46
- self.proxy: str = proxy
47
- proxy = (
48
- proxy
49
- or os.environ.get("all_proxy")
50
- or os.environ.get("ALL_PROXY")
51
- or os.environ.get("https_proxy")
52
- or os.environ.get("HTTPS_PROXY")
53
- or None
54
- )
55
- if proxy is not None and proxy.startswith("socks5h://"):
56
- proxy = "socks5://" + proxy[len("socks5h://") :]
57
- self.session = httpx.AsyncClient(
58
- proxies=proxy,
59
- timeout=900,
60
- headers=HEADERS_INIT_CONVER,
61
- )
62
-
63
- async def get_conversation(
64
- self,
65
- conversation_id: str = None,
66
- conversation_signature: str = None,
67
- client_id: str = None,
68
- ) -> dict:
69
- conversation_id = conversation_id or self.request.conversation_id
70
- conversation_signature = (
71
- conversation_signature or self.request.conversation_signature
72
- )
73
- client_id = client_id or self.request.client_id
74
- url = f"https://sydney.bing.com/sydney/GetConversation?conversationId={conversation_id}&source=cib&participantId={client_id}&conversationSignature={conversation_signature}&traceId={get_ran_hex()}"
75
- response = await self.session.get(url)
76
- return response.json()
77
-
78
- async def get_activity(self) -> dict:
79
- url = "https://www.bing.com/turing/conversation/chats"
80
- headers = HEADERS_INIT_CONVER.copy()
81
- if self.cookies is not None:
82
- for cookie in self.cookies:
83
- if cookie["name"] == "_U":
84
- headers["Cookie"] = f"SUID=A; _U={cookie['value']};"
85
- break
86
- response = await self.session.get(url, headers=headers)
87
- return response.json()
88
-
89
- async def ask_stream(
90
- self,
91
- prompt: str,
92
- wss_link: str = None,
93
- conversation_style: CONVERSATION_STYLE_TYPE = None,
94
- raw: bool = False,
95
- webpage_context: Union[str, None] = None,
96
- search_result: bool = False,
97
- locale: str = guess_locale(),
98
- ) -> Generator[bool, Union[dict, str], None]:
99
- """ """
100
- req_header = HEADERS
101
- if self.cookies is not None:
102
- ws_cookies = []
103
- for cookie in self.cookies:
104
- ws_cookies.append(f"{cookie['name']}={cookie['value']}")
105
- req_header.update({
106
- 'Cookie': ';'.join(ws_cookies),
107
- })
108
-
109
- # Check if websocket is closed
110
- async with connect(
111
- wss_link or "wss://sydney.bing.com/sydney/ChatHub",
112
- # extra_headers=HEADERS,
113
- extra_headers=req_header,
114
- max_size=None,
115
- ssl=ssl_context,
116
- ping_interval=None,
117
- ) as wss:
118
- await self._initial_handshake(wss)
119
- # Construct a ChatHub request
120
- self.request.update(
121
- prompt=prompt,
122
- conversation_style=conversation_style,
123
- webpage_context=webpage_context,
124
- search_result=search_result,
125
- locale=locale,
126
- )
127
- # Send request
128
- await wss.send(append_identifier(self.request.struct))
129
- draw = False
130
- resp_txt = ""
131
- result_text = ""
132
- resp_txt_no_link = ""
133
- retry_count = 5
134
- while True:
135
- if wss.closed:
136
- break
137
- msg = await wss.recv()
138
- if not msg:
139
- retry_count -= 1
140
- if retry_count == 0:
141
- raise Exception("No response from server")
142
- continue
143
- if isinstance(msg, str):
144
- objects = msg.split(DELIMITER)
145
- else:
146
- continue
147
- for obj in objects:
148
- if int(time()) % 6 == 0:
149
- await wss.send(append_identifier({"type": 6}))
150
- if obj is None or not obj:
151
- continue
152
- response = json.loads(obj)
153
- # print(response)
154
- if response.get("type") == 1 and response["arguments"][0].get(
155
- "messages",
156
- ):
157
- if not draw:
158
- if (
159
- response["arguments"][0]["messages"][0].get(
160
- "messageType",
161
- )
162
- == "GenerateContentQuery"
163
- ):
164
- async with ImageGenAsync(
165
- all_cookies=self.cookies
166
- ) as image_generator:
167
- images = await image_generator.get_images(
168
- response["arguments"][0]["messages"][0]["text"],
169
- )
170
- for i, image in enumerate(images):
171
- resp_txt = f"{resp_txt}\n![image{i}]({image})"
172
- draw = True
173
- if (
174
- (
175
- response["arguments"][0]["messages"][0][
176
- "contentOrigin"
177
- ]
178
- != "Apology"
179
- )
180
- and not draw
181
- and not raw
182
- ):
183
- resp_txt = result_text + response["arguments"][0][
184
- "messages"
185
- ][0]["adaptiveCards"][0]["body"][0].get("text", "")
186
- resp_txt_no_link = result_text + response["arguments"][
187
- 0
188
- ]["messages"][0].get("text", "")
189
- if response["arguments"][0]["messages"][0].get(
190
- "messageType",
191
- ):
192
- resp_txt = (
193
- resp_txt
194
- + response["arguments"][0]["messages"][0][
195
- "adaptiveCards"
196
- ][0]["body"][0]["inlines"][0].get("text")
197
- + "\n"
198
- )
199
- result_text = (
200
- result_text
201
- + response["arguments"][0]["messages"][0][
202
- "adaptiveCards"
203
- ][0]["body"][0]["inlines"][0].get("text")
204
- + "\n"
205
- )
206
- if not raw:
207
- yield False, resp_txt
208
-
209
- elif response.get("type") == 2:
210
- if response["item"]["result"].get("error"):
211
- await self.close()
212
- raise Exception(
213
- f"{response['item']['result']['value']}: {response['item']['result']['message']}",
214
- )
215
- if draw:
216
- cache = response["item"]["messages"][1]["adaptiveCards"][0][
217
- "body"
218
- ][0]["text"]
219
- response["item"]["messages"][1]["adaptiveCards"][0]["body"][
220
- 0
221
- ]["text"] = (cache + resp_txt)
222
- if (
223
- response["item"]["messages"][-1]["contentOrigin"]
224
- == "Apology"
225
- and resp_txt
226
- ):
227
- response["item"]["messages"][-1]["text"] = resp_txt_no_link
228
- response["item"]["messages"][-1]["adaptiveCards"][0][
229
- "body"
230
- ][0]["text"] = resp_txt
231
- print(
232
- "Preserved the message from being deleted",
233
- file=sys.stderr,
234
- )
235
- await wss.close()
236
- yield True, response
237
- return
238
- if response.get("type") != 2:
239
- if response.get("type") == 6:
240
- await wss.send(append_identifier({"type": 6}))
241
- elif response.get("type") == 7:
242
- await wss.send(append_identifier({"type": 7}))
243
- elif raw:
244
- yield False, response
245
-
246
- async def _initial_handshake(self, wss: WebSocketClientProtocol) -> None:
247
- await wss.send(append_identifier({"protocol": "json", "version": 1}))
248
- await wss.recv()
249
- await wss.send(append_identifier({"type": 6}))
250
-
251
- async def delete_conversation(
252
- self,
253
- conversation_id: str = None,
254
- conversation_signature: str = None,
255
- client_id: str = None,
256
- ) -> None:
257
- conversation_id = conversation_id or self.request.conversation_id
258
- conversation_signature = (
259
- conversation_signature or self.request.conversation_signature
260
- )
261
- client_id = client_id or self.request.client_id
262
- url = "https://sydney.bing.com/sydney/DeleteSingleConversation"
263
- await self.session.post(
264
- url,
265
- json={
266
- "conversationId": conversation_id,
267
- "conversationSignature": conversation_signature,
268
- "participant": {"id": client_id},
269
- "source": "cib",
270
- "optionsSets": ["autosave"],
271
- },
272
- )
273
-
274
- async def close(self) -> None:
275
- await self.session.aclose()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/constants.py DELETED
@@ -1,51 +0,0 @@
1
- import random
2
- import uuid
3
-
4
- DELIMITER = "\x1e"
5
-
6
-
7
- # Generate random IP between range 13.104.0.0/14
8
- FORWARDED_IP = f"1.0.0.{random.randint(0, 255)}"
9
-
10
- HEADERS = {
11
- "accept": "application/json",
12
- "accept-language": "en-US,en;q=0.9",
13
- "content-type": "application/json",
14
- "sec-ch-ua": '"Not_A Brand";v="99", Microsoft Edge";v="110", "Chromium";v="110"',
15
- "sec-ch-ua-arch": '"x86"',
16
- "sec-ch-ua-bitness": '"64"',
17
- "sec-ch-ua-full-version": '"109.0.1518.78"',
18
- "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"',
19
- "sec-ch-ua-mobile": "?0",
20
- "sec-ch-ua-model": "",
21
- "sec-ch-ua-platform": '"Windows"',
22
- "sec-ch-ua-platform-version": '"15.0.0"',
23
- "sec-fetch-dest": "empty",
24
- "sec-fetch-mode": "cors",
25
- "sec-fetch-site": "same-origin",
26
- "x-ms-client-request-id": str(uuid.uuid4()),
27
- "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32",
28
- "Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx",
29
- "Referrer-Policy": "origin-when-cross-origin",
30
- "x-forwarded-for": FORWARDED_IP,
31
- }
32
-
33
- HEADERS_INIT_CONVER = {
34
- "authority": "www.bing.com",
35
- "accept": "application/json",
36
- "accept-language": "en-US,en;q=0.9",
37
- "cache-control": "max-age=0",
38
- "sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"',
39
- "sec-ch-ua-arch": '"x86"',
40
- "sec-ch-ua-bitness": '"64"',
41
- "sec-ch-ua-full-version": '"110.0.1587.69"',
42
- "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"',
43
- "sec-ch-ua-mobile": "?0",
44
- "sec-ch-ua-model": '""',
45
- "sec-ch-ua-platform": '"Windows"',
46
- "sec-ch-ua-platform-version": '"15.0.0"',
47
- "upgrade-insecure-requests": "1",
48
- "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.46",
49
- "x-edge-shopping-flag": "1",
50
- "x-forwarded-for": FORWARDED_IP,
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/conversation.py DELETED
@@ -1,122 +0,0 @@
1
- import json
2
- import os
3
- from typing import List
4
- from typing import Union
5
-
6
- import httpx
7
-
8
- from .constants import HEADERS_INIT_CONVER
9
- from .exceptions import NotAllowedToAccess
10
-
11
-
12
- class Conversation:
13
- def __init__(
14
- self,
15
- proxy: Union[str, None] = None,
16
- async_mode: bool = False,
17
- cookies: Union[List[dict], None] = None,
18
- ) -> None:
19
- if async_mode:
20
- return
21
- self.struct: dict = {
22
- "conversationId": None,
23
- "clientId": None,
24
- "conversationSignature": None,
25
- "result": {"value": "Success", "message": None},
26
- }
27
- self.proxy = proxy
28
- proxy = (
29
- proxy
30
- or os.environ.get("all_proxy")
31
- or os.environ.get("ALL_PROXY")
32
- or os.environ.get("https_proxy")
33
- or os.environ.get("HTTPS_PROXY")
34
- or None
35
- )
36
- if proxy is not None and proxy.startswith("socks5h://"):
37
- proxy = "socks5://" + proxy[len("socks5h://") :]
38
- self.session = httpx.Client(
39
- proxies=proxy,
40
- timeout=900,
41
- headers=HEADERS_INIT_CONVER,
42
- )
43
- if cookies:
44
- for cookie in cookies:
45
- self.session.cookies.set(cookie["name"], cookie["value"])
46
- # Send GET request
47
- response = self.session.get(
48
- url=os.environ.get("BING_PROXY_URL")
49
- or "https://edgeservices.bing.com/edgesvc/turing/conversation/create",
50
- )
51
- if response.status_code != 200:
52
- print(f"Status code: {response.status_code}")
53
- print(response.text)
54
- print(response.url)
55
- raise Exception("Authentication failed")
56
- try:
57
- self.struct = response.json()
58
- except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
59
- raise Exception(
60
- "Authentication failed. You have not been accepted into the beta.",
61
- ) from exc
62
- if self.struct["result"]["value"] == "UnauthorizedRequest":
63
- raise NotAllowedToAccess(self.struct["result"]["message"])
64
-
65
- @staticmethod
66
- async def create(
67
- proxy: Union[str, None] = None,
68
- cookies: Union[List[dict], None] = None,
69
- ) -> "Conversation":
70
- self = Conversation(async_mode=True)
71
- self.struct = {
72
- "conversationId": None,
73
- "clientId": None,
74
- "conversationSignature": None,
75
- "result": {"value": "Success", "message": None},
76
- }
77
- self.proxy = proxy
78
- proxy = (
79
- proxy
80
- or os.environ.get("all_proxy")
81
- or os.environ.get("ALL_PROXY")
82
- or os.environ.get("https_proxy")
83
- or os.environ.get("HTTPS_PROXY")
84
- or None
85
- )
86
- if proxy is not None and proxy.startswith("socks5h://"):
87
- proxy = "socks5://" + proxy[len("socks5h://") :]
88
- transport = httpx.AsyncHTTPTransport(retries=900)
89
- # Convert cookie format to httpx format
90
- formatted_cookies = None
91
- if cookies:
92
- formatted_cookies = httpx.Cookies()
93
- for cookie in cookies:
94
- formatted_cookies.set(cookie["name"], cookie["value"])
95
- async with httpx.AsyncClient(
96
- proxies=proxy,
97
- timeout=30,
98
- headers=HEADERS_INIT_CONVER,
99
- transport=transport,
100
- cookies=formatted_cookies,
101
- ) as client:
102
- # Send GET request
103
- response = await client.get(
104
- url=os.environ.get("BING_PROXY_URL")
105
- or "https://www.bing.com/turing/conversation/create",
106
- follow_redirects=True,
107
- )
108
- if response.status_code != 200:
109
- print(f"Status code: {response.status_code}")
110
- print(response.text)
111
- print(response.url)
112
- raise Exception("Authentication failed")
113
- try:
114
- self.struct = response.json()
115
- except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
116
- print(response.text)
117
- raise Exception(
118
- "Authentication failed. You have not been accepted into the beta.",
119
- ) from exc
120
- if self.struct["result"]["value"] == "UnauthorizedRequest":
121
- raise NotAllowedToAccess(self.struct["result"]["message"])
122
- return self
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/conversation_style.py DELETED
@@ -1,56 +0,0 @@
1
- from enum import Enum
2
-
3
- try:
4
- from typing import Literal, Union
5
- except ImportError:
6
- from typing_extensions import Literal
7
- from typing import Optional
8
-
9
-
10
- class ConversationStyle(Enum):
11
- creative = [
12
- "nlu_direct_response_filter",
13
- "deepleo",
14
- "disable_emoji_spoken_text",
15
- "responsible_ai_policy_235",
16
- "enablemm",
17
- "h3imaginative",
18
- "objopinion",
19
- "dsblhlthcrd",
20
- "dv3sugg",
21
- "autosave",
22
- "clgalileo",
23
- "gencontentv3",
24
- ]
25
- balanced = [
26
- "nlu_direct_response_filter",
27
- "deepleo",
28
- "disable_emoji_spoken_text",
29
- "responsible_ai_policy_235",
30
- "enablemm",
31
- "galileo",
32
- "saharagenconv5",
33
- "objopinion",
34
- "dsblhlthcrd",
35
- "dv3sugg",
36
- "autosave",
37
- ]
38
- precise = [
39
- "nlu_direct_response_filter",
40
- "deepleo",
41
- "disable_emoji_spoken_text",
42
- "responsible_ai_policy_235",
43
- "enablemm",
44
- "h3precise",
45
- "objopinion",
46
- "dsblhlthcrd",
47
- "dv3sugg",
48
- "autosave",
49
- "clgalileo",
50
- "gencontentv3",
51
- ]
52
-
53
-
54
- CONVERSATION_STYLE_TYPE = Optional[
55
- Union[ConversationStyle, Literal["creative", "balanced", "precise"]]
56
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/exceptions.py DELETED
@@ -1,2 +0,0 @@
1
- class NotAllowedToAccess(Exception):
2
- pass
 
 
 
EdgeGPT/locale.py DELETED
@@ -1,85 +0,0 @@
1
- from enum import Enum
2
-
3
- try:
4
- from typing import Literal, Union
5
- except ImportError:
6
- from typing_extensions import Literal
7
- from typing import Optional
8
-
9
-
10
- class LocationHint(Enum):
11
- USA = {
12
- "locale": "en-US",
13
- "LocationHint": [
14
- {
15
- "country": "United States",
16
- "state": "California",
17
- "city": "Los Angeles",
18
- "timezoneoffset": 8,
19
- "countryConfidence": 8,
20
- "Center": {
21
- "Latitude": 34.0536909,
22
- "Longitude": -118.242766,
23
- },
24
- "RegionType": 2,
25
- "SourceType": 1,
26
- },
27
- ],
28
- }
29
- CHINA = {
30
- "locale": "zh-CN",
31
- "LocationHint": [
32
- {
33
- "country": "China",
34
- "state": "",
35
- "city": "Beijing",
36
- "timezoneoffset": 8,
37
- "countryConfidence": 8,
38
- "Center": {
39
- "Latitude": 39.9042,
40
- "Longitude": 116.4074,
41
- },
42
- "RegionType": 2,
43
- "SourceType": 1,
44
- },
45
- ],
46
- }
47
- EU = {
48
- "locale": "en-IE",
49
- "LocationHint": [
50
- {
51
- "country": "Norway",
52
- "state": "",
53
- "city": "Oslo",
54
- "timezoneoffset": 1,
55
- "countryConfidence": 8,
56
- "Center": {
57
- "Latitude": 59.9139,
58
- "Longitude": 10.7522,
59
- },
60
- "RegionType": 2,
61
- "SourceType": 1,
62
- },
63
- ],
64
- }
65
- UK = {
66
- "locale": "en-GB",
67
- "LocationHint": [
68
- {
69
- "country": "United Kingdom",
70
- "state": "",
71
- "city": "London",
72
- "timezoneoffset": 0,
73
- "countryConfidence": 8,
74
- "Center": {
75
- "Latitude": 51.5074,
76
- "Longitude": -0.1278,
77
- },
78
- "RegionType": 2,
79
- "SourceType": 1,
80
- },
81
- ],
82
- }
83
-
84
-
85
- LOCATION_HINT_TYPES = Optional[Union[LocationHint, Literal["USA", "CHINA", "EU", "UK"]]]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/main.py DELETED
@@ -1,244 +0,0 @@
1
- import argparse
2
- import asyncio
3
- import json
4
- import re
5
- import sys
6
- from pathlib import Path
7
-
8
- from EdgeGPT.EdgeGPT import Chatbot
9
- from prompt_toolkit import PromptSession
10
- from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
11
- from prompt_toolkit.completion import WordCompleter
12
- from prompt_toolkit.history import InMemoryHistory
13
- from prompt_toolkit.key_binding import KeyBindings
14
- from rich.live import Live
15
- from rich.markdown import Markdown
16
-
17
-
18
- def create_session() -> PromptSession:
19
- kb = KeyBindings()
20
-
21
- @kb.add("enter")
22
- def _(event) -> None:
23
- buffer_text = event.current_buffer.text
24
- if buffer_text.startswith("!"):
25
- event.current_buffer.validate_and_handle()
26
- else:
27
- event.current_buffer.insert_text("\n")
28
-
29
- @kb.add("escape")
30
- def _(event) -> None:
31
- if event.current_buffer.complete_state:
32
- # event.current_buffer.cancel_completion()
33
- event.current_buffer.text = ""
34
-
35
- return PromptSession(key_bindings=kb, history=InMemoryHistory())
36
-
37
-
38
- def create_completer(commands: list, pattern_str: str = "$") -> WordCompleter:
39
- return WordCompleter(words=commands, pattern=re.compile(pattern_str))
40
-
41
-
42
- def _create_history_logger(f):
43
- def logger(*args, **kwargs) -> None:
44
- tmp = sys.stdout
45
- sys.stdout = f
46
- print(*args, **kwargs, flush=True)
47
- sys.stdout = tmp
48
-
49
- return logger
50
-
51
-
52
- async def get_input_async(
53
- session: PromptSession = None,
54
- completer: WordCompleter = None,
55
- ) -> str:
56
- """
57
- Multiline input function.
58
- """
59
- return await session.prompt_async(
60
- completer=completer,
61
- multiline=True,
62
- auto_suggest=AutoSuggestFromHistory(),
63
- )
64
-
65
-
66
- async def async_main(args: argparse.Namespace) -> None:
67
- """
68
- Main function
69
- """
70
- print("Initializing...")
71
- print("Enter `alt+enter` or `escape+enter` to send a message")
72
- # Read and parse cookies
73
- cookies = None
74
- if args.cookie_file:
75
- file_path = Path(args.cookie_file)
76
- if file_path.exists():
77
- with file_path.open("r", encoding="utf-8") as f:
78
- cookies = json.load(f)
79
- bot = await Chatbot.create(proxy=args.proxy, cookies=cookies)
80
- session = create_session()
81
- completer = create_completer(["!help", "!exit", "!reset"])
82
- initial_prompt = args.prompt
83
-
84
- # Log chat history
85
- def p_hist(*args, **kwargs) -> None:
86
- pass
87
-
88
- if args.history_file:
89
- history_file_path = Path(args.history_file)
90
- f = history_file_path.open("a+", encoding="utf-8")
91
- p_hist = _create_history_logger(f)
92
-
93
- while True:
94
- print("\nYou:")
95
- p_hist("\nYou:")
96
- if initial_prompt:
97
- question = initial_prompt
98
- print(question)
99
- initial_prompt = None
100
- else:
101
- question = (
102
- input()
103
- if args.enter_once
104
- else await get_input_async(session=session, completer=completer)
105
- )
106
- print()
107
- p_hist(question + "\n")
108
- if question == "!exit":
109
- break
110
- if question == "!help":
111
- print(
112
- """
113
- !help - Show this help message
114
- !exit - Exit the program
115
- !reset - Reset the conversation
116
- """,
117
- )
118
- continue
119
- if question == "!reset":
120
- await bot.reset()
121
- continue
122
- print("Bot:")
123
- p_hist("Bot:")
124
- if args.no_stream:
125
- response = (
126
- await bot.ask(
127
- prompt=question,
128
- conversation_style=args.style,
129
- wss_link=args.wss_link,
130
- search_result=args.search_result,
131
- locale=args.locale,
132
- )
133
- )["item"]["messages"][-1]["adaptiveCards"][0]["body"][0]["text"]
134
- print(response)
135
- p_hist(response)
136
- else:
137
- wrote = 0
138
- if args.rich:
139
- md = Markdown("")
140
- with Live(md, auto_refresh=False) as live:
141
- async for final, response in bot.ask_stream(
142
- prompt=question,
143
- conversation_style=args.style,
144
- wss_link=args.wss_link,
145
- search_result=args.search_result,
146
- locale=args.locale,
147
- ):
148
- if not final:
149
- if not wrote:
150
- p_hist(response, end="")
151
- else:
152
- p_hist(response[wrote:], end="")
153
- if wrote > len(response):
154
- print(md)
155
- print(Markdown("***Bing revoked the response.***"))
156
- wrote = len(response)
157
- md = Markdown(response)
158
- live.update(md, refresh=True)
159
- else:
160
- async for final, response in bot.ask_stream(
161
- prompt=question,
162
- conversation_style=args.style,
163
- wss_link=args.wss_link,
164
- search_result=args.search_result,
165
- locale=args.locale,
166
- ):
167
- if not final:
168
- if not wrote:
169
- print(response, end="", flush=True)
170
- p_hist(response, end="")
171
- else:
172
- print(response[wrote:], end="", flush=True)
173
- p_hist(response[wrote:], end="")
174
- wrote = len(response)
175
- print()
176
- p_hist()
177
- if args.history_file:
178
- f.close()
179
- await bot.close()
180
-
181
-
182
- def main() -> None:
183
- print(
184
- """
185
- EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
186
- Repo: github.com/acheong08/EdgeGPT
187
- By: Antonio Cheong
188
-
189
- !help for help
190
-
191
- Type !exit to exit
192
- """,
193
- )
194
- parser = argparse.ArgumentParser()
195
- parser.add_argument("--enter-once", action="store_true")
196
- parser.add_argument("--search-result", action="store_true")
197
- parser.add_argument("--no-stream", action="store_true")
198
- parser.add_argument("--rich", action="store_true")
199
- parser.add_argument(
200
- "--proxy",
201
- help="Proxy URL (e.g. socks5://127.0.0.1:1080)",
202
- type=str,
203
- )
204
- parser.add_argument(
205
- "--wss-link",
206
- help="WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)",
207
- type=str,
208
- default="wss://sydney.bing.com/sydney/ChatHub",
209
- )
210
- parser.add_argument(
211
- "--style",
212
- choices=["creative", "balanced", "precise"],
213
- default="balanced",
214
- )
215
- parser.add_argument(
216
- "--prompt",
217
- type=str,
218
- default="",
219
- required=False,
220
- help="prompt to start with",
221
- )
222
- parser.add_argument(
223
- "--cookie-file",
224
- type=str,
225
- default="",
226
- required=False,
227
- help="path to cookie file",
228
- )
229
- parser.add_argument(
230
- "--history-file",
231
- type=str,
232
- default="",
233
- required=False,
234
- help="path to history file",
235
- )
236
- parser.add_argument(
237
- "--locale",
238
- type=str,
239
- default="en-US",
240
- required=False,
241
- help="your locale",
242
- )
243
- args = parser.parse_args()
244
- asyncio.run(async_main(args))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/request.py DELETED
@@ -1,156 +0,0 @@
1
- import uuid
2
- from datetime import datetime
3
- from typing import Union
4
-
5
- from .conversation_style import CONVERSATION_STYLE_TYPE
6
- from .conversation_style import ConversationStyle
7
- from .utilities import get_location_hint_from_locale
8
- from .utilities import get_ran_hex
9
- from .utilities import guess_locale
10
-
11
-
12
- class ChatHubRequest:
13
- def __init__(
14
- self,
15
- conversation_signature: str,
16
- client_id: str,
17
- conversation_id: str,
18
- invocation_id: int = 3,
19
- ) -> None:
20
- self.struct: dict = {}
21
-
22
- self.client_id: str = client_id
23
- self.conversation_id: str = conversation_id
24
- self.conversation_signature: str = conversation_signature
25
- self.invocation_id: int = invocation_id
26
-
27
- def update(
28
- self,
29
- prompt: str,
30
- conversation_style: CONVERSATION_STYLE_TYPE,
31
- webpage_context: Union[str, None] = None,
32
- search_result: bool = False,
33
- locale: str = guess_locale(),
34
- ) -> None:
35
- options = [
36
- "deepleo",
37
- "enable_debug_commands",
38
- "disable_emoji_spoken_text",
39
- "enablemm",
40
- ]
41
- if conversation_style:
42
- if not isinstance(conversation_style, ConversationStyle):
43
- conversation_style = getattr(ConversationStyle, conversation_style)
44
- options = conversation_style.value
45
- message_id = str(uuid.uuid4())
46
- # Get the current local time
47
- now_local = datetime.now()
48
-
49
- # Get the current UTC time
50
- now_utc = datetime.utcnow()
51
-
52
- # Calculate the time difference between local and UTC time
53
- timezone_offset = now_local - now_utc
54
-
55
- # Get the offset in hours and minutes
56
- offset_hours = int(timezone_offset.total_seconds() // 3600)
57
- offset_minutes = int((timezone_offset.total_seconds() % 3600) // 60)
58
-
59
- # Format the offset as a string
60
- offset_string = f"{offset_hours:+03d}:{offset_minutes:02d}"
61
-
62
- # Get current time
63
- timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + offset_string
64
- self.struct = {
65
- "arguments": [
66
- {
67
- "source": "cib",
68
- "optionsSets": options,
69
- "allowedMessageTypes": [
70
- "ActionRequest",
71
- "Chat",
72
- "Context",
73
- "InternalSearchQuery",
74
- "InternalSearchResult",
75
- "Disengaged",
76
- "InternalLoaderMessage",
77
- "Progress",
78
- "RenderCardRequest",
79
- "AdsQuery",
80
- "SemanticSerp",
81
- "GenerateContentQuery",
82
- "SearchQuery",
83
- ],
84
- "sliceIds": [
85
- "winmuid1tf",
86
- "styleoff",
87
- "ccadesk",
88
- "smsrpsuppv4cf",
89
- "ssrrcache",
90
- "contansperf",
91
- "crchatrev",
92
- "winstmsg2tf",
93
- "creatgoglt",
94
- "creatorv2t",
95
- "sydconfigoptt",
96
- "adssqovroff",
97
- "530pstho",
98
- "517opinion",
99
- "418dhlth",
100
- "512sprtic1s0",
101
- "emsgpr",
102
- "525ptrcps0",
103
- "529rweas0",
104
- "515oscfing2s0",
105
- "524vidansgs0",
106
- ],
107
- "verbosity": "verbose",
108
- "traceId": get_ran_hex(32),
109
- "isStartOfSession": self.invocation_id == 3,
110
- "message": {
111
- "locale": locale,
112
- "market": locale,
113
- "region": locale[-2:], # en-US -> US
114
- "locationHints": get_location_hint_from_locale(locale),
115
- "timestamp": timestamp,
116
- "author": "user",
117
- "inputMethod": "Keyboard",
118
- "text": prompt,
119
- "messageType": "Chat",
120
- "messageId": message_id,
121
- "requestId": message_id,
122
- },
123
- "tone": conversation_style.name.capitalize(), # Make first letter uppercase
124
- "requestId": message_id,
125
- "conversationSignature": self.conversation_signature,
126
- "participant": {
127
- "id": self.client_id,
128
- },
129
- "conversationId": self.conversation_id,
130
- },
131
- ],
132
- "invocationId": str(self.invocation_id),
133
- "target": "chat",
134
- "type": 4,
135
- }
136
- if search_result:
137
- have_search_result = [
138
- "InternalSearchQuery",
139
- "InternalSearchResult",
140
- "InternalLoaderMessage",
141
- "RenderCardRequest",
142
- ]
143
- self.struct["arguments"][0]["allowedMessageTypes"] += have_search_result
144
- if webpage_context:
145
- self.struct["arguments"][0]["previousMessages"] = [
146
- {
147
- "author": "user",
148
- "description": webpage_context,
149
- "contextType": "WebPage",
150
- "messageType": "Context",
151
- "messageId": "discover-web--page-ping-mriduna-----",
152
- },
153
- ]
154
- self.invocation_id += 1
155
-
156
- # print(timestamp)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
EdgeGPT/utilities.py DELETED
@@ -1,39 +0,0 @@
1
- import json
2
- import locale
3
- import random
4
- import sys
5
- from typing import Union
6
-
7
- from .constants import DELIMITER
8
- from .locale import LocationHint
9
-
10
-
11
- def append_identifier(msg: dict) -> str:
12
- # Convert dict to json string
13
- return json.dumps(msg, ensure_ascii=False) + DELIMITER
14
-
15
-
16
- def get_ran_hex(length: int = 32) -> str:
17
- return "".join(random.choice("0123456789abcdef") for _ in range(length))
18
-
19
-
20
- def get_location_hint_from_locale(locale: str) -> Union[dict, None]:
21
- locale = locale.lower()
22
- if locale == "en-gb":
23
- hint = LocationHint.UK.value
24
- elif locale == "en-ie":
25
- hint = LocationHint.EU.value
26
- elif locale == "zh-cn":
27
- hint = LocationHint.CHINA.value
28
- else:
29
- hint = LocationHint.USA.value
30
- return hint.get("LocationHint")
31
-
32
-
33
- def guess_locale() -> str:
34
- if sys.platform.startswith("win"):
35
- return "en-us"
36
- loc, _ = locale.getlocale()
37
- if not loc:
38
- return "en-us"
39
- return loc.replace("_", "-")