t.me/xtekky commited on
Commit
8c0f5e9
1 Parent(s): 6c64e45

updated quora(poe) (token creation patched atm)

Browse files
quora/__init__.py CHANGED
@@ -8,6 +8,9 @@ from pathlib import Path
8
  from random import choice, choices, randint
9
  from string import ascii_letters, digits
10
  from urllib import parse
 
 
 
11
 
12
  class PoeResponse:
13
 
@@ -91,6 +94,7 @@ class Model:
91
  "sec-ch-ua" : "\"Chromium\";v=\"112\", \"Google Chrome\";v=\"112\", \"Not:A-Brand\";v=\"99\"",
92
  "sec-ch-ua-mobile" : "?0",
93
  "sec-ch-ua-platform": "\"macOS\"",
 
94
  "sec-fetch-site" : "same-origin",
95
  "sec-fetch-mode" : "cors",
96
  "sec-fetch-dest" : "empty",
@@ -155,6 +159,7 @@ class Account:
155
  "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",
156
  "sec-fetch-site" : "same-origin",
157
  "sec-fetch-mode" : "navigate",
 
158
  "sec-fetch-user" : "?1",
159
  "sec-fetch-dest" : "document",
160
  "accept-encoding" : "gzip, deflate, br",
@@ -167,17 +172,21 @@ class Account:
167
 
168
  client.headers["poe-formkey"] = next_data['props']['formkey']
169
  client.headers["poe-tchannel"] = client.get('https://poe.com/api/settings').json()['tchannelData']['channel']
170
-
171
- payload = {
172
- "queryName": "MainSignupLoginSection_sendVerificationCodeMutation_Mutation",
173
- "variables": {
174
- "emailAddress": mail_address,
175
- "phoneNumber" : None
 
176
  },
177
- "query": "mutation MainSignupLoginSection_sendVerificationCodeMutation_Mutation(\n $emailAddress: String\n $phoneNumber: String\n) {\n sendVerificationCode(verificationReason: login, emailAddress: $emailAddress, phoneNumber: $phoneNumber) {\n status\n errorMessage\n }\n}\n"
178
- }
 
 
 
179
 
180
- response = client.post('https://poe.com/api/gql_POST', json=payload)
181
  if 'Bad Request' in response.text:
182
  if logging: print('bad request, retrying...' , response.json())
183
  Account.create(proxy = proxy, logging = logging)
@@ -197,7 +206,7 @@ class Account:
197
 
198
  if logging: print('code', mail_token)
199
 
200
- payload = {
201
  "queryName": "SignupOrLoginWithCodeSection_signupWithVerificationCodeMutation_Mutation",
202
  "variables": {
203
  "verificationCode" : mail_token,
@@ -205,9 +214,12 @@ class Account:
205
  "phoneNumber" : None
206
  },
207
  "query": "mutation SignupOrLoginWithCodeSection_signupWithVerificationCodeMutation_Mutation(\n $verificationCode: String!\n $emailAddress: String\n $phoneNumber: String\n) {\n signupWithVerificationCode(verificationCode: $verificationCode, emailAddress: $emailAddress, phoneNumber: $phoneNumber) {\n status\n errorMessage\n }\n}\n"
208
- }
 
 
 
209
 
210
- response = client.post('https://poe.com/api/gql_POST', json = payload)
211
  if logging: print('verify_code', response.json())
212
 
213
  token = parse.unquote(client.cookies.get_dict()['p-b'])
@@ -216,11 +228,17 @@ class Account:
216
  f.write(f'{token}\n')
217
 
218
  if enable_bot_creation:
219
- client.post("https://poe.com/api/gql_POST", json = {
 
220
  "queryName": "UserProfileConfigurePreviewModal_markMultiplayerNuxCompleted_Mutation",
221
  "variables": {},
222
  "query": "mutation UserProfileConfigurePreviewModal_markMultiplayerNuxCompleted_Mutation {\n markMultiplayerNuxCompleted {\n viewer {\n hasCompletedMultiplayerNux\n id\n }\n }\n}\n"
223
- })
 
 
 
 
 
224
 
225
  return token
226
 
@@ -305,4 +323,4 @@ class Completion:
305
  'completion_tokens' : len(chunk["text"]),
306
  'total_tokens' : len(prompt) + len(chunk["text"])
307
  }
308
- })
 
8
  from random import choice, choices, randint
9
  from string import ascii_letters, digits
10
  from urllib import parse
11
+ from os import urandom
12
+ from hashlib import md5
13
+ from json import dumps
14
 
15
  class PoeResponse:
16
 
 
94
  "sec-ch-ua" : "\"Chromium\";v=\"112\", \"Google Chrome\";v=\"112\", \"Not:A-Brand\";v=\"99\"",
95
  "sec-ch-ua-mobile" : "?0",
96
  "sec-ch-ua-platform": "\"macOS\"",
97
+ "content-type" : "application/json",
98
  "sec-fetch-site" : "same-origin",
99
  "sec-fetch-mode" : "cors",
100
  "sec-fetch-dest" : "empty",
 
159
  "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",
160
  "sec-fetch-site" : "same-origin",
161
  "sec-fetch-mode" : "navigate",
162
+ "content-type" : "application/json",
163
  "sec-fetch-user" : "?1",
164
  "sec-fetch-dest" : "document",
165
  "accept-encoding" : "gzip, deflate, br",
 
172
 
173
  client.headers["poe-formkey"] = next_data['props']['formkey']
174
  client.headers["poe-tchannel"] = client.get('https://poe.com/api/settings').json()['tchannelData']['channel']
175
+
176
+ payload = dumps(separators = (',', ':'), obj = {
177
+ 'queryName': 'MainSignupLoginSection_sendVerificationCodeMutation_Mutation',
178
+ 'variables': {
179
+ 'emailAddress': mail_address,
180
+ 'phoneNumber': None,
181
+ 'recaptchaToken': None,
182
  },
183
+ 'query': 'mutation MainSignupLoginSection_sendVerificationCodeMutation_Mutation(\n $emailAddress: String\n $phoneNumber: String\n $recaptchaToken: String\n) {\n sendVerificationCode(verificationReason: login, emailAddress: $emailAddress, phoneNumber: $phoneNumber, recaptchaToken: $recaptchaToken) {\n status\n errorMessage\n }\n}\n',
184
+ })
185
+
186
+ base_string = payload + client.headers["poe-formkey"] + 'WpuLMiXEKKE98j56k'
187
+ client.headers["poe-tag-id"] = md5(base_string.encode()).hexdigest()
188
 
189
+ response = client.post('https://poe.com/api/gql_POST', data=payload)
190
  if 'Bad Request' in response.text:
191
  if logging: print('bad request, retrying...' , response.json())
192
  Account.create(proxy = proxy, logging = logging)
 
206
 
207
  if logging: print('code', mail_token)
208
 
209
+ payload = dumps(separators = (',', ':'), obj={
210
  "queryName": "SignupOrLoginWithCodeSection_signupWithVerificationCodeMutation_Mutation",
211
  "variables": {
212
  "verificationCode" : mail_token,
 
214
  "phoneNumber" : None
215
  },
216
  "query": "mutation SignupOrLoginWithCodeSection_signupWithVerificationCodeMutation_Mutation(\n $verificationCode: String!\n $emailAddress: String\n $phoneNumber: String\n) {\n signupWithVerificationCode(verificationCode: $verificationCode, emailAddress: $emailAddress, phoneNumber: $phoneNumber) {\n status\n errorMessage\n }\n}\n"
217
+ })
218
+
219
+ base_string = payload + client.headers["poe-formkey"] + 'WpuLMiXEKKE98j56k'
220
+ client.headers["poe-tag-id"] = md5(base_string.encode()).hexdigest()
221
 
222
+ response = client.post('https://poe.com/api/gql_POST', data = payload)
223
  if logging: print('verify_code', response.json())
224
 
225
  token = parse.unquote(client.cookies.get_dict()['p-b'])
 
228
  f.write(f'{token}\n')
229
 
230
  if enable_bot_creation:
231
+
232
+ payload = {
233
  "queryName": "UserProfileConfigurePreviewModal_markMultiplayerNuxCompleted_Mutation",
234
  "variables": {},
235
  "query": "mutation UserProfileConfigurePreviewModal_markMultiplayerNuxCompleted_Mutation {\n markMultiplayerNuxCompleted {\n viewer {\n hasCompletedMultiplayerNux\n id\n }\n }\n}\n"
236
+ }
237
+
238
+ base_string = dumps(payload, separators = (',', ':')) + client.headers["poe-formkey"] + 'WpuLMiXEKKE98j56k'
239
+ client.headers["poe-tag-id"] = md5(base_string.encode()).hexdigest()
240
+
241
+ client.post("https://poe.com/api/gql_POST", json = payload)
242
 
243
  return token
244
 
 
323
  'completion_tokens' : len(chunk["text"]),
324
  'total_tokens' : len(prompt) + len(chunk["text"])
325
  }
326
+ })
quora/api.py CHANGED
@@ -1,19 +1,3 @@
1
- # ading2210/poe-api: a reverse engineered Python API wrapepr for Quora's Poe
2
- # Copyright (C) 2023 ading2210
3
-
4
- # This program is free software: you can redistribute it and/or modify
5
- # it under the terms of the GNU General Public License as published by
6
- # the Free Software Foundation, either version 3 of the License, or
7
- # (at your option) any later version.
8
-
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
-
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
-
17
  import requests
18
  import re
19
  import json
@@ -71,14 +55,6 @@ class Client:
71
  home_url = "https://poe.com"
72
  settings_url = "https://poe.com/api/settings"
73
 
74
- formkey = ""
75
- next_data = {}
76
- bots = {}
77
- active_messages = {}
78
- message_queues = {}
79
- ws = None
80
- ws_connected = False
81
-
82
  def __init__(self, token, proxy=None):
83
  self.proxy = proxy
84
  self.session = requests.Session()
@@ -90,6 +66,9 @@ class Client:
90
  }
91
  logger.info(f"Proxy enabled: {self.proxy}")
92
 
 
 
 
93
  self.session.cookies.set("p-b", token, domain="poe.com")
94
  self.headers = {
95
  "User-Agent": user_agent,
@@ -99,10 +78,10 @@ class Client:
99
  self.ws_domain = f"tch{random.randint(1, 1e6)}"
100
 
101
  self.session.headers.update(self.headers)
102
- self.next_data = self.get_next_data()
103
  self.channel = self.get_channel_data()
104
  self.connect_ws()
105
- self.bots = self.get_bots()
106
  self.bot_names = self.get_bot_names()
107
 
108
  self.gql_headers = {
@@ -112,7 +91,7 @@ class Client:
112
  self.gql_headers = {**self.gql_headers, **self.headers}
113
  self.subscribe()
114
 
115
- def get_next_data(self):
116
  logger.info("Downloading next_data...")
117
 
118
  r = request_with_retries(self.session.get, self.home_url)
@@ -120,8 +99,9 @@ class Client:
120
  json_text = re.search(json_regex, r.text).group(1)
121
  next_data = json.loads(json_text)
122
 
123
- self.formkey = next_data["props"]["formkey"]
124
- self.viewer = next_data["props"]["pageProps"]["payload"]["viewer"]
 
125
 
126
  return next_data
127
 
@@ -134,17 +114,23 @@ class Client:
134
  chat_data = r.json()["pageProps"]["payload"]["chatOfBotDisplayName"]
135
  return chat_data
136
 
137
- def get_bots(self):
138
- viewer = self.next_data["props"]["pageProps"]["payload"]["viewer"]
139
- if not "availableBots" in viewer:
140
- raise RuntimeError("Invalid token.")
141
- bot_list = viewer["availableBots"]
 
 
 
 
142
 
143
  bots = {}
144
  for bot in bot_list:
145
- chat_data = self.get_bot(bot["displayName"].lower())
146
  bots[chat_data["defaultBotObject"]["nickname"]] = chat_data
147
 
 
 
148
  return bots
149
 
150
  def get_bot_names(self):
@@ -170,9 +156,20 @@ class Client:
170
 
171
  def send_query(self, query_name, variables):
172
  for i in range(20):
173
- payload = generate_payload(query_name, variables)
 
 
 
 
 
 
 
 
 
 
174
  r = request_with_retries(
175
- self.session.post, self.gql_url, json=payload, headers=self.gql_headers)
 
176
  data = r.json()
177
  if data["data"] == None:
178
  logger.warn(
@@ -212,6 +209,7 @@ class Client:
212
  self.ws.run_forever(**kwargs)
213
 
214
  def connect_ws(self):
 
215
  self.ws = websocket.WebSocketApp(
216
  self.get_websocket_url(),
217
  header={"User-Agent": user_agent},
@@ -233,9 +231,10 @@ class Client:
233
  def on_ws_connect(self, ws):
234
  self.ws_connected = True
235
 
236
- def on_ws_close(self, ws, close_status_code):
237
  self.ws_connected = False
238
- logger.warn(f"Websocket closed with status {close_status_code}")
 
239
 
240
  def on_ws_error(self, ws, error):
241
  self.disconnect_ws()
@@ -345,22 +344,27 @@ class Client:
345
  def get_message_history(self, chatbot, count=25, cursor=None):
346
  logger.info(f"Downloading {count} messages from {chatbot}")
347
 
 
348
  if cursor == None:
349
  chat_data = self.get_bot(self.bot_names[chatbot])
350
  if not chat_data["messagesConnection"]["edges"]:
351
  return []
352
- cursor = chat_data["messagesConnection"]["edges"][-1]["cursor"]
 
 
353
 
354
  cursor = str(cursor)
355
  if count > 50:
356
  messages = self.get_message_history(
357
- chatbot, count=50, cursor=cursor)
358
  while count > 0:
 
359
  new_cursor = messages[0]["cursor"]
360
  new_messages = self.get_message_history(
361
  chatbot, min(50, count), cursor=new_cursor)
362
  messages = new_messages + messages
363
- count -= 50
 
364
  return messages
365
 
366
  result = self.send_query("ChatListPaginationQuery", {
@@ -368,7 +372,9 @@ class Client:
368
  "cursor": cursor,
369
  "id": self.bots[chatbot]["id"]
370
  })
371
- return result["data"]["node"]["messagesConnection"]["edges"]
 
 
372
 
373
  def delete_message(self, message_ids):
374
  logger.info(f"Deleting messages: {message_ids}")
@@ -398,4 +404,4 @@ class Client:
398
  logger.info(f"No more messages left to delete.")
399
 
400
 
401
- load_queries()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import requests
2
  import re
3
  import json
 
55
  home_url = "https://poe.com"
56
  settings_url = "https://poe.com/api/settings"
57
 
 
 
 
 
 
 
 
 
58
  def __init__(self, token, proxy=None):
59
  self.proxy = proxy
60
  self.session = requests.Session()
 
66
  }
67
  logger.info(f"Proxy enabled: {self.proxy}")
68
 
69
+ self.active_messages = {}
70
+ self.message_queues = {}
71
+
72
  self.session.cookies.set("p-b", token, domain="poe.com")
73
  self.headers = {
74
  "User-Agent": user_agent,
 
78
  self.ws_domain = f"tch{random.randint(1, 1e6)}"
79
 
80
  self.session.headers.update(self.headers)
81
+ self.next_data = self.get_next_data(overwrite_vars=True)
82
  self.channel = self.get_channel_data()
83
  self.connect_ws()
84
+ self.bots = self.get_bots(download_next_data=False)
85
  self.bot_names = self.get_bot_names()
86
 
87
  self.gql_headers = {
 
91
  self.gql_headers = {**self.gql_headers, **self.headers}
92
  self.subscribe()
93
 
94
+ def get_next_data(self, overwrite_vars=False):
95
  logger.info("Downloading next_data...")
96
 
97
  r = request_with_retries(self.session.get, self.home_url)
 
99
  json_text = re.search(json_regex, r.text).group(1)
100
  next_data = json.loads(json_text)
101
 
102
+ if overwrite_vars:
103
+ self.formkey = next_data["props"]["formkey"]
104
+ self.viewer = next_data["props"]["pageProps"]["payload"]["viewer"]
105
 
106
  return next_data
107
 
 
114
  chat_data = r.json()["pageProps"]["payload"]["chatOfBotDisplayName"]
115
  return chat_data
116
 
117
+ def get_bots(self, download_next_data=True):
118
+ if download_next_data:
119
+ next_data = self.get_next_data()
120
+ else:
121
+ next_data = self.next_data
122
+
123
+ if not "availableBots" in self.viewer:
124
+ raise RuntimeError("Invalid token or no bots are available.")
125
+ bot_list = self.viewer["availableBots"]
126
 
127
  bots = {}
128
  for bot in bot_list:
129
+ chat_data = self.get_bot(bot["displayName"])
130
  bots[chat_data["defaultBotObject"]["nickname"]] = chat_data
131
 
132
+ self.bots = bots
133
+ self.bot_names = self.get_bot_names()
134
  return bots
135
 
136
  def get_bot_names(self):
 
156
 
157
  def send_query(self, query_name, variables):
158
  for i in range(20):
159
+ json_data = generate_payload(query_name, variables)
160
+ payload = json.dumps(json_data, separators=(',', ':'))
161
+
162
+ base_string = payload + self.gql_headers['poe-formkey'] + 'WpuLMiXEKKE98j56k'
163
+
164
+ from hashlib import md5
165
+ headers = self.gql_headers |{
166
+ "content-type": "application/json",
167
+ "poe-tag-id": md5(base_string.encode()).hexdigest()
168
+ }
169
+
170
  r = request_with_retries(
171
+ self.session.post, self.gql_url, data=payload, headers=headers)
172
+
173
  data = r.json()
174
  if data["data"] == None:
175
  logger.warn(
 
209
  self.ws.run_forever(**kwargs)
210
 
211
  def connect_ws(self):
212
+ self.ws_connected = False
213
  self.ws = websocket.WebSocketApp(
214
  self.get_websocket_url(),
215
  header={"User-Agent": user_agent},
 
231
  def on_ws_connect(self, ws):
232
  self.ws_connected = True
233
 
234
+ def on_ws_close(self, ws, close_status_code, close_message):
235
  self.ws_connected = False
236
+ logger.warn(
237
+ f"Websocket closed with status {close_status_code}: {close_message}")
238
 
239
  def on_ws_error(self, ws, error):
240
  self.disconnect_ws()
 
344
  def get_message_history(self, chatbot, count=25, cursor=None):
345
  logger.info(f"Downloading {count} messages from {chatbot}")
346
 
347
+ messages = []
348
  if cursor == None:
349
  chat_data = self.get_bot(self.bot_names[chatbot])
350
  if not chat_data["messagesConnection"]["edges"]:
351
  return []
352
+ messages = chat_data["messagesConnection"]["edges"][:count]
353
+ cursor = chat_data["messagesConnection"]["pageInfo"]["startCursor"]
354
+ count -= len(messages)
355
 
356
  cursor = str(cursor)
357
  if count > 50:
358
  messages = self.get_message_history(
359
+ chatbot, count=50, cursor=cursor) + messages
360
  while count > 0:
361
+ count -= 50
362
  new_cursor = messages[0]["cursor"]
363
  new_messages = self.get_message_history(
364
  chatbot, min(50, count), cursor=new_cursor)
365
  messages = new_messages + messages
366
+ return messages
367
+ elif count <= 0:
368
  return messages
369
 
370
  result = self.send_query("ChatListPaginationQuery", {
 
372
  "cursor": cursor,
373
  "id": self.bots[chatbot]["id"]
374
  })
375
+ query_messages = result["data"]["node"]["messagesConnection"]["edges"]
376
+ messages = query_messages + messages
377
+ return messages
378
 
379
  def delete_message(self, message_ids):
380
  logger.info(f"Deleting messages: {message_ids}")
 
404
  logger.info(f"No more messages left to delete.")
405
 
406
 
407
+ load_queries()
quora/cookies.txt CHANGED
@@ -13,3 +13,5 @@ UP2wQVds17VFHh6IfCQFrA==
13
  FNgKEpc2r-XqWe0rHBfYpg==
14
  juCAh6kB0sUpXHvKik2woA==
15
  nBvuNYRLaE4xE4HuzBPiIQ==
 
 
 
13
  FNgKEpc2r-XqWe0rHBfYpg==
14
  juCAh6kB0sUpXHvKik2woA==
15
  nBvuNYRLaE4xE4HuzBPiIQ==
16
+ oyae3iClomSrk6RJywZ4iw==
17
+ 1Z27Ul8BTdNOhncT5H6wdg==
testing/poe_test.py CHANGED
@@ -6,7 +6,7 @@ print('token', token)
6
 
7
  sleep(2)
8
 
9
- for response in quora.StreamingCompletion.create(model = 'gpt-4',
10
  prompt = 'hello world',
11
  token = token):
12
 
 
6
 
7
  sleep(2)
8
 
9
+ for response in quora.StreamingCompletion.create(model = 'gpt-3.5-turbo',
10
  prompt = 'hello world',
11
  token = token):
12
 
{t3nsor → testing}/t3nsor_test.py RENAMED
File without changes