From da00dd8bbbd0c4c5fcbc5e1abfbd99a88f0edb0f Mon Sep 17 00:00:00 2001
From: orlnub123 <30984274+orlnub123@users.noreply.github.com>
Date: Thu, 19 Oct 2017 21:03:15 +0300
Subject: [PATCH] 2.0.0
---
README.rst | 122 ++++++++++-----------------------
cleverbot/__init__.py | 3 +-
cleverbot/_async.py | 125 ----------------------------------
cleverbot/abc.py | 38 +++++++++++
cleverbot/async_/__init__.py | 1 +
cleverbot/async_/cleverbot.py | 94 +++++++++++++++++++++++++
cleverbot/cleverbot.py | 103 ++++++----------------------
cleverbot/errors.py | 4 +-
setup.py | 5 +-
9 files changed, 193 insertions(+), 302 deletions(-)
delete mode 100644 cleverbot/_async.py
create mode 100644 cleverbot/abc.py
create mode 100644 cleverbot/async_/__init__.py
create mode 100644 cleverbot/async_/cleverbot.py
diff --git a/README.rst b/README.rst
index cda24a4..3a56eae 100644
--- a/README.rst
+++ b/README.rst
@@ -27,41 +27,30 @@ Example
Installing
----------
-Install it from PyPI with pip:
+Install it normally from PyPI with pip:
::
pip install cleverbot.py
-Or install it from GitHub using git:
-
-::
-
- git clone https://github.com/orlnub123/cleverbot.py
- cd cleverbot.py
- python setup.py install
-
-If you don't have pip or git you can also download the source and run ``python
-setup.py install`` on it.
-
-To install Cleverbot with asynchronous support you'll have to use pip and be on
-Python 3.4.2+.
+Or install it with the asynchronous dependencies (Python 3.4.2+ only):
::
pip install cleverbot.py[async]
-This is not required if you already have aiohttp 1.0.0 or later.
-
**Requirements:**
- Python 3.2+ or 2.6+
-- `A Cleverbot API key `_
+- `A Cleverbot API key `_
**Dependencies:**
- requests 1.0.0+
-- aiohttp 1.0.0+ (Optional, for asynchronous support)
+
++ **Asynchronous:**
+
+ - aiohttp 1.0.0+
Usage
-----
@@ -72,8 +61,6 @@ First import the package:
import cleverbot
---------------
-
Then initialize Cleverbot with your API key and optionally a cleverbot state
and or timeout:
@@ -81,14 +68,20 @@ and or timeout:
cb = cleverbot.Cleverbot('YOUR_API_KEY', cs='76nxdxIJ02AAA', timeout=60)
-The cleverbot state is the encoded state of the conversation so far and
-includes the whole conversation history up to that point.
+The cleverbot state is the encoded state of the conversation that you get from
+talking to Cleverbot and includes the whole conversation history.
+
+If you have the asynchronous dependencies and want to use Cleverbot
+asynchronously import ``cleverbot.async_`` and initialize Cleverbot from
+``cleverbot.async_.Cleverbot`` instead. The only differences are that ``say``
+is a coroutine and that you can pass an event loop to Cleverbot with a ``loop``
+keyword argument.
--------------
You can now start talking to Cleverbot.
-Get the reply from the input:
+Get the reply from the request:
.. code:: py
@@ -101,19 +94,10 @@ Or alternatively get it later:
cb.say("Hello")
reply = cb.output
-If you want to talk to Cleverbot asynchronously use ``asay`` instead:
-
-.. code:: py
-
- await cb.asay("Hello")
-
-``asay`` only works if you're on Python 3.4.2+ and have aiohttp installed.
-Experience with asyncio is recommended as you'll have to run it in an event
-loop.
-
-A big benefit of using ``asay`` is that it allows multiple requests to be sent
-at once instead of waiting for the previous request to return a response which
-can take significantly longer.
+You can also pass in keyword arguments such as ``cs`` to change the
+conversation, or ``vtext`` to change the current conversation's history. Read
+the "Parameters" section of `the official Cleverbot API docs
+`_ for more information.
--------------
@@ -127,7 +111,8 @@ As an example:
``cleverbot.errors.APIError: Missing or invalid API key or POST request, please
visit www.cleverbot.com/api``
-You can get the error message and additionally the HTTP status like so:
+You can get the error message and additionally the HTTP status from the error
+like so:
.. code:: py
@@ -136,76 +121,39 @@ You can get the error message and additionally the HTTP status like so:
except cleverbot.APIError as error:
print(error.error, error.status)
-This is also applicable to ``Timeout`` where you can get the defined timeout
+This is similar for ``Timeout`` where you can get the defined timeout
value with ``error.timeout``.
-Also, all Cleverbot errors subclass ``CleverbotError`` so you can use it to
-catch everything Cleverbot related.
+Additionally, all Cleverbot errors subclass ``CleverbotError`` so you can use
+it to catch every Cleverbot related error.
--------------
-Print out all of the data Cleverbot gained from the previous conversation:
-
-.. code:: py
-
- print(cb.data)
-
-Take note of the ``cs`` key as we'll use it to save the conversation in the
-next section.
-
-To access the data you can either get them from an attribute or directly get
-them from ``cb.data``:
+To access the data gained from the conversations you can either get them from
+an attribute as shown previously or directly get them from ``cb.data``:
.. code:: py
- cb.output == cb.data['output']
+ cb.conversation_id == cb.data['conversation_id']
However modifying the data with an attribute is only applicable to the
-cleverbot state.
-
-To get a list of all of the keys' descriptions either take a look at the
-``_query`` method's docstring in cleverbot.py or go to the 'JSON Reply' section
-in `the official Cleverbot API docs `_.
-
---------------
+cleverbot state by using the ``cs`` attribute.
-Save the conversation in preparation for a reset:
+For a list of all of the data's items' descriptions go to the "JSON Reply"
+section in `the official Cleverbot API docs
+`_.
-.. code:: py
-
- cs = cb.cs
-
-Reset Cleverbot, deleting all of the data it's gained from the previous
-conversations:
+To reset the data you can simply do the following:
.. code:: py
cb.reset()
-Note that if you try to get the cleverbot state now you'll get an error:
-
-``AttributeError: 'Cleverbot' object has no attribute 'cs'``
-
-Now start right where you left off by setting the cleverbot state you saved
-earlier:
-
-.. code:: py
-
- cb.cs = cs
-
-Or by setting it when creating a new Cleverbot instance:
-
-.. code:: py
-
- cb = cleverbot.Cleverbot('YOUR_API_KEY', cs=cs)
-
--------------
-When you're all done, close Cleverbot's connection to the API:
+When you're done with the current instance of Cleverbot, close Cleverbot's
+connection to the API:
.. code:: py
cb.close()
-
-This should only be done when you're not going to use the current instance of
-Cleverbot anymore.
diff --git a/cleverbot/__init__.py b/cleverbot/__init__.py
index 756207f..3c65f6e 100644
--- a/cleverbot/__init__.py
+++ b/cleverbot/__init__.py
@@ -1,5 +1,4 @@
-__all__ = ['Cleverbot', 'CleverbotError', 'APIError', 'DecodeError', 'Timeout']
-__version__ = '1.3.0'
+__version__ = '2.0.0'
from .cleverbot import Cleverbot
from .errors import CleverbotError, APIError, DecodeError, Timeout
diff --git a/cleverbot/_async.py b/cleverbot/_async.py
deleted file mode 100644
index 37e0977..0000000
--- a/cleverbot/_async.py
+++ /dev/null
@@ -1,125 +0,0 @@
-import asyncio
-
-import aiohttp
-import requests
-
-from . import __version__
-from .errors import APIError, DecodeError, Timeout
-
-
-def __init__(self, key, **kwargs):
- """Initialize Cleverbot with the given arguments.
-
- Arguments:
- key: The key argument is always required. It is your API key.
- cs: The cs argument stands for "cleverbot state". It is the encoded
- state of the conversation so far and includes the whole
- conversation history up to that point.
- timeout: How many seconds to wait for the API to send data before
- giving up and raising an error.
- loop: The event loop used for asay.
- **kwargs: Keyword arguments to pass into requests.Session.get and
- aiohttp.ClientSession.get
- """
- self.session = requests.Session()
- loop = kwargs.pop('loop', asyncio.get_event_loop())
- self.asession = aiohttp.ClientSession(loop=loop)
- self.key = key
- self.data = {}
- if 'cs' in kwargs:
- self.data['cs'] = kwargs.pop('cs')
- self.timeout = kwargs.pop('timeout', None)
- self.kwargs = kwargs
-
-
-@asyncio.coroutine
-def asay(self, text, **vtext):
- """Talk to Cleverbot asynchronously.
-
- Arguments:
- text: The text argument is what you want to say to Cleverbot, such as
- "hello".
- **vtext: If you wish to override the conversation history, you can pass
- in keyword arguments like vtext2 to override the last thing the bot
- said, vtext3 for the previous thing the user said, and so on.
-
- Returns:
- Cleverbot's reply.
-
- Raises:
- APIError: A Cleverbot API error occurred.
- 401: Unauthorised due to invalid API key.
- 404: API not found.
- 413: Request too large if you send a request over 16KB.
- 502 or 504: Unable to get reply from API server, please contact us.
- 503: Too many requests from a single IP address or API key.
- DecodeError: An error occurred while reading the reply.
- Timeout: The request timed out.
- """
- params = {
- 'key': self.key,
- 'input': text,
- 'wrapper': 'cleverbot.py'
- }
- try:
- params['cs'] = self.data['cs']
- except KeyError:
- pass
- if vtext:
- params.update(vtext)
- return (yield from self._aquery(params))
-
-
-def close(self):
- """Close the connections to the API."""
- self.session.close()
- self.asession.close()
-
-
-@asyncio.coroutine
-def _aquery(self, params):
- """Get Cleverbot's reply and store the data in a dictionary.
-
- Keys:
- cs: State of the conversation so far, which contains an encoded copy of
- the conversation id and history.
- interaction_count: How many pairs of bot/user interactions have
- occurred so far.
- input: The entire user input, with any spaces trimmed off both ends.
- output: Cleverbot's reply.
- conversation_id: Identifier for this conversation between user and bot.
- errorline: Any error information from Cleverbot, this is different from
- general the general errors described below.
- time_taken: The number of milliseconds the bot took to respond.
- time_elapsed: Approximate number of seconds since conversation started.
- interaction_1 to interaction_50: Record of the previous interactions.
- The interaction variables come in pairs. For example interaction_1
- contains the last thing the user said, and interaction_1_other is
- the bot's reply. interaction_2 was the user's previous input and so
- on. So to read the whole conversation in order, you have to display
- the interaction variables in reverse order, eg interaction_5,
- interaction_5_other, interaction_4, interaction_4_other, etc. Note
- that if an interaction didn't occur, the interaction_other will not
- be defined.
- """
- headers = {
- 'User-Agent': 'cleverbot.py/' + __version__ + ' '
- '(+https://github.com/orlnub123/cleverbot.py)'
- }
- try:
- reply = yield from self.asession.get(
- self.url, params=params, headers=headers, timeout=self.timeout,
- **self.kwargs)
- except asyncio.TimeoutError:
- raise Timeout(self.timeout)
- else:
- try:
- data = yield from reply.json()
- except ValueError as error:
- raise DecodeError(error)
- else:
- if reply.status == 200:
- self.data = data
- return data['output']
- else:
- raise APIError(data['error'], data['status'])
diff --git a/cleverbot/abc.py b/cleverbot/abc.py
new file mode 100644
index 0000000..54dc8ed
--- /dev/null
+++ b/cleverbot/abc.py
@@ -0,0 +1,38 @@
+from __future__ import absolute_import
+
+import abc
+
+
+class CleverbotBase:
+ """Base class for Cleverbot."""
+
+ url = 'https://www.cleverbot.com/getreply'
+
+ def __getattribute__(self, attr):
+ """Allow access to the stored data through attributes."""
+ try:
+ return super(CleverbotBase, self).__getattribute__(attr)
+ except AttributeError as error:
+ try:
+ return super(
+ CleverbotBase, self).__getattribute__('data')[attr]
+ except KeyError:
+ raise error
+
+ def __setattr__(self, attr, value):
+ """Allow modifying the cleverbot state with an attribute."""
+ if attr == 'cs':
+ self.data['cs'] = value
+ else:
+ super(CleverbotBase, self).__setattr__(attr, value)
+
+ @abc.abstractmethod
+ def say(self):
+ pass
+
+ def reset(self):
+ """Reset all of Cleverbot's stored data."""
+ self.data = {}
+
+
+CleverbotBase = abc.ABCMeta('CleverbotBase', (), dict(CleverbotBase.__dict__))
diff --git a/cleverbot/async_/__init__.py b/cleverbot/async_/__init__.py
new file mode 100644
index 0000000..c5587f0
--- /dev/null
+++ b/cleverbot/async_/__init__.py
@@ -0,0 +1 @@
+from .cleverbot import Cleverbot
diff --git a/cleverbot/async_/cleverbot.py b/cleverbot/async_/cleverbot.py
new file mode 100644
index 0000000..ccea117
--- /dev/null
+++ b/cleverbot/async_/cleverbot.py
@@ -0,0 +1,94 @@
+import asyncio
+
+import aiohttp
+
+from .. import __version__
+from ..abc import CleverbotBase
+from ..errors import APIError, DecodeError, Timeout
+
+
+class Cleverbot(CleverbotBase):
+ """An asynchronous Cleverbot API wrapper."""
+
+ def __init__(self, key, *, cs=None, timeout=None, loop=None):
+ """Initialize Cleverbot with the given arguments.
+
+ Arguments:
+ key: The key argument is always required. It is your API key.
+ cs: The cs argument stands for "cleverbot state". It is the encoded
+ state of the conversation so far and includes the whole
+ conversation history up to that point.
+ timeout: How many seconds to wait for the API to send data before
+ giving up and raising an error.
+ loop: The event loop used for the asynchronous requests.
+ """
+ self.key = key
+ self.data = {}
+ if cs is not None:
+ self.data['cs'] = cs
+ self.timeout = timeout
+ loop = asyncio.get_event_loop() if loop is None else loop
+ self.session = aiohttp.ClientSession(loop=loop)
+
+ @asyncio.coroutine
+ def say(self, input=None, **kwargs):
+ """Talk to Cleverbot.
+
+ Arguments:
+ input: The input argument is what you want to say to Cleverbot,
+ such as "hello".
+ **kwargs: Keyword arguments to update the request parameters with.
+
+ Returns:
+ Cleverbot's reply.
+
+ Raises:
+ APIError: A Cleverbot API error occurred.
+ 401: Unauthorised due to invalid API key.
+ 404: API not found.
+ 413: Request too large if you send a request over 16KB.
+ 502 or 504: Unable to get reply from API server, please contact
+ us.
+ 503: Too many requests from a single IP address or API key.
+ DecodeError: An error occurred while reading the reply.
+ Timeout: The request timed out.
+ """
+ params = {
+ 'key': self.key,
+ 'wrapper': 'cleverbot.py'
+ }
+ if input is not None:
+ params['input'] = input
+ try:
+ params['cs'] = self.data['cs']
+ except KeyError:
+ pass
+ if kwargs:
+ params.update(kwargs)
+
+ headers = {
+ 'User-Agent': 'cleverbot.py/' + __version__ + ' '
+ '(+https://github.com/orlnub123/cleverbot.py)'
+ }
+ try:
+ reply = yield from self.session.get(
+ self.url, params=params, headers=headers, timeout=self.timeout)
+ except asyncio.TimeoutError:
+ raise Timeout(self.timeout)
+ else:
+ try:
+ data = yield from reply.json()
+ except ValueError as error:
+ raise DecodeError(error)
+ else:
+ if reply.status == 200:
+ self.data = data
+ return data['output']
+ else:
+ raise APIError(data['error'], data['status'])
+ finally:
+ reply.release()
+
+ def close(self):
+ """Close the connection to the API."""
+ self.session.close()
diff --git a/cleverbot/cleverbot.py b/cleverbot/cleverbot.py
index 3b72076..1f6a7e4 100644
--- a/cleverbot/cleverbot.py
+++ b/cleverbot/cleverbot.py
@@ -1,16 +1,13 @@
-__all__ = ['Cleverbot']
-
import requests
from . import __version__
-from .errors import CleverbotError, APIError, DecodeError, Timeout
+from .abc import CleverbotBase
+from .errors import APIError, DecodeError, Timeout
-class Cleverbot(object):
+class Cleverbot(CleverbotBase):
"""A Cleverbot API wrapper."""
- url = 'https://www.cleverbot.com/getreply'
-
def __init__(self, key, **kwargs):
"""Initialize Cleverbot with the given arguments.
@@ -21,43 +18,24 @@ def __init__(self, key, **kwargs):
conversation history up to that point.
timeout: How many seconds to wait for the API to send data before
giving up and raising an error.
- **kwargs: Keyword arguments to pass into requests.Session.get
"""
- self.session = requests.Session()
self.key = key
self.data = {}
if 'cs' in kwargs:
self.data['cs'] = kwargs.pop('cs')
self.timeout = kwargs.pop('timeout', None)
- self.kwargs = kwargs
-
- def __getattribute__(self, attr):
- """Allow access to the stored data through attributes."""
- try:
- return super(Cleverbot, self).__getattribute__(attr)
- except AttributeError as error:
- try:
- return super(Cleverbot, self).__getattribute__('data')[attr]
- except KeyError:
- raise error
-
- def __setattr__(self, attr, value):
- """Allow modifying the cleverbot state with an attribute."""
- if attr == 'cs':
- self.data['cs'] = value
- else:
- super(Cleverbot, self).__setattr__(attr, value)
+ self.session = requests.Session()
+ if kwargs:
+ raise TypeError("__init__() got an unexpected keyword argument "
+ + repr(list(kwargs.keys())[0]))
- def say(self, text, **vtext):
+ def say(self, input=None, **kwargs):
"""Talk to Cleverbot.
Arguments:
- text: The text argument is what you want to say to Cleverbot, such
- as "hello".
- **vtext: If you wish to override the conversation history, you can
- pass in keyword arguments like vtext2 to override the last
- thing the bot said, vtext3 for the previous thing the user
- said, and so on.
+ input: The input argument is what you want to say to Cleverbot,
+ such as "hello".
+ **kwargs: Keyword arguments to update the request parameters with.
Returns:
Cleverbot's reply.
@@ -75,65 +53,23 @@ def say(self, text, **vtext):
"""
params = {
'key': self.key,
- 'input': text,
+ 'input': input,
'wrapper': 'cleverbot.py'
}
try:
params['cs'] = self.data['cs']
except KeyError:
pass
- if vtext:
- params.update(vtext)
- return self._query(params)
+ if kwargs:
+ params.update(kwargs)
- def asay(self, *args, **kwargs):
- """Look in _async.py for the actual method."""
- raise CleverbotError("asay requires aiohttp and Python 3.4.2+")
-
- def reset(self):
- """Reset all of Cleverbot's stored data."""
- self.data = {}
-
- def close(self):
- """Close the connection to the API."""
- self.session.close()
-
- def _query(self, params):
- """Get Cleverbot's reply and store the data in a dictionary.
-
- Keys:
- cs: State of the conversation so far, which contains an encoded
- copy of the conversation id and history.
- interaction_count: How many pairs of bot/user interactions have
- occurred so far.
- input: The entire user input, with any spaces trimmed off both
- ends.
- output: Cleverbot's reply.
- conversation_id: Identifier for this conversation between user and
- bot.
- errorline: Any error information from Cleverbot, this is different
- from general the general errors described below.
- time_taken: The number of milliseconds the bot took to respond.
- time_elapsed: Approximate number of seconds since conversation
- started.
- interaction_1 to interaction_50: Record of the previous
- interactions. The interaction variables come in pairs. For
- example interaction_1 contains the last thing the user said,
- and interaction_1_other is the bot's reply. interaction_2 was
- the user's previous input and so on. So to read the whole
- conversation in order, you have to display the interaction
- variables in reverse order, eg interaction_5,
- interaction_5_other, interaction_4, interaction_4_other, etc.
- Note that if an interaction didn't occur, the interaction_other
- will not be defined.
- """
headers = {
'User-Agent': 'cleverbot.py/' + __version__ + ' '
'(+https://github.com/orlnub123/cleverbot.py)'
}
try:
- reply = self.session.get(self.url, params=params, headers=headers,
- timeout=self.timeout, **self.kwargs)
+ reply = self.session.get(
+ self.url, params=params, headers=headers, timeout=self.timeout)
except requests.Timeout:
raise Timeout(self.timeout)
else:
@@ -148,7 +84,6 @@ def _query(self, params):
else:
raise APIError(data['error'], data['status'])
- try:
- from ._async import __init__, asay, close, _aquery
- except (ImportError, SyntaxError):
- pass
+ def close(self):
+ """Close the connection to the API."""
+ self.session.close()
diff --git a/cleverbot/errors.py b/cleverbot/errors.py
index 2caf25d..0035390 100644
--- a/cleverbot/errors.py
+++ b/cleverbot/errors.py
@@ -22,7 +22,7 @@ def __init__(self, error, status):
class DecodeError(CleverbotError):
"""Raised when a decode error occurs while reading the reply.
- This shouldn't happen.
+ Reset Cleverbot to fix it.
"""
@@ -31,5 +31,5 @@ class Timeout(CleverbotError):
def __init__(self, timeout):
super(Timeout, self).__init__(
- "Request timed out after {} seconds".format(timeout))
+ "Request timed out after {0} seconds".format(timeout))
self.timeout = timeout
diff --git a/setup.py b/setup.py
index 149b66c..802f390 100644
--- a/setup.py
+++ b/setup.py
@@ -19,12 +19,12 @@
name='cleverbot.py',
version=version,
description='A Cleverbot API wrapper for Python with asynchronous '
- 'functionality.',
+ 'functionality.',
long_description=readme,
url='https://github.com/orlnub123/cleverbot.py',
author='orlnub123',
license='MIT',
- packages=['cleverbot'],
+ packages=['cleverbot', 'cleverbot.async_'],
install_requires=['requests>=1.0.0'],
extras_require={'async': ['aiohttp>=1.0.0']},
classifiers=[
@@ -42,6 +42,7 @@
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
'Topic :: Internet',
'Topic :: Games/Entertainment',
'Topic :: Software Development :: Libraries :: Python Modules',