JeffJing commited on
Commit
9560b45
·
1 Parent(s): a46994b

Upload 9 files

Browse files
revChatGPT/.DS_Store ADDED
Binary file (6.15 kB). View file
 
revChatGPT/Unofficial.py ADDED
@@ -0,0 +1,859 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+ import re
4
+ import uuid
5
+ from time import sleep
6
+
7
+ import tls_client
8
+ import undetected_chromedriver as uc
9
+ from requests.exceptions import HTTPError
10
+ from selenium.webdriver.common.by import By
11
+ from selenium.webdriver.support import expected_conditions as EC
12
+ from selenium.webdriver.support.ui import WebDriverWait
13
+
14
+ # Disable all logging
15
+ logging.basicConfig(level=logging.ERROR)
16
+
17
+ BASE_URL = "https://chat.openai.com/"
18
+
19
+
20
+ class Chrome(uc.Chrome):
21
+ def __del__(self):
22
+ self.quit()
23
+
24
+
25
+ class Chatbot:
26
+ def __init__(
27
+ self,
28
+ config,
29
+ conversation_id=None,
30
+ parent_id=None,
31
+ no_refresh=False,
32
+ ) -> None:
33
+ self.config = config
34
+ self.session = tls_client.Session(
35
+ client_identifier="chrome_108",
36
+ )
37
+ if "proxy" in config:
38
+ if type(config["proxy"]) != str:
39
+ raise Exception("Proxy must be a string!")
40
+ proxies = {
41
+ "http": config["proxy"],
42
+ "https": config["proxy"],
43
+ }
44
+ self.session.proxies.update(proxies)
45
+ if "verbose" in config:
46
+ if type(config["verbose"]) != bool:
47
+ raise Exception("Verbose must be a boolean!")
48
+ self.verbose = config["verbose"]
49
+ else:
50
+ self.verbose = False
51
+ self.conversation_id = conversation_id
52
+ self.parent_id = parent_id
53
+ self.conversation_mapping = {}
54
+ self.conversation_id_prev_queue = []
55
+ self.parent_id_prev_queue = []
56
+ self.isMicrosoftLogin = False
57
+ # stdout colors
58
+ self.GREEN = "\033[92m"
59
+ self.WARNING = "\033[93m"
60
+ self.ENDCOLOR = "\033[0m"
61
+ if "email" in config and "password" in config:
62
+ if type(config["email"]) != str:
63
+ raise Exception("Email must be a string!")
64
+ if type(config["password"]) != str:
65
+ raise Exception("Password must be a string!")
66
+ self.email = config["email"]
67
+ self.password = config["password"]
68
+ if "isMicrosoftLogin" in config and config["isMicrosoftLogin"] == True:
69
+ self.isMicrosoftLogin = True
70
+ self.__microsoft_login()
71
+ else:
72
+ self.__email_login()
73
+ elif "session_token" in config:
74
+ if no_refresh:
75
+ self.__get_cf_cookies()
76
+ return
77
+ if type(config["session_token"]) != str:
78
+ raise Exception("Session token must be a string!")
79
+ self.session_token = config["session_token"]
80
+ self.session.cookies.set(
81
+ "__Secure-next-auth.session-token",
82
+ config["session_token"],
83
+ )
84
+ self.__get_cf_cookies()
85
+ else:
86
+ raise Exception("Invalid config!")
87
+ self.__retry_refresh()
88
+
89
+ def __retry_refresh(self):
90
+ retries = 5
91
+ refresh = True
92
+ while refresh:
93
+ try:
94
+ self.__refresh_session()
95
+ refresh = False
96
+ except Exception as exc:
97
+ if retries == 0:
98
+ raise exc
99
+ retries -= 1
100
+
101
+ def ask(
102
+ self,
103
+ prompt,
104
+ conversation_id=None,
105
+ parent_id=None,
106
+ gen_title=False,
107
+ session_token=None,
108
+ ):
109
+ """
110
+ Ask a question to the chatbot
111
+ :param prompt: String
112
+ :param conversation_id: UUID
113
+ :param parent_id: UUID
114
+ :param gen_title: Boolean
115
+ :param session_token: String
116
+ """
117
+ if session_token:
118
+ self.session.cookies.set(
119
+ "__Secure-next-auth.session-token",
120
+ session_token,
121
+ )
122
+ self.session_token = session_token
123
+ self.config["session_token"] = session_token
124
+ self.__retry_refresh()
125
+ self.__map_conversations()
126
+ if conversation_id == None:
127
+ conversation_id = self.conversation_id
128
+ if parent_id == None:
129
+ parent_id = (
130
+ self.parent_id
131
+ if conversation_id == self.conversation_id
132
+ else self.conversation_mapping[conversation_id]
133
+ )
134
+ data = {
135
+ "action": "next",
136
+ "messages": [
137
+ {
138
+ "id": str(uuid.uuid4()),
139
+ "role": "user",
140
+ "content": {"content_type": "text", "parts": [prompt]},
141
+ },
142
+ ],
143
+ "conversation_id": conversation_id,
144
+ "parent_message_id": parent_id or str(uuid.uuid4()),
145
+ "model": "text-davinci-002-render"
146
+ if self.config.get("paid") is not True
147
+ else "text-davinci-002-render-paid",
148
+ }
149
+ new_conv = data["conversation_id"] is None
150
+ self.conversation_id_prev_queue.append(
151
+ data["conversation_id"],
152
+ ) # for rollback
153
+ self.parent_id_prev_queue.append(data["parent_message_id"])
154
+ response = self.session.post(
155
+ url=BASE_URL + "backend-api/conversation",
156
+ data=json.dumps(data),
157
+ timeout_seconds=180,
158
+ )
159
+ if response.status_code != 200:
160
+ print(response.text)
161
+ self.__refresh_session()
162
+ raise HTTPError(
163
+ f"Wrong response code: {response.status_code}! Refreshing session...",
164
+ )
165
+ else:
166
+ try:
167
+ response = response.text.splitlines()[-4]
168
+ response = response[6:]
169
+ except Exception as exc:
170
+ print("Incorrect response from OpenAI API")
171
+ raise Exception("Incorrect response from OpenAI API") from exc
172
+ # Check if it is JSON
173
+ if response.startswith("{"):
174
+ response = json.loads(response)
175
+ self.parent_id = response["message"]["id"]
176
+ self.conversation_id = response["conversation_id"]
177
+ message = response["message"]["content"]["parts"][0]
178
+ res = {
179
+ "message": message,
180
+ "conversation_id": self.conversation_id,
181
+ "parent_id": self.parent_id,
182
+ }
183
+ if gen_title and new_conv:
184
+ try:
185
+ title = self.__gen_title(
186
+ self.conversation_id,
187
+ self.parent_id,
188
+ )["title"]
189
+ except Exception as exc:
190
+ split = prompt.split(" ")
191
+ title = " ".join(split[:3]) + ("..." if len(split) > 3 else "")
192
+ res["title"] = title
193
+ return res
194
+ else:
195
+ return None
196
+
197
+ def __check_response(self, response):
198
+ if response.status_code != 200:
199
+ print(response.text)
200
+ raise Exception("Response code error: ", response.status_code)
201
+
202
+ def get_conversations(self, offset=0, limit=20):
203
+ """
204
+ Get conversations
205
+ :param offset: Integer
206
+ :param limit: Integer
207
+ """
208
+ url = BASE_URL + f"backend-api/conversations?offset={offset}&limit={limit}"
209
+ response = self.session.get(url)
210
+ self.__check_response(response)
211
+ data = json.loads(response.text)
212
+ return data["items"]
213
+
214
+ def get_msg_history(self, id):
215
+ """
216
+ Get message history
217
+ :param id: UUID of conversation
218
+ """
219
+ url = BASE_URL + f"backend-api/conversation/{id}"
220
+ response = self.session.get(url)
221
+ self.__check_response(response)
222
+ data = json.loads(response.text)
223
+ return data
224
+
225
+ def __gen_title(self, id, message_id):
226
+ """
227
+ Generate title for conversation
228
+ """
229
+ url = BASE_URL + f"backend-api/conversation/gen_title/{id}"
230
+ response = self.session.post(
231
+ url,
232
+ data=json.dumps(
233
+ {
234
+ "message_id": message_id,
235
+ "model": "text-davinci-002-render"
236
+ if self.config.get("paid") is not True
237
+ else "text-davinci-002-render-paid",
238
+ },
239
+ ),
240
+ )
241
+ self.__check_response(response)
242
+ data = json.loads(response.text)
243
+ return data
244
+
245
+ def change_title(self, id, title):
246
+ """
247
+ Change title of conversation
248
+ :param id: UUID of conversation
249
+ :param title: String
250
+ """
251
+ url = BASE_URL + f"backend-api/conversation/{id}"
252
+ response = self.session.patch(url, data=f'{{"title": "{title}"}}')
253
+ self.__check_response(response)
254
+
255
+ def delete_conversation(self, id):
256
+ """
257
+ Delete conversation
258
+ :param id: UUID of conversation
259
+ """
260
+ url = BASE_URL + f"backend-api/conversation/{id}"
261
+ response = self.session.patch(url, data='{"is_visible": false}')
262
+ self.__check_response(response)
263
+
264
+ def clear_conversations(self):
265
+ """
266
+ Delete all conversations
267
+ """
268
+ url = BASE_URL + "backend-api/conversations"
269
+ response = self.session.patch(url, data='{"is_visible": false}')
270
+ self.__check_response(response)
271
+
272
+ def __map_conversations(self):
273
+ conversations = self.get_conversations()
274
+ histories = [self.get_msg_history(x["id"]) for x in conversations]
275
+ for x, y in zip(conversations, histories):
276
+ self.conversation_mapping[x["id"]] = y["current_node"]
277
+
278
+ def __refresh_session(self, session_token=None):
279
+ if session_token:
280
+ self.session.cookies.set(
281
+ "__Secure-next-auth.session-token",
282
+ session_token,
283
+ )
284
+ self.session_token = session_token
285
+ self.config["session_token"] = session_token
286
+ url = BASE_URL + "api/auth/session"
287
+ response = self.session.get(url, timeout_seconds=180)
288
+ if response.status_code == 403:
289
+ self.__get_cf_cookies()
290
+ raise Exception("Clearance refreshing...")
291
+ try:
292
+ if "error" in response.json():
293
+ raise Exception(
294
+ f"Failed to refresh session! Error: {response.json()['error']}",
295
+ )
296
+ elif (
297
+ response.status_code != 200
298
+ or response.json() == {}
299
+ or "accessToken" not in response.json()
300
+ ):
301
+ raise Exception(
302
+ f"Response code: {response.status_code} \n Response: {response.text}",
303
+ )
304
+ else:
305
+ self.session.headers.update(
306
+ {
307
+ "Authorization": "Bearer " + response.json()["accessToken"],
308
+ },
309
+ )
310
+ self.session_token = self.session.cookies._find(
311
+ "__Secure-next-auth.session-token",
312
+ )
313
+ except Exception:
314
+ print("Failed to refresh session!")
315
+ if self.isMicrosoftLogin:
316
+ print("Attempting to re-authenticate...")
317
+ self.__microsoft_login()
318
+ else:
319
+ self.__email_login()
320
+
321
+ def reset_chat(self) -> None:
322
+ """
323
+ Reset the conversation ID and parent ID.
324
+
325
+ :return: None
326
+ """
327
+ self.conversation_id = None
328
+ self.parent_id = str(uuid.uuid4())
329
+
330
+ def __microsoft_login(self) -> None:
331
+ """
332
+ Login to OpenAI via Microsoft Login Authentication.
333
+
334
+ :return: None
335
+ """
336
+ driver = None
337
+ try:
338
+ # Open the browser
339
+ self.cf_cookie_found = False
340
+ self.puid_cookie_found = False
341
+ self.session_cookie_found = False
342
+ self.agent_found = False
343
+ self.cf_clearance = None
344
+ self.puid_cookie = None
345
+ self.user_agent = None
346
+ options = self.__get_ChromeOptions()
347
+ print("Spawning browser...")
348
+ driver = uc.Chrome(
349
+ enable_cdp_events=True,
350
+ options=options,
351
+ driver_executable_path=self.config.get("driver_exec_path"),
352
+ browser_executable_path=self.config.get("browser_exec_path"),
353
+ )
354
+ print("Browser spawned.")
355
+ driver.add_cdp_listener(
356
+ "Network.responseReceivedExtraInfo",
357
+ lambda msg: self.__detect_cookies(msg),
358
+ )
359
+ driver.add_cdp_listener(
360
+ "Network.requestWillBeSentExtraInfo",
361
+ lambda msg: self.__detect_user_agent(msg),
362
+ )
363
+ driver.get(BASE_URL)
364
+ while not self.agent_found or not self.cf_cookie_found:
365
+ sleep(5)
366
+ self.__refresh_headers(
367
+ cf_clearance=self.cf_clearance,
368
+ puid_cookie=self.puid_cookie,
369
+ user_agent=self.user_agent,
370
+ )
371
+ # Wait for the login button to appear
372
+ WebDriverWait(driver, 120).until(
373
+ EC.element_to_be_clickable(
374
+ (By.XPATH, "//button[contains(text(), 'Log in')]"),
375
+ ),
376
+ )
377
+ # Click the login button
378
+ driver.find_element(
379
+ by=By.XPATH,
380
+ value="//button[contains(text(), 'Log in')]",
381
+ ).click()
382
+ # Wait for the Login with Microsoft button to be clickable
383
+ WebDriverWait(driver, 60).until(
384
+ EC.element_to_be_clickable(
385
+ (By.XPATH, "//button[@data-provider='windowslive']"),
386
+ ),
387
+ )
388
+ # Click the Login with Microsoft button
389
+ driver.find_element(
390
+ by=By.XPATH,
391
+ value="//button[@data-provider='windowslive']",
392
+ ).click()
393
+ # Wait for the email input field to appear
394
+ WebDriverWait(driver, 60).until(
395
+ EC.visibility_of_element_located(
396
+ (By.XPATH, "//input[@type='email']"),
397
+ ),
398
+ )
399
+ # Enter the email
400
+ driver.find_element(
401
+ by=By.XPATH,
402
+ value="//input[@type='email']",
403
+ ).send_keys(self.config["email"])
404
+ # Wait for the Next button to be clickable
405
+ WebDriverWait(driver, 60).until(
406
+ EC.element_to_be_clickable(
407
+ (By.XPATH, "//input[@type='submit']"),
408
+ ),
409
+ )
410
+ # Click the Next button
411
+ driver.find_element(
412
+ by=By.XPATH,
413
+ value="//input[@type='submit']",
414
+ ).click()
415
+ # Wait for the password input field to appear
416
+ WebDriverWait(driver, 60).until(
417
+ EC.visibility_of_element_located(
418
+ (By.XPATH, "//input[@type='password']"),
419
+ ),
420
+ )
421
+ # Enter the password
422
+ driver.find_element(
423
+ by=By.XPATH,
424
+ value="//input[@type='password']",
425
+ ).send_keys(self.config["password"])
426
+ # Wait for the Sign in button to be clickable
427
+ WebDriverWait(driver, 60).until(
428
+ EC.element_to_be_clickable(
429
+ (By.XPATH, "//input[@type='submit']"),
430
+ ),
431
+ )
432
+ # Click the Sign in button
433
+ driver.find_element(
434
+ by=By.XPATH,
435
+ value="//input[@type='submit']",
436
+ ).click()
437
+ # Wait for the Allow button to appear
438
+ WebDriverWait(driver, 60).until(
439
+ EC.element_to_be_clickable(
440
+ (By.XPATH, "//input[@type='submit']"),
441
+ ),
442
+ )
443
+ # click Yes button
444
+ driver.find_element(
445
+ by=By.XPATH,
446
+ value="//input[@type='submit']",
447
+ ).click()
448
+ # wait for input box to appear (to make sure we're signed in)
449
+ WebDriverWait(driver, 60).until(
450
+ EC.visibility_of_element_located(
451
+ (By.XPATH, "//textarea"),
452
+ ),
453
+ )
454
+ while not self.session_cookie_found:
455
+ sleep(5)
456
+ print(self.GREEN + "Login successful." + self.ENDCOLOR)
457
+ finally:
458
+ # Close the browser
459
+ if driver is not None:
460
+ driver.quit()
461
+ del driver
462
+
463
+ def __email_login(self) -> None:
464
+ """
465
+ Login to OpenAI via Email/Password Authentication and 2Captcha.
466
+
467
+ :return: None
468
+ """
469
+ # Open the browser
470
+ driver = None
471
+ try:
472
+ self.cf_cookie_found = False
473
+ self.puid_cookie_found = False
474
+ self.session_cookie_found = False
475
+ self.agent_found = False
476
+ self.cf_clearance = None
477
+ self.puid_cookie = None
478
+ self.user_agent = None
479
+ options = self.__get_ChromeOptions()
480
+ print("Spawning browser...")
481
+ driver = uc.Chrome(
482
+ enable_cdp_events=True,
483
+ options=options,
484
+ driver_executable_path=self.config.get("driver_exec_path"),
485
+ browser_executable_path=self.config.get("browser_exec_path"),
486
+ )
487
+ print("Browser spawned.")
488
+ driver.add_cdp_listener(
489
+ "Network.responseReceivedExtraInfo",
490
+ lambda msg: self.__detect_cookies(msg),
491
+ )
492
+ driver.add_cdp_listener(
493
+ "Network.requestWillBeSentExtraInfo",
494
+ lambda msg: self.__detect_user_agent(msg),
495
+ )
496
+ driver.get(BASE_URL)
497
+ while not self.agent_found or not self.cf_cookie_found:
498
+ sleep(5)
499
+ self.__refresh_headers(
500
+ cf_clearance=self.cf_clearance,
501
+ puid_cookie=self.puid_cookie,
502
+ user_agent=self.user_agent,
503
+ )
504
+ # Wait for the login button to appear
505
+ WebDriverWait(driver, 120).until(
506
+ EC.element_to_be_clickable(
507
+ (By.XPATH, "//button[contains(text(), 'Log in')]"),
508
+ ),
509
+ )
510
+ # Click the login button
511
+ driver.find_element(
512
+ by=By.XPATH,
513
+ value="//button[contains(text(), 'Log in')]",
514
+ ).click()
515
+ # Wait for the email input field to appear
516
+ WebDriverWait(driver, 60).until(
517
+ EC.visibility_of_element_located(
518
+ (By.ID, "username"),
519
+ ),
520
+ )
521
+ # Enter the email
522
+ driver.find_element(by=By.ID, value="username").send_keys(
523
+ self.config["email"],
524
+ )
525
+ # Wait for the Continue button to be clickable
526
+ WebDriverWait(driver, 60).until(
527
+ EC.element_to_be_clickable(
528
+ (By.XPATH, "//button[@type='submit']"),
529
+ ),
530
+ )
531
+ # Click the Continue button
532
+ driver.find_element(
533
+ by=By.XPATH,
534
+ value="//button[@type='submit']",
535
+ ).click()
536
+ # Wait for the password input field to appear
537
+ WebDriverWait(driver, 60).until(
538
+ EC.visibility_of_element_located(
539
+ (By.ID, "password"),
540
+ ),
541
+ )
542
+ # Enter the password
543
+ driver.find_element(by=By.ID, value="password").send_keys(
544
+ self.config["password"],
545
+ )
546
+ # Wait for the Sign in button to be clickable
547
+ WebDriverWait(driver, 60).until(
548
+ EC.element_to_be_clickable(
549
+ (By.XPATH, "//button[@type='submit']"),
550
+ ),
551
+ )
552
+ # Click the Sign in button
553
+ driver.find_element(
554
+ by=By.XPATH,
555
+ value="//button[@type='submit']",
556
+ ).click()
557
+ # wait for input box to appear (to make sure we're signed in)
558
+ WebDriverWait(driver, 60).until(
559
+ EC.visibility_of_element_located(
560
+ (By.XPATH, "//textarea"),
561
+ ),
562
+ )
563
+ while not self.session_cookie_found or not self.puid_cookie_found:
564
+ sleep(5)
565
+ print(self.GREEN + "Login successful." + self.ENDCOLOR)
566
+ finally:
567
+ if driver is not None:
568
+ # Close the browser
569
+ driver.quit()
570
+ del driver
571
+
572
+ def __get_ChromeOptions(self):
573
+ options = uc.ChromeOptions()
574
+ options.add_argument("--start_maximized")
575
+ options.add_argument("--disable-extensions")
576
+ options.add_argument("--disable-application-cache")
577
+ options.add_argument("--disable-gpu")
578
+ options.add_argument("--no-sandbox")
579
+ options.add_argument("--disable-setuid-sandbox")
580
+ options.add_argument("--disable-dev-shm-usage")
581
+ if self.config.get("proxy", "") != "":
582
+ options.add_argument("--proxy-server=" + self.config["proxy"])
583
+ return options
584
+
585
+ def __get_cf_cookies(self) -> None:
586
+ """
587
+ Get cloudflare cookies.
588
+
589
+ :return: None
590
+ """
591
+ driver = None
592
+ try:
593
+ self.cf_cookie_found = False
594
+ self.agent_found = False
595
+ self.puid_cookie_found = False
596
+ self.cf_clearance = None
597
+ self.puid_cookie = None
598
+ self.user_agent = None
599
+ options = self.__get_ChromeOptions()
600
+ print("Spawning browser...")
601
+ driver = uc.Chrome(
602
+ enable_cdp_events=True,
603
+ options=options,
604
+ driver_executable_path=self.config.get("driver_exec_path"),
605
+ browser_executable_path=self.config.get("browser_exec_path"),
606
+ )
607
+ print("Browser spawned.")
608
+ driver.add_cdp_listener(
609
+ "Network.responseReceivedExtraInfo",
610
+ lambda msg: self.__detect_cookies(msg),
611
+ )
612
+ driver.add_cdp_listener(
613
+ "Network.requestWillBeSentExtraInfo",
614
+ lambda msg: self.__detect_user_agent(msg),
615
+ )
616
+ driver.get("https://chat.openai.com/chat")
617
+ while (
618
+ not self.agent_found
619
+ or not self.cf_cookie_found
620
+ or not self.puid_cookie_found
621
+ ):
622
+ sleep(5)
623
+ finally:
624
+ # Close the browser
625
+ if driver is not None:
626
+ driver.quit()
627
+ del driver
628
+ self.__refresh_headers(
629
+ cf_clearance=self.cf_clearance,
630
+ puid_cookie=self.puid_cookie,
631
+ user_agent=self.user_agent,
632
+ )
633
+
634
+ def __detect_cookies(self, message):
635
+ if "params" in message:
636
+ if "headers" in message["params"]:
637
+ if "set-cookie" in message["params"]["headers"]:
638
+ # Use regex to get the cookie for cf_clearance=*;
639
+ cf_clearance_cookie = re.search(
640
+ "cf_clearance=.*?;",
641
+ message["params"]["headers"]["set-cookie"],
642
+ )
643
+ puid_cookie = re.search(
644
+ "_puid=.*?;",
645
+ message["params"]["headers"]["set-cookie"],
646
+ )
647
+ session_cookie = re.search(
648
+ "__Secure-next-auth.session-token=.*?;",
649
+ message["params"]["headers"]["set-cookie"],
650
+ )
651
+ if cf_clearance_cookie and not self.cf_cookie_found:
652
+ print("Found Cloudflare Cookie!")
653
+ # remove the semicolon and 'cf_clearance=' from the string
654
+ raw_cf_cookie = cf_clearance_cookie.group(0)
655
+ self.cf_clearance = raw_cf_cookie.split("=")[1][:-1]
656
+ if self.verbose:
657
+ print(
658
+ self.GREEN
659
+ + "Cloudflare Cookie: "
660
+ + self.ENDCOLOR
661
+ + self.cf_clearance,
662
+ )
663
+ self.cf_cookie_found = True
664
+ if puid_cookie and not self.puid_cookie_found:
665
+ raw_puid_cookie = puid_cookie.group(0)
666
+ self.puid_cookie = raw_puid_cookie.split("=")[1][:-1]
667
+ self.session.cookies.set(
668
+ "_puid",
669
+ self.puid_cookie,
670
+ )
671
+ if self.verbose:
672
+ print(
673
+ self.GREEN
674
+ + "puid Cookie: "
675
+ + self.ENDCOLOR
676
+ + self.puid_cookie,
677
+ )
678
+ self.puid_cookie_found = True
679
+ if session_cookie and not self.session_cookie_found:
680
+ print("Found Session Token!")
681
+ # remove the semicolon and '__Secure-next-auth.session-token=' from the string
682
+ raw_session_cookie = session_cookie.group(0)
683
+ self.session_token = raw_session_cookie.split("=")[1][:-1]
684
+ self.session.cookies.set(
685
+ "__Secure-next-auth.session-token",
686
+ self.session_token,
687
+ )
688
+ if self.verbose:
689
+ print(
690
+ self.GREEN
691
+ + "Session Token: "
692
+ + self.ENDCOLOR
693
+ + self.session_token,
694
+ )
695
+ self.session_cookie_found = True
696
+
697
+ def __detect_user_agent(self, message):
698
+ if "params" in message:
699
+ if "headers" in message["params"]:
700
+ if "user-agent" in message["params"]["headers"]:
701
+ # Use regex to get the cookie for cf_clearance=*;
702
+ user_agent = message["params"]["headers"]["user-agent"]
703
+ self.user_agent = user_agent
704
+ self.agent_found = True
705
+ self.__refresh_headers(
706
+ cf_clearance=self.cf_clearance,
707
+ puid_cookie=self.puid_cookie,
708
+ user_agent=self.user_agent,
709
+ )
710
+
711
+ def __refresh_headers(self, cf_clearance, puid_cookie, user_agent):
712
+ del self.session.cookies["cf_clearance"]
713
+ del self.session.cookies["_puid"]
714
+ self.session.headers.clear()
715
+ self.session.cookies.set("cf_clearance", cf_clearance)
716
+ self.session.cookies.set("_puid", puid_cookie)
717
+ self.session.headers.update(
718
+ {
719
+ "Accept": "text/event-stream",
720
+ "Authorization": "Bearer ",
721
+ "Content-Type": "application/json",
722
+ "User-Agent": user_agent,
723
+ "X-Openai-Assistant-App-Id": "",
724
+ "Connection": "close",
725
+ "Accept-Language": "en-US,en;q=0.9",
726
+ "Referer": "https://chat.openai.com/chat",
727
+ },
728
+ )
729
+
730
+ def rollback_conversation(self, num=1) -> None:
731
+ """
732
+ Rollback the conversation.
733
+ :param num: The number of messages to rollback
734
+ :return: None
735
+ """
736
+ for i in range(num):
737
+ self.conversation_id = self.conversation_id_prev_queue.pop()
738
+ self.parent_id = self.parent_id_prev_queue.pop()
739
+
740
+
741
+ def get_input(prompt):
742
+ # Display the prompt
743
+ print(prompt, end="")
744
+
745
+ # Initialize an empty list to store the input lines
746
+ lines = []
747
+
748
+ # Read lines of input until the user enters an empty line
749
+ while True:
750
+ line = input()
751
+ if line == "":
752
+ break
753
+ lines.append(line)
754
+
755
+ # Join the lines, separated by newlines, and store the result
756
+ user_input = "\n".join(lines)
757
+
758
+ # Return the input
759
+ return user_input
760
+
761
+
762
+ from os import getenv
763
+ from os.path import exists
764
+
765
+
766
+ def configure():
767
+ config_files = ["config.json"]
768
+ xdg_config_home = getenv("XDG_CONFIG_HOME")
769
+ if xdg_config_home:
770
+ config_files.append(f"{xdg_config_home}/revChatGPT/config.json")
771
+ user_home = getenv("HOME")
772
+ if user_home:
773
+ config_files.append(f"{user_home}/.config/revChatGPT/config.json")
774
+
775
+ config_file = next((f for f in config_files if exists(f)), None)
776
+ if config_file:
777
+ with open(config_file, encoding="utf-8") as f:
778
+ config = json.load(f)
779
+ else:
780
+ print("No config file found.")
781
+ raise Exception("No config file found.")
782
+ return config
783
+
784
+
785
+ def chatGPT_main(config):
786
+ print("Logging in...")
787
+ chatbot = Chatbot(config)
788
+ while True:
789
+ prompt = get_input("\nYou:\n")
790
+ if prompt.startswith("!"):
791
+ if prompt == "!help":
792
+ print(
793
+ """
794
+ !help - Show this message
795
+ !reset - Forget the current conversation
796
+ !refresh - Refresh the session authentication
797
+ !config - Show the current configuration
798
+ !rollback x - Rollback the conversation (x being the number of messages to rollback)
799
+ !exit - Exit this program
800
+ """,
801
+ )
802
+ continue
803
+ elif prompt == "!reset":
804
+ chatbot.reset_chat()
805
+ print("Chat session successfully reset.")
806
+ continue
807
+ elif prompt == "!refresh":
808
+ chatbot.__refresh_session()
809
+ print("Session successfully refreshed.\n")
810
+ continue
811
+ elif prompt == "!config":
812
+ print(json.dumps(chatbot.config, indent=4))
813
+ continue
814
+ elif prompt.startswith("!rollback"):
815
+ # Default to 1 rollback if no number is specified
816
+ try:
817
+ rollback = int(prompt.split(" ")[1])
818
+ except IndexError:
819
+ rollback = 1
820
+ chatbot.rollback_conversation(rollback)
821
+ print(f"Rolled back {rollback} messages.")
822
+ continue
823
+ elif prompt.startswith("!setconversation"):
824
+ try:
825
+ chatbot.config["conversation"] = prompt.split(" ")[1]
826
+ print("Conversation has been changed")
827
+ except IndexError:
828
+ print("Please include conversation UUID in command")
829
+ continue
830
+ elif prompt == "!exit":
831
+ break
832
+ try:
833
+ print("Chatbot: ")
834
+ message = chatbot.ask(
835
+ prompt,
836
+ conversation_id=chatbot.config.get("conversation"),
837
+ parent_id=chatbot.config.get("parent_id"),
838
+ )
839
+ print(message["message"])
840
+ except Exception as exc:
841
+ print("Something went wrong!")
842
+ print(exc)
843
+ continue
844
+
845
+
846
+ def main():
847
+ print(
848
+ """
849
+ ChatGPT - A command-line interface to OpenAI's ChatGPT (https://chat.openai.com/chat)
850
+ Repo: github.com/acheong08/ChatGPT
851
+ """,
852
+ )
853
+ print("Type '!help' to show a full list of commands")
854
+ print("Press enter twice to submit your question.\n")
855
+ chatGPT_main(configure())
856
+
857
+
858
+ if __name__ == "__main__":
859
+ main()
revChatGPT/V1.py ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Standard ChatGPT
3
+ """
4
+ import json
5
+ import logging
6
+ import uuid
7
+ from os import environ
8
+ from os import getenv
9
+ from os.path import exists
10
+ from random import choice
11
+
12
+ import requests
13
+ from OpenAIAuth.OpenAIAuth import OpenAIAuth
14
+
15
+ # Disable all logging
16
+ logging.basicConfig(level=logging.ERROR)
17
+
18
+ BASE_URL = environ.get("CHATGPT_BASE_URL") or choice(
19
+ ["https://chatgpt-proxy.fly.dev/", "https://chatgpt-proxy2.fly.dev/"]
20
+ )
21
+
22
+
23
+ class Error(Exception):
24
+ """Base class for exceptions in this module."""
25
+
26
+ source: str
27
+ message: str
28
+ code: int
29
+
30
+
31
+ class Chatbot:
32
+ """
33
+ Chatbot class for ChatGPT
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ config,
39
+ conversation_id=None,
40
+ parent_id=None,
41
+ ) -> None:
42
+ self.config = config
43
+ self.session = requests.Session()
44
+ if "proxy" in config:
45
+ if isinstance(config["proxy"], str) is False:
46
+ raise Exception("Proxy must be a string!")
47
+ proxies = {
48
+ "http": config["proxy"],
49
+ "https": config["proxy"],
50
+ }
51
+ self.session.proxies.update(proxies)
52
+ if "verbose" in config:
53
+ if type(config["verbose"]) != bool:
54
+ raise Exception("Verbose must be a boolean!")
55
+ self.verbose = config["verbose"]
56
+ else:
57
+ self.verbose = False
58
+ self.conversation_id = conversation_id
59
+ self.parent_id = parent_id
60
+ self.conversation_mapping = {}
61
+ self.conversation_id_prev_queue = []
62
+ self.parent_id_prev_queue = []
63
+ if "email" in config and "password" in config:
64
+ pass
65
+ elif "session_token" in config:
66
+ pass
67
+ elif "access_token" in config:
68
+ self.__refresh_headers(config["access_token"])
69
+ else:
70
+ raise Exception("No login details provided!")
71
+ if "access_token" not in config:
72
+ self.__login()
73
+
74
+ def __refresh_headers(self, access_token):
75
+ self.session.headers.clear()
76
+ self.session.headers.update(
77
+ {
78
+ "Accept": "text/event-stream",
79
+ "Authorization": f"Bearer {access_token}",
80
+ "Content-Type": "application/json",
81
+ "X-Openai-Assistant-App-Id": "",
82
+ "Connection": "close",
83
+ "Accept-Language": "en-US,en;q=0.9",
84
+ "Referer": "https://chat.openai.com/chat",
85
+ },
86
+ )
87
+
88
+ def __login(self):
89
+ if (
90
+ "email" not in self.config or "password" not in self.config
91
+ ) and "session_token" not in self.config:
92
+ raise Exception("No login details provided!")
93
+ auth = OpenAIAuth(
94
+ email_address=self.config.get("email"),
95
+ password=self.config.get("password"),
96
+ proxy=self.config.get("proxy"),
97
+ )
98
+ if self.config.get("session_token"):
99
+ auth.session_token = self.config["session_token"]
100
+ auth.get_access_token()
101
+ if auth.access_token is None:
102
+ del self.config["session_token"]
103
+ self.__login()
104
+ return
105
+ else:
106
+ auth.begin()
107
+ self.config["session_token"] = auth.session_token
108
+ auth.get_access_token()
109
+
110
+ self.__refresh_headers(auth.access_token)
111
+
112
+ def ask(
113
+ self,
114
+ prompt,
115
+ conversation_id=None,
116
+ parent_id=None,
117
+ # gen_title=True,
118
+ ):
119
+ """
120
+ Ask a question to the chatbot
121
+ :param prompt: String
122
+ :param conversation_id: UUID
123
+ :param parent_id: UUID
124
+ :param gen_title: Boolean
125
+ """
126
+ if conversation_id is not None and parent_id is None:
127
+ self.__map_conversations()
128
+ if conversation_id is None:
129
+ conversation_id = self.conversation_id
130
+ if parent_id is None:
131
+ parent_id = (
132
+ self.parent_id
133
+ if conversation_id == self.conversation_id
134
+ else self.conversation_mapping[conversation_id]
135
+ )
136
+ # new_conv = conversation_id is None
137
+ data = {
138
+ "action": "next",
139
+ "messages": [
140
+ {
141
+ "id": str(uuid.uuid4()),
142
+ "role": "user",
143
+ "content": {"content_type": "text", "parts": [prompt]},
144
+ },
145
+ ],
146
+ "conversation_id": conversation_id,
147
+ "parent_message_id": parent_id or str(uuid.uuid4()),
148
+ "model": "text-davinci-002-render-sha"
149
+ if not self.config.get("paid")
150
+ else "text-davinci-002-render-paid",
151
+ }
152
+ # new_conv = data["conversation_id"] is None
153
+ self.conversation_id_prev_queue.append(
154
+ data["conversation_id"],
155
+ ) # for rollback
156
+ self.parent_id_prev_queue.append(data["parent_message_id"])
157
+ response = self.session.post(
158
+ url=BASE_URL + "api/conversation",
159
+ data=json.dumps(data),
160
+ timeout=360,
161
+ stream=True,
162
+ )
163
+ self.__check_response(response)
164
+ for line in response.iter_lines():
165
+ line = str(line)[2:-1]
166
+ if line == "" or line is None:
167
+ continue
168
+ if "data: " in line:
169
+ line = line[6:]
170
+ if line == "[DONE]":
171
+ break
172
+
173
+ # Replace accidentally escaped double quotes
174
+ line = line.replace('\\"', '"')
175
+ line = line.replace("\\'", "'")
176
+ line = line.replace("\\\\", "\\")
177
+ # Try parse JSON
178
+ try:
179
+ line = json.loads(line)
180
+ except json.decoder.JSONDecodeError:
181
+ continue
182
+ if not self.__check_fields(line):
183
+ print("Field missing")
184
+ print(line)
185
+ continue
186
+ message = line["message"]["content"]["parts"][0]
187
+ conversation_id = line["conversation_id"]
188
+ parent_id = line["message"]["id"]
189
+ yield {
190
+ "message": message,
191
+ "conversation_id": conversation_id,
192
+ "parent_id": parent_id,
193
+ }
194
+ if parent_id is not None:
195
+ self.parent_id = parent_id
196
+ if conversation_id is not None:
197
+ self.conversation_id = conversation_id
198
+
199
+ def __check_fields(self, data: dict) -> bool:
200
+ try:
201
+ data["message"]["content"]
202
+ except TypeError:
203
+ return False
204
+ except KeyError:
205
+ return False
206
+ return True
207
+
208
+ def __check_response(self, response):
209
+ if response.status_code != 200:
210
+ print(response.text)
211
+ error = Error()
212
+ error.source = "OpenAI"
213
+ error.code = response.status_code
214
+ error.message = response.text
215
+ raise error
216
+
217
+ def get_conversations(self, offset=0, limit=20):
218
+ """
219
+ Get conversations
220
+ :param offset: Integer
221
+ :param limit: Integer
222
+ """
223
+ url = BASE_URL + f"api/conversations?offset={offset}&limit={limit}"
224
+ response = self.session.get(url)
225
+ self.__check_response(response)
226
+ data = json.loads(response.text)
227
+ return data["items"]
228
+
229
+ def get_msg_history(self, convo_id):
230
+ """
231
+ Get message history
232
+ :param id: UUID of conversation
233
+ """
234
+ url = BASE_URL + f"api/conversation/{convo_id}"
235
+ response = self.session.get(url)
236
+ self.__check_response(response)
237
+ data = json.loads(response.text)
238
+ return data
239
+
240
+ # def __gen_title(self, convo_id, message_id):
241
+ # """
242
+ # Generate title for conversation
243
+ # """
244
+ # url = BASE_URL + f"api/conversation/gen_title/{convo_id}"
245
+ # response = self.session.post(
246
+ # url,
247
+ # data=json.dumps(
248
+ # {"message_id": message_id, "model": "text-davinci-002-render"},
249
+ # ),
250
+ # )
251
+ # self.__check_response(response)
252
+
253
+ def change_title(self, convo_id, title):
254
+ """
255
+ Change title of conversation
256
+ :param id: UUID of conversation
257
+ :param title: String
258
+ """
259
+ url = BASE_URL + f"api/conversation/{convo_id}"
260
+ response = self.session.patch(url, data=f'{{"title": "{title}"}}')
261
+ self.__check_response(response)
262
+
263
+ def delete_conversation(self, convo_id):
264
+ """
265
+ Delete conversation
266
+ :param id: UUID of conversation
267
+ """
268
+ url = BASE_URL + f"api/conversation/{convo_id}"
269
+ response = self.session.patch(url, data='{"is_visible": false}')
270
+ self.__check_response(response)
271
+
272
+ def clear_conversations(self):
273
+ """
274
+ Delete all conversations
275
+ """
276
+ url = BASE_URL + "api/conversations"
277
+ response = self.session.patch(url, data='{"is_visible": false}')
278
+ self.__check_response(response)
279
+
280
+ def __map_conversations(self):
281
+ conversations = self.get_conversations()
282
+ histories = [self.get_msg_history(x["id"]) for x in conversations]
283
+ for x, y in zip(conversations, histories):
284
+ self.conversation_mapping[x["id"]] = y["current_node"]
285
+
286
+ def reset_chat(self) -> None:
287
+ """
288
+ Reset the conversation ID and parent ID.
289
+
290
+ :return: None
291
+ """
292
+ self.conversation_id = None
293
+ self.parent_id = str(uuid.uuid4())
294
+
295
+ def rollback_conversation(self, num=1) -> None:
296
+ """
297
+ Rollback the conversation.
298
+ :param num: The number of messages to rollback
299
+ :return: None
300
+ """
301
+ for _ in range(num):
302
+ self.conversation_id = self.conversation_id_prev_queue.pop()
303
+ self.parent_id = self.parent_id_prev_queue.pop()
304
+
305
+
306
+ def get_input(prompt):
307
+ """
308
+ Multiline input function.
309
+ """
310
+ # Display the prompt
311
+ print(prompt, end="")
312
+
313
+ # Initialize an empty list to store the input lines
314
+ lines = []
315
+
316
+ # Read lines of input until the user enters an empty line
317
+ while True:
318
+ line = input()
319
+ if line == "":
320
+ break
321
+ lines.append(line)
322
+
323
+ # Join the lines, separated by newlines, and store the result
324
+ user_input = "\n".join(lines)
325
+
326
+ # Return the input
327
+ return user_input
328
+
329
+
330
+ def configure():
331
+ """
332
+ Looks for a config file in the following locations:
333
+ """
334
+ config_files = ["config.json"]
335
+ xdg_config_home = getenv("XDG_CONFIG_HOME")
336
+ if xdg_config_home:
337
+ config_files.append(f"{xdg_config_home}/revChatGPT/config.json")
338
+ user_home = getenv("HOME")
339
+ if user_home:
340
+ config_files.append(f"{user_home}/.config/revChatGPT/config.json")
341
+
342
+ config_file = next((f for f in config_files if exists(f)), None)
343
+ if config_file:
344
+ with open(config_file, encoding="utf-8") as f:
345
+ config = json.load(f)
346
+ else:
347
+ print("No config file found.")
348
+ raise Exception("No config file found.")
349
+ return config
350
+
351
+
352
+ def main(config):
353
+ """
354
+ Main function for the chatGPT program.
355
+ """
356
+ print("Logging in...")
357
+ chatbot = Chatbot(config)
358
+ while True:
359
+ prompt = get_input("\nYou:\n")
360
+ if prompt.startswith("!"):
361
+ if prompt == "!help":
362
+ print(
363
+ """
364
+ !help - Show this message
365
+ !reset - Forget the current conversation
366
+ !config - Show the current configuration
367
+ !rollback x - Rollback the conversation (x being the number of messages to rollback)
368
+ !exit - Exit this program
369
+ """,
370
+ )
371
+ continue
372
+ elif prompt == "!reset":
373
+ chatbot.reset_chat()
374
+ print("Chat session successfully reset.")
375
+ continue
376
+ elif prompt == "!config":
377
+ print(json.dumps(chatbot.config, indent=4))
378
+ continue
379
+ elif prompt.startswith("!rollback"):
380
+ # Default to 1 rollback if no number is specified
381
+ try:
382
+ rollback = int(prompt.split(" ")[1])
383
+ except IndexError:
384
+ rollback = 1
385
+ chatbot.rollback_conversation(rollback)
386
+ print(f"Rolled back {rollback} messages.")
387
+ continue
388
+ elif prompt.startswith("!setconversation"):
389
+ try:
390
+ chatbot.config["conversation"] = prompt.split(" ")[1]
391
+ print("Conversation has been changed")
392
+ except IndexError:
393
+ print("Please include conversation UUID in command")
394
+ continue
395
+ elif prompt == "!exit":
396
+ break
397
+ print("Chatbot: ")
398
+ prev_text = ""
399
+ for data in chatbot.ask(
400
+ prompt,
401
+ conversation_id=chatbot.config.get("conversation"),
402
+ parent_id=chatbot.config.get("parent_id"),
403
+ ):
404
+ message = data["message"][len(prev_text) :]
405
+ print(message, end="", flush=True)
406
+ prev_text = data["message"]
407
+ print()
408
+ # print(message["message"])
409
+
410
+
411
+ if __name__ == "__main__":
412
+ print(
413
+ """
414
+ ChatGPT - A command-line interface to OpenAI's ChatGPT (https://chat.openai.com/chat)
415
+ Repo: github.com/acheong08/ChatGPT
416
+ """,
417
+ )
418
+ print("Type '!help' to show a full list of commands")
419
+ print("Press enter twice to submit your question.\n")
420
+ main(configure())
revChatGPT/V2.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Official API for ChatGPT
3
+ """
4
+ import asyncio
5
+ import json
6
+ import os
7
+ import sys
8
+
9
+ import httpx
10
+ import requests
11
+ import tiktoken
12
+ from OpenAIAuth.OpenAIAuth import OpenAIAuth
13
+
14
+ ENCODER = tiktoken.get_encoding("gpt2")
15
+
16
+
17
+ def get_max_tokens(prompt: str) -> int:
18
+ """
19
+ Get the max tokens for a prompt
20
+ """
21
+ return 4000 - len(ENCODER.encode(prompt))
22
+
23
+
24
+ class Message:
25
+ """
26
+ A single exchange between the user and the bot
27
+ """
28
+
29
+ def __init__(self, text: str, author: str) -> None:
30
+ self.text: str = text
31
+ self.author: str = author
32
+
33
+
34
+ class Conversation:
35
+ """
36
+ A single conversation
37
+ """
38
+
39
+ def __init__(self) -> None:
40
+ self.messages: list[Message] = []
41
+
42
+
43
+ CONVERSATION_BUFFER: int = int(os.environ.get("CONVERSATION_BUFFER") or 1500)
44
+
45
+
46
+ class Conversations:
47
+ """
48
+ Conversation handler
49
+ """
50
+
51
+ def __init__(self) -> None:
52
+ self.conversations: dict[str][Conversation] = {}
53
+
54
+ def add_message(self, message: Message, conversation_id: str) -> None:
55
+ """
56
+ Adds a message to a conversation
57
+ """
58
+ if conversation_id not in self.conversations:
59
+ self.conversations[conversation_id] = Conversation()
60
+ self.conversations[conversation_id].messages.append(message)
61
+
62
+ def get(self, conversation_id: str) -> str:
63
+ """
64
+ Builds a conversation string from a conversation id
65
+ """
66
+ if conversation_id not in self.conversations:
67
+ return ""
68
+ # Build conversation string from messages and check if it's too long
69
+ conversation = ""
70
+ for message in self.conversations[conversation_id].messages:
71
+ conversation += f"{message.author}: {message.text}<|im_sep|>\n\n"
72
+ if len(ENCODER.encode(conversation)) > 4000 - CONVERSATION_BUFFER:
73
+ self.purge_history(conversation_id)
74
+ return self.get(conversation_id)
75
+ return conversation
76
+
77
+ def purge_history(self, conversation_id: str, num: int = 1):
78
+ """
79
+ Remove oldest messages from a conversation
80
+ """
81
+ if conversation_id not in self.conversations:
82
+ return
83
+ self.conversations[conversation_id].messages = self.conversations[
84
+ conversation_id
85
+ ].messages[num:]
86
+
87
+ def rollback(self, conversation_id: str, num: int = 1):
88
+ """
89
+ Remove latest messages from a conversation
90
+ """
91
+ if conversation_id not in self.conversations:
92
+ return
93
+ self.conversations[conversation_id].messages = self.conversations[
94
+ conversation_id
95
+ ].messages[:-num]
96
+
97
+ def remove(self, conversation_id: str) -> None:
98
+ """
99
+ Removes a conversation
100
+ """
101
+ if conversation_id in self.conversations:
102
+ del self.conversations[conversation_id]
103
+
104
+
105
+ BASE_PROMPT = (
106
+ os.environ.get("BASE_PROMPT")
107
+ or """You are ChatGPT, a large language model by OpenAI. Respond conversationally\n\n\n"""
108
+ )
109
+
110
+ PROXY_URL = os.environ.get("PROXY_URL") or "https://chat.duti.tech"
111
+
112
+
113
+ class Chatbot:
114
+ """
115
+ Handles everything seamlessly
116
+ """
117
+
118
+ def __init__(
119
+ self,
120
+ email: str,
121
+ password: str,
122
+ paid: bool = False,
123
+ proxy=None,
124
+ insecure: bool = False,
125
+ session_token: str = None,
126
+ ) -> None:
127
+ self.proxy = proxy
128
+ self.email: str = email
129
+ self.password: str = password
130
+ self.session_token = session_token
131
+ self.insecure: bool = insecure
132
+ self.api_key: str
133
+ self.paid: bool = paid
134
+ self.conversations = Conversations()
135
+ self.login(email, password, proxy, insecure, session_token)
136
+
137
+ async def ask(self, prompt: str, conversation_id: str = None) -> dict:
138
+ """
139
+ Gets a response from the API
140
+ """
141
+ if conversation_id is None:
142
+ conversation_id = "default"
143
+ self.conversations.add_message(
144
+ Message(prompt, "User"),
145
+ conversation_id=conversation_id,
146
+ )
147
+ conversation: str = self.conversations.get(conversation_id)
148
+ # Build request body
149
+ body = self.__get_config()
150
+ body["prompt"] = BASE_PROMPT + conversation + "ChatGPT: "
151
+ body["max_tokens"] = get_max_tokens(conversation)
152
+ async with httpx.AsyncClient(proxies=self.proxy if self.proxy else None).stream(
153
+ method="POST",
154
+ url=PROXY_URL + "/completions",
155
+ data=json.dumps(body),
156
+ headers={"Authorization": f"Bearer {self.api_key}"},
157
+ timeout=1080,
158
+ ) as response:
159
+ full_result = ""
160
+ async for line in response.aiter_lines():
161
+ if response.status_code == 429:
162
+ print("error: " + "Too many requests")
163
+ raise Exception("Too many requests")
164
+ elif response.status_code == 523:
165
+ print(
166
+ "error: "
167
+ + "Origin is unreachable. Ensure that you are authenticated and are using the correct pricing model.",
168
+ )
169
+ raise Exception(
170
+ "Origin is unreachable. Ensure that you are authenticated and are using the correct pricing model.",
171
+ )
172
+ elif response.status_code == 503:
173
+ print("error: " + "OpenAI error!")
174
+ raise Exception("OpenAI error!")
175
+ elif response.status_code != 200:
176
+ print("error: " + "Unknown error")
177
+ raise Exception("Unknown error")
178
+ line = line.strip()
179
+ if line == "\n" or line == "":
180
+ continue
181
+ if line == "data: [DONE]":
182
+ break
183
+ try:
184
+ # Remove "data: " from the start of the line
185
+ data = json.loads(line[6:])
186
+ if data is None:
187
+ continue
188
+ full_result += data["choices"][0]["text"].replace("<|im_end|>", "")
189
+ if "choices" not in data:
190
+ continue
191
+ yield data
192
+ except json.JSONDecodeError:
193
+ continue
194
+ self.conversations.add_message(
195
+ Message(full_result, "ChatGPT"),
196
+ conversation_id=conversation_id,
197
+ )
198
+
199
+ def __get_config(self) -> dict:
200
+ return {
201
+ "temperature": float(os.environ.get("TEMPERATURE") or 0.5),
202
+ "top_p": float(os.environ.get("TOP_P") or 1),
203
+ "stop": ["<|im_end|>", "<|im_sep|>"],
204
+ "presence_penalty": float(os.environ.get("PRESENCE_PENALTY") or 1.0),
205
+ "paid": self.paid,
206
+ "stream": True,
207
+ }
208
+
209
+ def login(self, email, password, proxy, insecure, session_token) -> None:
210
+ """
211
+ Login to the API
212
+ """
213
+ if not insecure:
214
+ auth = OpenAIAuth(email_address=email, password=password, proxy=proxy)
215
+ if session_token:
216
+ auth.session_token = session_token
217
+ auth.get_access_token()
218
+ self.api_key = auth.access_token
219
+ if self.api_key is None:
220
+ self.session_token = None
221
+ self.login(email, password, proxy, insecure, None)
222
+ return
223
+ auth.begin()
224
+ self.session_token = auth.session_token
225
+ self.api_key = auth.access_token
226
+ else:
227
+ auth_request = requests.post(
228
+ PROXY_URL + "/auth",
229
+ json={"email": email, "password": password},
230
+ timeout=10,
231
+ )
232
+ self.api_key = auth_request.json()["accessToken"]
233
+
234
+
235
+ def get_input(prompt):
236
+ """
237
+ Multi-line input
238
+ """
239
+ # Display the prompt
240
+ print(prompt, end="")
241
+
242
+ # Initialize an empty list to store the input lines
243
+ lines = []
244
+
245
+ # Read lines of input until the user enters an empty line
246
+ while True:
247
+ line = input()
248
+ if line == "":
249
+ break
250
+ lines.append(line)
251
+
252
+ # Join the lines, separated by newlines, and store the result
253
+ user_input = "\n".join(lines)
254
+
255
+ # Return the input
256
+ return user_input
257
+
258
+
259
+ async def main():
260
+ """
261
+ Testing main function
262
+ """
263
+ import argparse
264
+
265
+ print(
266
+ """
267
+ ChatGPT - A command-line interface to OpenAI's ChatGPT (https://chat.openai.com/chat)
268
+ Repo: github.com/acheong08/ChatGPT
269
+ """,
270
+ )
271
+ parser = argparse.ArgumentParser()
272
+ parser.add_argument(
273
+ "-e",
274
+ "--email",
275
+ help="Your OpenAI email address",
276
+ required=False,
277
+ )
278
+ parser.add_argument(
279
+ "-p",
280
+ "--password",
281
+ help="Your OpenAI password",
282
+ required=False,
283
+ )
284
+ parser.add_argument(
285
+ "--paid",
286
+ help="Use the paid API",
287
+ action="store_true",
288
+ )
289
+ parser.add_argument(
290
+ "--proxy",
291
+ help="Use a proxy",
292
+ required=False,
293
+ type=str,
294
+ default=None,
295
+ )
296
+ parser.add_argument(
297
+ "--insecure-auth",
298
+ help="Use an insecure authentication method to bypass OpenAI's geo-blocking",
299
+ action="store_true",
300
+ )
301
+ parser.add_argument(
302
+ "--session_token",
303
+ help="Alternative to email and password authentication. Use this if you have Google/Microsoft account.",
304
+ required=False,
305
+ )
306
+ args = parser.parse_args()
307
+
308
+ if (args.email is None or args.password is None) and args.session_token is None:
309
+ print("error: " + "Please provide your email and password")
310
+ return
311
+ print("Logging in...")
312
+ chatbot = Chatbot(
313
+ args.email,
314
+ args.password,
315
+ paid=args.paid,
316
+ proxy=args.proxy,
317
+ insecure=args.insecure_auth,
318
+ session_token=args.session_token,
319
+ )
320
+ print("Logged in\n")
321
+
322
+ print("Type '!help' to show a full list of commands")
323
+ print("Press enter twice to submit your question.\n")
324
+
325
+ def commands(command: str) -> bool:
326
+ if command == "!help":
327
+ print(
328
+ """
329
+ !help - Show this help message
330
+ !reset - Clear the current conversation
331
+ !rollback <int> - Remove the latest <int> messages from the conversation
332
+ !exit - Exit the program
333
+ """,
334
+ )
335
+ elif command == "!reset":
336
+ chatbot.conversations.remove("default")
337
+ print("Conversation cleared")
338
+ elif command.startswith("!rollback"):
339
+ try:
340
+ num = int(command.split(" ")[1])
341
+ chatbot.conversations.rollback("default", num)
342
+ print(f"Removed {num} messages from the conversation")
343
+ except IndexError:
344
+ print("Please specify the number of messages to remove")
345
+ except ValueError:
346
+ print("Please specify a valid number of messages to remove")
347
+ elif command == "!exit":
348
+ print("Exiting...")
349
+ sys.exit(0)
350
+ else:
351
+ return False
352
+ return True
353
+
354
+ try:
355
+ while True:
356
+ prompt = get_input("\nYou:\n")
357
+ if prompt.startswith("!"):
358
+ if commands(prompt):
359
+ continue
360
+ print("ChatGPT:")
361
+ async for line in chatbot.ask(prompt=prompt):
362
+ result = line["choices"][0]["text"].replace("<|im_end|>", "")
363
+ print(result, end="")
364
+ sys.stdout.flush()
365
+ print()
366
+ except KeyboardInterrupt:
367
+ print("Exiting...")
368
+ sys.exit(0)
369
+
370
+
371
+ if __name__ == "__main__":
372
+ asyncio.run(main())
revChatGPT/__init__.py ADDED
File without changes
revChatGPT/__pycache__/Unofficial.cpython-39.pyc ADDED
Binary file (18.9 kB). View file
 
revChatGPT/__pycache__/V1.cpython-39.pyc ADDED
Binary file (10.4 kB). View file
 
revChatGPT/__pycache__/V2.cpython-39.pyc ADDED
Binary file (9.5 kB). View file
 
revChatGPT/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (169 Bytes). View file