├── requirements.txt
├── __pycache__
├── POE.cpython-310.pyc
├── api.cpython-310.pyc
└── poe.cpython-310.pyc
├── example.py
├── README.md
├── poe.py
└── api.py
/requirements.txt:
--------------------------------------------------------------------------------
1 | fastapi==0.95.1
2 | requests==2.28.1
3 | uvicorn==0.22.0
4 |
--------------------------------------------------------------------------------
/__pycache__/POE.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspekts/PoeAPI/HEAD/__pycache__/POE.cpython-310.pyc
--------------------------------------------------------------------------------
/__pycache__/api.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspekts/PoeAPI/HEAD/__pycache__/api.cpython-310.pyc
--------------------------------------------------------------------------------
/__pycache__/poe.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspekts/PoeAPI/HEAD/__pycache__/poe.cpython-310.pyc
--------------------------------------------------------------------------------
/example.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import os
3 | cookie = os.environ.get("cookie")
4 | formkey = os.environ.get("formkey")
5 | url = "http://localhost:3000/chat/a2"
6 |
7 | x = requests.get(url, params={"cookie":cookie,"formkey":formkey ,"message":"hi"})
8 | print(x.json())
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Quora Poe reverse-engineered API
2 | This is a self hostable, reverse-engineered API for Quora's Poe that allows access to the following chatbots:
3 |
4 | 1. Sage - OpenAI (capybara)
5 | 4. Claude - Anthropic (a2)
6 | 5. ChatGPT - OpenAI (chinchilla)
7 | ### Requirements
8 | To use this API, you will need to have the following cookies:
9 |
10 | 2. Quora-Formkey: This is obtained by logging in to Quora.com, viewing the page source, and finding the "formkey" dictionary key (Normally line 14). Use its value in the Quora-Formkey field.
11 | 3. Cookie: 'm-b=xxxx' - This is the value of the cookie with the key m-b, which is present in the list of cookies used on Quora.com (not poe.com), you can simply inspect cookies in Chrome to get it.
12 | ### Setup
13 |
14 | Simple Setup
15 |
16 |
17 |
18 | - Clone this repository
19 |
20 | ```bash
21 | git clone https://github.com/aspekts/PoeAPI.git
22 | ```
23 |
24 | - Install dependencies
25 |
26 | ```bash
27 | pip install -r requirements.txt
28 | ```
29 |
30 | - Run the API
31 |
32 | ```bash
33 | uvicorn api:app --reload
34 | ```
35 |
36 | - Verify that the API is running by running:
37 |
38 | ```bash
39 | curl localhost:8000
40 | ```
41 |
42 | - Get a url for the API by running:
43 |
44 | ```bash
45 | ngrok http 8000
46 | ```
47 |
48 | Access the url given, in your browser to confirm it works. This is your bot server URL.
49 |
50 |
51 | ### Example
52 | ```bash
53 | curl http://127.0.0.1/chat/capybara -d '{"Cookie: m-b=xxxx" , "formkey: xxxxx", "message":"What is the meaning of life?"}'
54 | ```
55 | Response:
56 | ```json
57 | {"message":"The meaning of life is to live it.","status":"success", "chat_id": "xxxxx"}
58 | ```
59 |
60 | ### Disclaimer
61 | This repository is for educational and research purposes only, and the use of this API for any other purpose is at your own risk. We are not responsible for any actions taken by users of this API.
62 |
63 | ### Credits
64 |
65 | POE.com Reverse Engineered CLI - [Credits to Vaibhavk97](https://github.com/vaibhavk97/Poe)
66 |
--------------------------------------------------------------------------------
/poe.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import time
3 |
4 | url = 'https://www.quora.com/poe_api/gql_POST'
5 |
6 | headers = {
7 | 'Host': 'www.quora.com',
8 | 'Accept': '*/*',
9 | 'apollographql-client-version': '1.1.6-65',
10 | 'Accept-Language': 'en-US,en;q=0.9',
11 | 'User-Agent': 'Poe 1.1.6 rv:65 env:prod (iPhone14,2; iOS 16.2; en_US)',
12 | 'apollographql-client-name': 'com.quora.app.Experts-apollo-ios',
13 | 'Connection': 'keep-alive',
14 | 'Content-Type': 'application/json',
15 | }
16 |
17 | def set_auth(key, value):
18 | headers[key] = value
19 |
20 | def load_chat_id_map(bot="a2"):
21 | data = {
22 | 'operationName': 'ChatViewQuery',
23 | 'query': 'query ChatViewQuery($bot: String!) {\n chatOfBot(bot: $bot) {\n __typename\n ...ChatFragment\n }\n}\nfragment ChatFragment on Chat {\n __typename\n id\n chatId\n defaultBotNickname\n shouldShowDisclaimer\n}',
24 | 'variables': {
25 | 'bot': bot
26 | }
27 | }
28 | response = requests.post(url, headers=headers, json=data)
29 | return response.json()['data']['chatOfBot']['chatId']
30 |
31 | def send_message(message,bot="a2",chat_id=""):
32 | data = {
33 | "operationName": "AddHumanMessageMutation",
34 | "query": "mutation AddHumanMessageMutation($chatId: BigInt!, $bot: String!, $query: String!, $source: MessageSource, $withChatBreak: Boolean! = false) {\n messageCreate(\n chatId: $chatId\n bot: $bot\n query: $query\n source: $source\n withChatBreak: $withChatBreak\n ) {\n __typename\n message {\n __typename\n ...MessageFragment\n chat {\n __typename\n id\n shouldShowDisclaimer\n }\n }\n chatBreak {\n __typename\n ...MessageFragment\n }\n }\n}\nfragment MessageFragment on Message {\n id\n __typename\n messageId\n text\n linkifiedText\n authorNickname\n state\n vote\n voteReason\n creationTime\n suggestedReplies\n}",
35 | "variables": {
36 | "bot": bot,
37 | "chatId": chat_id,
38 | "query": message,
39 | "source": None,
40 | "withChatBreak": False
41 | }
42 | }
43 | _ = requests.post(url, headers=headers, json=data)
44 |
45 | def clear_context(chatid):
46 | data = {
47 | "operationName": "AddMessageBreakMutation",
48 | "query": "mutation AddMessageBreakMutation($chatId: BigInt!) {\n messageBreakCreate(chatId: $chatId) {\n __typename\n message {\n __typename\n ...MessageFragment\n }\n }\n}\nfragment MessageFragment on Message {\n id\n __typename\n messageId\n text\n linkifiedText\n authorNickname\n state\n vote\n voteReason\n creationTime\n suggestedReplies\n}",
49 | "variables": {
50 | "chatId": chatid
51 | }
52 | }
53 | _ = requests.post(url, headers=headers, json=data)
54 |
55 | def get_latest_message(bot):
56 | data = {
57 | "operationName": "ChatPaginationQuery",
58 | "query": "query ChatPaginationQuery($bot: String!, $before: String, $last: Int! = 10) {\n chatOfBot(bot: $bot) {\n id\n __typename\n messagesConnection(before: $before, last: $last) {\n __typename\n pageInfo {\n __typename\n hasPreviousPage\n }\n edges {\n __typename\n node {\n __typename\n ...MessageFragment\n }\n }\n }\n }\n}\nfragment MessageFragment on Message {\n id\n __typename\n messageId\n text\n linkifiedText\n authorNickname\n state\n vote\n voteReason\n creationTime\n}",
59 | "variables": {
60 | "before": None,
61 | "bot": bot,
62 | "last": 1
63 | }
64 | }
65 | author_nickname = ""
66 | state = "incomplete"
67 | while True:
68 | time.sleep(2)
69 | response = requests.post(url, headers=headers, json=data)
70 | response_json = response.json()
71 | text = response_json['data']['chatOfBot']['messagesConnection']['edges'][-1]['node']['text']
72 | state = response_json['data']['chatOfBot']['messagesConnection']['edges'][-1]['node']['state']
73 | author_nickname = response_json['data']['chatOfBot']['messagesConnection']['edges'][-1]['node']['authorNickname']
74 | if author_nickname==bot and state=='complete':
75 | break
76 | return text
77 |
78 |
--------------------------------------------------------------------------------
/api.py:
--------------------------------------------------------------------------------
1 | from poe import load_chat_id_map, clear_context, send_message, get_latest_message, set_auth
2 | import os
3 | from typing import Annotated
4 | from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Header
5 | from fastapi.responses import HTMLResponse, JSONResponse
6 | from pydantic import BaseModel
7 | #--------------------------------------------------------------------------
8 | app = FastAPI(
9 | title="Free Poe.com API",
10 | description="This is an API for Poe.com, a chatbot that uses GPT-3 to chat with you.",
11 | version="0.0.1",
12 | redoc_url="/docs",
13 | docs_url=None
14 | )
15 | bots =['capybara','a2','chinchilla']
16 |
17 | class Item(BaseModel):
18 | bot: str
19 | message: str
20 | cookie: str
21 | formkey: str
22 |
23 | @app.post(
24 | "/chat/{bot}",
25 | response_model=Item,
26 | summary="Chat",
27 | description="This endpoint allows you to chat with the chosen bot on Poe.com. The bot will respond to your message. You can choose between the following bots: Sage (OpenAI): `capybara` , Claude-Instant (Anthropic): `a2` , ChatGPT (OpenAI): `chinchilla`",
28 | responses={
29 | 200: {
30 | "description": "Success",
31 | "content": {
32 | "application/json": {
33 | "example": {
34 | "message": "Hello!",
35 | "status": "success",
36 | "chat_id": "123456789"
37 | }
38 | }
39 | }
40 | },
41 | 400: {
42 | "description": "Bad Request",
43 | "content": {
44 | "application/json": {
45 | "example": {
46 | "message": "Bad Request",
47 | "details": "The bot you selected is not available. Please choose one of the following bots: capybara, a2, chinchilla",
48 | "status": "error",
49 | }
50 | }
51 | }
52 | },
53 | "422": {
54 | "description": "Unprocessable Entity",
55 | },
56 | 500: {
57 | "description": "Internal Server Error",
58 | "content": {
59 | "application/json": {
60 | "example": {
61 | "message": "Internal Server Error",
62 | "status": "error",
63 | }
64 | }
65 | }
66 | }
67 | }
68 | )
69 |
70 | async def chat(bot:str, options: Item):
71 | """
72 | This is a Python function that allows users to select and chat with a bot from Poe.com.
73 | """
74 | if bot not in bots:
75 | return JSONResponse(status_code=400, content={"message" : "Bad Request", "details":"The bot you selected is not available. Please choose one of the following bots: capybara, a2, chinchilla", "status":"error"})
76 | if options.cookie is None or options.formkey is None:
77 | return JSONResponse(status_code=400, content={"message" : "Bad Request", "details":"At least one of the headers is not provided. Please ensure both the formkey and cookie headers are set.", "status":"error"})
78 | try:
79 | set_auth('Quora-Formkey',options.formkey)
80 | set_auth('Cookie',options.cookie)
81 | chat_id = load_chat_id_map(options.bot)
82 | clear_context(chat_id)
83 | send_message(options.message,options.bot,chat_id)
84 | reply = get_latest_message(options.bot)
85 | return JSONResponse(status_code=200, content={"message" : reply, "status":"success", "chat_id":chat_id })
86 | except Exception as e:
87 | print(e)
88 | return JSONResponse(status_code=500, content={"message" : "Internal Server Error", "status":"error"})
89 |
90 | @app.get("/")
91 | async def main():
92 |
93 | """
94 | This function sets up a basic endpoint for the Poe.com API that returns a simple HTML response with
95 | a link to the API documentation.
96 | :return: The `main` function is returning an HTML response with a title "Poe.com API" and a
97 | paragraph with a link to the API documentation. When the user navigates to the root URL ("/"), they
98 | will see this HTML content.
99 | """
100 | content = """
101 |
102 | Poe.com API
103 | Go to /docs to see the API documentation.
104 |
105 | """
106 | return HTMLResponse(content=content)
107 |
--------------------------------------------------------------------------------