1
0
Fork 0

updated poe api (gpt4)

fixed tls_client issue by switching to request Session, and update the poe api
This commit is contained in:
t.me/xtekky 2023-04-06 21:24:04 +02:00
parent 24f10a3f8f
commit 76571f2591
6 changed files with 245 additions and 40 deletions

View file

@ -1,6 +1,6 @@
from poe.api import Client as PoeClient
from poe.mail import Mail
from tls_client import Session
from requests import Session
from re import search, findall
from json import loads
from time import sleep, time
@ -48,11 +48,10 @@ class PoeResponse:
def json(self) -> dict:
return self.response_dict
class Account:
def create(proxy: None or str = None, logging: bool = False):
client = Session(client_identifier = "chrome110")
client = Session()
client.proxies = {
'http': f'http://{proxy}',
'https': f'http://{proxy}'} if proxy else None

View file

@ -22,6 +22,7 @@ import logging
import time
import queue
import threading
import traceback
import websocket
from pathlib import Path
from urllib.parse import urlparse
@ -124,6 +125,15 @@ class Client:
return next_data
def get_bot(self, display_name):
url = f'https://poe.com/_next/data/{self.next_data["buildId"]}/{display_name}.json'
logger.info("Downloading "+url)
r = request_with_retries(self.session.get, url)
chat_data = r.json()["pageProps"]["payload"]["chatOfBotDisplayName"]
return chat_data
def get_bots(self):
viewer = self.next_data["props"]["pageProps"]["payload"]["viewer"]
if not "availableBots" in viewer:
@ -132,13 +142,7 @@ class Client:
bots = {}
for bot in bot_list:
url = f'https://poe.com/_next/data/{self.next_data["buildId"]}/{bot["displayName"].lower()}.json'
logger.info("Downloading "+url)
r = request_with_retries(self.session.get, url)
chat_data = r.json()[
"pageProps"]["payload"]["chatOfBotDisplayName"]
chat_data = self.get_bot(bot["displayName"].lower())
bots[chat_data["defaultBotObject"]["nickname"]] = chat_data
return bots
@ -165,11 +169,8 @@ class Client:
return f'wss://{self.ws_domain}.tch.{channel["baseHost"]}/up/{channel["boxName"]}/updates'+query
def send_query(self, query_name, variables):
# print(f'send_query: {query_name} {variables}')
for i in range(20):
payload = generate_payload(query_name, variables)
# print(f'query_payload: {query_name} {variables}')
r = request_with_retries(
self.session.post, self.gql_url, json=payload, headers=self.gql_headers)
data = r.json()
@ -216,7 +217,8 @@ class Client:
header={"User-Agent": user_agent},
on_message=self.on_message,
on_open=self.on_ws_connect,
on_error=self.on_ws_error
on_error=self.on_ws_error,
on_close=self.on_ws_close
)
t = threading.Thread(target=self.ws_run_thread, daemon=True)
t.start()
@ -231,27 +233,44 @@ class Client:
def on_ws_connect(self, ws):
self.ws_connected = True
def on_ws_close(self, ws, close_status_code):
self.ws_connected = False
logger.warn(f"Websocket closed with status {close_status_code}")
def on_ws_error(self, ws, error):
logger.warn(f"Websocket returned error: {error}")
self.disconnect_ws()
self.connect_ws()
def on_message(self, ws, msg):
data = json.loads(msg)
message = json.loads(data["messages"][0])[
"payload"]["data"]["messageAdded"]
try:
data = json.loads(msg)
copied_dict = self.active_messages.copy()
for key, value in copied_dict.items():
# add the message to the appropriate queue
if value == message["messageId"] and key in self.message_queues:
self.message_queues[key].put(message)
if not "messages" in data:
return
# indicate that the response id is tied to the human message id
elif key != "pending" and value == None and message["state"] != "complete":
self.active_messages[key] = message["messageId"]
self.message_queues[key].put(message)
for message_str in data["messages"]:
message_data = json.loads(message_str)
if message_data["message_type"] != "subscriptionUpdate":
continue
message = message_data["payload"]["data"]["messageAdded"]
copied_dict = self.active_messages.copy()
for key, value in copied_dict.items():
# add the message to the appropriate queue
if value == message["messageId"] and key in self.message_queues:
self.message_queues[key].put(message)
return
# indicate that the response id is tied to the human message id
elif key != "pending" and value == None and message["state"] != "complete":
self.active_messages[key] = message["messageId"]
self.message_queues[key].put(message)
return
except Exception:
logger.error(traceback.format_exc())
self.disconnect_ws()
self.connect_ws()
def send_message(self, chatbot, message, with_chat_break=False, timeout=20):
# if there is another active message, wait until it has finished sending
@ -262,8 +281,11 @@ class Client:
self.active_messages["pending"] = None
logger.info(f"Sending message to {chatbot}: {message}")
message_data = self.send_query("AddHumanMessageMutation", {
# reconnect websocket
if not self.ws_connected:
self.disconnect_ws()
self.connect_ws()
message_data = self.send_query("SendMessageMutation", {
"bot": chatbot,
"query": message,
"chatId": self.bots[chatbot]["chatId"],
@ -272,11 +294,11 @@ class Client:
})
del self.active_messages["pending"]
if not message_data["data"]["messageCreateWithStatus"]["messageLimit"]["canSend"]:
if not message_data["data"]["messageEdgeCreate"]["message"]:
raise RuntimeError(f"Daily limit reached for {chatbot}.")
try:
human_message = message_data["data"]["messageCreateWithStatus"]
human_message_id = human_message["message"]["messageId"]
human_message = message_data["data"]["messageEdgeCreate"]["message"]
human_message_id = human_message["node"]["messageId"]
except TypeError:
raise RuntimeError(
f"An unknown error occured. Raw response data: {message_data}")
@ -313,4 +335,67 @@ class Client:
del self.active_messages[human_message_id]
del self.message_queues[human_message_id]
load_queries()
def send_chat_break(self, chatbot):
logger.info(f"Sending chat break to {chatbot}")
result = self.send_query("AddMessageBreakMutation", {
"chatId": self.bots[chatbot]["chatId"]
})
return result["data"]["messageBreakCreate"]["message"]
def get_message_history(self, chatbot, count=25, cursor=None):
logger.info(f"Downloading {count} messages from {chatbot}")
if cursor == None:
chat_data = self.get_bot(self.bot_names[chatbot])
if not chat_data["messagesConnection"]["edges"]:
return []
cursor = chat_data["messagesConnection"]["edges"][-1]["cursor"]
cursor = str(cursor)
if count > 50:
messages = self.get_message_history(
chatbot, count=50, cursor=cursor)
while count > 0:
new_cursor = messages[0]["cursor"]
new_messages = self.get_message_history(
chatbot, min(50, count), cursor=new_cursor)
messages = new_messages + messages
count -= 50
return messages
result = self.send_query("ChatListPaginationQuery", {
"count": count,
"cursor": cursor,
"id": self.bots[chatbot]["id"]
})
return result["data"]["node"]["messagesConnection"]["edges"]
def delete_message(self, message_ids):
logger.info(f"Deleting messages: {message_ids}")
if not type(message_ids) is list:
message_ids = [int(message_ids)]
result = self.send_query("DeleteMessageMutation", {
"messageIds": message_ids
})
def purge_conversation(self, chatbot, count=-1):
logger.info(f"Purging messages from {chatbot}")
last_messages = self.get_message_history(chatbot, count=50)[::-1]
while last_messages:
message_ids = []
for message in last_messages:
if count == 0:
break
count -= 1
message_ids.append(message["node"]["messageId"])
self.delete_message(message_ids)
if count == 0:
return
last_messages = self.get_message_history(chatbot, count=50)[::-1]
logger.info(f"No more messages left to delete.")
load_queries()

View file

@ -1 +1,7 @@
SmPiNXZI9hBTuf3viz74PA==
zw7RoKQfeEehiaelYMRWeA==
NEttgJ_rRQdO05Tppx6hFw==
3OnmC0r9njYdNWhWszdQJg==
8hZKR7MxwUTEHvO45TEViw==
Eea6BqK0AmosTKzoI3AAow==
pUEbtxobN_QUSpLIR8RGww==

View file

@ -11,6 +11,12 @@ query ChatListPaginationQuery(
}
fragment BotImage_bot on Bot {
displayName
...botHelpers_useDeletion_bot
...BotImage_useProfileImage_bot
}
fragment BotImage_useProfileImage_bot on Bot {
image {
__typename
... on LocalBotImage {
@ -20,7 +26,7 @@ fragment BotImage_bot on Bot {
url
}
}
displayName
...botHelpers_useDeletion_bot
}
fragment ChatMessageDownvotedButton_message on Message {
@ -33,7 +39,7 @@ fragment ChatMessageDropdownMenu_message on Message {
messageId
vote
text
linkifiedText
author
...chatHelpers_isBotMessage
}
@ -54,6 +60,9 @@ fragment ChatMessageInputView_chat on Chat {
dailyBalance
shouldShowRemainingMessageCount
}
hasClearContext
isDown
...botHelpers_useDeletion_bot
id
}
shouldShowDisclaimer
@ -88,6 +97,10 @@ fragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {
fragment ChatMessageSuggestedReplies_chat on Chat {
...ChatWelcomeView_chat
...ChatMessageSuggestedReplies_SuggestedReplyButton_chat
defaultBotObject {
hasWelcomeTopics
id
}
}
fragment ChatMessageSuggestedReplies_message on Message {
@ -97,10 +110,13 @@ fragment ChatMessageSuggestedReplies_message on Message {
fragment ChatMessage_chat on Chat {
defaultBotObject {
...ChatPageDisclaimer_bot
hasWelcomeTopics
hasSuggestedReplies
disclaimerText
messageLimit {
...ChatPageRateLimitedBanner_messageLimit
}
...ChatPageDisclaimer_bot
id
}
...ChatMessageSuggestedReplies_chat
@ -114,6 +130,7 @@ fragment ChatMessage_message on Message {
author
linkifiedText
state
contentType
...ChatMessageSuggestedReplies_message
...ChatMessageFeedbackButtons_message
...ChatMessageOverflowButton_message
@ -122,12 +139,15 @@ fragment ChatMessage_message on Message {
...chatHelpers_isChatBreak
...chatHelpers_useTimeoutLevel
...MarkdownLinkInner_message
...IdAnnotation_node
}
fragment ChatMessagesView_chat on Chat {
...ChatMessage_chat
...ChatWelcomeView_chat
...IdAnnotation_node
defaultBotObject {
hasWelcomeTopics
messageLimit {
...ChatPageRateLimitedBanner_messageLimit
}
@ -152,23 +172,42 @@ fragment ChatPageDeleteFooter_chat on Chat {
}
fragment ChatPageDisclaimer_bot on Bot {
disclaimer
disclaimerText
}
fragment ChatPageMainFooter_chat on Chat {
defaultBotObject {
...ChatPageMainFooter_useAccessMessage_bot
id
}
...ChatMessageInputView_chat
...ChatPageShareFooter_chat
...ChatPageDeleteFooter_chat
}
fragment ChatPageMainFooter_edges on MessageEdge {
...ChatMessageInputView_edges
}
fragment ChatPageMainFooter_useAccessMessage_bot on Bot {
...botHelpers_useDeletion_bot
...botHelpers_useViewerCanAccessPrivateBot
}
fragment ChatPageMain_chat_1G22uz on Chat {
id
chatId
...ChatMessageInputView_chat
...ChatPageShareFooter_chat
...ChatPageDeleteFooter_chat
...ChatMessagesView_chat
...MarkdownLinkInner_chat
...chatHelpers_useUpdateStaleChat_chat
...ChatSubscriptionPaywallContextWrapper_chat
...ChatPageMainFooter_chat
messagesConnection(last: $count, before: $cursor) {
edges {
...ChatMessagesView_edges
...ChatMessageInputView_edges
...ChatPageMainFooter_edges
...MarkdownLinkInner_edges
node {
...chatHelpers_useUpdateStaleChat_message
@ -217,6 +256,11 @@ fragment ChatWelcomeView_chat on Chat {
}
}
fragment IdAnnotation_node on Node {
__isNode: __typename
id
}
fragment MarkdownLinkInner_chat on Chat {
id
chatId
@ -263,6 +307,15 @@ fragment SubscriptionPaywallModal_bot on Bot {
...BotImage_bot
}
fragment botHelpers_useDeletion_bot on Bot {
deletionState
}
fragment botHelpers_useViewerCanAccessPrivateBot on Bot {
isPrivateBot
viewerIsCreator
}
fragment chatHelpers_isBotMessage on Message {
...chatHelpers_isHumanMessage
...chatHelpers_isChatBreak
@ -292,8 +345,8 @@ fragment chatHelpers_useSendMessage_chat on Chat {
id
chatId
defaultBotObject {
nickname
id
nickname
}
shouldShowDisclaimer
}
@ -303,10 +356,19 @@ fragment chatHelpers_useTimeoutLevel on Message {
state
text
messageId
chat {
chatId
defaultBotNickname
id
}
}
fragment chatHelpers_useUpdateStaleChat_chat on Chat {
chatId
defaultBotObject {
contextClearWindowSecs
id
}
...chatHelpers_useSendChatBreak_chat
}

View file

@ -0,0 +1,40 @@
mutation chatHelpers_sendMessageMutation_Mutation(
$chatId: BigInt!
$bot: String!
$query: String!
$source: MessageSource
$withChatBreak: Boolean!
) {
messageEdgeCreate(chatId: $chatId, bot: $bot, query: $query, source: $source, withChatBreak: $withChatBreak) {
chatBreak {
cursor
node {
id
messageId
text
author
suggestedReplies
creationTime
state
}
id
}
message {
cursor
node {
id
messageId
text
author
suggestedReplies
creationTime
state
chat {
shouldShowDisclaimer
id
}
}
id
}
}
}

13
testing/poe_test.py Normal file
View file

@ -0,0 +1,13 @@
import poe
from time import sleep
token = poe.Account.create(proxy = 'xtekky:ogingoi2n3g@geo.iproyal.com:12321',logging = True)
print('token', token)
sleep(2)
for response in poe.StreamingCompletion.create(model = 'gpt-4',
prompt = 'hello world',
token = token):
print(response.completion.choices[0].text, end="", flush=True)