返回提交历史
Added
.gitignore
+16
-0
Modified
quora/__init__.py
+350
-238
Modified
quora/api.py
+152
-106
Modified
quora/mail.py
+42
-42
Modified
requirements.txt
+3
-1
XFEstudio/gpt4free
updated quora module, added selenium to get cookie
a37920b2
代码差异
5 个文件
+563
-387
@@ -0,0 +1,16 @@
1
# Default ignored files
2
/shelf/
3
/workspace.xml
4
# Editor-based HTTP Client requests
5
/httpRequests/
6
# Datasource local storage ignored files
7
/dataSources/
8
/dataSources.local.xml
9
10
.idea/
11
12
*/__pycache__/
13
14
*.log
15
16
cookie.json
@@ -55,10 +55,7 @@ def load_queries():
55
55
56
56
57
57
def generate_payload(query_name, variables):
58
return {
59
"query": queries[query_name],
60
"variables": variables
61
}
58
return {"query": queries[query_name], "variables": variables}
62
59
63
60
64
61
def request_with_retries(method, *args, **kwargs):
@@ -69,7 +66,8 @@ def request_with_retries(method, *args, **kwargs):
69
66
if r.status_code == 200:
70
67
return r
71
68
logger.warn(
72
f"Server returned a status code of {r.status_code} while downloading {url}. Retrying ({i+1}/{attempts})...")
69
f"Server returned a status code of {r.status_code} while downloading {url}. Retrying ({i+1}/{attempts})..."
70
)
73
71
74
72
raise RuntimeError(f"Failed to download {url} too many times.")
75
73
@@ -84,15 +82,13 @@ class Client:
84
82
self.proxy = proxy
85
83
self.session = requests.Session()
86
84
self.adapter = requests.adapters.HTTPAdapter(
87
pool_connections=100, pool_maxsize=100)
85
pool_connections=100, pool_maxsize=100
86
)
88
87
self.session.mount("http://", self.adapter)
89
88
self.session.mount("https://", self.adapter)
90
89
91
90
if proxy:
92
self.session.proxies = {
93
"http": self.proxy,
94
"https": self.proxy
95
}
91
self.session.proxies = {"http": self.proxy, "https": self.proxy}
96
92
logger.info(f"Proxy enabled: {self.proxy}")
97
93
98
94
self.active_messages = {}
@@ -124,11 +120,11 @@ class Client:
124
120
self.subscribe()
125
121
126
122
def extract_formkey(self, html):
127
script_regex = r'<script>if\(.+\)throw new Error;(.+)</script>'
123
script_regex = r"<script>if\(.+\)throw new Error;(.+)</script>"
128
124
script_text = re.search(script_regex, html).group(1)
129
125
key_regex = r'var .="([0-9a-f]+)",'
130
126
key_text = re.search(key_regex, script_text).group(1)
131
cipher_regex = r'.\[(\d+)\]=.\[(\d+)\]'
127
cipher_regex = r".\[(\d+)\]=.\[(\d+)\]"
132
128
cipher_pairs = re.findall(cipher_regex, script_text)
133
129
134
130
formkey_list = [""] * len(cipher_pairs)
@@ -143,7 +139,9 @@ class Client:
143
139
logger.info("Downloading next_data...")
144
140
145
141
r = request_with_retries(self.session.get, self.home_url)
146
json_regex = r'<script id="__NEXT_DATA__" type="application\/json">(.+?)</script>'
142
json_regex = (
143
r'<script id="__NEXT_DATA__" type="application\/json">(.+?)</script>'
144
)
147
145
json_text = re.search(json_regex, r.text).group(1)
148
146
next_data = json.loads(json_text)
149
147
@@ -181,8 +179,7 @@ class Client:
181
179
bots[chat_data["defaultBotObject"]["nickname"]] = chat_data
182
180
183
181
for bot in bot_list:
184
thread = threading.Thread(
185
target=get_bot_thread, args=(bot,), daemon=True)
182
thread = threading.Thread(target=get_bot_thread, args=(bot,), daemon=True)
186
183
threads.append(thread)
187
184
188
185
for thread in threads:
@@ -216,50 +213,59 @@ class Client:
216
213
if channel is None:
217
214
channel = self.channel
218
215
query = f'?min_seq={channel["minSeq"]}&channel={channel["channel"]}&hash={channel["channelHash"]}'
219
return f'wss://{self.ws_domain}.tch.{channel["baseHost"]}/up/{channel["boxName"]}/updates'+query
216
return (
217
f'wss://{self.ws_domain}.tch.{channel["baseHost"]}/up/{channel["boxName"]}/updates'
218
+ query
219
)
220
220
221
221
def send_query(self, query_name, variables):
222
222
for i in range(20):
223
223
json_data = generate_payload(query_name, variables)
224
224
payload = json.dumps(json_data, separators=(",", ":"))
225
225
226
base_string = payload + \
227
self.gql_headers["poe-formkey"] + "WpuLMiXEKKE98j56k"
226
base_string = (
227
payload + self.gql_headers["poe-formkey"] + "WpuLMiXEKKE98j56k"
228
)
228
229
229
230
headers = {
230
231
"content-type": "application/json",
231
"poe-tag-id": hashlib.md5(base_string.encode()).hexdigest()
232
"poe-tag-id": hashlib.md5(base_string.encode()).hexdigest(),
232
233
}
233
234
headers = {**self.gql_headers, **headers}
234
235
235
236
r = request_with_retries(
236
self.session.post, self.gql_url, data=payload, headers=headers)
237
self.session.post, self.gql_url, data=payload, headers=headers
238
)
237
239
238
240
data = r.json()
239
241
if data["data"] == None:
240
242
logger.warn(
241
f'{query_name} returned an error: {data["errors"][0]["message"]} | Retrying ({i+1}/20)')
243
f'{query_name} returned an error: {data["errors"][0]["message"]} | Retrying ({i+1}/20)'
244
)
242
245
time.sleep(2)
243
246
continue
244
247
245
248
return r.json()
246
249
247
raise RuntimeError(f'{query_name} failed too many times.')
250
raise RuntimeError(f"{query_name} failed too many times.")
248
251
249
252
def subscribe(self):
250
253
logger.info("Subscribing to mutations")
251
result = self.send_query("SubscriptionsMutation", {
252
"subscriptions": [
253
{
254
"subscriptionName": "messageAdded",
255
"query": queries["MessageAddedSubscription"]
256
},
257
{
258
"subscriptionName": "viewerStateUpdated",
259
"query": queries["ViewerStateUpdatedSubscription"]
260
}
261
]
262
})
254
result = self.send_query(
255
"SubscriptionsMutation",
256
{
257
"subscriptions": [
258
{
259
"subscriptionName": "messageAdded",
260
"query": queries["MessageAddedSubscription"],
261
},
262
{
263
"subscriptionName": "viewerStateUpdated",
264
"query": queries["ViewerStateUpdatedSubscription"],
265
},
266
]
267
},
268
)
263
269
264
270
def ws_run_thread(self):
265
271
kwargs = {}
@@ -268,7 +274,7 @@ class Client:
268
274
kwargs = {
269
275
"proxy_type": proxy_parsed.scheme,
270
276
"http_proxy_host": proxy_parsed.hostname,
271
"http_proxy_port": proxy_parsed.port
277
"http_proxy_port": proxy_parsed.port,
272
278
}
273
279
274
280
self.ws.run_forever(**kwargs)
@@ -281,7 +287,7 @@ class Client:
281
287
on_message=self.on_message,
282
288
on_open=self.on_ws_connect,
283
289
on_error=self.on_ws_error,
284
on_close=self.on_ws_close
290
on_close=self.on_ws_close,
285
291
)
286
292
t = threading.Thread(target=self.ws_run_thread, daemon=True)
287
293
t.start()
@@ -299,7 +305,8 @@ class Client:
299
305
def on_ws_close(self, ws, close_status_code, close_message):
300
306
self.ws_connected = False
301
307
logger.warn(
302
f"Websocket closed with status {close_status_code}: {close_message}")
308
f"Websocket closed with status {close_status_code}: {close_message}"
309
)
303
310
304
311
def on_ws_error(self, ws, error):
305
312
self.disconnect_ws()
@@ -326,7 +333,11 @@ class Client:
326
333
return
327
334
328
335
# indicate that the response id is tied to the human message id
329
elif key != "pending" and value == None and message["state"] != "complete":
336
elif (
337
key != "pending"
338
and value == None
339
and message["state"] != "complete"
340
):
330
341
self.active_messages[key] = message["messageId"]
331
342
self.message_queues[key].put(message)
332
343
return
@@ -352,13 +363,16 @@ class Client:
352
363
self.setup_connection()
353
364
self.connect_ws()
354
365
355
message_data = self.send_query("SendMessageMutation", {
356
"bot": chatbot,
357
"query": message,
358
"chatId": self.bots[chatbot]["chatId"],
359
"source": None,
360
"withChatBreak": with_chat_break
361
})
366
message_data = self.send_query(
367
"SendMessageMutation",
368
{
369
"bot": chatbot,
370
"query": message,
371
"chatId": self.bots[chatbot]["chatId"],
372
"source": None,
373
"withChatBreak": with_chat_break,
374
},
375
)
362
376
del self.active_messages["pending"]
363
377
364
378
if not message_data["data"]["messageEdgeCreate"]["message"]:
@@ -368,7 +382,8 @@ class Client:
368
382
human_message_id = human_message["node"]["messageId"]
369
383
except TypeError:
370
384
raise RuntimeError(
371
f"An unknown error occurred. Raw response data: {message_data}")
385
f"An unknown error occurred. Raw response data: {message_data}"
386
)
372
387
373
388
# indicate that the current message is waiting for a response
374
389
self.active_messages[human_message_id] = None
@@ -378,8 +393,7 @@ class Client:
378
393
message_id = None
379
394
while True:
380
395
try:
381
message = self.message_queues[human_message_id].get(
382
timeout=timeout)
396
message = self.message_queues[human_message_id].get(timeout=timeout)
383
397
except queue.Empty:
384
398
del self.active_messages[human_message_id]
385
399
del self.message_queues[human_message_id]
@@ -393,7 +407,7 @@ class Client:
393
407
continue
394
408
395
409
# update info about response
396
message["text_new"] = message["text"][len(last_text):]
410
message["text_new"] = message["text"][len(last_text) :]
397
411
last_text = message["text"]
398
412
message_id = message["messageId"]
399
413
@@ -404,9 +418,9 @@ class Client:
404
418
405
419
def send_chat_break(self, chatbot):
406
420
logger.info(f"Sending chat break to {chatbot}")
407
result = self.send_query("AddMessageBreakMutation", {
408
"chatId": self.bots[chatbot]["chatId"]
409
})
421
result = self.send_query(
422
"AddMessageBreakMutation", {"chatId": self.bots[chatbot]["chatId"]}
423
)
410
424
return result["data"]["messageBreakCreate"]["message"]
411
425
412
426
def get_message_history(self, chatbot, count=25, cursor=None):
@@ -423,23 +437,24 @@ class Client:
423
437
424
438
cursor = str(cursor)
425
439
if count > 50:
426
messages = self.get_message_history(
427
chatbot, count=50, cursor=cursor) + messages
440
messages = (
441
self.get_message_history(chatbot, count=50, cursor=cursor) + messages
442
)
428
443
while count > 0:
429
444
count -= 50
430
445
new_cursor = messages[0]["cursor"]
431
446
new_messages = self.get_message_history(
432
chatbot, min(50, count), cursor=new_cursor)
447
chatbot, min(50, count), cursor=new_cursor
448
)
433
449
messages = new_messages + messages
434
450
return messages
435
451
elif count <= 0:
436
452
return messages
437
453
438
result = self.send_query("ChatListPaginationQuery", {
439
"count": count,
440
"cursor": cursor,
441
"id": self.bots[chatbot]["id"]
442
})
454
result = self.send_query(
455
"ChatListPaginationQuery",
456
{"count": count, "cursor": cursor, "id": self.bots[chatbot]["id"]},
457
)
443
458
query_messages = result["data"]["node"]["messagesConnection"]["edges"]
444
459
messages = query_messages + messages
445
460
return messages
@@ -449,9 +464,7 @@ class Client:
449
464
if not type(message_ids) is list:
450
465
message_ids = [int(message_ids)]
451
466
452
result = self.send_query("DeleteMessageMutation", {
453
"messageIds": message_ids
454
})
467
result = self.send_query("DeleteMessageMutation", {"messageIds": message_ids})
455
468
456
469
def purge_conversation(self, chatbot, count=-1):
457
470
logger.info(f"Purging messages from {chatbot}")
@@ -471,60 +484,93 @@ class Client:
471
484
last_messages = self.get_message_history(chatbot, count=50)[::-1]
472
485
logger.info(f"No more messages left to delete.")
473
486
474
def create_bot(self, handle, prompt="", base_model="chinchilla", description="",
475
intro_message="", api_key=None, api_bot=False, api_url=None,
476
prompt_public=True, pfp_url=None, linkification=False,
477
markdown_rendering=True, suggested_replies=False, private=False):
478
result = self.send_query("PoeBotCreateMutation", {
479
"model": base_model,
480
"handle": handle,
481
"prompt": prompt,
482
"isPromptPublic": prompt_public,
483
"introduction": intro_message,
484
"description": description,
485
"profilePictureUrl": pfp_url,
486
"apiUrl": api_url,
487
"apiKey": api_key,
488
"isApiBot": api_bot,
489
"hasLinkification": linkification,
490
"hasMarkdownRendering": markdown_rendering,
491
"hasSuggestedReplies": suggested_replies,
492
"isPrivateBot": private
493
})
487
def create_bot(
488
self,
489
handle,
490
prompt="",
491
base_model="chinchilla",
492
description="",
493
intro_message="",
494
api_key=None,
495
api_bot=False,
496
api_url=None,
497
prompt_public=True,
498
pfp_url=None,
499
linkification=False,
500
markdown_rendering=True,
501
suggested_replies=False,
502
private=False,
503
):
504
result = self.send_query(
505
"PoeBotCreateMutation",
506
{
507
"model": base_model,
508
"handle": handle,
509
"prompt": prompt,
510
"isPromptPublic": prompt_public,
511
"introduction": intro_message,
512
"description": description,
513
"profilePictureUrl": pfp_url,
514
"apiUrl": api_url,
515
"apiKey": api_key,
516
"isApiBot": api_bot,
517
"hasLinkification": linkification,
518
"hasMarkdownRendering": markdown_rendering,
519
"hasSuggestedReplies": suggested_replies,
520
"isPrivateBot": private,
521
},
522
)
494
523
495
524
data = result["data"]["poeBotCreate"]
496
525
if data["status"] != "success":
497
526
raise RuntimeError(
498
f"Poe returned an error while trying to create a bot: {data['status']}")
527
f"Poe returned an error while trying to create a bot: {data['status']}"
528
)
499
529
self.get_bots()
500
530
return data
501
531
502
def edit_bot(self, bot_id, handle, prompt="", base_model="chinchilla", description="",
503
intro_message="", api_key=None, api_url=None, private=False,
504
prompt_public=True, pfp_url=None, linkification=False,
505
markdown_rendering=True, suggested_replies=False):
506
507
result = self.send_query("PoeBotEditMutation", {
508
"baseBot": base_model,
509
"botId": bot_id,
510
"handle": handle,
511
"prompt": prompt,
512
"isPromptPublic": prompt_public,
513
"introduction": intro_message,
514
"description": description,
515
"profilePictureUrl": pfp_url,
516
"apiUrl": api_url,
517
"apiKey": api_key,
518
"hasLinkification": linkification,
519
"hasMarkdownRendering": markdown_rendering,
520
"hasSuggestedReplies": suggested_replies,
521
"isPrivateBot": private
522
})
532
def edit_bot(
533
self,
534
bot_id,
535
handle,
536
prompt="",
537
base_model="chinchilla",
538
description="",
539
intro_message="",
540
api_key=None,
541
api_url=None,
542
private=False,
543
prompt_public=True,
544
pfp_url=None,
545
linkification=False,
546
markdown_rendering=True,
547
suggested_replies=False,
548
):
549
result = self.send_query(
550
"PoeBotEditMutation",
551
{
552
"baseBot": base_model,
553
"botId": bot_id,
554
"handle": handle,
555
"prompt": prompt,
556
"isPromptPublic": prompt_public,
557
"introduction": intro_message,
558
"description": description,
559
"profilePictureUrl": pfp_url,
560
"apiUrl": api_url,
561
"apiKey": api_key,
562
"hasLinkification": linkification,
563
"hasMarkdownRendering": markdown_rendering,
564
"hasSuggestedReplies": suggested_replies,
565
"isPrivateBot": private,
566
},
567
)
523
568
524
569
data = result["data"]["poeBotEdit"]
525
570
if data["status"] != "success":
526
571
raise RuntimeError(
527
f"Poe returned an error while trying to edit a bot: {data['status']}")
572
f"Poe returned an error while trying to edit a bot: {data['status']}"
573
)
528
574
self.get_bots()
529
575
return data
530
576
@@ -1,66 +1,66 @@
1
from json import loads
2
from re import findall
3
from time import sleep
4
5
from fake_useragent import UserAgent
1
6
from requests import Session
2
from time import sleep
3
from re import search, findall
4
from json import loads
7
5
8
6
9
class Emailnator:
7
10
def __init__(self) -> None:
8
11
self.client = Session()
9
self.client.get('https://www.emailnator.com/', timeout=6)
12
self.client.get("https://www.emailnator.com/", timeout=6)
10
13
self.cookies = self.client.cookies.get_dict()
11
14
12
15
self.client.headers = {
13
'authority' : 'www.emailnator.com',
14
'origin' : 'https://www.emailnator.com',
15
'referer' : 'https://www.emailnator.com/',
16
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36 Edg/101.0.1722.39',
17
'x-xsrf-token' : self.client.cookies.get("XSRF-TOKEN")[:-3]+"=",
16
"authority": "www.emailnator.com",
17
"origin": "https://www.emailnator.com",
18
"referer": "https://www.emailnator.com/",
19
"user-agent": UserAgent().random,
20
"x-xsrf-token": self.client.cookies.get("XSRF-TOKEN")[:-3] + "=",
18
21
}
19
22
20
23
self.email = None
21
24
22
25
def get_mail(self):
23
response = self.client.post('https://www.emailnator.com/generate-email',json = {
24
'email': [
25
'domain',
26
'plusGmail',
27
'dotGmail',
28
]
29
})
30
26
response = self.client.post(
27
"https://www.emailnator.com/generate-email",
28
json={
29
"email": [
30
"domain",
31
"plusGmail",
32
"dotGmail",
33
]
34
},
35
)
36
31
37
self.email = loads(response.text)["email"][0]
32
38
return self.email
33
39
34
40
def get_message(self):
35
41
print("waiting for code...")
36
42
37
43
while True:
38
44
sleep(2)
39
mail_token = self.client.post('https://www.emailnator.com/message-list',
40
json = {'email': self.email})
41
45
mail_token = self.client.post(
46
"https://www.emailnator.com/message-list", json={"email": self.email}
47
)
48
42
49
mail_token = loads(mail_token.text)["messageData"]
43
50
44
51
if len(mail_token) == 2:
45
52
print(mail_token[1]["messageID"])
46
53
break
47
48
mail_context = self.client.post('https://www.emailnator.com/message-list', json = {
49
'email' : self.email,
50
'messageID': mail_token[1]["messageID"],
51
})
52
53
return mail_context.text
54
55
# mail_client = Emailnator()
56
# mail_adress = mail_client.get_mail()
57
54
58
# print(mail_adress)
55
mail_context = self.client.post(
56
"https://www.emailnator.com/message-list",
57
json={
58
"email": self.email,
59
"messageID": mail_token[1]["messageID"],
60
},
61
)
59
62
60
# mail_content = mail_client.get_message()
61
62
# print(mail_content)
63
64
# code = findall(r';">(\d{6,7})</div>', mail_content)[0]
65
# print(code)
63
return mail_context.text
66
64
65
def get_verification_code(self):
66
return findall(r';">(\d{6,7})</div>', self.get_message())[0]
@@ -4,4 +4,6 @@ tls-client
4
4
pypasser
5
5
names
6
6
colorama
7
curl_cffi
7
curl_cffi
8
selenium
9
fake-useragent