Coder Social home page Coder Social logo

ading2210 / poe-api Goto Github PK

View Code? Open in Web Editor NEW
2.5K 34.0 315.0 169 KB

[UNMAINTAINED] A reverse engineered Python API wrapper for Quora's Poe, which provides free access to ChatGPT, GPT-4, and Claude.

Home Page: https://pypi.org/project/poe-api

License: GNU General Public License v3.0

Python 100.00%
python library graphql quora poe chatgpt

poe-api's Introduction

Important:

This project has been unmaintained for over a month and does not work. See issue #231 for more details.

Python Poe API

PyPi Version

This is a reverse engineered API wrapper for Quora's Poe, which allows you free access to OpenAI's ChatGPT and GPT-4, as well as Anthropic's Claude.

Table of Contents:

Table of contents generated with markdown-toc.

Features:

  • Log in with token
  • Proxy requests + websocket
  • Download bot list
  • Send messages
  • Stream bot responses
  • Clear conversation context
  • Download conversation history
  • Delete messages
  • Purge an entire conversation
  • Create custom bots
  • Edit your custom bots
  • Use pre-existing third party bots

Installation:

You can install this library by running the following command:

pip3 install poe-api

This library depends on quickjs, which does not have prebuilt binaries available for Python 3.11. Pip will attempt to compile it, but will fail if python-dev is not installed.

On Linux, you can install it via the instructions listed here: https://stackoverflow.com/questions/21530577/fatal-error-python-h-no-such-file-or-directory

On Windows and MacOS, python-dev should be included with your existing Python installation.

Documentation:

Examples can be found in the /examples directory. To run these examples, pass in your token as a command-line argument.

python3 examples/temporary_message.py "TOKEN_HERE"

Finding Your Token:

Log into Poe on any desktop web browser, then open your browser's developer tools (also known as "inspect") and look for the value of the p-b cookie in the following menus:

  • Chromium: Devtools > Application > Cookies > poe.com
  • Firefox: Devtools > Storage > Cookies
  • Safari: Devtools > Storage > Cookies

Note that excessive usage of this library may lead to your account getting banned. It is recommended that you set your own rate limits, and that you use an alt account that you don't value. See issue #118 for more details. If your requests are infrequent, the risk for a ban is very low.

Using the Client:

To use this library, simply import poe and create a poe.Client instance. The Client class accepts the following arguments:

  • token - The token to use.
  • proxy = None - The proxy to use, in the format protocol://host:port. The socks5h protocol is recommended, as it also proxies the DNS queries.
  • device_id = None - The device ID to use. If this is not specified, it will be randomly generated and stored on the disk.
  • headers = headers - The headers to use. This defaults to the headers specified in poe.headers.
  • client_identifier = client_identifier - The client identifier that will be passed into the TLS client library. This defaults to the one specified in poe.client_identifier.
  • formkey = None - The formkey to use. Only supply this option if initializing the client without it fails. You can find this value by going into your browser's devtools, looking in the network tab for a gql_POST request, and taking the value of the poe-formkey request header. The formkey that you use must come from a browser that matches the headers that you've set in poe.headers. Keep in mind that the default browser is Chromium 112.

Regular Example:

import poe
client = poe.Client("TOKEN_HERE")

Proxied Example:

import poe
client = poe.Client("TOKEN_HERE", proxy="socks5h://178.62.100.151:59166")

Note that the following examples assume client is the name of your poe.Client instance. If the token is invalid, a RuntimeError will be raised.

Downloading the Available Bots:

The client downloads all of the available bots upon initialization and stores them within client.bots. A dictionary that maps bot codenames to their display names can be found at client.bot_names. If you want to refresh these values, you can call client.get_bots. This function takes the following arguments:

  • download_next_data = True - Whether or not to re-download the __NEXT_DATA__, which is required if the bot list has changed.
print(json.dumps(client.bot_names, indent=2))
"""
{
  "chinchilla": "ChatGPT",
  "a2": "Claude-instant",
  "capybara": "Assistant",
  "a2_100k": "Claude-instant-100k",
  "llama_2_7b_chat": "Llama-2-7b",
  "llama_2_13b_chat": "Llama-2-13b",
  "a2_2": "Claude-2-100k",
  "llama_2_70b_chat": "Llama-2-70b",
  "agouti": "ChatGPT-16k",
  "beaver": "GPT-4",
  "vizcacha": "GPT-4-32k",
  "acouchy": "Google-PaLM"
}
"""

Note that, on free accounts, Claude+ (a2_2) has a limit of 3 messages per day and GPT-4 (beaver) has a limit of 1 message per day. Claude-instant-100k (c2_100k) is completely inaccessible for free accounts. For all the other chatbots, there seems to be a rate limit of 10 messages per minute.

Using 3rd Party Bots:

To get a list of 3rd party bots, use client.explore_bots, which accepts the following arguments:

  • end_cursor = None - The cursor to use when fetching the list.
  • count = 25 - The number of bots that is returned.

The function will return a dict containing a list of bots and the cursor for the next page:

print(json.dumps(client.explore_bots(count=1), indent=2))
"""
{
  "bots": [
    {
      "id": "Qm90OjEwMzI2MDI=",
      "displayName": "leocooks",
      "deletionState": "not_deleted",
      "image": {
        "__typename": "UrlBotImage",
        "url": "https://qph.cf2.quoracdn.net/main-thumb-pb-1032602-200-uorvomwowfgmatdvrtwajtwwqlujmmgu.jpeg"
      },
      "botId": 1032602,
      "followerCount": 1922,
      "description": "Above average meals for average cooks, made simple by world-renowned chef, Leonardo",
      "__typename": "Bot"
    }
  ],
  "end_cursor": "1000172"
}
"""

To get a specific third party bot, you can use client.get_bot_by_codename, which accept's the bot's codename as its only argument.

client.get_bot_by_codename("JapaneseTutor")

Since display names are the same as the codenames for custom bots, you can simply pass the bot's display name into client.send_message to send it a message.

Creating New Bots:

You can create a new bot using the client.create_bot function, which accepts the following arguments:

  • handle - The handle of the new bot.
  • prompt = "" - The prompt for the new bot.
  • display_name = None - The display name for the new bot.
  • base_model = "chinchilla" - The model that the new bot uses. This must be either "chinchilla" (ChatGPT) or "a2" (Claude). If you've subscribed, you can use "beaver" (ChatGPT4) or "a2_2"` (Claude-2-100k).
  • description = "" - The description for the new bot.
  • intro_message = "" - The intro message for the new bot. If this is an empty string then the bot will not have an intro message.
  • prompt_public = True - Whether or not the prompt should be publicly visible.
  • pfp_url = None - The URL for the bot's profile picture. Currently, there is no way to actually upload a custom image using this library.
  • linkification = False - Whether or not the bot should turn some text in the response into clickable links.
  • markdown_rendering = True - Whether or not to enable markdown rendering for the bot's responses.
  • suggested_replies = False - Whether or not the bot should suggest possible replies after each response.
  • private = False - Whether or not the bot should be private.
  • temperature = None: - The temperature for the new bot.

Use these arguments if you want the new bot to use your own API (as detailed here):

  • api_key = None - The API key for the new bot.
  • api_bot = False - Whether or not the bot has API functionally enabled.
  • api_url = None - The API URL for the new bot.

A full example of how to create and edit bots is located at examples/create_bot.py.

new_bot = client.create_bot(bot_name, "prompt goes here", base_model="a2")

Editing a Bot:

You can edit a custom bot using the client.edit_bot function, which accepts the following arguments:

  • bot_id - The botId of the bot to edit. This can also be set to None.
  • handle - The handle for the bot you're editing.
  • prompt - The new prompt for the bot.
  • new_handle = None - The new handle for the bot. By default the handle will not change.
  • display_name = None - The new display name for the bot.
  • base_model = "chinchilla" - The new model that the bot uses. This must be either "chinchilla" (ChatGPT) or "a2" (Claude). If you've subscribed, you can use "beaver" (ChatGPT4) or "a2_2"` (Claude-2-100k).
  • description = "" - The new description for the bot.
  • intro_message = "" - The new intro message for the bot. If this is an empty string then the bot will not have an intro message.
  • prompt_public = True - Whether or not the prompt should be publicly visible.
  • pfp_url = None - The URL for the bot's profile picture. Currently, there is no way to actually upload a custom image using this library.
  • linkification = False - Whether or not the bot should turn some text in the response into clickable links.
  • markdown_rendering = True - Whether or not to enable markdown rendering for the bot's responses.
  • suggested_replies = False - Whether or not the bot should suggest possible replies after each response.
  • private = False - Whether or not the bot should be private.
  • temperature = None: - The new temperature for the this bot.

Bot API related arguments:

  • api_key = None - The new API key for the bot.
  • api_url = None - The new API URL for the bot.

A full example of how to create and edit bots is located at examples/create_bot.py.

edit_result = client.edit_bot(1086981, "bot_handle_here", base_model="a2")

Sending Messages:

You can use the client.send_message function to send a message to a chatbot, which accepts the following arguments:

  • chatbot - The codename of the chatbot. (example: capybara)
  • message - The message to send to the chatbot.
  • with_chat_break = False - Whether the conversation context should be cleared.
  • timeout = 20 - The max number of seconds in between received chunks until a RuntimeError is raised.
  • async_recv = True - Whether or not to make the receive_POST request async. If this is disabled, then there will be an extra wait of about 3 seconds after the message is complete.
  • suggest_callback = None - Callback for suggested replies. See examples/send_message.py for an example on how to use this.

The function is a generator which returns the most recent version of the generated message whenever it is updated.

Streamed Example:

message = "Summarize the GNU GPL v3"
for chunk in client.send_message("capybara", message):
  print(chunk["text_new"], end="", flush=True)

Non-Streamed Example:

message = "Summarize the GNU GPL v3"
for chunk in client.send_message("capybara", message):
  pass
print(chunk["text"])

You can also send multiple messages in parallel using threading and receive their responses separately, as demonstrated in /examples/parallel_messages.py. Note that if you send messages too fast, the server will give an error, but the request will eventually succeed.

The client.is_busy function can be used to check if there is currently a message being received.

Clearing the Conversation Context:

If you want to clear the the context of a conversation without sending a message, you can use client.send_chat_break. The only argument is the codename of the bot whose context will be cleared.

client.send_chat_break("capybara")

The function returns the message which represents the chat break.

Downloading Conversation History:

To download past messages in a conversation, use the client.get_message_history function, which accepts the following arguments:

  • chatbot - The codename of the chatbot.
  • count = 25 - The number of messages to download.
  • cursor = None - The message ID to start at instead of the latest one.

Note that if you don't specify a cursor, the client will have to perform an extra request to determine what the latest cursor is.

The returned messages are ordered from oldest to newest.

message_history = client.get_message_history("capybara", count=10)
print(json.dumps(message_history, indent=2))
"""
[
  {
    "node": {
      "id": "TWVzc2FnZToxMDEwNzYyODU=",
      "messageId": 101076285,
      "creationTime": 1679298157718888,
      "text": "",
      "author": "chat_break",
      "linkifiedText": "",
      "state": "complete",
      "suggestedReplies": [],
      "vote": null,
      "voteReason": null,
      "__typename": "Message"
    },
    "cursor": "101076285",
    "id": "TWVzc2FnZUVkZ2U6MTAxMDc2Mjg1OjEwMTA3NjI4NQ=="
  },
  ...
]
"""

Deleting Messages:

To delete messages, use the client.delete_message function, which accepts a single argument. You can pass a single message ID into it to delete a single message, or you can pass a list of message IDs to delete multiple messages at once.

#delete a single message
client.delete_message(96105719)

#delete multiple messages at once
client.delete_message([96105719, 96097108, 96097078, 96084421, 96084402])

Purging a Conversation:

To purge an entire conversation, or just the last few messages, you can use the client.purge_conversation function. This function accepts the following arguments:

  • chatbot - The codename of the chatbot.
  • count = -1 - The number of messages to be deleted, starting from the latest one. The default behavior is to delete every single message.
#purge just the last 10 messages
client.purge_conversation("capybara", count=10)

#purge the entire conversation
client.purge_conversation("capybara")

Purging All Conversations:

To purge every conversation in your account, use the client.purge_all_conversations function. This function doesn't need any arguments.

>>> client.purge_all_conversations()

Getting the Remaining Messages:

To get the number of messages remaining in the quota for a conversation, use the client.get_remaining_messages function. This function accepts the following arguments:

  • chatbot - The codename of the chatbot.

The function will return the number of messages remaining, or None if the bot does not have a quota.

>>> client.get_remaining_messages("beaver")
1

Misc:

Changing the Logging Level:

If you want to show debug messages, simply call poe.logger.setLevel.

import poe
import logging
poe.logger.setLevel(logging.INFO)

Setting a Custom User-Agent:

If you want to change the headers that are spoofed, set poe.headers after importing the library.

To use your browser's own headers, visit this site, and copy-paste its contents.

import poe
poe.headers = {
  "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0",
  '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',
  "Accept-Encoding": "gzip, deflate, br",
  'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  "Upgrade-Insecure-Requests": "1"
}

The following headers will be ignored and overwritten:

{
  "Referrer": "https://poe.com/",
  "Origin": "https://poe.com",
  "Host": "poe.com",
  "Cache-Control": "no-cache",
  "Sec-Fetch-Dest": "document",
  "Sec-Fetch-Mode": "navigate",
  "Sec-Fetch-Site": "same-origin",
  "Sec-Fetch-User": "?1",
}

Previously, this was done through poe.user_agent, but that variable is now completely ignored.

You'd also want to change poe.client_identifier to match the user-agent that you have set. See the Python-TLS-Client documentation for some sample values. Keep in mind that spoofing Chrome/Firefox versions >= 110 may be detectable.

poe.client_identifier = "chrome_107"

Setting a Custom Device ID:

If you want to change the device ID that is being spoofed, you can use the poe.set_device_id, which accepts the following arguments:

  • user_id - The user ID of the account you want to change the device ID for. The user ID can be found at client.viewer["poeUser"]["id"].
  • device_id - The new device ID. This is a 32 character UUID string in the following format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
poe.set_device_id("UGMlVXqlcLYyMOATMDsKNTMz", "6d659b04-043a-41f8-97c7-fb7d7fe9ad34")

The device IDs are saved to ~/.config/poe-api/device_id.json on Unix-like systems, and C:\Users\<user>\AppData\Roaming\poe-api\device_id.json on Windows.

Additionally, the poe.get_device_id function or client.device_id can be used to retrieve the saved device ID.

>>> poe.get_device_id("UGMlVXqlcLYyMOATMDsKNTMz")
#6d659b04-043a-41f8-97c7-fb7d7fe9ad34

>>> client.device_id
#6d659b04-043a-41f8-97c7-fb7d7fe9ad34

Copyright:

This program is licensed under the GNU GPL v3. Most code, with the exception of the GraphQL queries, has been written by me, ading2210.

Reverse engineering the poe-tag-id header has been done by xtekky in PR #39.

The client.get_remaining_messages function was written by Snowad14 in PR #46.

Detection avoidance and fetching the third party bots has been done by acheong08 in PR #79.

Most of the GraphQL queries are taken from muharamdani/poe, which is licensed under the ISC License.

Copyright Notice:

ading2210/poe-api: a reverse engineered Python API wrapper for Quora's Poe
Copyright (C) 2023 ading2210

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

poe-api's People

Contributors

200-0k avatar ading2210 avatar canxin121 avatar classicoldsong avatar cohee1207 avatar mak448a avatar nikitolproject avatar snowad14 avatar thelime1 avatar tic-top avatar xtekky avatar yarkis01 avatar zentixua avatar zukixa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

poe-api's Issues

A suggestion about function send_message

poe-api/poe-api/src/poe.py

Lines 283 to 284 in eb4125a

while None in self.active_messages.values():
time.sleep(0.01)

In some cases, a message may not receive a reply due to network issues, which can cause the entire Q&A process to become stuck. To avoid this, it might be helpful to consider implementing a waiting timeout mechanism.

custom bots?

poe added new feature - custom bots.
how i can access to him with api?
image

ERROR:websocket:Connection to remote host was lost. - goodbye

The connection will be disconnected if I have the api connected for certain period of time without any input, how to resolve it?

import poe

client = poe.Client(config.TOKEN")

message =''
reply ='CHATGPT: '

print('Please enter your question:')
while True:  
    reply ='CHATGPT: '
    message = input('USER: ')
    if message != 'exit':
        for chunk in client.send_message("chinchilla", message):
            print(chunk["text_new"], end="", flush=True)
            reply = reply + chunk["text_new"]
        print('\n')

    else:
        break

ERROR:
ERROR:websocket:Connection to remote host was lost. - goodbye
ERROR:websocket:error from callback <bound method Client.on_ws_close of <poe.Client object at 0x000002496EFD7F50>>: Client.on_ws_close() takes 3 positional arguments but 4 were given
ERROR:websocket:error from callback <bound method Client.on_ws_close of <poe.Client object at 0x000002496EFD7F50>>: Client.on_ws_close() takes 3 positional arguments but 4 were given
Exception in thread Thread-4 (ws_run_thread):
Traceback (most recent call last):
File "C:\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
self.run()
File "C:\Python311\Lib\threading.py", line 975, in run
self._target(*self._args, **self._kwargs)
File "C:\Python311\Lib\site-packages\poe.py", line 182, in ws_run_thread
self.ws.run_forever(**kwargs)
File "C:\Python311\Lib\site-packages\websocket_app.py", line 354, in run_forever
raise WebSocketException("socket is already opened")

If you regenerate a long answer immediately after generating it, the second answer may not be fully displayed

For example

Code

message = "I hope you can generate an article about the Pacific Ocean, about 300 words"
for chunk in client.send_message("capybara", message):
    pass
print(chunk["text"])
message = "I hope you can generate an article about the Atlantic Ocean"
for chunk in client.send_message("capybara", message):
    pass
print(chunk["text"])

Output

Certainly, here's an article about the Pacific Ocean:

# The Pacific Ocean: A Vast and Diverse Body of Water

The Pacific Ocean is the largest body of water on Earth, covering an area of approximately 63.8 million square miles. It stretches from the Arctic Ocean in the north to the Southern Ocean in the south, and from the western shores of North and South America to the eastern shores of Asia and Australia.

The Pacific Ocean is home to an incredible variety of marine life, from tiny plankton to massive whales, and from colorful coral reefs to deep-sea trenches. Some of the most famous marine animals in the world inhabit the Pacific, including great white sharks, blue whales, and sea turtles.

In addition to its diverse marine life, the Pacific Ocean is also home to a number of important islands and archipelagos, including Hawaii, Fiji, and the Galapagos Islands. These islands are not only important for their ecological significance, but also for their cultural and historical importance to the people who live on them.

The Pacific Ocean is also an important source of natural resources, including fish, oil, and minerals. Many countries around the Pacific Rim rely on the ocean for their livelihoods, and the Pacific is a major shipping route for goods traveling between Asia and North America.

Despite its vast size and importance, the Pacific Ocean is facing a number of threats, including overfishing, pollution, and climate change. Scientists and policymakers are working to address these challenges and protect the health of this important body of water for future generations.

One of the most significant environmental challenges facing the Pacific Ocean is plastic pollution. The ocean's currents have created vast areas of floating plastic waste, known as the Great Pacific Garbage Patch. This pollution not only harms marine life, but also poses a threat to human health, as microplastics are now found in seafood and drinking water.

Another challenge facing the Pacific Ocean is the acidification of its waters. As carbon dioxide from human activities dissolves in ocean waters, it lowers the pH of the water, making it more acidic. This can have devastating effects on marine ecosystems and the species that depend on them.

Despite these challenges, there is hope for the Pacific Ocean. Efforts are underway to address plastic pollution and reduce carbon emissions to mitigate the effects of climate change. Conservation measures are also being implemented to protect marine life and their habitats.

In conclusion, the Pacific Ocean is a vast and diverse body of water that is home to an incredible array of marine life, important islands and archipelagos, and valuable natural resources. Despite the challenges it faces, the Pacific remains one of the most important and awe-inspiring features of our planet. It is up to each of us to do our part to protect this vital resource for future generations.
Certainly, here's an article about the Atlantic 

limit message to Last 50 messages

I like this great project, but I want to download all message about 200 messages,message_history = client.get_message_history("capybara", count=1000)
but the output only shows last 50 messages, could you lift up the limit?

AttributeError: 'NoneType' object has no attribute 'group'

Seems like the static variables in the HTML page are constantly changing
image
image

However, a minor change in the regex would solve this issue.

script_regex = r'<script>if\("undefined"==typeof window\)throw new Error;(.+)</script>'

script_regex = r'<script>if\(.+\)throw new Error;(.+)</script>'
script_text = re.search(script_regex, html).group(1)
key_regex = r'var .="([0-9a-f]+)",'
key_text = re.search(key_regex, script_text).group(1)
cipher_regex = r'.\[(\d+)\]=.\[(\d+)\]'
cipher_pairs = re.findall(cipher_regex, script_text)

Datastreaming with flask.

I posted this earlier but it was closed so I apologize for reopening.

#29 (comment)

I have tried following the flask documentation to stream the generator but it's not working. I am not sure if it has something to do with the addon itself or if this is not possible but if the code contains any mention of "poe_client.send_message" I instantly receive "WARNING:root:SendMessageMutation returned an error: Server Error | Retrying (1/20)". I've tried streaming with context, just printing to the console to see if it will work, literally everything and I have not found a working way to do it. If someone could help me figure out a working way to stream the output to a flask page I would appreciate it. Thanks!

poe.com api patch solved

@ading2210

from json import dumps
from hashlib import md5

a = {
    "queryName":"AnnotateWithIdsProviderQuery",
    "variables":{},
    "query":"query AnnotateWithIdsProviderQuery {\n  viewer {\n    canAnnotateWithIds: booleanGate(gateName: \"annotate_with_ids\")\n    id\n  }\n}\n"
}

form_key = '0eed5c7455f62fe3dbec3e300de99c10'
global_key      = 'WpuLMiXEKKE98j56k'

base_string = dumps(a, separators = (',', ':')) + form_key + global_key
print(md5(base_string.encode()).hexdigest()) 

do not have time to make a pull request

Websocket error

Api was working perfectly before and i would get results for my queries on the shell or on a python script, it seems they have patched retrieval. I can still send questions, but i get the following error once i run it

WARNING:root:Websocket returned error: 'data'
ERROR:websocket:error from callback <bound method Client.on_message of <poe.Client object at 0x000001BDADC18EE0>>: 'data'
WARNING:root:Websocket returned error: 'data'
ERROR:websocket:error from callback <bound method Client.on_message of <poe.Client object at 0x000001BDADC18EE0>>: 'data'

I have to go on the site to view the answers.

cannot get gql_POST, status code 400

This started happening to me just now, and it prevents me from using the module.

WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (2/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (3/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (4/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (5/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (6/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (7/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (8/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (9/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (10/10)...
Traceback (most recent call last):
  File "C:\Users\alfred\e\Python\server\main 3.11.py", line 54, in <module>
    ai = poe.Client(token)
  File "C:\Users\alfred\AppData\Local\Programs\Python\Python311\Lib\site-packages\poe.py", line 79, in __init__
    self.subscribe()
  File "C:\Users\alfred\AppData\Local\Programs\Python\Python311\Lib\site-packages\poe.py", line 160, in subscribe
    result = self.send_query("SubscriptionsMutation", {
  File "C:\Users\alfred\AppData\Local\Programs\Python\Python311\Lib\site-packages\poe.py", line 147, in send_query
    r = request_with_retries(self.session.post, self.gql_url, json=payload, headers=self.gql_headers)
  File "C:\Users\alfred\AppData\Local\Programs\Python\Python311\Lib\site-packages\poe.py", line 37, in request_with_retries
      raise RuntimeError(f"Failed to download {url} too many times.")
RuntimeError: Failed to download https://poe.com/api/gql_POST too many times.

I went to the URL that it had specified (https://poe.com/api/gql_POST) and the only thing on the page was {"error":"Method Not Allowed"}

If there is no message for a long time, the connection will be broken

Is there any way to keep it active all the time?

error msg:

  File "/usr/local/lib/python3.9/dist-packages/poe.py", line 260, in send_message
    raise RuntimeError("Response timed out.")
RuntimeError: Response timed out.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/chatgpt/chatgpt.py", line 82, in askViaPoe
    for data in client.send_message(node, q, with_chat_break=False):
  File "/usr/local/lib/python3.9/dist-packages/poe.py", line 260, in send_message
    raise RuntimeError("Response timed out.")
RuntimeError: Response timed out.

Poe gpt-4 loophole got Patched

Bad News! Editing or creating a bot with a free account using a paid bot type returns error

WARNING:root:PoeBotCreateMutation returned an error: Server Error | Retrying (1/20)
WARNING:root:PoeBotCreateMutation returned an error: Server Error | Retrying (2/20)

Related to #4

Am i being blocked? JSONDecodeError

Thank you all for your attention. I have used my paid poe account to do some translations for my dataset. And after running some conversations. The following shows:

Output exceeds the [size limit](command:workbench.action.openSettings?%5B%22notebook.output.textLineLimit%22%5D). Open the full output data [in a text editor](command:workbench.action.openLargeOutput?e08f65f3-1db0-488c-b3d2-977c613611ce)
---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
File /usr/local/lib/python3.8/dist-packages/requests/models.py:971, in Response.json(self, **kwargs)
    970 try:
--> 971     return complexjson.loads(self.text, **kwargs)
    972 except JSONDecodeError as e:
    973     # Catch JSON-related errors and raise as requests.JSONDecodeError
    974     # This aliases json.JSONDecodeError and simplejson.JSONDecodeError

File /usr/lib/python3.8/json/__init__.py:357, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    354 if (cls is None and object_hook is None and
    355         parse_int is None and parse_float is None and
    356         parse_constant is None and object_pairs_hook is None and not kw):
--> 357     return _default_decoder.decode(s)
    358 if cls is None:

File /usr/lib/python3.8/json/decoder.py:337, in JSONDecoder.decode(self, s, _w)
    333 """Return the Python representation of ``s`` (a ``str`` instance
    334 containing a JSON document).
    335 
    336 """
--> 337 obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338 end = _w(s, end).end()

File /usr/lib/python3.8/json/decoder.py:355, in JSONDecoder.raw_decode(self, s, idx)
...
    973     # Catch JSON-related errors and raise as requests.JSONDecodeError
    974     # This aliases json.JSONDecodeError and simplejson.JSONDecodeError
--> 975     raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

chat work but purge error

purge_conversation or get history got an error,but send message work:

import poe
client = poe.Client("","")
print(client.bot_names)
message = "hi"
for chunk in client.send_message("chinchilla", message):
print(chunk["text_new"], end="", flush=True)
client.purge_conversation("chinchilla", count=2)

{'capybara': 'Sage', 'beaver': 'GPT-4', 'a2_2': 'Claude+', 'a2': 'Claude-instant', 'chinchilla': 'ChatGPT', 'nutria': 'Dragonfly'}
Hello again! Is there something you would like to ask or discuss?Traceback (most recent call last):
File "e:\desktop\chatGPT\testpoe.py", line 9, in
his=client.purge_conversation("chinchilla", count=2)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\C\AppData\Local\Programs\Python\Python311\Lib\site-packages\poe.py", line 338, in purge_conversation
last_messages = self.get_message_history(chatbot, count=50)[::-1]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\C\AppData\Local\Programs\Python\Python311\Lib\site-packages\poe.py", line 325, in get_message_history
return result["data"]["node"]["messagesConnection"]["edges"]
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not subscriptable

Wondering how to datastream on flask server.

I am wondering if it's possible to stream the output to a flask page? I have tried a few different methods but none seem to be working. If anyone could help I would appreciate it!

Here is what I was using to try and stream:
https://gist.github.com/fengsp/19e1890fd80e16edc8a4

Here's some things I tried that failed.

(Just to see if it was the poe client thing (it was))
@app.route("/chatbot2", methods=['GET', 'POST'])
def chatbot2():
poe_client = poe.Client("TOKEN")
chatbot_2_question = flask.request.form.get('question')
return poe_client.send_message("capybara", chatbot_2_question, with_chat_break=True)

@app.route("/chatbot2", methods=['GET', 'POST'])
def chatbot2():
poe_client = poe.Client("token")
chatbot_2_question = flask.request.form.get('question')
for chunk in poe_client.send_message("capybara", chatbot_2_question, with_chat_break=True):
print(chunk["text_new"], end="", flush=True)
return "ok"

@app.route("/chatbot2", methods=['GET', 'POST'])
def chatbot2():
poe_client = poe.Client("token")
chatbot_2_question = flask.request.form.get('question')
def gen2():
for chunk in poe_client.send_message("capybara", chatbot_2_question, with_chat_break=True):
yield chunk["text_new"]
return Response(gen2())

error

ERROR:websocket:error from callback <bound method Client.on_message of <poe.Client object at 0x0000022514959790>>: 'data'

Stopped working (becoming a daily pattern)

I got this bunch of error codes:

Sent message:  hello
127.0.0.1 - - [16/Apr/2023 00:48:55] "POST /chatbot HTTP/1.1" 500 -
INFO:werkzeug:127.0.0.1 - - [16/Apr/2023 00:48:55] "POST /chatbot HTTP/1.1" 500 -
Traceback (most recent call last):
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask\app.py", line 2551, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask\app.py", line 2531, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask_cors\extension.py", line 165, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
                                                ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask\app.py", line 2528, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask\app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask_cors\extension.py", line 165, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
                                                ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask\app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\flask\app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\OneDrive\Documents\GitHub\gptCensorFree\chatbot_api.py", line 34, in chatbot
    client = poe.Client(token)
             ^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\poe.py", line 69, in __init__
    self.next_data = self.get_next_data(overwrite_vars=True)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\everp\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\poe.py", line 91, in get_next_data
    self.formkey = next_data["props"]["formkey"]
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'formkey'

Great work!

I just found the repo. Well done! i'm currently playing with it. Do you know what the daily limits are?

high interneet usage

after a few hours internet usage was around 500mb
is that normal?
how to save bandwidth

ModuleNotFoundError: No module named 'poe'

I installed these requirements:
pip3 install poe-client
pip3 install poe-api

Getting this error:
poe-api\examples>temporary_message.py
Traceback (most recent call last):
File "D:\poe-api\examples\temporary_message.py", line 1, in
import poe
ModuleNotFoundError: No module named 'poe'

I can't make the bot with beaver type; server doesn't respond to my request

DEBUG:urllib3.connectionpool:https://poe.com:443 "POST /api/gql_POST HTTP/1.1" 200 89
WARNING:root:PoeBotEditMutation returned an error: Server Error | Retrying (20/20)
Traceback (most recent call last):
File "/workspaces/poe-api-wk/main.py", line 17, in
edit_result = client.edit_bot(bot_id, handle, base_model="a2_2")
File "/home/codespace/.python/current/lib/python3.10/site-packages/poe.py", line 459, in edit_bot
result = self.send_query("PoeBotEditMutation", {
File "/home/codespace/.python/current/lib/python3.10/site-packages/poe.py", line 206, in send_query
raise RuntimeError(f'{query_name} failed too many times.')
RuntimeError: PoeBotEditMutation failed too many times.

No response when connecting with POE

Failed when execute the following code, seems that some wrong with POE server

import poe

token = "oEIU***" # token
print('start')
client = poe.Client(token)
#client = poe.Client(token, proxy="socks5://178.62.100.151:59166")
print(client.bot_names)

Result

start
nothing appears ... stuck in some where

an error

image
Correct an error, here in the red circle is “Client”

Tokens expiring

Seems like some tokens are expired after a set amount of time without logging in the web directly? This renders temporary emails useless because they removed password and sends an auth code to login. Do you think there could be a way to bypass this / keep the token alive somehow?

Multiple conversation threads

I'm trying to wrap my head about this.
Basically, I'd like to have multiple independent conversations without chat breaks with one bot.
It's probably not possible, I would need multiple tokens to do so, right?

Login using email and password credentials instead of cookie token

Hi, is it possible to login using email and password credentials instead of cookie token? I have two poe accounts, and when I log into another, the other cookie token will become invalid. In order to allow to use api, does that mean I should never delete the cookies after using it?

Websocket connection is not proxied (original title: Connection to websocket timed out, please fix)

The error is as follows, the code was fine the last time it was run

Traceback (most recent call last):
  File "other\aa.py", line 253, in <module>
    main()
  File "other\aa.py", line 248, in main
    for chunk in client.send_message("capybara", message, with_chat_break=True):
  File "other\aa.py", line 159, in send_message
    ws.connect(url, header={"User-Agent": user_agent})
  File "Python\Python310\site-packages\websocket\_core.py", line 244, in connect
    self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
  File "Python310\site-packages\websocket\_http.py", line 130, in connect
    sock = _open_socket(addrinfo_list, options.sockopt, options.timeout)
  File "Python310\site-packages\websocket\_http.py", line 197, in _open_socket
    raise error
  File "Python310\site-packages\websocket\_http.py", line 185, in _open_socket
    sock.connect(address)
TimeoutError: [WinError 10060] The connection attempt failed because the connecting party did not reply correctly after a certain period of time or the connected host did not respond.

websocket error

hello, an error occurs when trying to send a request. the standard example is used. token is valid. however, the messages are sent and visible on the poe site, and there is a response to them. it looks like the script can't get a response

log:

INFO:root:Downloading next_data...
INFO:root:Downloading channel data...
INFO:websocket:Websocket connected
INFO:root:Downloading https://poe.com/_next/data/Hg4LfZ0u8OBG2VQvcnVk7/sage.json
INFO:root:Downloading https://poe.com/_next/data/Hg4LfZ0u8OBG2VQvcnVk7/gpt-4.json
INFO:root:Downloading https://poe.com/_next/data/Hg4LfZ0u8OBG2VQvcnVk7/claude+.json
INFO:root:Downloading https://poe.com/_next/data/Hg4LfZ0u8OBG2VQvcnVk7/claude-instant.json
INFO:root:Downloading https://poe.com/_next/data/Hg4LfZ0u8OBG2VQvcnVk7/chatgpt.json
INFO:root:Downloading https://poe.com/_next/data/Hg4LfZ0u8OBG2VQvcnVk7/dragonfly.json
INFO:root:Subscribing to mutations
INFO:root:Sending message to capybara: Summarize the GNU GPL v3
ERROR:websocket:error from callback <bound method Client.on_message of <poe.Client object at 0x0000020B5DDA4100>>: 'data'
WARNING:root:Websocket returned error: 'data'
INFO:websocket:Websocket connected
ERROR:websocket:error from callback <bound method Client.on_message of <poe.Client object at 0x0000020B5DDA4100>>: 'data'
WARNING:root:Websocket returned error: 'data'

Code:

import poe
import logging

poe.logger.setLevel(logging.INFO)
poe.user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"

client = poe.Client('token')

message = "Summarize the GNU GPL v3"
for chunk in client.send_message("capybara", message):
  print(chunk["text_new"], end="", flush=True)

image

Server returned a status code of 400

It seems that poe changed the query mechanism and the current version of th library does not pass the authentication.
Traceback:

WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (1/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (2/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (3/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (4/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (5/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (6/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (7/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (8/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (9/10)...
WARNING:root:Server returned a status code of 400 while downloading https://poe.com/api/gql_POST. Retrying (10/10)...
Traceback (most recent call last):
   File "./script.py", line 14, in <module>
     client = poe.Client("REDACTED")
   File "lib/python3.9/site-packages/poe.py", line 79, in __init__
     self.subscribe()
   File "lib/python3.9/site-packages/poe.py", line 160, in subscribe
     result = self.send_query("SubscriptionsMutation", {
   File "lib/python3.9/site-packages/poe.py", line 147, in send_query
     r = request_with_retries(self.session.post, self.gql_url, json=payload, headers=self.gql_headers)
   File "lib/python3.9/site-packages/poe.py", line 37, in request_with_retries
     raise RuntimeError(f"Failed to download {url} too many times.")
 RuntimeError: Failed to download https://poe.com/api/gql_POST too many times.

ERROR:websocket:Connection to remote host was lost. - goodbye

I am using version 2.9
I have this error after running for several hours, is it inevitable that the connection will close itself?

USER: ERROR:websocket:Connection to remote host was lost. - goodbye
WARNING:root:Websocket closed with status None: None

Does poe.com support embedding api?

such as:

'''python
openai.Embedding.create(
input=[text],
engine=CFG.get_azure_deployment_id_for_model(
"text-embedding-ada-002"
),
)
'''

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

full error code:
Traceback (most recent call last):
File "/storage/emulated/0/New_folder/writepozaza.py", line 2, in
client = poe.Client("my token")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/poe.py", line 76, in init
self.bots = self.get_bots()
^^^^^^^^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/poe.py", line 117, in get_bots
chat_data = self.get_bot(bot["displayName"].lower())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/poe.py", line 105, in get_bot
chat_data = r.json()["pageProps"]["payload"]["chatOfBotDisplayName"]
^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/requests/models.py", line 898, in json
return complexjson.loads(self.text, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/json/init.py", line 346, in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/data/com.termux/files/usr/lib/python3.11/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

SSLError

I would like to ask that I have obtained the token now, but an error occurred while running the example:
running code:

import poe
import logging
import sys

#send a message and immediately delete it

client = poe.Client("my token")

message = "Who are you?"
for chunk in client.send_message("capybara", message, with_chat_break=True):
  print(chunk["text_new"], end="", flush=True)

#delete the 3 latest messages, including the chat break
client.purge_conversation("capybara", count=3)

exception:
Traceback (most recent call last):
File "C:/Users/hammour/Desktop/poe-api-main/examples/temporary_message.py", line 7, in
client = poe.Client("my token", proxy="socks5://127.0.0.1:7890")
File "C:\Users\hammour\Desktop\poe-api-main\poe-api\src\poe.py", line 73, in init
self.next_data = self.get_next_data()
File "C:\Users\hammour\Desktop\poe-api-main\poe-api\src\poe.py", line 89, in get_next_data
r = request_with_retries(self.session.get, self.home_url)
File "C:\Users\hammour\Desktop\poe-api-main\poe-api\src\poe.py", line 32, in request_with_retries
r = method(*args, **kwargs)
File "D:\Anaconda\envs\cvstudy\lib\site-packages\requests\sessions.py", line 555, in get
return self.request('GET', url, **kwargs)
File "D:\Anaconda\envs\cvstudy\lib\site-packages\requests\sessions.py", line 542, in request
resp = self.send(prep, **send_kwargs)
File "D:\Anaconda\envs\cvstudy\lib\site-packages\requests\sessions.py", line 655, in send
r = adapter.send(request, **kwargs)
File "D:\Anaconda\envs\cvstudy\lib\site-packages\requests\adapters.py", line 510, in send
raise ProxyError(e, request=request)
requests.exceptions.ProxyError: HTTPSConnectionPool(host='poe.com', port=443): Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.', OSError(0, 'Error')))

It seems like this code is wrong:
client = poe.Client("my token")

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.