t.me/xtekky commited on
Commit
1396122
1 Parent(s): 81e0330

poe api gpt-4

Browse files
Files changed (36) hide show
  1. README.md +38 -0
  2. poe/__init__.py +214 -0
  3. poe/api.py +316 -0
  4. poe/cookies.txt +1 -0
  5. poe/graphql/AddHumanMessageMutation.graphql +52 -0
  6. poe/graphql/AddMessageBreakMutation.graphql +17 -0
  7. poe/graphql/AutoSubscriptionMutation.graphql +7 -0
  8. poe/graphql/BioFragment.graphql +8 -0
  9. poe/graphql/ChatAddedSubscription.graphql +5 -0
  10. poe/graphql/ChatFragment.graphql +6 -0
  11. poe/graphql/ChatListPaginationQuery.graphql +316 -0
  12. poe/graphql/ChatPaginationQuery.graphql +26 -0
  13. poe/graphql/ChatViewQuery.graphql +8 -0
  14. poe/graphql/DeleteHumanMessagesMutation.graphql +7 -0
  15. poe/graphql/DeleteMessageMutation.graphql +7 -0
  16. poe/graphql/HandleFragment.graphql +8 -0
  17. poe/graphql/LoginWithVerificationCodeMutation.graphql +13 -0
  18. poe/graphql/MessageAddedSubscription.graphql +100 -0
  19. poe/graphql/MessageDeletedSubscription.graphql +6 -0
  20. poe/graphql/MessageFragment.graphql +13 -0
  21. poe/graphql/MessageRemoveVoteMutation.graphql +7 -0
  22. poe/graphql/MessageSetVoteMutation.graphql +7 -0
  23. poe/graphql/SendVerificationCodeForLoginMutation.graphql +12 -0
  24. poe/graphql/ShareMessagesMutation.graphql +9 -0
  25. poe/graphql/SignupWithVerificationCodeMutation.graphql +13 -0
  26. poe/graphql/StaleChatUpdateMutation.graphql +7 -0
  27. poe/graphql/SubscriptionsMutation.graphql +9 -0
  28. poe/graphql/SummarizePlainPostQuery.graphql +3 -0
  29. poe/graphql/SummarizeQuotePostQuery.graphql +3 -0
  30. poe/graphql/SummarizeSharePostQuery.graphql +3 -0
  31. poe/graphql/UserSnippetFragment.graphql +14 -0
  32. poe/graphql/ViewerInfoQuery.graphql +21 -0
  33. poe/graphql/ViewerStateFragment.graphql +30 -0
  34. poe/graphql/ViewerStateUpdatedSubscription.graphql +43 -0
  35. poe/graphql/__init__.py +0 -0
  36. poe/mail.py +62 -0
README.md CHANGED
@@ -1,5 +1,43 @@
1
  working on it...
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  `t3nsor` (use like openai pypi package)
4
 
5
  Import t3nsor:
 
1
  working on it...
2
 
3
+ `poe` (use like openai pypi package) - gpt-4
4
+
5
+ Import poe:
6
+
7
+ ```python
8
+ import poe
9
+
10
+ # poe.Account.create
11
+ # poe.Completion.create
12
+ # poe.StreamCompletion.create
13
+ ```
14
+
15
+ Create Token (3-6s)
16
+ ```python
17
+ token = poe.Account.create(logging = True)
18
+ print('token', token)
19
+ ```
20
+
21
+ Streaming Response
22
+ ```python
23
+
24
+ for response in poe.StreamingCompletion.create(model = 'gpt-4',
25
+ prompt = 'hello world',
26
+ token = token):
27
+
28
+ print(response.completion.choices[0].text, end="", flush=True)
29
+ ```
30
+
31
+ Normal Response:
32
+ ```python
33
+
34
+ response = poe.Completion.create(model = 'gpt-4',
35
+ prompt = 'hello world',
36
+ token = token)
37
+
38
+ print(response.completion.choices[0].text)
39
+ ```
40
+
41
  `t3nsor` (use like openai pypi package)
42
 
43
  Import t3nsor:
poe/__init__.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from poe.api import Client as PoeClient
2
+ from poe.mail import Mail
3
+ from tls_client import Session
4
+ from re import search, findall
5
+ from json import loads
6
+ from time import sleep, time
7
+ from pathlib import Path
8
+ from random import choice
9
+ from urllib import parse
10
+
11
+ class PoeResponse:
12
+
13
+ class Completion:
14
+
15
+ class Choices:
16
+ def __init__(self, choice: dict) -> None:
17
+ self.text = choice['text']
18
+ self.content = self.text.encode()
19
+ self.index = choice['index']
20
+ self.logprobs = choice['logprobs']
21
+ self.finish_reason = choice['finish_reason']
22
+
23
+ def __repr__(self) -> str:
24
+ return f'''<__main__.APIResponse.Completion.Choices(\n text = {self.text.encode()},\n index = {self.index},\n logprobs = {self.logprobs},\n finish_reason = {self.finish_reason})object at 0x1337>'''
25
+
26
+ def __init__(self, choices: dict) -> None:
27
+ self.choices = [self.Choices(choice) for choice in choices]
28
+
29
+ class Usage:
30
+ def __init__(self, usage_dict: dict) -> None:
31
+ self.prompt_tokens = usage_dict['prompt_tokens']
32
+ self.completion_tokens = usage_dict['completion_tokens']
33
+ self.total_tokens = usage_dict['total_tokens']
34
+
35
+ def __repr__(self):
36
+ return f'''<__main__.APIResponse.Usage(\n prompt_tokens = {self.prompt_tokens},\n completion_tokens = {self.completion_tokens},\n total_tokens = {self.total_tokens})object at 0x1337>'''
37
+
38
+ def __init__(self, response_dict: dict) -> None:
39
+
40
+ self.response_dict = response_dict
41
+ self.id = response_dict['id']
42
+ self.object = response_dict['object']
43
+ self.created = response_dict['created']
44
+ self.model = response_dict['model']
45
+ self.completion = self.Completion(response_dict['choices'])
46
+ self.usage = self.Usage(response_dict['usage'])
47
+
48
+ def json(self) -> dict:
49
+ return self.response_dict
50
+
51
+
52
+ class Account:
53
+ def create(proxy: None or str = None, logging: bool = False):
54
+
55
+ client = Session(client_identifier = "chrome110")
56
+ client.proxies = {
57
+ 'http': f'http://{proxy}',
58
+ 'https': f'http://{proxy}'} if proxy else None
59
+
60
+ mail = Mail(client.proxies)
61
+ mail_token = None
62
+ mail_address = mail.get_mail()
63
+
64
+ if logging: print('email', mail_address)
65
+
66
+ client.headers = {
67
+ "host" : "poe.com",
68
+ "connection" : "keep-alive",
69
+ "cache-control" : "max-age=0",
70
+ "sec-ch-ua" : "\"Microsoft Edge\";v=\"111\", \"Not(A:Brand\";v=\"8\", \"Chromium\";v=\"111\"",
71
+ "sec-ch-ua-mobile" : "?0",
72
+ "sec-ch-ua-platform": "\"macOS\"",
73
+ "user-agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.54",
74
+ "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",
75
+ "sec-fetch-site" : "same-origin",
76
+ "sec-fetch-mode" : "navigate",
77
+ "sec-fetch-user" : "?1",
78
+ "sec-fetch-dest" : "document",
79
+ "accept-encoding" : "gzip, deflate, br",
80
+ "accept-language" : "en-GB,en;q=0.9,en-US;q=0.8",
81
+ "upgrade-insecure-requests": "1",
82
+ }
83
+
84
+ init = client.get('https://poe.com/login')
85
+ next_data = loads(search(r'json">(.+?)</script>', init.text).group(1))
86
+
87
+ client.headers["poe-formkey"] = next_data['props']['formkey']
88
+ client.headers["poe-tchannel"] = client.get('https://poe.com/api/settings').json()['tchannelData']['channel']
89
+
90
+ payload = {
91
+ "queryName": "MainSignupLoginSection_sendVerificationCodeMutation_Mutation",
92
+ "variables": {
93
+ "emailAddress": mail_address,
94
+ "phoneNumber" : None
95
+ },
96
+ "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"
97
+ }
98
+
99
+ response = client.post('https://poe.com/api/gql_POST', json=payload)
100
+ if 'Bad Request' in response.text:
101
+ if logging: print('bad request, retrying...' , response.json())
102
+ Account.create(proxy = proxy, logging = logging)
103
+
104
+ if logging: print('send_code' ,response.json())
105
+
106
+ while True:
107
+ sleep(1)
108
+ inbox = mail.fetch_inbox()
109
+
110
+ for _ in inbox:
111
+ content = mail.get_message(_["id"])
112
+ mail_token = findall(r';">(\d{6,7})</div>', content['html'][0])[0]
113
+
114
+ if mail_token:
115
+ break
116
+
117
+ if logging: print('code', mail_token)
118
+
119
+ payload = {
120
+ "queryName": "SignupOrLoginWithCodeSection_signupWithVerificationCodeMutation_Mutation",
121
+ "variables": {
122
+ "verificationCode" : mail_token,
123
+ "emailAddress" : mail_address,
124
+ "phoneNumber" : None
125
+ },
126
+ "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"
127
+ }
128
+
129
+ response = client.post('https://poe.com/api/gql_POST', json = payload)
130
+ if logging: print('verify_code', response.json())
131
+
132
+ token = parse.unquote(client.cookies.get_dict()['p-b'])
133
+
134
+ with open(Path(__file__).resolve().parent / 'cookies.txt', 'a') as f:
135
+ f.write(f'{token}\n')
136
+
137
+ return token
138
+
139
+ def get():
140
+ cookies = open(Path(__file__).resolve().parent / 'cookies.txt', 'r').read().splitlines()
141
+ return choice(cookies)
142
+
143
+ class StreamingCompletion:
144
+ def create(
145
+ model : str = 'gpt-4',
146
+ prompt: str = 'hello world',
147
+ token : str = ''):
148
+
149
+ models = {
150
+ 'sage' : 'capybara',
151
+ 'gpt-4' : 'beaver',
152
+ 'claude+': 'a2_2',
153
+ 'claude' : 'a2',
154
+ 'gpt-3.5': 'chinchilla'
155
+ }
156
+
157
+ client = PoeClient(token)
158
+
159
+ for chunk in client.send_message(models[model], prompt):
160
+
161
+ yield PoeResponse({
162
+ 'id' : chunk["messageId"],
163
+ 'object' : 'text_completion',
164
+ 'created': chunk['creationTime'],
165
+ 'model' : models[model],
166
+ 'choices': [{
167
+ 'text' : chunk["text_new"],
168
+ 'index' : 0,
169
+ 'logprobs' : None,
170
+ 'finish_reason' : 'stop'
171
+ }],
172
+ 'usage': {
173
+ 'prompt_tokens' : len(prompt),
174
+ 'completion_tokens' : len(chunk["text_new"]),
175
+ 'total_tokens' : len(prompt) + len(chunk["text_new"])
176
+ }
177
+ })
178
+
179
+ class Completion:
180
+ def create(
181
+ model : str = 'gpt-4',
182
+ prompt: str = 'hello world',
183
+ token : str = ''):
184
+
185
+ models = {
186
+ 'sage' : 'capybara',
187
+ 'gpt-4' : 'beaver',
188
+ 'claude+': 'a2_2',
189
+ 'claude' : 'a2',
190
+ 'gpt-3.5': 'chinchilla'
191
+ }
192
+
193
+ client = PoeClient(token)
194
+
195
+ for chunk in client.send_message(models[model], prompt):
196
+ pass
197
+
198
+ return PoeResponse({
199
+ 'id' : chunk["messageId"],
200
+ 'object' : 'text_completion',
201
+ 'created': chunk['creationTime'],
202
+ 'model' : models[model],
203
+ 'choices': [{
204
+ 'text' : chunk["text"],
205
+ 'index' : 0,
206
+ 'logprobs' : None,
207
+ 'finish_reason' : 'stop'
208
+ }],
209
+ 'usage': {
210
+ 'prompt_tokens' : len(prompt),
211
+ 'completion_tokens' : len(chunk["text"]),
212
+ 'total_tokens' : len(prompt) + len(chunk["text"])
213
+ }
214
+ })
poe/api.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
20
+ import random
21
+ import logging
22
+ import time
23
+ import queue
24
+ import threading
25
+ import websocket
26
+ from pathlib import Path
27
+ from urllib.parse import urlparse
28
+
29
+ parent_path = Path(__file__).resolve().parent
30
+ queries_path = parent_path / "graphql"
31
+ queries = {}
32
+
33
+ logging.basicConfig()
34
+ logger = logging.getLogger()
35
+
36
+ user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"
37
+
38
+
39
+ def load_queries():
40
+ for path in queries_path.iterdir():
41
+ if path.suffix != ".graphql":
42
+ continue
43
+ with open(path) as f:
44
+ queries[path.stem] = f.read()
45
+
46
+
47
+ def generate_payload(query_name, variables):
48
+ return {
49
+ "query": queries[query_name],
50
+ "variables": variables
51
+ }
52
+
53
+
54
+ def request_with_retries(method, *args, **kwargs):
55
+ attempts = kwargs.get("attempts") or 10
56
+ url = args[0]
57
+ for i in range(attempts):
58
+ r = method(*args, **kwargs)
59
+ if r.status_code == 200:
60
+ return r
61
+ logger.warn(
62
+ f"Server returned a status code of {r.status_code} while downloading {url}. Retrying ({i+1}/{attempts})...")
63
+
64
+ raise RuntimeError(f"Failed to download {url} too many times.")
65
+
66
+
67
+ class Client:
68
+ gql_url = "https://poe.com/api/gql_POST"
69
+ gql_recv_url = "https://poe.com/api/receive_POST"
70
+ home_url = "https://poe.com"
71
+ settings_url = "https://poe.com/api/settings"
72
+
73
+ formkey = ""
74
+ next_data = {}
75
+ bots = {}
76
+ active_messages = {}
77
+ message_queues = {}
78
+ ws = None
79
+ ws_connected = False
80
+
81
+ def __init__(self, token, proxy=None):
82
+ self.proxy = proxy
83
+ self.session = requests.Session()
84
+
85
+ if proxy:
86
+ self.session.proxies = {
87
+ "http": self.proxy,
88
+ "https": self.proxy
89
+ }
90
+ logger.info(f"Proxy enabled: {self.proxy}")
91
+
92
+ self.session.cookies.set("p-b", token, domain="poe.com")
93
+ self.headers = {
94
+ "User-Agent": user_agent,
95
+ "Referrer": "https://poe.com/",
96
+ "Origin": "https://poe.com",
97
+ }
98
+ self.ws_domain = f"tch{random.randint(1, 1e6)}"
99
+
100
+ self.session.headers.update(self.headers)
101
+ self.next_data = self.get_next_data()
102
+ self.channel = self.get_channel_data()
103
+ self.connect_ws()
104
+ self.bots = self.get_bots()
105
+ self.bot_names = self.get_bot_names()
106
+
107
+ self.gql_headers = {
108
+ "poe-formkey": self.formkey,
109
+ "poe-tchannel": self.channel["channel"],
110
+ }
111
+ self.gql_headers = {**self.gql_headers, **self.headers}
112
+ self.subscribe()
113
+
114
+ def get_next_data(self):
115
+ logger.info("Downloading next_data...")
116
+
117
+ r = request_with_retries(self.session.get, self.home_url)
118
+ json_regex = r'<script id="__NEXT_DATA__" type="application\/json">(.+?)</script>'
119
+ json_text = re.search(json_regex, r.text).group(1)
120
+ next_data = json.loads(json_text)
121
+
122
+ self.formkey = next_data["props"]["formkey"]
123
+ self.viewer = next_data["props"]["pageProps"]["payload"]["viewer"]
124
+
125
+ return next_data
126
+
127
+ def get_bots(self):
128
+ viewer = self.next_data["props"]["pageProps"]["payload"]["viewer"]
129
+ if not "availableBots" in viewer:
130
+ raise RuntimeError("Invalid token.")
131
+ bot_list = viewer["availableBots"]
132
+
133
+ bots = {}
134
+ for bot in bot_list:
135
+ url = f'https://poe.com/_next/data/{self.next_data["buildId"]}/{bot["displayName"].lower()}.json'
136
+ logger.info("Downloading "+url)
137
+
138
+ r = request_with_retries(self.session.get, url)
139
+
140
+ chat_data = r.json()[
141
+ "pageProps"]["payload"]["chatOfBotDisplayName"]
142
+ bots[chat_data["defaultBotObject"]["nickname"]] = chat_data
143
+
144
+ return bots
145
+
146
+ def get_bot_names(self):
147
+ bot_names = {}
148
+ for bot_nickname in self.bots:
149
+ bot_obj = self.bots[bot_nickname]["defaultBotObject"]
150
+ bot_names[bot_nickname] = bot_obj["displayName"]
151
+ return bot_names
152
+
153
+ def get_channel_data(self, channel=None):
154
+ logger.info("Downloading channel data...")
155
+ r = request_with_retries(self.session.get, self.settings_url)
156
+ data = r.json()
157
+
158
+ self.formkey = data["formkey"]
159
+ return data["tchannelData"]
160
+
161
+ def get_websocket_url(self, channel=None):
162
+ if channel is None:
163
+ channel = self.channel
164
+ query = f'?min_seq={channel["minSeq"]}&channel={channel["channel"]}&hash={channel["channelHash"]}'
165
+ return f'wss://{self.ws_domain}.tch.{channel["baseHost"]}/up/{channel["boxName"]}/updates'+query
166
+
167
+ def send_query(self, query_name, variables):
168
+ # print(f'send_query: {query_name} {variables}')
169
+
170
+ for i in range(20):
171
+ payload = generate_payload(query_name, variables)
172
+ # print(f'query_payload: {query_name} {variables}')
173
+ r = request_with_retries(
174
+ self.session.post, self.gql_url, json=payload, headers=self.gql_headers)
175
+ data = r.json()
176
+ if data["data"] == None:
177
+ logger.warn(
178
+ f'{query_name} returned an error: {data["errors"][0]["message"]} | Retrying ({i+1}/20)')
179
+ time.sleep(2)
180
+ continue
181
+
182
+ return r.json()
183
+
184
+ raise RuntimeError(f'{query_name} failed too many times.')
185
+
186
+ def subscribe(self):
187
+ logger.info("Subscribing to mutations")
188
+ result = self.send_query("SubscriptionsMutation", {
189
+ "subscriptions": [
190
+ {
191
+ "subscriptionName": "messageAdded",
192
+ "query": queries["MessageAddedSubscription"]
193
+ },
194
+ {
195
+ "subscriptionName": "viewerStateUpdated",
196
+ "query": queries["ViewerStateUpdatedSubscription"]
197
+ }
198
+ ]
199
+ })
200
+
201
+ def ws_run_thread(self):
202
+ kwargs = {}
203
+ if self.proxy:
204
+ proxy_parsed = urlparse(self.proxy)
205
+ kwargs = {
206
+ "proxy_type": proxy_parsed.scheme,
207
+ "http_proxy_host": proxy_parsed.hostname,
208
+ "http_proxy_port": proxy_parsed.port
209
+ }
210
+
211
+ self.ws.run_forever(**kwargs)
212
+
213
+ def connect_ws(self):
214
+ self.ws = websocket.WebSocketApp(
215
+ self.get_websocket_url(),
216
+ header={"User-Agent": user_agent},
217
+ on_message=self.on_message,
218
+ on_open=self.on_ws_connect,
219
+ on_error=self.on_ws_error
220
+ )
221
+ t = threading.Thread(target=self.ws_run_thread, daemon=True)
222
+ t.start()
223
+ while not self.ws_connected:
224
+ time.sleep(0.01)
225
+
226
+ def disconnect_ws(self):
227
+ if self.ws:
228
+ self.ws.close()
229
+ self.ws_connected = False
230
+
231
+ def on_ws_connect(self, ws):
232
+ self.ws_connected = True
233
+
234
+ def on_ws_error(self, ws, error):
235
+ logger.warn(f"Websocket returned error: {error}")
236
+ self.disconnect_ws()
237
+ self.connect_ws()
238
+
239
+ def on_message(self, ws, msg):
240
+ data = json.loads(msg)
241
+ message = json.loads(data["messages"][0])[
242
+ "payload"]["data"]["messageAdded"]
243
+
244
+ copied_dict = self.active_messages.copy()
245
+ for key, value in copied_dict.items():
246
+ # add the message to the appropriate queue
247
+ if value == message["messageId"] and key in self.message_queues:
248
+ self.message_queues[key].put(message)
249
+ return
250
+
251
+ # indicate that the response id is tied to the human message id
252
+ elif key != "pending" and value == None and message["state"] != "complete":
253
+ self.active_messages[key] = message["messageId"]
254
+ self.message_queues[key].put(message)
255
+
256
+ def send_message(self, chatbot, message, with_chat_break=False, timeout=20):
257
+ # if there is another active message, wait until it has finished sending
258
+ while None in self.active_messages.values():
259
+ time.sleep(0.01)
260
+
261
+ # None indicates that a message is still in progress
262
+ self.active_messages["pending"] = None
263
+
264
+ logger.info(f"Sending message to {chatbot}: {message}")
265
+
266
+ message_data = self.send_query("AddHumanMessageMutation", {
267
+ "bot": chatbot,
268
+ "query": message,
269
+ "chatId": self.bots[chatbot]["chatId"],
270
+ "source": None,
271
+ "withChatBreak": with_chat_break
272
+ })
273
+ del self.active_messages["pending"]
274
+
275
+ if not message_data["data"]["messageCreateWithStatus"]["messageLimit"]["canSend"]:
276
+ raise RuntimeError(f"Daily limit reached for {chatbot}.")
277
+ try:
278
+ human_message = message_data["data"]["messageCreateWithStatus"]
279
+ human_message_id = human_message["message"]["messageId"]
280
+ except TypeError:
281
+ raise RuntimeError(
282
+ f"An unknown error occured. Raw response data: {message_data}")
283
+
284
+ # indicate that the current message is waiting for a response
285
+ self.active_messages[human_message_id] = None
286
+ self.message_queues[human_message_id] = queue.Queue()
287
+
288
+ last_text = ""
289
+ message_id = None
290
+ while True:
291
+ try:
292
+ message = self.message_queues[human_message_id].get(
293
+ timeout=timeout)
294
+ except queue.Empty:
295
+ del self.active_messages[human_message_id]
296
+ del self.message_queues[human_message_id]
297
+ raise RuntimeError("Response timed out.")
298
+
299
+ # only break when the message is marked as complete
300
+ if message["state"] == "complete":
301
+ if last_text and message["messageId"] == message_id:
302
+ break
303
+ else:
304
+ continue
305
+
306
+ # update info about response
307
+ message["text_new"] = message["text"][len(last_text):]
308
+ last_text = message["text"]
309
+ message_id = message["messageId"]
310
+
311
+ yield message
312
+
313
+ del self.active_messages[human_message_id]
314
+ del self.message_queues[human_message_id]
315
+
316
+ load_queries()
poe/cookies.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ SmPiNXZI9hBTuf3viz74PA==
poe/graphql/AddHumanMessageMutation.graphql ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mutation AddHumanMessageMutation(
2
+ $chatId: BigInt!
3
+ $bot: String!
4
+ $query: String!
5
+ $source: MessageSource
6
+ $withChatBreak: Boolean! = false
7
+ ) {
8
+ messageCreateWithStatus(
9
+ chatId: $chatId
10
+ bot: $bot
11
+ query: $query
12
+ source: $source
13
+ withChatBreak: $withChatBreak
14
+ ) {
15
+ message {
16
+ id
17
+ __typename
18
+ messageId
19
+ text
20
+ linkifiedText
21
+ authorNickname
22
+ state
23
+ vote
24
+ voteReason
25
+ creationTime
26
+ suggestedReplies
27
+ chat {
28
+ id
29
+ shouldShowDisclaimer
30
+ }
31
+ }
32
+ messageLimit{
33
+ canSend
34
+ numMessagesRemaining
35
+ resetTime
36
+ shouldShowReminder
37
+ }
38
+ chatBreak {
39
+ id
40
+ __typename
41
+ messageId
42
+ text
43
+ linkifiedText
44
+ authorNickname
45
+ state
46
+ vote
47
+ voteReason
48
+ creationTime
49
+ suggestedReplies
50
+ }
51
+ }
52
+ }
poe/graphql/AddMessageBreakMutation.graphql ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mutation AddMessageBreakMutation($chatId: BigInt!) {
2
+ messageBreakCreate(chatId: $chatId) {
3
+ message {
4
+ id
5
+ __typename
6
+ messageId
7
+ text
8
+ linkifiedText
9
+ authorNickname
10
+ state
11
+ vote
12
+ voteReason
13
+ creationTime
14
+ suggestedReplies
15
+ }
16
+ }
17
+ }
poe/graphql/AutoSubscriptionMutation.graphql ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mutation AutoSubscriptionMutation($subscriptions: [AutoSubscriptionQuery!]!) {
2
+ autoSubscribe(subscriptions: $subscriptions) {
3
+ viewer {
4
+ id
5
+ }
6
+ }
7
+ }
poe/graphql/BioFragment.graphql ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fragment BioFragment on Viewer {
2
+ id
3
+ poeUser {
4
+ id
5
+ uid
6
+ bio
7
+ }
8
+ }
poe/graphql/ChatAddedSubscription.graphql ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ subscription ChatAddedSubscription {
2
+ chatAdded {
3
+ ...ChatFragment
4
+ }
5
+ }
poe/graphql/ChatFragment.graphql ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fragment ChatFragment on Chat {
2
+ id
3
+ chatId
4
+ defaultBotNickname
5
+ shouldShowDisclaimer
6
+ }
poe/graphql/ChatListPaginationQuery.graphql ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ query ChatListPaginationQuery(
2
+ $count: Int = 5
3
+ $cursor: String
4
+ $id: ID!
5
+ ) {
6
+ node(id: $id) {
7
+ __typename
8
+ ...ChatPageMain_chat_1G22uz
9
+ id
10
+ }
11
+ }
12
+
13
+ fragment BotImage_bot on Bot {
14
+ image {
15
+ __typename
16
+ ... on LocalBotImage {
17
+ localName
18
+ }
19
+ ... on UrlBotImage {
20
+ url
21
+ }
22
+ }
23
+ displayName
24
+ }
25
+
26
+ fragment ChatMessageDownvotedButton_message on Message {
27
+ ...MessageFeedbackReasonModal_message
28
+ ...MessageFeedbackOtherModal_message
29
+ }
30
+
31
+ fragment ChatMessageDropdownMenu_message on Message {
32
+ id
33
+ messageId
34
+ vote
35
+ text
36
+ linkifiedText
37
+ ...chatHelpers_isBotMessage
38
+ }
39
+
40
+ fragment ChatMessageFeedbackButtons_message on Message {
41
+ id
42
+ messageId
43
+ vote
44
+ voteReason
45
+ ...ChatMessageDownvotedButton_message
46
+ }
47
+
48
+ fragment ChatMessageInputView_chat on Chat {
49
+ id
50
+ chatId
51
+ defaultBotObject {
52
+ nickname
53
+ messageLimit {
54
+ dailyBalance
55
+ shouldShowRemainingMessageCount
56
+ }
57
+ id
58
+ }
59
+ shouldShowDisclaimer
60
+ ...chatHelpers_useSendMessage_chat
61
+ ...chatHelpers_useSendChatBreak_chat
62
+ }
63
+
64
+ fragment ChatMessageInputView_edges on MessageEdge {
65
+ node {
66
+ ...chatHelpers_isChatBreak
67
+ ...chatHelpers_isHumanMessage
68
+ state
69
+ text
70
+ id
71
+ }
72
+ }
73
+
74
+ fragment ChatMessageOverflowButton_message on Message {
75
+ text
76
+ ...ChatMessageDropdownMenu_message
77
+ ...chatHelpers_isBotMessage
78
+ }
79
+
80
+ fragment ChatMessageSuggestedReplies_SuggestedReplyButton_chat on Chat {
81
+ ...chatHelpers_useSendMessage_chat
82
+ }
83
+
84
+ fragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {
85
+ messageId
86
+ }
87
+
88
+ fragment ChatMessageSuggestedReplies_chat on Chat {
89
+ ...ChatWelcomeView_chat
90
+ ...ChatMessageSuggestedReplies_SuggestedReplyButton_chat
91
+ }
92
+
93
+ fragment ChatMessageSuggestedReplies_message on Message {
94
+ suggestedReplies
95
+ ...ChatMessageSuggestedReplies_SuggestedReplyButton_message
96
+ }
97
+
98
+ fragment ChatMessage_chat on Chat {
99
+ defaultBotObject {
100
+ ...ChatPageDisclaimer_bot
101
+ messageLimit {
102
+ ...ChatPageRateLimitedBanner_messageLimit
103
+ }
104
+ id
105
+ }
106
+ ...ChatMessageSuggestedReplies_chat
107
+ ...ChatWelcomeView_chat
108
+ }
109
+
110
+ fragment ChatMessage_message on Message {
111
+ id
112
+ messageId
113
+ text
114
+ author
115
+ linkifiedText
116
+ state
117
+ ...ChatMessageSuggestedReplies_message
118
+ ...ChatMessageFeedbackButtons_message
119
+ ...ChatMessageOverflowButton_message
120
+ ...chatHelpers_isHumanMessage
121
+ ...chatHelpers_isBotMessage
122
+ ...chatHelpers_isChatBreak
123
+ ...chatHelpers_useTimeoutLevel
124
+ ...MarkdownLinkInner_message
125
+ }
126
+
127
+ fragment ChatMessagesView_chat on Chat {
128
+ ...ChatMessage_chat
129
+ ...ChatWelcomeView_chat
130
+ defaultBotObject {
131
+ messageLimit {
132
+ ...ChatPageRateLimitedBanner_messageLimit
133
+ }
134
+ id
135
+ }
136
+ }
137
+
138
+ fragment ChatMessagesView_edges on MessageEdge {
139
+ node {
140
+ id
141
+ messageId
142
+ creationTime
143
+ ...ChatMessage_message
144
+ ...chatHelpers_isBotMessage
145
+ ...chatHelpers_isHumanMessage
146
+ ...chatHelpers_isChatBreak
147
+ }
148
+ }
149
+
150
+ fragment ChatPageDeleteFooter_chat on Chat {
151
+ ...MessageDeleteConfirmationModal_chat
152
+ }
153
+
154
+ fragment ChatPageDisclaimer_bot on Bot {
155
+ disclaimer
156
+ }
157
+
158
+ fragment ChatPageMain_chat_1G22uz on Chat {
159
+ id
160
+ chatId
161
+ ...ChatMessageInputView_chat
162
+ ...ChatPageShareFooter_chat
163
+ ...ChatPageDeleteFooter_chat
164
+ ...ChatMessagesView_chat
165
+ ...MarkdownLinkInner_chat
166
+ ...chatHelpers_useUpdateStaleChat_chat
167
+ ...ChatSubscriptionPaywallContextWrapper_chat
168
+ messagesConnection(last: $count, before: $cursor) {
169
+ edges {
170
+ ...ChatMessagesView_edges
171
+ ...ChatMessageInputView_edges
172
+ ...MarkdownLinkInner_edges
173
+ node {
174
+ ...chatHelpers_useUpdateStaleChat_message
175
+ id
176
+ __typename
177
+ }
178
+ cursor
179
+ id
180
+ }
181
+ pageInfo {
182
+ hasPreviousPage
183
+ startCursor
184
+ }
185
+ id
186
+ }
187
+ }
188
+
189
+ fragment ChatPageRateLimitedBanner_messageLimit on MessageLimit {
190
+ numMessagesRemaining
191
+ }
192
+
193
+ fragment ChatPageShareFooter_chat on Chat {
194
+ chatId
195
+ }
196
+
197
+ fragment ChatSubscriptionPaywallContextWrapper_chat on Chat {
198
+ defaultBotObject {
199
+ messageLimit {
200
+ numMessagesRemaining
201
+ shouldShowRemainingMessageCount
202
+ }
203
+ ...SubscriptionPaywallModal_bot
204
+ id
205
+ }
206
+ }
207
+
208
+ fragment ChatWelcomeView_ChatWelcomeButton_chat on Chat {
209
+ ...chatHelpers_useSendMessage_chat
210
+ }
211
+
212
+ fragment ChatWelcomeView_chat on Chat {
213
+ ...ChatWelcomeView_ChatWelcomeButton_chat
214
+ defaultBotObject {
215
+ displayName
216
+ id
217
+ }
218
+ }
219
+
220
+ fragment MarkdownLinkInner_chat on Chat {
221
+ id
222
+ chatId
223
+ defaultBotObject {
224
+ nickname
225
+ id
226
+ }
227
+ ...chatHelpers_useSendMessage_chat
228
+ }
229
+
230
+ fragment MarkdownLinkInner_edges on MessageEdge {
231
+ node {
232
+ state
233
+ id
234
+ }
235
+ }
236
+
237
+ fragment MarkdownLinkInner_message on Message {
238
+ messageId
239
+ }
240
+
241
+ fragment MessageDeleteConfirmationModal_chat on Chat {
242
+ id
243
+ }
244
+
245
+ fragment MessageFeedbackOtherModal_message on Message {
246
+ id
247
+ messageId
248
+ }
249
+
250
+ fragment MessageFeedbackReasonModal_message on Message {
251
+ id
252
+ messageId
253
+ }
254
+
255
+ fragment SubscriptionPaywallModal_bot on Bot {
256
+ displayName
257
+ messageLimit {
258
+ dailyLimit
259
+ numMessagesRemaining
260
+ shouldShowRemainingMessageCount
261
+ resetTime
262
+ }
263
+ ...BotImage_bot
264
+ }
265
+
266
+ fragment chatHelpers_isBotMessage on Message {
267
+ ...chatHelpers_isHumanMessage
268
+ ...chatHelpers_isChatBreak
269
+ }
270
+
271
+ fragment chatHelpers_isChatBreak on Message {
272
+ author
273
+ }
274
+
275
+ fragment chatHelpers_isHumanMessage on Message {
276
+ author
277
+ }
278
+
279
+ fragment chatHelpers_useSendChatBreak_chat on Chat {
280
+ id
281
+ chatId
282
+ defaultBotObject {
283
+ nickname
284
+ introduction
285
+ model
286
+ id
287
+ }
288
+ shouldShowDisclaimer
289
+ }
290
+
291
+ fragment chatHelpers_useSendMessage_chat on Chat {
292
+ id
293
+ chatId
294
+ defaultBotObject {
295
+ nickname
296
+ id
297
+ }
298
+ shouldShowDisclaimer
299
+ }
300
+
301
+ fragment chatHelpers_useTimeoutLevel on Message {
302
+ id
303
+ state
304
+ text
305
+ messageId
306
+ }
307
+
308
+ fragment chatHelpers_useUpdateStaleChat_chat on Chat {
309
+ chatId
310
+ ...chatHelpers_useSendChatBreak_chat
311
+ }
312
+
313
+ fragment chatHelpers_useUpdateStaleChat_message on Message {
314
+ creationTime
315
+ ...chatHelpers_isChatBreak
316
+ }
poe/graphql/ChatPaginationQuery.graphql ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ query ChatPaginationQuery($bot: String!, $before: String, $last: Int! = 10) {
2
+ chatOfBot(bot: $bot) {
3
+ id
4
+ __typename
5
+ messagesConnection(before: $before, last: $last) {
6
+ pageInfo {
7
+ hasPreviousPage
8
+ }
9
+ edges {
10
+ node {
11
+ id
12
+ __typename
13
+ messageId
14
+ text
15
+ linkifiedText
16
+ authorNickname
17
+ state
18
+ vote
19
+ voteReason
20
+ creationTime
21
+ suggestedReplies
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
poe/graphql/ChatViewQuery.graphql ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ query ChatViewQuery($bot: String!) {
2
+ chatOfBot(bot: $bot) {
3
+ id
4
+ chatId
5
+ defaultBotNickname
6
+ shouldShowDisclaimer
7
+ }
8
+ }
poe/graphql/DeleteHumanMessagesMutation.graphql ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mutation DeleteHumanMessagesMutation($messageIds: [BigInt!]!) {
2
+ messagesDelete(messageIds: $messageIds) {
3
+ viewer {
4
+ id
5
+ }
6
+ }
7
+ }
poe/graphql/DeleteMessageMutation.graphql ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mutation deleteMessageMutation(
2
+ $messageIds: [BigInt!]!
3
+ ) {
4
+ messagesDelete(messageIds: $messageIds) {
5
+ edgeIds
6
+ }
7
+ }
poe/graphql/HandleFragment.graphql ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fragment HandleFragment on Viewer {
2
+ id
3
+ poeUser {
4
+ id
5
+ uid
6
+ handle
7
+ }
8
+ }
poe/graphql/LoginWithVerificationCodeMutation.graphql ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mutation LoginWithVerificationCodeMutation(
2
+ $verificationCode: String!
3
+ $emailAddress: String
4
+ $phoneNumber: String
5
+ ) {
6
+ loginWithVerificationCode(
7
+ verificationCode: $verificationCode
8
+ emailAddress: $emailAddress
9
+ phoneNumber: $phoneNumber
10
+ ) {
11
+ status
12
+ }
13
+ }
poe/graphql/MessageAddedSubscription.graphql ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ subscription messageAdded (
2
+ $chatId: BigInt!
3
+ ) {
4
+ messageAdded(chatId: $chatId) {
5
+ id
6
+ messageId
7
+ creationTime
8
+ state
9
+ ...ChatMessage_message
10
+ ...chatHelpers_isBotMessage
11
+ }
12
+ }
13
+
14
+ fragment ChatMessageDownvotedButton_message on Message {
15
+ ...MessageFeedbackReasonModal_message
16
+ ...MessageFeedbackOtherModal_message
17
+ }
18
+
19
+ fragment ChatMessageDropdownMenu_message on Message {
20
+ id
21
+ messageId
22
+ vote
23
+ text
24
+ linkifiedText
25
+ ...chatHelpers_isBotMessage
26
+ }
27
+
28
+ fragment ChatMessageFeedbackButtons_message on Message {
29
+ id
30
+ messageId
31
+ vote
32
+ voteReason
33
+ ...ChatMessageDownvotedButton_message
34
+ }
35
+
36
+ fragment ChatMessageOverflowButton_message on Message {
37
+ text
38
+ ...ChatMessageDropdownMenu_message
39
+ ...chatHelpers_isBotMessage
40
+ }
41
+
42
+ fragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {
43
+ messageId
44
+ }
45
+
46
+ fragment ChatMessageSuggestedReplies_message on Message {
47
+ suggestedReplies
48
+ ...ChatMessageSuggestedReplies_SuggestedReplyButton_message
49
+ }
50
+
51
+ fragment ChatMessage_message on Message {
52
+ id
53
+ messageId
54
+ text
55
+ author
56
+ linkifiedText
57
+ state
58
+ ...ChatMessageSuggestedReplies_message
59
+ ...ChatMessageFeedbackButtons_message
60
+ ...ChatMessageOverflowButton_message
61
+ ...chatHelpers_isHumanMessage
62
+ ...chatHelpers_isBotMessage
63
+ ...chatHelpers_isChatBreak
64
+ ...chatHelpers_useTimeoutLevel
65
+ ...MarkdownLinkInner_message
66
+ }
67
+
68
+ fragment MarkdownLinkInner_message on Message {
69
+ messageId
70
+ }
71
+
72
+ fragment MessageFeedbackOtherModal_message on Message {
73
+ id
74
+ messageId
75
+ }
76
+
77
+ fragment MessageFeedbackReasonModal_message on Message {
78
+ id
79
+ messageId
80
+ }
81
+
82
+ fragment chatHelpers_isBotMessage on Message {
83
+ ...chatHelpers_isHumanMessage
84
+ ...chatHelpers_isChatBreak
85
+ }
86
+
87
+ fragment chatHelpers_isChatBreak on Message {
88
+ author
89
+ }
90
+
91
+ fragment chatHelpers_isHumanMessage on Message {
92
+ author
93
+ }
94
+
95
+ fragment chatHelpers_useTimeoutLevel on Message {
96
+ id
97
+ state
98
+ text
99
+ messageId
100
+ }
poe/graphql/MessageDeletedSubscription.graphql ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ subscription MessageDeletedSubscription($chatId: BigInt!) {
2
+ messageDeleted(chatId: $chatId) {
3
+ id
4
+ messageId
5
+ }
6
+ }
poe/graphql/MessageFragment.graphql ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fragment MessageFragment on Message {
2
+ id
3
+ __typename
4
+ messageId
5
+ text
6
+ linkifiedText
7
+ authorNickname
8
+ state
9
+ vote
10
+ voteReason
11
+ creationTime
12
+ suggestedReplies
13
+ }
poe/graphql/MessageRemoveVoteMutation.graphql ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mutation MessageRemoveVoteMutation($messageId: BigInt!) {
2
+ messageRemoveVote(messageId: $messageId) {
3
+ message {
4
+ ...MessageFragment
5
+ }
6
+ }
7
+ }
poe/graphql/MessageSetVoteMutation.graphql ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mutation MessageSetVoteMutation($messageId: BigInt!, $voteType: VoteType!, $reason: String) {
2
+ messageSetVote(messageId: $messageId, voteType: $voteType, reason: $reason) {
3
+ message {
4
+ ...MessageFragment
5
+ }
6
+ }
7
+ }
poe/graphql/SendVerificationCodeForLoginMutation.graphql ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mutation SendVerificationCodeForLoginMutation(
2
+ $emailAddress: String
3
+ $phoneNumber: String
4
+ ) {
5
+ sendVerificationCode(
6
+ verificationReason: login
7
+ emailAddress: $emailAddress
8
+ phoneNumber: $phoneNumber
9
+ ) {
10
+ status
11
+ }
12
+ }
poe/graphql/ShareMessagesMutation.graphql ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ mutation ShareMessagesMutation(
2
+ $chatId: BigInt!
3
+ $messageIds: [BigInt!]!
4
+ $comment: String
5
+ ) {
6
+ messagesShare(chatId: $chatId, messageIds: $messageIds, comment: $comment) {
7
+ shareCode
8
+ }
9
+ }
poe/graphql/SignupWithVerificationCodeMutation.graphql ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mutation SignupWithVerificationCodeMutation(
2
+ $verificationCode: String!
3
+ $emailAddress: String
4
+ $phoneNumber: String
5
+ ) {
6
+ signupWithVerificationCode(
7
+ verificationCode: $verificationCode
8
+ emailAddress: $emailAddress
9
+ phoneNumber: $phoneNumber
10
+ ) {
11
+ status
12
+ }
13
+ }
poe/graphql/StaleChatUpdateMutation.graphql ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ mutation StaleChatUpdateMutation($chatId: BigInt!) {
2
+ staleChatUpdate(chatId: $chatId) {
3
+ message {
4
+ ...MessageFragment
5
+ }
6
+ }
7
+ }
poe/graphql/SubscriptionsMutation.graphql ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ mutation subscriptionsMutation(
2
+ $subscriptions: [AutoSubscriptionQuery!]!
3
+ ) {
4
+ autoSubscribe(subscriptions: $subscriptions) {
5
+ viewer {
6
+ id
7
+ }
8
+ }
9
+ }
poe/graphql/SummarizePlainPostQuery.graphql ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ query SummarizePlainPostQuery($comment: String!) {
2
+ summarizePlainPost(comment: $comment)
3
+ }
poe/graphql/SummarizeQuotePostQuery.graphql ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ query SummarizeQuotePostQuery($comment: String, $quotedPostId: BigInt!) {
2
+ summarizeQuotePost(comment: $comment, quotedPostId: $quotedPostId)
3
+ }
poe/graphql/SummarizeSharePostQuery.graphql ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ query SummarizeSharePostQuery($comment: String!, $chatId: BigInt!, $messageIds: [BigInt!]!) {
2
+ summarizeSharePost(comment: $comment, chatId: $chatId, messageIds: $messageIds)
3
+ }
poe/graphql/UserSnippetFragment.graphql ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fragment UserSnippetFragment on PoeUser {
2
+ id
3
+ uid
4
+ bio
5
+ handle
6
+ fullName
7
+ viewerIsFollowing
8
+ isPoeOnlyUser
9
+ profilePhotoURLTiny: profilePhotoUrl(size: tiny)
10
+ profilePhotoURLSmall: profilePhotoUrl(size: small)
11
+ profilePhotoURLMedium: profilePhotoUrl(size: medium)
12
+ profilePhotoURLLarge: profilePhotoUrl(size: large)
13
+ isFollowable
14
+ }
poe/graphql/ViewerInfoQuery.graphql ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ query ViewerInfoQuery {
2
+ viewer {
3
+ id
4
+ uid
5
+ ...ViewerStateFragment
6
+ ...BioFragment
7
+ ...HandleFragment
8
+ hasCompletedMultiplayerNux
9
+ poeUser {
10
+ id
11
+ ...UserSnippetFragment
12
+ }
13
+ messageLimit{
14
+ canSend
15
+ numMessagesRemaining
16
+ resetTime
17
+ shouldShowReminder
18
+ }
19
+ }
20
+ }
21
+
poe/graphql/ViewerStateFragment.graphql ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fragment ViewerStateFragment on Viewer {
2
+ id
3
+ __typename
4
+ iosMinSupportedVersion: integerGate(gateName: "poe_ios_min_supported_version")
5
+ iosMinEncouragedVersion: integerGate(
6
+ gateName: "poe_ios_min_encouraged_version"
7
+ )
8
+ macosMinSupportedVersion: integerGate(
9
+ gateName: "poe_macos_min_supported_version"
10
+ )
11
+ macosMinEncouragedVersion: integerGate(
12
+ gateName: "poe_macos_min_encouraged_version"
13
+ )
14
+ showPoeDebugPanel: booleanGate(gateName: "poe_show_debug_panel")
15
+ enableCommunityFeed: booleanGate(gateName: "enable_poe_shares_feed")
16
+ linkifyText: booleanGate(gateName: "poe_linkify_response")
17
+ enableSuggestedReplies: booleanGate(gateName: "poe_suggested_replies")
18
+ removeInviteLimit: booleanGate(gateName: "poe_remove_invite_limit")
19
+ enableInAppPurchases: booleanGate(gateName: "poe_enable_in_app_purchases")
20
+ availableBots {
21
+ nickname
22
+ displayName
23
+ profilePicture
24
+ isDown
25
+ disclaimer
26
+ subtitle
27
+ poweredBy
28
+ }
29
+ }
30
+
poe/graphql/ViewerStateUpdatedSubscription.graphql ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ subscription viewerStateUpdated {
2
+ viewerStateUpdated {
3
+ id
4
+ ...ChatPageBotSwitcher_viewer
5
+ }
6
+ }
7
+
8
+ fragment BotHeader_bot on Bot {
9
+ displayName
10
+ messageLimit {
11
+ dailyLimit
12
+ }
13
+ ...BotImage_bot
14
+ }
15
+
16
+ fragment BotImage_bot on Bot {
17
+ image {
18
+ __typename
19
+ ... on LocalBotImage {
20
+ localName
21
+ }
22
+ ... on UrlBotImage {
23
+ url
24
+ }
25
+ }
26
+ displayName
27
+ }
28
+
29
+ fragment BotLink_bot on Bot {
30
+ displayName
31
+ }
32
+
33
+ fragment ChatPageBotSwitcher_viewer on Viewer {
34
+ availableBots {
35
+ id
36
+ messageLimit {
37
+ dailyLimit
38
+ }
39
+ ...BotLink_bot
40
+ ...BotHeader_bot
41
+ }
42
+ allowUserCreatedBots: booleanGate(gateName: "enable_user_created_bots")
43
+ }
poe/graphql/__init__.py ADDED
File without changes
poe/mail.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from requests import Session
2
+ from string import ascii_letters
3
+ from random import choices
4
+
5
+ class Mail:
6
+ def __init__(self, proxies: dict = None) -> None:
7
+ self.client = Session()
8
+ self.client.proxies = None #proxies
9
+ self.client.headers = {
10
+ "host": "api.mail.tm",
11
+ "connection": "keep-alive",
12
+ "sec-ch-ua": "\"Google Chrome\";v=\"111\", \"Not(A:Brand\";v=\"8\", \"Chromium\";v=\"111\"",
13
+ "accept": "application/json, text/plain, */*",
14
+ "content-type": "application/json",
15
+ "sec-ch-ua-mobile": "?0",
16
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
17
+ "sec-ch-ua-platform": "\"macOS\"",
18
+ "origin": "https://mail.tm",
19
+ "sec-fetch-site": "same-site",
20
+ "sec-fetch-mode": "cors",
21
+ "sec-fetch-dest": "empty",
22
+ "referer": "https://mail.tm/",
23
+ "accept-encoding": "gzip, deflate, br",
24
+ "accept-language": "en-GB,en-US;q=0.9,en;q=0.8"
25
+ }
26
+
27
+ def get_mail(self) -> str:
28
+ token = ''.join(choices(ascii_letters, k=10)).lower()
29
+
30
+ init = self.client.post("https://api.mail.tm/accounts", json={
31
+ "address" : f"{token}@bugfoo.com",
32
+ "password": token
33
+ })
34
+
35
+ if init.status_code == 201:
36
+ resp = self.client.post("https://api.mail.tm/token", json = {
37
+ **init.json(),
38
+ "password": token
39
+ })
40
+
41
+ self.client.headers['authorization'] = 'Bearer ' + resp.json()['token']
42
+
43
+ return f"{token}@bugfoo.com"
44
+
45
+ else:
46
+ raise Exception("Failed to create email")
47
+
48
+ def fetch_inbox(self):
49
+ return self.client.get(f"https://api.mail.tm/messages").json()["hydra:member"]
50
+
51
+ def get_message(self, message_id: str):
52
+ return self.client.get(f"https://api.mail.tm/messages/{message_id}").json()
53
+
54
+ def get_message_content(self, message_id: str):
55
+ return self.get_message(message_id)["text"]
56
+
57
+
58
+ # if __name__ == "__main__":
59
+ # client = Mail()
60
+ # client.get_mail()
61
+
62
+