xiaolv commited on
Commit
57bdef1
1 Parent(s): 53fae0e

Upload EdgeGPT.py

Browse files
Files changed (1) hide show
  1. EdgeGPT.py +883 -0
EdgeGPT.py ADDED
@@ -0,0 +1,883 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main.py
3
+ """
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import asyncio
8
+ import json
9
+ import os
10
+ import random
11
+ import re
12
+ import ssl
13
+ import sys
14
+ import uuid
15
+ from enum import Enum
16
+ from pathlib import Path
17
+ from typing import Generator
18
+ from typing import Literal
19
+ from typing import Optional
20
+ from typing import Union
21
+
22
+ import certifi
23
+ import httpx
24
+ import websockets.client as websockets
25
+ from BingImageCreator import ImageGenAsync
26
+ from prompt_toolkit import PromptSession
27
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
28
+ from prompt_toolkit.completion import WordCompleter
29
+ from prompt_toolkit.history import InMemoryHistory
30
+ from prompt_toolkit.key_binding import KeyBindings
31
+ from rich.live import Live
32
+ from rich.markdown import Markdown
33
+
34
+ DELIMITER = "\x1e"
35
+
36
+
37
+ # Generate random IP between range 13.104.0.0/14
38
+ FORWARDED_IP = (
39
+ f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}"
40
+ )
41
+
42
+ HEADERS = {
43
+ "accept": "application/json",
44
+ "accept-language": "en-US,en;q=0.9",
45
+ "content-type": "application/json",
46
+ "sec-ch-ua": '"Not_A Brand";v="99", "Microsoft Edge";v="110", "Chromium";v="110"',
47
+ "sec-ch-ua-arch": '"x86"',
48
+ "sec-ch-ua-bitness": '"64"',
49
+ "sec-ch-ua-full-version": '"109.0.1518.78"',
50
+ "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"',
51
+ "sec-ch-ua-mobile": "?0",
52
+ "sec-ch-ua-model": "",
53
+ "sec-ch-ua-platform": '"Windows"',
54
+ "sec-ch-ua-platform-version": '"15.0.0"',
55
+ "sec-fetch-dest": "empty",
56
+ "sec-fetch-mode": "cors",
57
+ "sec-fetch-site": "same-origin",
58
+ "x-ms-client-request-id": str(uuid.uuid4()),
59
+ "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32",
60
+ "Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx",
61
+ "Referrer-Policy": "origin-when-cross-origin",
62
+ "x-forwarded-for": FORWARDED_IP,
63
+ }
64
+
65
+ HEADERS_INIT_CONVER = {
66
+ "authority": "edgeservices.bing.com",
67
+ "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",
68
+ "accept-language": "en-US,en;q=0.9",
69
+ "cache-control": "max-age=0",
70
+ "sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"',
71
+ "sec-ch-ua-arch": '"x86"',
72
+ "sec-ch-ua-bitness": '"64"',
73
+ "sec-ch-ua-full-version": '"110.0.1587.69"',
74
+ "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"',
75
+ "sec-ch-ua-mobile": "?0",
76
+ "sec-ch-ua-model": '""',
77
+ "sec-ch-ua-platform": '"Windows"',
78
+ "sec-ch-ua-platform-version": '"15.0.0"',
79
+ "sec-fetch-dest": "document",
80
+ "sec-fetch-mode": "navigate",
81
+ "sec-fetch-site": "none",
82
+ "sec-fetch-user": "?1",
83
+ "upgrade-insecure-requests": "1",
84
+ "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",
85
+ "x-edge-shopping-flag": "1",
86
+ "x-forwarded-for": FORWARDED_IP,
87
+ }
88
+
89
+ ssl_context = ssl.create_default_context()
90
+ ssl_context.load_verify_locations(certifi.where())
91
+
92
+
93
+ class NotAllowedToAccess(Exception):
94
+ pass
95
+
96
+
97
+ class ConversationStyle(Enum):
98
+ creative = [
99
+ "nlu_direct_response_filter",
100
+ "deepleo",
101
+ "disable_emoji_spoken_text",
102
+ "responsible_ai_policy_235",
103
+ "enablemm",
104
+ "h3imaginative",
105
+ "travelansgnd",
106
+ "dv3sugg",
107
+ "clgalileo",
108
+ "gencontentv3",
109
+ "dv3sugg",
110
+ "responseos",
111
+ "e2ecachewrite",
112
+ "cachewriteext",
113
+ "nodlcpcwrite",
114
+ "travelansgnd",
115
+ "nojbfedge",
116
+ ]
117
+ balanced = [
118
+ "nlu_direct_response_filter",
119
+ "deepleo",
120
+ "disable_emoji_spoken_text",
121
+ "responsible_ai_policy_235",
122
+ "enablemm",
123
+ "galileo",
124
+ "dv3sugg",
125
+ "responseos",
126
+ "e2ecachewrite",
127
+ "cachewriteext",
128
+ "nodlcpcwrite",
129
+ "travelansgnd",
130
+ "nojbfedge",
131
+ ]
132
+ precise = [
133
+ "nlu_direct_response_filter",
134
+ "deepleo",
135
+ "disable_emoji_spoken_text",
136
+ "responsible_ai_policy_235",
137
+ "enablemm",
138
+ "galileo",
139
+ "dv3sugg",
140
+ "responseos",
141
+ "e2ecachewrite",
142
+ "cachewriteext",
143
+ "nodlcpcwrite",
144
+ "travelansgnd",
145
+ "h3precise",
146
+ "clgalileo",
147
+ "nojbfedge",
148
+ ]
149
+
150
+
151
+ CONVERSATION_STYLE_TYPE = Optional[
152
+ Union[ConversationStyle, Literal["creative", "balanced", "precise"]]
153
+ ]
154
+
155
+
156
+ def _append_identifier(msg: dict) -> str:
157
+ """
158
+ Appends special character to end of message to identify end of message
159
+ """
160
+ # Convert dict to json string
161
+ return json.dumps(msg, ensure_ascii=False) + DELIMITER
162
+
163
+
164
+ def _get_ran_hex(length: int = 32) -> str:
165
+ """
166
+ Returns random hex string
167
+ """
168
+ return "".join(random.choice("0123456789abcdef") for _ in range(length))
169
+
170
+
171
+ class _ChatHubRequest:
172
+ """
173
+ Request object for ChatHub
174
+ """
175
+
176
+ def __init__(
177
+ self,
178
+ conversation_signature: str,
179
+ client_id: str,
180
+ conversation_id: str,
181
+ invocation_id: int = 0,
182
+ ) -> None:
183
+ self.struct: dict = {}
184
+
185
+ self.client_id: str = client_id
186
+ self.conversation_id: str = conversation_id
187
+ self.conversation_signature: str = conversation_signature
188
+ self.invocation_id: int = invocation_id
189
+
190
+ def update(
191
+ self,
192
+ prompt: str,
193
+ conversation_style: CONVERSATION_STYLE_TYPE,
194
+ options: list | None = None,
195
+ webpage_context: str | None = None,
196
+ search_result: bool = False,
197
+ ) -> None:
198
+ """
199
+ Updates request object
200
+ """
201
+ if options is None:
202
+ options = [
203
+ "deepleo",
204
+ "enable_debug_commands",
205
+ "disable_emoji_spoken_text",
206
+ "enablemm",
207
+ ]
208
+ if conversation_style:
209
+ if not isinstance(conversation_style, ConversationStyle):
210
+ conversation_style = getattr(ConversationStyle, conversation_style)
211
+ options = conversation_style.value
212
+ self.struct = {
213
+ "arguments": [
214
+ {
215
+ "source": "cib",
216
+ "optionsSets": options,
217
+ "allowedMessageTypes": [
218
+ "Chat",
219
+ "Disengaged",
220
+ "AdsQuery",
221
+ "SemanticSerp",
222
+ "GenerateContentQuery",
223
+ "SearchQuery",
224
+ ],
225
+ "sliceIds": [
226
+ "chk1cf",
227
+ "nopreloadsscf",
228
+ "winlongmsg2tf",
229
+ "perfimpcomb",
230
+ "sugdivdis",
231
+ "sydnoinputt",
232
+ "wpcssopt",
233
+ "wintone2tf",
234
+ "0404sydicnbs0",
235
+ "405suggbs0",
236
+ "scctl",
237
+ "330uaugs0",
238
+ "0329resp",
239
+ "udscahrfon",
240
+ "udstrblm5",
241
+ "404e2ewrt",
242
+ "408nodedups0",
243
+ "403tvlansgnd",
244
+ ],
245
+ "traceId": _get_ran_hex(32),
246
+ "isStartOfSession": self.invocation_id == 0,
247
+ "message": {
248
+ "author": "user",
249
+ "inputMethod": "Keyboard",
250
+ "text": prompt,
251
+ "messageType": "Chat",
252
+ },
253
+ "conversationSignature": self.conversation_signature,
254
+ "participant": {
255
+ "id": self.client_id,
256
+ },
257
+ "conversationId": self.conversation_id,
258
+ },
259
+ ],
260
+ "invocationId": str(self.invocation_id),
261
+ "target": "chat",
262
+ "type": 4,
263
+ }
264
+ if search_result:
265
+ have_search_result = [
266
+ "InternalSearchQuery",
267
+ "InternalSearchResult",
268
+ "InternalLoaderMessage",
269
+ "RenderCardRequest",
270
+ ]
271
+ self.struct["arguments"][0]["allowedMessageTypes"] += have_search_result
272
+ if webpage_context:
273
+ self.struct["arguments"][0]["previousMessages"] = [
274
+ {
275
+ "author": "user",
276
+ "description": webpage_context,
277
+ "contextType": "WebPage",
278
+ "messageType": "Context",
279
+ "messageId": "discover-web--page-ping-mriduna-----",
280
+ },
281
+ ]
282
+ self.invocation_id += 1
283
+
284
+
285
+ class _Conversation:
286
+ """
287
+ Conversation API
288
+ """
289
+
290
+ def __init__(
291
+ self,
292
+ cookies: dict | None = None,
293
+ proxy: str | None = None,
294
+ async_mode: bool = False,
295
+ ) -> None:
296
+ if async_mode:
297
+ return
298
+ self.struct: dict = {
299
+ "conversationId": None,
300
+ "clientId": None,
301
+ "conversationSignature": None,
302
+ "result": {"value": "Success", "message": None},
303
+ }
304
+ self.proxy = proxy
305
+ proxy = (
306
+ proxy
307
+ or os.environ.get("all_proxy")
308
+ or os.environ.get("ALL_PROXY")
309
+ or os.environ.get("https_proxy")
310
+ or os.environ.get("HTTPS_PROXY")
311
+ or None
312
+ )
313
+ if proxy is not None and proxy.startswith("socks5h://"):
314
+ proxy = "socks5://" + proxy[len("socks5h://") :]
315
+ self.session = httpx.Client(
316
+ proxies=proxy,
317
+ timeout=300,
318
+ headers=HEADERS_INIT_CONVER,
319
+ )
320
+ for cookie in cookies:
321
+ self.session.cookies.set(cookie["name"], cookie["value"])
322
+
323
+ # Send GET request
324
+ response = self.session.get(
325
+ url=os.environ.get("BING_PROXY_URL")
326
+ or "https://edgeservices.bing.com/edgesvc/turing/conversation/create",
327
+ )
328
+ if response.status_code != 200:
329
+ response = self.session.get(
330
+ "https://edge.churchless.tech/edgesvc/turing/conversation/create",
331
+ )
332
+ if response.status_code != 200:
333
+ print(f"Status code: {response.status_code}")
334
+ print(response.text)
335
+ print(response.url)
336
+ raise Exception("Authentication failed")
337
+ try:
338
+ self.struct = response.json()
339
+ except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
340
+ raise Exception(
341
+ "Authentication failed. You have not been accepted into the beta.",
342
+ ) from exc
343
+ if self.struct["result"]["value"] == "UnauthorizedRequest":
344
+ raise NotAllowedToAccess(self.struct["result"]["message"])
345
+
346
+ @staticmethod
347
+ async def create(
348
+ cookies: dict,
349
+ proxy: str | None = None,
350
+ ) -> _Conversation:
351
+ self = _Conversation(async_mode=True)
352
+ self.struct = {
353
+ "conversationId": None,
354
+ "clientId": None,
355
+ "conversationSignature": None,
356
+ "result": {"value": "Success", "message": None},
357
+ }
358
+ self.proxy = proxy
359
+ proxy = (
360
+ proxy
361
+ or os.environ.get("all_proxy")
362
+ or os.environ.get("ALL_PROXY")
363
+ or os.environ.get("https_proxy")
364
+ or os.environ.get("HTTPS_PROXY")
365
+ or None
366
+ )
367
+ if proxy is not None and proxy.startswith("socks5h://"):
368
+ proxy = "socks5://" + proxy[len("socks5h://") :]
369
+ transport = httpx.AsyncHTTPTransport(retries=10)
370
+ async with httpx.AsyncClient(
371
+ proxies=proxy,
372
+ timeout=300,
373
+ headers=HEADERS_INIT_CONVER,
374
+ transport=transport,
375
+ ) as client:
376
+ for cookie in cookies:
377
+ client.cookies.set(cookie["name"], cookie["value"])
378
+
379
+ # Send GET request
380
+ response = await client.get(
381
+ url=os.environ.get("BING_PROXY_URL")
382
+ or "https://edgeservices.bing.com/edgesvc/turing/conversation/create",
383
+ )
384
+ if response.status_code != 200:
385
+ response = await client.get(
386
+ "https://edge.churchless.tech/edgesvc/turing/conversation/create",
387
+ )
388
+ if response.status_code != 200:
389
+ print(f"Status code: {response.status_code}")
390
+ print(response.text)
391
+ print(response.url)
392
+ raise Exception("Authentication failed")
393
+ try:
394
+ self.struct = response.json()
395
+ except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
396
+ raise Exception(
397
+ "Authentication failed. You have not been accepted into the beta.",
398
+ ) from exc
399
+ if self.struct["result"]["value"] == "UnauthorizedRequest":
400
+ raise NotAllowedToAccess(self.struct["result"]["message"])
401
+ return self
402
+
403
+
404
+ class _ChatHub:
405
+ """
406
+ Chat API
407
+ """
408
+
409
+ def __init__(self, conversation: _Conversation) -> None:
410
+ self.wss: websockets.WebSocketClientProtocol | None = None
411
+ self.request: _ChatHubRequest
412
+ self.loop: bool
413
+ self.task: asyncio.Task
414
+ self.request = _ChatHubRequest(
415
+ conversation_signature=conversation.struct["conversationSignature"],
416
+ client_id=conversation.struct["clientId"],
417
+ conversation_id=conversation.struct["conversationId"],
418
+ )
419
+
420
+ async def ask_stream(
421
+ self,
422
+ prompt: str,
423
+ wss_link: str,
424
+ cookies: str,
425
+ conversation_style: CONVERSATION_STYLE_TYPE = None,
426
+ raw: bool = False,
427
+ options: dict = None,
428
+ webpage_context: str | None = None,
429
+ search_result: bool = False,
430
+ ) -> Generator[str, None, None]:
431
+ """
432
+ Ask a question to the bot
433
+ """
434
+ if self.wss and not self.wss.closed:
435
+ await self.wss.close()
436
+ # Check if websocket is closed
437
+ self.wss = await websockets.connect(
438
+ wss_link,
439
+ extra_headers=HEADERS,
440
+ max_size=None,
441
+ ssl=ssl_context,
442
+ )
443
+ await self._initial_handshake()
444
+ if self.request.invocation_id == 0:
445
+ # Construct a ChatHub request
446
+ self.request.update(
447
+ prompt=prompt,
448
+ conversation_style=conversation_style,
449
+ options=options,
450
+ webpage_context=webpage_context,
451
+ search_result=search_result,
452
+ )
453
+ else:
454
+ async with httpx.AsyncClient() as client:
455
+ response = await client.post(
456
+ "https://sydney.bing.com/sydney/UpdateConversation/",
457
+ json={
458
+ "messages": [
459
+ {
460
+ "author": "user",
461
+ "description": webpage_context,
462
+ "contextType": "WebPage",
463
+ "messageType": "Context",
464
+ },
465
+ ],
466
+ "conversationId": self.request.conversation_id,
467
+ "source": "cib",
468
+ "traceId": _get_ran_hex(32),
469
+ "participant": {"id": self.request.client_id},
470
+ "conversationSignature": self.request.conversation_signature,
471
+ },
472
+ )
473
+ if response.status_code != 200:
474
+ print(f"Status code: {response.status_code}")
475
+ print(response.text)
476
+ print(response.url)
477
+ raise Exception("Update web page context failed")
478
+ # Construct a ChatHub request
479
+ self.request.update(
480
+ prompt=prompt,
481
+ conversation_style=conversation_style,
482
+ options=options,
483
+ )
484
+ # Send request
485
+ await self.wss.send(_append_identifier(self.request.struct))
486
+ final = False
487
+ draw = False
488
+ resp_txt = ""
489
+ result_text = ""
490
+ resp_txt_no_link = ""
491
+ while not final:
492
+ objects = str(await self.wss.recv()).split(DELIMITER)
493
+ for obj in objects:
494
+ if obj is None or not obj:
495
+ continue
496
+ response = json.loads(obj)
497
+ if response.get("type") != 2 and raw:
498
+ yield False, response
499
+ elif response.get("type") == 1 and response["arguments"][0].get(
500
+ "messages",
501
+ ):
502
+ if not draw:
503
+ if (
504
+ response["arguments"][0]["messages"][0].get("messageType")
505
+ == "GenerateContentQuery"
506
+ ):
507
+ for item in cookies:
508
+ if item["name"] == "_U":
509
+ U = item["value"]
510
+ async with ImageGenAsync(U, True) as image_generator:
511
+ images = await image_generator.get_images(
512
+ response["arguments"][0]["messages"][0]["text"],
513
+ )
514
+ for i, image in enumerate(images):
515
+ resp_txt = resp_txt + f"\n![image{i}]({image})"
516
+ draw = True
517
+ if (
518
+ response["arguments"][0]["messages"][0]["contentOrigin"]
519
+ != "Apology"
520
+ ) and not draw:
521
+ resp_txt = result_text + response["arguments"][0][
522
+ "messages"
523
+ ][0]["adaptiveCards"][0]["body"][0].get("text", "")
524
+ resp_txt_no_link = result_text + response["arguments"][0][
525
+ "messages"
526
+ ][0].get("text", "")
527
+ if response["arguments"][0]["messages"][0].get(
528
+ "messageType",
529
+ ):
530
+ resp_txt = (
531
+ resp_txt
532
+ + response["arguments"][0]["messages"][0][
533
+ "adaptiveCards"
534
+ ][0]["body"][0]["inlines"][0].get("text")
535
+ + "\n"
536
+ )
537
+ result_text = (
538
+ result_text
539
+ + response["arguments"][0]["messages"][0][
540
+ "adaptiveCards"
541
+ ][0]["body"][0]["inlines"][0].get("text")
542
+ + "\n"
543
+ )
544
+ yield False, resp_txt
545
+
546
+ elif response.get("type") == 2:
547
+ if draw:
548
+ cache = response["item"]["messages"][1]["adaptiveCards"][0][
549
+ "body"
550
+ ][0]["text"]
551
+ response["item"]["messages"][1]["adaptiveCards"][0]["body"][0][
552
+ "text"
553
+ ] = (cache + resp_txt)
554
+ if (
555
+ response["item"]["messages"][-1]["contentOrigin"] == "Apology"
556
+ and resp_txt
557
+ ):
558
+ response["item"]["messages"][-1]["text"] = resp_txt_no_link
559
+ response["item"]["messages"][-1]["adaptiveCards"][0]["body"][0][
560
+ "text"
561
+ ] = resp_txt
562
+ print(
563
+ "Preserved the message from being deleted",
564
+ file=sys.stderr,
565
+ )
566
+ final = True
567
+ yield True, response
568
+
569
+ async def _initial_handshake(self) -> None:
570
+ await self.wss.send(_append_identifier({"protocol": "json", "version": 1}))
571
+ await self.wss.recv()
572
+
573
+ async def close(self) -> None:
574
+ """
575
+ Close the connection
576
+ """
577
+ if self.wss and not self.wss.closed:
578
+ await self.wss.close()
579
+
580
+
581
+ class Chatbot:
582
+ """
583
+ Combines everything to make it seamless
584
+ """
585
+
586
+ def __init__(
587
+ self,
588
+ cookies: dict = None,
589
+ proxy: str | None = None,
590
+ cookie_path: str = None,
591
+ ) -> None:
592
+ if cookies is None:
593
+ cookies = {}
594
+ if cookie_path is not None:
595
+ try:
596
+ with open(cookie_path, encoding="utf-8") as f:
597
+ self.cookies = json.load(f)
598
+ except FileNotFoundError as exc:
599
+ raise FileNotFoundError("Cookie file not found") from exc
600
+ else:
601
+ self.cookies = cookies
602
+ self.proxy: str | None = proxy
603
+ self.chat_hub: _ChatHub = _ChatHub(
604
+ _Conversation(self.cookies, self.proxy),
605
+ )
606
+ print(f"*" * 100)
607
+ print(f">>>>>>> 执行了小吕自己的本地代码")
608
+ print(f"*" * 100)
609
+
610
+ @staticmethod
611
+ async def create(
612
+ cookies: dict = None,
613
+ proxy: str | None = None,
614
+ cookie_path: str = None,
615
+ ):
616
+ self = Chatbot.__new__(Chatbot)
617
+ if cookies is None:
618
+ cookies = {}
619
+ if cookie_path is not None:
620
+ try:
621
+ with open(cookie_path, encoding="utf-8") as f:
622
+ self.cookies = json.load(f)
623
+ except FileNotFoundError as exc:
624
+ raise FileNotFoundError("Cookie file not found") from exc
625
+ else:
626
+ self.cookies = cookies
627
+ self.proxy = proxy
628
+ self.chat_hub = _ChatHub(
629
+ await _Conversation.create(self.cookies, self.proxy),
630
+ )
631
+ return self
632
+
633
+ async def ask(
634
+ self,
635
+ prompt: str,
636
+ wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
637
+ conversation_style: CONVERSATION_STYLE_TYPE = None,
638
+ options: dict = None,
639
+ webpage_context: str | None = None,
640
+ search_result: bool = False,
641
+ ) -> dict:
642
+ """
643
+ Ask a question to the bot
644
+ """
645
+ async for final, response in self.chat_hub.ask_stream(
646
+ prompt=prompt,
647
+ conversation_style=conversation_style,
648
+ wss_link=wss_link,
649
+ options=options,
650
+ cookies=self.cookies,
651
+ webpage_context=webpage_context,
652
+ search_result=search_result,
653
+ ):
654
+ if final:
655
+ return response
656
+ await self.chat_hub.wss.close()
657
+ return {}
658
+
659
+ async def ask_stream(
660
+ self,
661
+ prompt: str,
662
+ wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
663
+ conversation_style: CONVERSATION_STYLE_TYPE = None,
664
+ raw: bool = False,
665
+ options: dict = None,
666
+ webpage_context: str | None = None,
667
+ search_result: bool = False,
668
+ ) -> Generator[str, None, None]:
669
+ """
670
+ Ask a question to the bot
671
+ """
672
+ async for response in self.chat_hub.ask_stream(
673
+ prompt=prompt,
674
+ conversation_style=conversation_style,
675
+ wss_link=wss_link,
676
+ raw=raw,
677
+ options=options,
678
+ cookies=self.cookies,
679
+ webpage_context=webpage_context,
680
+ search_result=search_result,
681
+ ):
682
+ yield response
683
+
684
+ async def close(self) -> None:
685
+ """
686
+ Close the connection
687
+ """
688
+ await self.chat_hub.close()
689
+
690
+ async def reset(self) -> None:
691
+ """
692
+ Reset the conversation
693
+ """
694
+ await self.close()
695
+ self.chat_hub = _ChatHub(
696
+ await _Conversation.create(self.cookies, self.proxy),
697
+ )
698
+
699
+
700
+ async def _get_input_async(
701
+ session: PromptSession = None,
702
+ completer: WordCompleter = None,
703
+ ) -> str:
704
+ """
705
+ Multiline input function.
706
+ """
707
+ return await session.prompt_async(
708
+ completer=completer,
709
+ multiline=True,
710
+ auto_suggest=AutoSuggestFromHistory(),
711
+ )
712
+
713
+
714
+ def _create_session() -> PromptSession:
715
+ kb = KeyBindings()
716
+
717
+ @kb.add("enter")
718
+ def _(event):
719
+ buffer_text = event.current_buffer.text
720
+ if buffer_text.startswith("!"):
721
+ event.current_buffer.validate_and_handle()
722
+ else:
723
+ event.current_buffer.insert_text("\n")
724
+
725
+ @kb.add("escape")
726
+ def _(event):
727
+ if event.current_buffer.complete_state:
728
+ # event.current_buffer.cancel_completion()
729
+ event.current_buffer.text = ""
730
+
731
+ return PromptSession(key_bindings=kb, history=InMemoryHistory())
732
+
733
+
734
+ def _create_completer(commands: list, pattern_str: str = "$"):
735
+ return WordCompleter(words=commands, pattern=re.compile(pattern_str))
736
+
737
+
738
+ async def async_main(args: argparse.Namespace) -> None:
739
+ """
740
+ Main function
741
+ """
742
+ print("Initializing...")
743
+ print("Enter `alt+enter` or `escape+enter` to send a message")
744
+ bot = await Chatbot.create(proxy=args.proxy, cookies=args.cookies)
745
+ session = _create_session()
746
+ completer = _create_completer(["!help", "!exit", "!reset"])
747
+ initial_prompt = args.prompt
748
+
749
+ while True:
750
+ print("\nYou:")
751
+ if initial_prompt:
752
+ question = initial_prompt
753
+ print(question)
754
+ initial_prompt = None
755
+ else:
756
+ question = (
757
+ input()
758
+ if args.enter_once
759
+ else await _get_input_async(session=session, completer=completer)
760
+ )
761
+ print()
762
+ if question == "!exit":
763
+ break
764
+ if question == "!help":
765
+ print(
766
+ """
767
+ !help - Show this help message
768
+ !exit - Exit the program
769
+ !reset - Reset the conversation
770
+ """,
771
+ )
772
+ continue
773
+ if question == "!reset":
774
+ await bot.reset()
775
+ continue
776
+ print("Bot:")
777
+ if args.no_stream:
778
+ print(
779
+ (
780
+ await bot.ask(
781
+ prompt=question,
782
+ conversation_style=args.style,
783
+ wss_link=args.wss_link,
784
+ )
785
+ )["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"],
786
+ )
787
+ else:
788
+ wrote = 0
789
+ if args.rich:
790
+ md = Markdown("")
791
+ with Live(md, auto_refresh=False) as live:
792
+ async for final, response in bot.ask_stream(
793
+ prompt=question,
794
+ conversation_style=args.style,
795
+ wss_link=args.wss_link,
796
+ ):
797
+ if not final:
798
+ if wrote > len(response):
799
+ print(md)
800
+ print(Markdown("***Bing revoked the response.***"))
801
+ wrote = len(response)
802
+ md = Markdown(response)
803
+ live.update(md, refresh=True)
804
+ else:
805
+ async for final, response in bot.ask_stream(
806
+ prompt=question,
807
+ conversation_style=args.style,
808
+ wss_link=args.wss_link,
809
+ ):
810
+ if not final:
811
+ if not wrote:
812
+ print(response, end="", flush=True)
813
+ else:
814
+ print(response[wrote:], end="", flush=True)
815
+ wrote = len(response)
816
+ print()
817
+ await bot.close()
818
+
819
+
820
+ def main() -> None:
821
+ print(
822
+ """
823
+ EdgeGPT - A demo of reverse engineering the Bing GPT chatbot
824
+ Repo: github.com/acheong08/EdgeGPT
825
+ By: Antonio Cheong
826
+
827
+ !help for help
828
+
829
+ Type !exit to exit
830
+ """,
831
+ )
832
+ parser = argparse.ArgumentParser()
833
+ parser.add_argument("--enter-once", action="store_true")
834
+ parser.add_argument("--no-stream", action="store_true")
835
+ parser.add_argument("--rich", action="store_true")
836
+ parser.add_argument(
837
+ "--proxy",
838
+ help="Proxy URL (e.g. socks5://127.0.0.1:1080)",
839
+ type=str,
840
+ )
841
+ parser.add_argument(
842
+ "--wss-link",
843
+ help="WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)",
844
+ type=str,
845
+ default="wss://sydney.bing.com/sydney/ChatHub",
846
+ )
847
+ parser.add_argument(
848
+ "--style",
849
+ choices=["creative", "balanced", "precise"],
850
+ default="balanced",
851
+ )
852
+ parser.add_argument(
853
+ "--cookie-file",
854
+ type=str,
855
+ default=os.environ.get("COOKIE_FILE", ""),
856
+ required=False,
857
+ help="Cookie file used for authentication (defaults to COOKIE_FILE environment variable)",
858
+ )
859
+ parser.add_argument(
860
+ "--prompt",
861
+ type=str,
862
+ default="",
863
+ required=False,
864
+ help="prompt to start with",
865
+ )
866
+ args = parser.parse_args()
867
+ if not args.cookie_file:
868
+ parser.print_help()
869
+ parser.exit(
870
+ 1,
871
+ "ERROR: use --cookie-file or set the COOKIE_FILE environment variable",
872
+ )
873
+ try:
874
+ args.cookies = json.loads(Path(args.cookie_file).read_text(encoding="utf-8"))
875
+ except OSError as exc:
876
+ print(f"Could not open cookie file: {exc}", file=sys.stderr)
877
+ sys.exit(1)
878
+
879
+ asyncio.run(async_main(args))
880
+
881
+
882
+ if __name__ == "__main__":
883
+ main()