├── 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 | --------------------------------------------------------------------------------