├── .codeclimate.yml ├── .github ├── lock.yml └── workflows │ └── pythonpublish.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── chatterbot ├── __init__.py ├── __main__.py ├── adapters.py ├── chatterbot.py ├── comparisons.py ├── constants.py ├── conversation.py ├── corpus.py ├── exceptions.py ├── ext │ ├── __init__.py │ ├── django_chatterbot │ │ ├── __init__.py │ │ ├── abstract_models.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_statement_extra_data.py │ │ │ ├── 0003_change_occurrence_default.py │ │ │ ├── 0004_rename_in_response_to.py │ │ │ ├── 0005_statement_created_at.py │ │ │ ├── 0006_create_conversation.py │ │ │ ├── 0007_response_created_at.py │ │ │ ├── 0008_update_conversations.py │ │ │ ├── 0009_tags.py │ │ │ ├── 0010_statement_text.py │ │ │ ├── 0011_blank_extra_data.py │ │ │ ├── 0012_statement_created_at.py │ │ │ ├── 0013_change_conversations.py │ │ │ ├── 0014_remove_statement_extra_data.py │ │ │ ├── 0015_statement_persona.py │ │ │ ├── 0016_statement_stemmed_text.py │ │ │ ├── 0017_tags_unique.py │ │ │ ├── 0018_text_max_length.py │ │ │ └── __init__.py │ │ ├── model_admin.py │ │ ├── models.py │ │ └── settings.py │ └── sqlalchemy_app │ │ ├── __init__.py │ │ └── models.py ├── filters.py ├── languages.py ├── logic │ ├── __init__.py │ ├── best_match.py │ ├── logic_adapter.py │ ├── mathematical_evaluation.py │ ├── specific_response.py │ ├── time_adapter.py │ └── unit_conversion.py ├── parsing.py ├── preprocessors.py ├── response_selection.py ├── search.py ├── storage │ ├── __init__.py │ ├── django_storage.py │ ├── mongodb.py │ ├── sql_storage.py │ └── storage_adapter.py ├── tagging.py ├── trainers.py └── utils.py ├── dev-requirements.txt ├── docs ├── _includes │ └── python_module_structure.txt ├── _static │ ├── bigrams.svg │ ├── chatterbot-process-flow.svg │ ├── dialog-processing-flow.svg │ ├── favicon.ico │ ├── github-mark.png │ ├── so-icon.png │ ├── statement-relationship.svg │ ├── statement-response-relationship.svg │ ├── style.css │ └── training-graph.svg ├── _templates │ ├── footer.html │ └── layout.html ├── chatterbot.rst ├── commands.rst ├── comparisons.rst ├── conf.py ├── contributing.rst ├── conversations.rst ├── corpus.rst ├── development.rst ├── django │ ├── index.rst │ ├── settings.rst │ ├── views.rst │ └── wsgi.rst ├── encoding.rst ├── examples.rst ├── faq.rst ├── filters.rst ├── glossary.rst ├── index.rst ├── logic │ ├── create-a-logic-adapter.rst │ ├── index.rst │ └── response_selection.rst ├── packaging.rst ├── preprocessors.rst ├── quickstart.rst ├── releases.rst ├── setup.rst ├── statements.txt ├── storage │ ├── create-a-storage-adapter.rst │ ├── index.rst │ └── text-search.rst ├── testing.rst ├── training.rst ├── tutorial.rst ├── upgrading.rst └── utils.rst ├── examples ├── __init__.py ├── basic_example.py ├── convert_units.py ├── default_response_example.py ├── django_app │ ├── README.rst │ ├── example_app │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── static │ │ │ ├── css │ │ │ │ ├── bootstrap.css │ │ │ │ └── custom.css │ │ │ ├── favicon.ico │ │ │ ├── img │ │ │ │ └── chatterbot.png │ │ │ └── js │ │ │ │ ├── bootstrap.js │ │ │ │ ├── jquery.js │ │ │ │ └── js.cookie.js │ │ ├── templates │ │ │ ├── app.html │ │ │ └── nav.html │ │ ├── urls.py │ │ ├── views.py │ │ └── wsgi.py │ ├── manage.py │ ├── requirements.txt │ └── tests │ │ ├── __init__.py │ │ ├── test_api.py │ │ └── test_example.py ├── export_example.py ├── learning_feedback_example.py ├── math_and_time.py ├── memory_sql_example.py ├── specific_response_example.py ├── tagged_dataset_example.py ├── terminal_example.py ├── terminal_mongo_example.py ├── tkinter_gui.py ├── training_example_chatterbot_corpus.py ├── training_example_list_data.py └── training_example_ubuntu_corpus.py ├── graphics ├── README.md ├── ad.png ├── ad.xcf ├── banner.png ├── chatterbot.xcf └── scan.jpg ├── requirements.txt ├── runtests.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── base_case.py ├── logic │ ├── __init__.py │ ├── test_best_match.py │ ├── test_data_cache.py │ ├── test_logic_adapter.py │ ├── test_mathematical_evaluation.py │ ├── test_specific_response.py │ ├── test_time.py │ └── test_unit_conversion.py ├── storage │ ├── __init__.py │ ├── test_mongo_adapter.py │ ├── test_sql_adapter.py │ └── test_storage_adapter.py ├── test_adapter_validation.py ├── test_benchmarks.py ├── test_chatbot.py ├── test_cli.py ├── test_comparisons.py ├── test_conversations.py ├── test_corpus.py ├── test_examples.py ├── test_filters.py ├── test_initialization.py ├── test_languages.py ├── test_parsing.py ├── test_preprocessors.py ├── test_response_selection.py ├── test_search.py ├── test_tagging.py ├── test_turing.py ├── test_utils.py └── training │ ├── __init__.py │ ├── test_chatterbot_corpus_training.py │ ├── test_data │ └── get_search.json │ ├── test_list_training.py │ ├── test_training.py │ └── test_ubuntu_corpus_training.py ├── tests_django ├── __init__.py ├── base_case.py ├── test_chatbot.py ├── test_chatterbot_corpus_training.py ├── test_chatterbot_settings.py ├── test_django_adapter.py ├── test_logic_adapter_integration.py ├── test_settings.py └── test_statement_integration.py └── tox.ini /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | pep8: 3 | enabled: true 4 | ratings: 5 | paths: 6 | - "**.py" 7 | exclude_paths: 8 | - tests/* 9 | - examples/* 10 | - chatterbot/ext/django_chatterbot/migrations/* 11 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads 2 | 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 300 5 | 6 | # Skip issues and pull requests created before a given timestamp. Timestamp must 7 | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable 8 | skipCreatedBefore: false 9 | 10 | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable 11 | exemptLabels: 12 | - bug 13 | - deprecation 14 | - documentation 15 | - enhancement 16 | - example 17 | - feature 18 | - testing 19 | 20 | # Label to add before locking, such as `outdated`. Set to `false` to disable 21 | lockLabel: false 22 | 23 | # Comment to post before locking. Set to `false` to disable 24 | lockComment: > 25 | This thread has been automatically locked since there has not been 26 | any recent activity after it was closed. Please open a new issue for 27 | related bugs. 28 | 29 | # Assign `resolved` as the reason for locking. Set to `false` to disable 30 | setLockReason: false 31 | 32 | # Limit to only `issues` or `pulls` 33 | only: issues 34 | 35 | # Repository to extend settings from 36 | # _extends: repo 37 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: 3 | # https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 4 | 5 | name: Upload Python Package 6 | 7 | on: 8 | release: 9 | types: [created] 10 | 11 | jobs: 12 | deploy: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python 19 | uses: actions/setup-python@v1 20 | with: 21 | python-version: '3.x' 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install setuptools wheel twine 26 | - name: Build and publish 27 | env: 28 | TWINE_USERNAME: __token__ 29 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 30 | run: | 31 | python setup.py sdist bdist_wheel 32 | twine upload dist/* 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | build 3 | dist 4 | venv 5 | .env 6 | .out 7 | .tox 8 | .coverage 9 | *.pyc 10 | *.swp 11 | *.egg-info 12 | *.egg/* 13 | *.eggs/* 14 | *.doctrees 15 | 16 | # Database files 17 | *.sqlite3 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | cache: pip 3 | 4 | python: 5 | - '3.6' 6 | 7 | os: 8 | - linux 9 | 10 | services: 11 | - mongodb 12 | 13 | env: 14 | - CHATTERBOT_SHOW_TRAINING_PROGRESS=0 15 | 16 | install: 17 | - pip install tox coveralls codeclimate-test-reporter 18 | 19 | addons: 20 | code_climate: 21 | repo_token: 3ec30a156224df0f59620967241d9659086e918fd824f4f69b8ce7b55b5a590f 22 | 23 | script: 24 | - tox 25 | 26 | after_success: 27 | - coveralls 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 - 2019, Gunther Cox 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of ChatterBot nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include requirements.txt 4 | include setup.cfg 5 | 6 | global-exclude __pycache__ 7 | global-exclude *.py[co] 8 | global-exclude .* -------------------------------------------------------------------------------- /chatterbot/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ChatterBot is a machine learning, conversational dialog engine. 3 | """ 4 | from .chatterbot import ChatBot 5 | 6 | 7 | __all__ = ( 8 | 'ChatBot', 9 | ) 10 | -------------------------------------------------------------------------------- /chatterbot/__main__.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import sys 3 | import os 4 | 5 | 6 | def get_chatterbot_version(): 7 | config = configparser.ConfigParser() 8 | 9 | current_directory = os.path.dirname(os.path.abspath(__file__)) 10 | parent_directory = os.path.abspath(os.path.join(current_directory, os.pardir)) 11 | config_file_path = os.path.join(parent_directory, 'setup.cfg') 12 | 13 | config.read(config_file_path) 14 | 15 | return config['chatterbot']['version'] 16 | 17 | 18 | if __name__ == '__main__': 19 | if '--version' in sys.argv: 20 | print(get_chatterbot_version()) 21 | -------------------------------------------------------------------------------- /chatterbot/adapters.py: -------------------------------------------------------------------------------- 1 | class Adapter(object): 2 | """ 3 | A superclass for all adapter classes. 4 | 5 | :param chatbot: A ChatBot instance. 6 | """ 7 | 8 | def __init__(self, chatbot, **kwargs): 9 | self.chatbot = chatbot 10 | 11 | class AdapterMethodNotImplementedError(NotImplementedError): 12 | """ 13 | An exception to be raised when an adapter method has not been implemented. 14 | Typically this indicates that the developer is expected to implement the 15 | method in a subclass. 16 | """ 17 | 18 | def __init__(self, message='This method must be overridden in a subclass method.'): 19 | """ 20 | Set the message for the exception. 21 | """ 22 | super().__init__(message) 23 | 24 | class InvalidAdapterTypeException(Exception): 25 | """ 26 | An exception to be raised when an adapter 27 | of an unexpected class type is received. 28 | """ 29 | pass 30 | -------------------------------------------------------------------------------- /chatterbot/constants.py: -------------------------------------------------------------------------------- 1 | """ 2 | ChatterBot constants 3 | """ 4 | 5 | ''' 6 | The maximum length of characters that the text of a statement can contain. 7 | The number 255 is used because that is the maximum length of a char field 8 | in most databases. This value should be enforced on a per-model basis by 9 | the data model for each storage adapter. 10 | ''' 11 | STATEMENT_TEXT_MAX_LENGTH = 255 12 | 13 | ''' 14 | The maximum length of characters that the text label of a conversation can contain. 15 | The number 32 was chosen because that is the length of the string representation 16 | of a UUID4 with no hyphens. 17 | ''' 18 | CONVERSATION_LABEL_MAX_LENGTH = 32 19 | 20 | ''' 21 | The maximum length of text that can be stored in the persona field of the statement model. 22 | ''' 23 | PERSONA_MAX_LENGTH = 50 24 | 25 | # The maximum length of characters that the name of a tag can contain 26 | TAG_NAME_MAX_LENGTH = 50 27 | 28 | DEFAULT_DJANGO_APP_NAME = 'django_chatterbot' 29 | -------------------------------------------------------------------------------- /chatterbot/conversation.py: -------------------------------------------------------------------------------- 1 | from pytz import UTC 2 | from datetime import datetime 3 | from dateutil import parser as date_parser 4 | 5 | 6 | class StatementMixin(object): 7 | """ 8 | This class has shared methods used to 9 | normalize different statement models. 10 | """ 11 | 12 | statement_field_names = [ 13 | 'id', 14 | 'text', 15 | 'search_text', 16 | 'conversation', 17 | 'persona', 18 | 'tags', 19 | 'in_response_to', 20 | 'search_in_response_to', 21 | 'created_at', 22 | ] 23 | 24 | extra_statement_field_names = [] 25 | 26 | def get_statement_field_names(self): 27 | """ 28 | Return the list of field names for the statement. 29 | """ 30 | return self.statement_field_names + self.extra_statement_field_names 31 | 32 | def get_tags(self): 33 | """ 34 | Return the list of tags for this statement. 35 | """ 36 | return self.tags 37 | 38 | def add_tags(self, *tags): 39 | """ 40 | Add a list of strings to the statement as tags. 41 | """ 42 | self.tags.extend(tags) 43 | 44 | def serialize(self): 45 | """ 46 | :returns: A dictionary representation of the statement object. 47 | :rtype: dict 48 | """ 49 | data = {} 50 | 51 | for field_name in self.get_statement_field_names(): 52 | format_method = getattr(self, 'get_{}'.format( 53 | field_name 54 | ), None) 55 | 56 | if format_method: 57 | data[field_name] = format_method() 58 | else: 59 | data[field_name] = getattr(self, field_name) 60 | 61 | return data 62 | 63 | 64 | class Statement(StatementMixin): 65 | """ 66 | A statement represents a single spoken entity, sentence or 67 | phrase that someone can say. 68 | """ 69 | 70 | __slots__ = ( 71 | 'id', 72 | 'text', 73 | 'search_text', 74 | 'conversation', 75 | 'persona', 76 | 'tags', 77 | 'in_response_to', 78 | 'search_in_response_to', 79 | 'created_at', 80 | 'confidence', 81 | 'storage', 82 | ) 83 | 84 | def __init__(self, text, in_response_to=None, **kwargs): 85 | 86 | self.id = kwargs.get('id') 87 | self.text = str(text) 88 | self.search_text = kwargs.get('search_text', '') 89 | self.conversation = kwargs.get('conversation', '') 90 | self.persona = kwargs.get('persona', '') 91 | self.tags = kwargs.pop('tags', []) 92 | self.in_response_to = in_response_to 93 | self.search_in_response_to = kwargs.get('search_in_response_to', '') 94 | self.created_at = kwargs.get('created_at', datetime.now()) 95 | 96 | if not isinstance(self.created_at, datetime): 97 | self.created_at = date_parser.parse(self.created_at) 98 | 99 | # Set timezone to UTC if no timezone was provided 100 | if not self.created_at.tzinfo: 101 | self.created_at = self.created_at.replace(tzinfo=UTC) 102 | 103 | # This is the confidence with which the chat bot believes 104 | # this is an accurate response. This value is set when the 105 | # statement is returned by the chat bot. 106 | self.confidence = 0 107 | 108 | self.storage = None 109 | 110 | def __str__(self): 111 | return self.text 112 | 113 | def __repr__(self): 114 | return '' % (self.text) 115 | 116 | def save(self): 117 | """ 118 | Save the statement in the database. 119 | """ 120 | self.storage.update(self) 121 | -------------------------------------------------------------------------------- /chatterbot/corpus.py: -------------------------------------------------------------------------------- 1 | import os 2 | import io 3 | import glob 4 | from pathlib import Path 5 | from chatterbot.exceptions import OptionalDependencyImportError 6 | 7 | try: 8 | from chatterbot_corpus.corpus import DATA_DIRECTORY 9 | except (ImportError, ModuleNotFoundError): 10 | # Default to the home directory of the current user 11 | DATA_DIRECTORY = os.path.join( 12 | Path.home(), 13 | 'chatterbot_corpus', 14 | 'data' 15 | ) 16 | 17 | 18 | CORPUS_EXTENSION = 'yml' 19 | 20 | 21 | def get_file_path(dotted_path, extension='json'): 22 | """ 23 | Reads a dotted file path and returns the file path. 24 | """ 25 | # If the operating system's file path seperator character is in the string 26 | if os.sep in dotted_path or '/' in dotted_path: 27 | # Assume the path is a valid file path 28 | return dotted_path 29 | 30 | parts = dotted_path.split('.') 31 | if parts[0] == 'chatterbot': 32 | parts.pop(0) 33 | parts[0] = DATA_DIRECTORY 34 | 35 | corpus_path = os.path.join(*parts) 36 | 37 | path_with_extension = '{}.{}'.format(corpus_path, extension) 38 | if os.path.exists(path_with_extension): 39 | corpus_path = path_with_extension 40 | 41 | return corpus_path 42 | 43 | 44 | def read_corpus(file_name): 45 | """ 46 | Read and return the data from a corpus json file. 47 | """ 48 | try: 49 | import yaml 50 | except ImportError: 51 | message = ( 52 | 'Unable to import "yaml".\n' 53 | 'Please install "pyyaml" to enable chatterbot corpus functionality:\n' 54 | 'pip3 install pyyaml' 55 | ) 56 | raise OptionalDependencyImportError(message) 57 | 58 | with io.open(file_name, encoding='utf-8') as data_file: 59 | return yaml.safe_load(data_file) 60 | 61 | 62 | def list_corpus_files(dotted_path): 63 | """ 64 | Return a list of file paths to each data file in the specified corpus. 65 | """ 66 | corpus_path = get_file_path(dotted_path, extension=CORPUS_EXTENSION) 67 | paths = [] 68 | 69 | if os.path.isdir(corpus_path): 70 | paths = glob.glob(corpus_path + '/**/*.' + CORPUS_EXTENSION, recursive=True) 71 | else: 72 | paths.append(corpus_path) 73 | 74 | paths.sort() 75 | return paths 76 | 77 | 78 | def load_corpus(*data_file_paths): 79 | """ 80 | Return the data contained within a specified corpus. 81 | """ 82 | for file_path in data_file_paths: 83 | corpus = [] 84 | corpus_data = read_corpus(file_path) 85 | 86 | conversations = corpus_data.get('conversations', []) 87 | corpus.extend(conversations) 88 | 89 | categories = corpus_data.get('categories', []) 90 | 91 | yield corpus, categories, file_path 92 | -------------------------------------------------------------------------------- /chatterbot/exceptions.py: -------------------------------------------------------------------------------- 1 | class OptionalDependencyImportError(ImportError): 2 | """ 3 | An exception raised when a feature requires an optional dependency to be installed. 4 | """ 5 | pass 6 | -------------------------------------------------------------------------------- /chatterbot/ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/chatterbot/ext/__init__.py -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = ( 2 | 'chatterbot.ext.django_chatterbot.apps.DjangoChatterBotConfig' 3 | ) 4 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/abstract_models.py: -------------------------------------------------------------------------------- 1 | from chatterbot.conversation import StatementMixin 2 | from chatterbot import constants 3 | from django.db import models 4 | from django.utils import timezone 5 | from django.conf import settings 6 | 7 | 8 | DJANGO_APP_NAME = constants.DEFAULT_DJANGO_APP_NAME 9 | STATEMENT_MODEL = 'Statement' 10 | TAG_MODEL = 'Tag' 11 | 12 | if hasattr(settings, 'CHATTERBOT'): 13 | """ 14 | Allow related models to be overridden in the project settings. 15 | Default to the original settings if one is not defined. 16 | """ 17 | DJANGO_APP_NAME = settings.CHATTERBOT.get( 18 | 'django_app_name', 19 | DJANGO_APP_NAME 20 | ) 21 | STATEMENT_MODEL = settings.CHATTERBOT.get( 22 | 'statement_model', 23 | STATEMENT_MODEL 24 | ) 25 | 26 | 27 | class AbstractBaseTag(models.Model): 28 | """ 29 | The abstract base tag allows other models to be created 30 | using the attributes that exist on the default models. 31 | """ 32 | 33 | name = models.SlugField( 34 | max_length=constants.TAG_NAME_MAX_LENGTH, 35 | unique=True 36 | ) 37 | 38 | class Meta: 39 | abstract = True 40 | 41 | def __str__(self): 42 | return self.name 43 | 44 | 45 | class AbstractBaseStatement(models.Model, StatementMixin): 46 | """ 47 | The abstract base statement allows other models to be created 48 | using the attributes that exist on the default models. 49 | """ 50 | 51 | text = models.CharField( 52 | max_length=constants.STATEMENT_TEXT_MAX_LENGTH 53 | ) 54 | 55 | search_text = models.CharField( 56 | max_length=constants.STATEMENT_TEXT_MAX_LENGTH, 57 | blank=True 58 | ) 59 | 60 | conversation = models.CharField( 61 | max_length=constants.CONVERSATION_LABEL_MAX_LENGTH 62 | ) 63 | 64 | created_at = models.DateTimeField( 65 | default=timezone.now, 66 | help_text='The date and time that the statement was created at.' 67 | ) 68 | 69 | in_response_to = models.CharField( 70 | max_length=constants.STATEMENT_TEXT_MAX_LENGTH, 71 | null=True 72 | ) 73 | 74 | search_in_response_to = models.CharField( 75 | max_length=constants.STATEMENT_TEXT_MAX_LENGTH, 76 | blank=True 77 | ) 78 | 79 | persona = models.CharField( 80 | max_length=constants.PERSONA_MAX_LENGTH 81 | ) 82 | 83 | tags = models.ManyToManyField( 84 | TAG_MODEL, 85 | related_name='statements' 86 | ) 87 | 88 | # This is the confidence with which the chat bot believes 89 | # this is an accurate response. This value is set when the 90 | # statement is returned by the chat bot. 91 | confidence = 0 92 | 93 | class Meta: 94 | abstract = True 95 | 96 | def __str__(self): 97 | if len(self.text.strip()) > 60: 98 | return '{}...'.format(self.text[:57]) 99 | elif len(self.text.strip()) > 0: 100 | return self.text 101 | return '' 102 | 103 | def get_tags(self): 104 | """ 105 | Return the list of tags for this statement. 106 | (Overrides the method from StatementMixin) 107 | """ 108 | return list(self.tags.values_list('name', flat=True)) 109 | 110 | def add_tags(self, *tags): 111 | """ 112 | Add a list of strings to the statement as tags. 113 | (Overrides the method from StatementMixin) 114 | """ 115 | for _tag in tags: 116 | self.tags.get_or_create(name=_tag) 117 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from chatterbot.ext.django_chatterbot.model_admin import StatementAdmin, TagAdmin 3 | from chatterbot.ext.django_chatterbot.models import Statement, Tag 4 | 5 | 6 | admin.site.register(Statement, StatementAdmin) 7 | admin.site.register(Tag, TagAdmin) 8 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DjangoChatterBotConfig(AppConfig): 5 | 6 | name = 'chatterbot.ext.django_chatterbot' 7 | label = 'django_chatterbot' 8 | verbose_name = 'Django ChatterBot' 9 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | import django.db.models.deletion 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | initial = True 8 | 9 | dependencies = [] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='Response', 14 | fields=[ 15 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 16 | ('occurrence', models.PositiveIntegerField(default=0)), 17 | ], 18 | ), 19 | migrations.CreateModel( 20 | name='Statement', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('text', models.CharField(max_length=255, unique=True)), 24 | ], 25 | ), 26 | migrations.AddField( 27 | model_name='response', 28 | name='response', 29 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='django_chatterbot.Statement'), 30 | ), 31 | migrations.AddField( 32 | model_name='response', 33 | name='statement', 34 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response_to', to='django_chatterbot.Statement'), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.2 on 2016-10-30 12:13 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0001_initial'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='statement', 14 | name='extra_data', 15 | field=models.CharField(default='{}', max_length=500), 16 | preserve_default=False, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9 on 2016-12-12 00:06 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0002_statement_extra_data'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='response', 14 | name='occurrence', 15 | field=models.PositiveIntegerField(default=1), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.3 on 2016-12-04 23:52 2 | from django.db import migrations, models 3 | import django.db.models.deletion 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0003_change_occurrence_default'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='response', 15 | name='statement', 16 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response', to='django_chatterbot.Statement'), 17 | ), 18 | migrations.AlterField( 19 | model_name='response', 20 | name='response', 21 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='django_chatterbot.Statement'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.1 on 2016-12-29 19:20 2 | from django.db import migrations, models 3 | import django.utils.timezone 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0004_rename_in_response_to'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='statement', 15 | name='created_at', 16 | field=models.DateTimeField( 17 | default=django.utils.timezone.now, 18 | help_text='The date and time that this statement was created at.' 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9 on 2017-01-17 07:02 2 | from django.db import migrations, models 3 | import django.db.models.deletion 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('django_chatterbot', '0005_statement_created_at'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Conversation', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ], 19 | ), 20 | migrations.AlterField( 21 | model_name='statement', 22 | name='created_at', 23 | field=models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time that this statement was created at.'), 24 | ), 25 | migrations.AddField( 26 | model_name='conversation', 27 | name='statements', 28 | field=models.ManyToManyField(help_text='The statements in this conversation.', related_name='conversation', to='django_chatterbot.Statement'), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11 on 2017-07-18 00:16 2 | from django.db import migrations, models 3 | import django.utils.timezone 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0006_create_conversation'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='response', 15 | name='created_at', 16 | field=models.DateTimeField( 17 | default=django.utils.timezone.now, 18 | help_text='The date and time that this response was created at.' 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11 on 2017-07-18 11:25 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0007_response_created_at'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='conversation', 14 | name='statements', 15 | ), 16 | migrations.RemoveField( 17 | model_name='response', 18 | name='occurrence', 19 | ), 20 | migrations.RemoveField( 21 | model_name='statement', 22 | name='created_at', 23 | ), 24 | migrations.AddField( 25 | model_name='conversation', 26 | name='responses', 27 | field=models.ManyToManyField(help_text='The responses in this conversation.', related_name='conversations', to='django_chatterbot.Response'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0009_tags.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11a1 on 2017-07-07 00:12 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0008_update_conversations'), 9 | ] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='Tag', 14 | fields=[ 15 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 16 | ('name', models.SlugField()), 17 | ], 18 | options={ 19 | 'abstract': False, 20 | }, 21 | ), 22 | migrations.AlterField( 23 | model_name='statement', 24 | name='text', 25 | field=models.CharField(max_length=255, unique=True), 26 | ), 27 | migrations.AddField( 28 | model_name='tag', 29 | name='statements', 30 | field=models.ManyToManyField(related_name='tags', to='django_chatterbot.Statement'), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11.4 on 2017-08-16 00:56 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0009_tags'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='statement', 14 | name='text', 15 | field=models.CharField(max_length=400, unique=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11.4 on 2017-08-20 13:55 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0010_statement_text'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='statement', 14 | name='extra_data', 15 | field=models.CharField(blank=True, max_length=500), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | import django.utils.timezone 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0011_blank_extra_data'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='statement', 14 | name='created_at', 15 | field=models.DateTimeField( 16 | default=django.utils.timezone.now, 17 | help_text='The date and time that the statement was created at.' 18 | ), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11 on 2018-09-13 01:01 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0012_statement_created_at'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='conversation', 14 | name='responses', 15 | ), 16 | migrations.RemoveField( 17 | model_name='response', 18 | name='response', 19 | ), 20 | migrations.RemoveField( 21 | model_name='response', 22 | name='statement', 23 | ), 24 | migrations.AddField( 25 | model_name='statement', 26 | name='conversation', 27 | field=models.CharField(default='default', max_length=32), 28 | preserve_default=False, 29 | ), 30 | migrations.AddField( 31 | model_name='statement', 32 | name='in_response_to', 33 | field=models.CharField(max_length=400, null=True), 34 | ), 35 | migrations.AlterField( 36 | model_name='statement', 37 | name='text', 38 | field=models.CharField(max_length=400), 39 | ), 40 | migrations.DeleteModel( 41 | name='Conversation', 42 | ), 43 | migrations.DeleteModel( 44 | name='Response', 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0013_change_conversations'), 8 | ] 9 | 10 | operations = [ 11 | migrations.RemoveField( 12 | model_name='statement', 13 | name='extra_data', 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0014_remove_statement_extra_data'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name='statement', 13 | name='persona', 14 | field=models.CharField(default='', max_length=50), 15 | preserve_default=False, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0015_statement_persona'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name='statement', 13 | name='search_text', 14 | field=models.CharField(blank=True, max_length=400), 15 | ), 16 | migrations.AddField( 17 | model_name='statement', 18 | name='search_in_response_to', 19 | field=models.CharField(blank=True, max_length=400), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0016_statement_stemmed_text'), 8 | ] 9 | 10 | operations = [ 11 | migrations.RemoveField( 12 | model_name='tag', 13 | name='statements', 14 | ), 15 | migrations.AddField( 16 | model_name='statement', 17 | name='tags', 18 | field=models.ManyToManyField( 19 | related_name='statements', 20 | to='django_chatterbot.Tag' 21 | ), 22 | ), 23 | migrations.AlterField( 24 | model_name='tag', 25 | name='name', 26 | field=models.SlugField(unique=True), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0017_tags_unique'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='statement', 13 | name='in_response_to', 14 | field=models.CharField(max_length=255, null=True), 15 | ), 16 | migrations.AlterField( 17 | model_name='statement', 18 | name='search_in_response_to', 19 | field=models.CharField(blank=True, max_length=255), 20 | ), 21 | migrations.AlterField( 22 | model_name='statement', 23 | name='search_text', 24 | field=models.CharField(blank=True, max_length=255), 25 | ), 26 | migrations.AlterField( 27 | model_name='statement', 28 | name='text', 29 | field=models.CharField(max_length=255), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/chatterbot/ext/django_chatterbot/migrations/__init__.py -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/model_admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | 4 | class StatementAdmin(admin.ModelAdmin): 5 | list_display = ('text', 'in_response_to', 'conversation', 'created_at', ) 6 | list_filter = ('text', 'created_at', ) 7 | search_fields = ('text', ) 8 | 9 | 10 | class TagAdmin(admin.ModelAdmin): 11 | list_display = ('name', ) 12 | list_filter = ('name', ) 13 | search_fields = ('name', ) 14 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/models.py: -------------------------------------------------------------------------------- 1 | from chatterbot.ext.django_chatterbot.abstract_models import AbstractBaseStatement, AbstractBaseTag 2 | 3 | 4 | class Statement(AbstractBaseStatement): 5 | """ 6 | A statement represents a single spoken entity, sentence or 7 | phrase that someone can say. 8 | """ 9 | pass 10 | 11 | 12 | class Tag(AbstractBaseTag): 13 | """ 14 | A label that categorizes a statement. 15 | """ 16 | pass 17 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Default ChatterBot settings for Django. 3 | """ 4 | from django.conf import settings 5 | from chatterbot import constants 6 | 7 | 8 | CHATTERBOT_SETTINGS = getattr(settings, 'CHATTERBOT', {}) 9 | 10 | CHATTERBOT_DEFAULTS = { 11 | 'name': 'ChatterBot', 12 | 'storage_adapter': 'chatterbot.storage.DjangoStorageAdapter', 13 | 'django_app_name': constants.DEFAULT_DJANGO_APP_NAME 14 | } 15 | 16 | CHATTERBOT = CHATTERBOT_DEFAULTS.copy() 17 | CHATTERBOT.update(CHATTERBOT_SETTINGS) 18 | -------------------------------------------------------------------------------- /chatterbot/ext/sqlalchemy_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/chatterbot/ext/sqlalchemy_app/__init__.py -------------------------------------------------------------------------------- /chatterbot/ext/sqlalchemy_app/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Table, Column, Integer, String, DateTime, ForeignKey 2 | from sqlalchemy.orm import relationship 3 | from sqlalchemy.sql import func 4 | from sqlalchemy.ext.declarative import declared_attr, declarative_base 5 | 6 | from chatterbot.conversation import StatementMixin 7 | from chatterbot import constants 8 | 9 | 10 | class ModelBase(object): 11 | """ 12 | An augmented base class for SqlAlchemy models. 13 | """ 14 | 15 | @declared_attr 16 | def __tablename__(cls): 17 | """ 18 | Return the lowercase class name as the name of the table. 19 | """ 20 | return cls.__name__.lower() 21 | 22 | id = Column( 23 | Integer, 24 | primary_key=True, 25 | autoincrement=True 26 | ) 27 | 28 | 29 | Base = declarative_base(cls=ModelBase) 30 | 31 | 32 | tag_association_table = Table( 33 | 'tag_association', 34 | Base.metadata, 35 | Column('tag_id', Integer, ForeignKey('tag.id')), 36 | Column('statement_id', Integer, ForeignKey('statement.id')) 37 | ) 38 | 39 | 40 | class Tag(Base): 41 | """ 42 | A tag that describes a statement. 43 | """ 44 | 45 | name = Column( 46 | String(constants.TAG_NAME_MAX_LENGTH), 47 | unique=True 48 | ) 49 | 50 | 51 | class Statement(Base, StatementMixin): 52 | """ 53 | A Statement represents a sentence or phrase. 54 | """ 55 | 56 | confidence = 0 57 | 58 | text = Column( 59 | String(constants.STATEMENT_TEXT_MAX_LENGTH) 60 | ) 61 | 62 | search_text = Column( 63 | String(constants.STATEMENT_TEXT_MAX_LENGTH), 64 | nullable=False, 65 | server_default='' 66 | ) 67 | 68 | conversation = Column( 69 | String(constants.CONVERSATION_LABEL_MAX_LENGTH), 70 | nullable=False, 71 | server_default='' 72 | ) 73 | 74 | created_at = Column( 75 | DateTime(timezone=True), 76 | server_default=func.now() 77 | ) 78 | 79 | tags = relationship( 80 | 'Tag', 81 | secondary=lambda: tag_association_table, 82 | backref='statements' 83 | ) 84 | 85 | in_response_to = Column( 86 | String(constants.STATEMENT_TEXT_MAX_LENGTH), 87 | nullable=True 88 | ) 89 | 90 | search_in_response_to = Column( 91 | String(constants.STATEMENT_TEXT_MAX_LENGTH), 92 | nullable=False, 93 | server_default='' 94 | ) 95 | 96 | persona = Column( 97 | String(constants.PERSONA_MAX_LENGTH), 98 | nullable=False, 99 | server_default='' 100 | ) 101 | 102 | def get_tags(self): 103 | """ 104 | Return a list of tags for this statement. 105 | """ 106 | return [tag.name for tag in self.tags] 107 | 108 | def add_tags(self, *tags): 109 | """ 110 | Add a list of strings to the statement as tags. 111 | """ 112 | self.tags.extend([ 113 | Tag(name=tag) for tag in tags 114 | ]) 115 | -------------------------------------------------------------------------------- /chatterbot/filters.py: -------------------------------------------------------------------------------- 1 | def get_recent_repeated_responses(chatbot, conversation, sample=10, threshold=3, quantity=3): 2 | """ 3 | A filter that eliminates possibly repetitive responses to prevent 4 | a chat bot from repeating statements that it has recently said. 5 | """ 6 | from collections import Counter 7 | 8 | # Get the most recent statements from the conversation 9 | conversation_statements = list(chatbot.storage.filter( 10 | conversation=conversation, 11 | order_by=['id'] 12 | ))[sample * -1:] 13 | 14 | text_of_recent_responses = [ 15 | statement.text for statement in conversation_statements 16 | ] 17 | 18 | counter = Counter(text_of_recent_responses) 19 | 20 | # Find the n most common responses from the conversation 21 | most_common = counter.most_common(quantity) 22 | 23 | return [ 24 | counted[0] for counted in most_common 25 | if counted[1] >= threshold 26 | ] 27 | -------------------------------------------------------------------------------- /chatterbot/logic/__init__.py: -------------------------------------------------------------------------------- 1 | from chatterbot.logic.logic_adapter import LogicAdapter 2 | from chatterbot.logic.best_match import BestMatch 3 | from chatterbot.logic.mathematical_evaluation import MathematicalEvaluation 4 | from chatterbot.logic.specific_response import SpecificResponseAdapter 5 | from chatterbot.logic.time_adapter import TimeLogicAdapter 6 | from chatterbot.logic.unit_conversion import UnitConversion 7 | 8 | __all__ = ( 9 | 'LogicAdapter', 10 | 'BestMatch', 11 | 'MathematicalEvaluation', 12 | 'SpecificResponseAdapter', 13 | 'TimeLogicAdapter', 14 | 'UnitConversion', 15 | ) 16 | -------------------------------------------------------------------------------- /chatterbot/logic/mathematical_evaluation.py: -------------------------------------------------------------------------------- 1 | from chatterbot.logic import LogicAdapter 2 | from chatterbot.conversation import Statement 3 | from chatterbot import languages 4 | 5 | 6 | class MathematicalEvaluation(LogicAdapter): 7 | """ 8 | The MathematicalEvaluation logic adapter parses input to determine 9 | whether the user is asking a question that requires math to be done. 10 | If so, the equation is extracted from the input and returned with 11 | the evaluated result. 12 | 13 | For example: 14 | User: 'What is three plus five?' 15 | Bot: 'Three plus five equals eight' 16 | 17 | :kwargs: 18 | * *language* (``object``) -- 19 | The language is set to ``chatterbot.languages.ENG`` for English by default. 20 | """ 21 | 22 | def __init__(self, chatbot, **kwargs): 23 | super().__init__(chatbot, **kwargs) 24 | 25 | self.language = kwargs.get('language', languages.ENG) 26 | self.cache = {} 27 | 28 | def can_process(self, statement): 29 | """ 30 | Determines whether it is appropriate for this 31 | adapter to respond to the user input. 32 | """ 33 | response = self.process(statement) 34 | self.cache[statement.text] = response 35 | return response.confidence == 1 36 | 37 | def process(self, statement, additional_response_selection_parameters=None): 38 | """ 39 | Takes a statement string. 40 | Returns the equation from the statement with the mathematical terms solved. 41 | """ 42 | from mathparse import mathparse 43 | 44 | input_text = statement.text 45 | 46 | # Use the result cached by the process method if it exists 47 | if input_text in self.cache: 48 | cached_result = self.cache[input_text] 49 | self.cache = {} 50 | return cached_result 51 | 52 | # Getting the mathematical terms within the input statement 53 | expression = mathparse.extract_expression(input_text, language=self.language.ISO_639.upper()) 54 | 55 | response = Statement(text=expression) 56 | 57 | try: 58 | response.text = '{} = {}'.format( 59 | response.text, 60 | mathparse.parse(expression, language=self.language.ISO_639.upper()) 61 | ) 62 | 63 | # The confidence is 1 if the expression could be evaluated 64 | response.confidence = 1 65 | except mathparse.PostfixTokenEvaluationException: 66 | response.confidence = 0 67 | 68 | return response 69 | -------------------------------------------------------------------------------- /chatterbot/logic/specific_response.py: -------------------------------------------------------------------------------- 1 | from chatterbot.logic import LogicAdapter 2 | 3 | 4 | class SpecificResponseAdapter(LogicAdapter): 5 | """ 6 | Return a specific response to a specific input. 7 | 8 | :kwargs: 9 | * *input_text* (``str``) -- 10 | The input text that triggers this logic adapter. 11 | * *output_text* (``str``) -- 12 | The output text returned by this logic adapter. 13 | """ 14 | 15 | def __init__(self, chatbot, **kwargs): 16 | super().__init__(chatbot, **kwargs) 17 | from chatterbot.conversation import Statement 18 | 19 | self.input_text = kwargs.get('input_text') 20 | 21 | output_text = kwargs.get('output_text') 22 | self.response_statement = Statement(text=output_text) 23 | 24 | def can_process(self, statement): 25 | if statement.text == self.input_text: 26 | return True 27 | 28 | return False 29 | 30 | def process(self, statement, additional_response_selection_parameters=None): 31 | 32 | if statement.text == self.input_text: 33 | self.response_statement.confidence = 1 34 | else: 35 | self.response_statement.confidence = 0 36 | 37 | return self.response_statement 38 | -------------------------------------------------------------------------------- /chatterbot/logic/time_adapter.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from chatterbot.logic import LogicAdapter 3 | from chatterbot.conversation import Statement 4 | from chatterbot.exceptions import OptionalDependencyImportError 5 | 6 | 7 | class TimeLogicAdapter(LogicAdapter): 8 | """ 9 | The TimeLogicAdapter returns the current time. 10 | 11 | :kwargs: 12 | * *positive* (``list``) -- 13 | The time-related questions used to identify time questions. 14 | Defaults to a list of English sentences. 15 | * *negative* (``list``) -- 16 | The non-time-related questions used to identify time questions. 17 | Defaults to a list of English sentences. 18 | """ 19 | 20 | def __init__(self, chatbot, **kwargs): 21 | super().__init__(chatbot, **kwargs) 22 | try: 23 | from nltk import NaiveBayesClassifier 24 | except ImportError: 25 | message = ( 26 | 'Unable to import "nltk".\n' 27 | 'Please install "nltk" before using the TimeLogicAdapter:\n' 28 | 'pip3 install nltk' 29 | ) 30 | raise OptionalDependencyImportError(message) 31 | 32 | self.positive = kwargs.get('positive', [ 33 | 'what time is it', 34 | 'hey what time is it', 35 | 'do you have the time', 36 | 'do you know the time', 37 | 'do you know what time it is', 38 | 'what is the time' 39 | ]) 40 | 41 | self.negative = kwargs.get('negative', [ 42 | 'it is time to go to sleep', 43 | 'what is your favorite color', 44 | 'i had a great time', 45 | 'thyme is my favorite herb', 46 | 'do you have time to look at my essay', 47 | 'how do you have the time to do all this' 48 | 'what is it' 49 | ]) 50 | 51 | labeled_data = ( 52 | [ 53 | (name, 0) for name in self.negative 54 | ] + [ 55 | (name, 1) for name in self.positive 56 | ] 57 | ) 58 | 59 | train_set = [ 60 | (self.time_question_features(text), n) for (text, n) in labeled_data 61 | ] 62 | 63 | self.classifier = NaiveBayesClassifier.train(train_set) 64 | 65 | def time_question_features(self, text): 66 | """ 67 | Provide an analysis of significant features in the string. 68 | """ 69 | features = {} 70 | 71 | # A list of all words from the known sentences 72 | all_words = " ".join(self.positive + self.negative).split() 73 | 74 | # A list of the first word in each of the known sentence 75 | all_first_words = [] 76 | for sentence in self.positive + self.negative: 77 | all_first_words.append( 78 | sentence.split(' ', 1)[0] 79 | ) 80 | 81 | for word in text.split(): 82 | features['first_word({})'.format(word)] = (word in all_first_words) 83 | 84 | for word in text.split(): 85 | features['contains({})'.format(word)] = (word in all_words) 86 | 87 | for letter in 'abcdefghijklmnopqrstuvwxyz': 88 | features['count({})'.format(letter)] = text.lower().count(letter) 89 | features['has({})'.format(letter)] = (letter in text.lower()) 90 | 91 | return features 92 | 93 | def process(self, statement, additional_response_selection_parameters=None): 94 | now = datetime.now() 95 | 96 | time_features = self.time_question_features(statement.text.lower()) 97 | confidence = self.classifier.classify(time_features) 98 | response = Statement(text='The current time is ' + now.strftime('%I:%M %p')) 99 | 100 | response.confidence = confidence 101 | return response 102 | -------------------------------------------------------------------------------- /chatterbot/preprocessors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Statement pre-processors. 3 | """ 4 | 5 | 6 | def clean_whitespace(statement): 7 | """ 8 | Remove any consecutive whitespace characters from the statement text. 9 | """ 10 | import re 11 | 12 | # Replace linebreaks and tabs with spaces 13 | statement.text = statement.text.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ') 14 | 15 | # Remove any leeding or trailing whitespace 16 | statement.text = statement.text.strip() 17 | 18 | # Remove consecutive spaces 19 | statement.text = re.sub(' +', ' ', statement.text) 20 | 21 | return statement 22 | 23 | 24 | def unescape_html(statement): 25 | """ 26 | Convert escaped html characters into unescaped html characters. 27 | For example: "<b>" becomes "". 28 | """ 29 | import html 30 | 31 | statement.text = html.unescape(statement.text) 32 | 33 | return statement 34 | 35 | 36 | def convert_to_ascii(statement): 37 | """ 38 | Converts unicode characters to ASCII character equivalents. 39 | For example: "på fédéral" becomes "pa federal". 40 | """ 41 | import unicodedata 42 | 43 | text = unicodedata.normalize('NFKD', statement.text) 44 | text = text.encode('ascii', 'ignore').decode('utf-8') 45 | 46 | statement.text = str(text) 47 | return statement 48 | -------------------------------------------------------------------------------- /chatterbot/response_selection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Response selection methods determines which response should be used in 3 | the event that multiple responses are generated within a logic adapter. 4 | """ 5 | import logging 6 | 7 | 8 | def get_most_frequent_response(input_statement, response_list, storage=None): 9 | """ 10 | :param input_statement: A statement, that closely matches an input to the chat bot. 11 | :type input_statement: Statement 12 | 13 | :param response_list: A list of statement options to choose a response from. 14 | :type response_list: list 15 | 16 | :param storage: An instance of a storage adapter to allow the response selection 17 | method to access other statements if needed. 18 | :type storage: StorageAdapter 19 | 20 | :return: The response statement with the greatest number of occurrences. 21 | :rtype: Statement 22 | """ 23 | matching_response = None 24 | occurrence_count = -1 25 | 26 | logger = logging.getLogger(__name__) 27 | logger.info('Selecting response with greatest number of occurrences.') 28 | 29 | for statement in response_list: 30 | count = len(list(storage.filter( 31 | text=statement.text, 32 | in_response_to=input_statement.text) 33 | )) 34 | 35 | # Keep the more common statement 36 | if count >= occurrence_count: 37 | matching_response = statement 38 | occurrence_count = count 39 | 40 | # Choose the most commonly occuring matching response 41 | return matching_response 42 | 43 | 44 | def get_first_response(input_statement, response_list, storage=None): 45 | """ 46 | :param input_statement: A statement, that closely matches an input to the chat bot. 47 | :type input_statement: Statement 48 | 49 | :param response_list: A list of statement options to choose a response from. 50 | :type response_list: list 51 | 52 | :param storage: An instance of a storage adapter to allow the response selection 53 | method to access other statements if needed. 54 | :type storage: StorageAdapter 55 | 56 | :return: Return the first statement in the response list. 57 | :rtype: Statement 58 | """ 59 | logger = logging.getLogger(__name__) 60 | logger.info('Selecting first response from list of {} options.'.format( 61 | len(response_list) 62 | )) 63 | return response_list[0] 64 | 65 | 66 | def get_random_response(input_statement, response_list, storage=None): 67 | """ 68 | :param input_statement: A statement, that closely matches an input to the chat bot. 69 | :type input_statement: Statement 70 | 71 | :param response_list: A list of statement options to choose a response from. 72 | :type response_list: list 73 | 74 | :param storage: An instance of a storage adapter to allow the response selection 75 | method to access other statements if needed. 76 | :type storage: StorageAdapter 77 | 78 | :return: Choose a random response from the selection. 79 | :rtype: Statement 80 | """ 81 | from random import choice 82 | logger = logging.getLogger(__name__) 83 | logger.info('Selecting a response from list of {} options.'.format( 84 | len(response_list) 85 | )) 86 | return choice(response_list) 87 | -------------------------------------------------------------------------------- /chatterbot/storage/__init__.py: -------------------------------------------------------------------------------- 1 | from chatterbot.storage.storage_adapter import StorageAdapter 2 | from chatterbot.storage.django_storage import DjangoStorageAdapter 3 | from chatterbot.storage.mongodb import MongoDatabaseAdapter 4 | from chatterbot.storage.sql_storage import SQLStorageAdapter 5 | 6 | 7 | __all__ = ( 8 | 'StorageAdapter', 9 | 'DjangoStorageAdapter', 10 | 'MongoDatabaseAdapter', 11 | 'SQLStorageAdapter', 12 | ) 13 | -------------------------------------------------------------------------------- /chatterbot/tagging.py: -------------------------------------------------------------------------------- 1 | import string 2 | from chatterbot import languages 3 | 4 | 5 | class LowercaseTagger(object): 6 | """ 7 | Returns the text in lowercase. 8 | """ 9 | 10 | def __init__(self, language=None): 11 | self.language = language or languages.ENG 12 | 13 | def get_text_index_string(self, text): 14 | return text.lower() 15 | 16 | 17 | class PosLemmaTagger(object): 18 | 19 | def __init__(self, language=None): 20 | import spacy 21 | 22 | self.language = language or languages.ENG 23 | 24 | self.punctuation_table = str.maketrans(dict.fromkeys(string.punctuation)) 25 | 26 | self.nlp = spacy.load(self.language.ISO_639_1.lower()) 27 | 28 | def get_text_index_string(self, text): 29 | """ 30 | Return a string of text containing part-of-speech, lemma pairs. 31 | """ 32 | bigram_pairs = [] 33 | 34 | if len(text) <= 2: 35 | text_without_punctuation = text.translate(self.punctuation_table) 36 | if len(text_without_punctuation) >= 1: 37 | text = text_without_punctuation 38 | 39 | document = self.nlp(text) 40 | 41 | if len(text) <= 2: 42 | bigram_pairs = [ 43 | token.lemma_.lower() for token in document 44 | ] 45 | else: 46 | tokens = [ 47 | token for token in document if token.is_alpha and not token.is_stop 48 | ] 49 | 50 | if len(tokens) < 2: 51 | tokens = [ 52 | token for token in document if token.is_alpha 53 | ] 54 | 55 | for index in range(1, len(tokens)): 56 | bigram_pairs.append('{}:{}'.format( 57 | tokens[index - 1].pos_, 58 | tokens[index].lemma_.lower() 59 | )) 60 | 61 | if not bigram_pairs: 62 | bigram_pairs = [ 63 | token.lemma_.lower() for token in document 64 | ] 65 | 66 | return ' '.join(bigram_pairs) 67 | -------------------------------------------------------------------------------- /chatterbot/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | ChatterBot utility functions 3 | """ 4 | 5 | 6 | def import_module(dotted_path): 7 | """ 8 | Imports the specified module based on the 9 | dot notated import path for the module. 10 | """ 11 | import importlib 12 | 13 | module_parts = dotted_path.split('.') 14 | module_path = '.'.join(module_parts[:-1]) 15 | module = importlib.import_module(module_path) 16 | 17 | return getattr(module, module_parts[-1]) 18 | 19 | 20 | def initialize_class(data, *args, **kwargs): 21 | """ 22 | :param data: A string or dictionary containing a import_path attribute. 23 | """ 24 | if isinstance(data, dict): 25 | import_path = data.get('import_path') 26 | data.update(kwargs) 27 | Class = import_module(import_path) 28 | 29 | return Class(*args, **data) 30 | else: 31 | Class = import_module(data) 32 | 33 | return Class(*args, **kwargs) 34 | 35 | 36 | def validate_adapter_class(validate_class, adapter_class): 37 | """ 38 | Raises an exception if validate_class is not a 39 | subclass of adapter_class. 40 | 41 | :param validate_class: The class to be validated. 42 | :type validate_class: class 43 | 44 | :param adapter_class: The class type to check against. 45 | :type adapter_class: class 46 | 47 | :raises: Adapter.InvalidAdapterTypeException 48 | """ 49 | from chatterbot.adapters import Adapter 50 | 51 | # If a dictionary was passed in, check if it has an import_path attribute 52 | if isinstance(validate_class, dict): 53 | 54 | if 'import_path' not in validate_class: 55 | raise Adapter.InvalidAdapterTypeException( 56 | 'The dictionary {} must contain a value for "import_path"'.format( 57 | str(validate_class) 58 | ) 59 | ) 60 | 61 | # Set the class to the import path for the next check 62 | validate_class = validate_class.get('import_path') 63 | 64 | if not issubclass(import_module(validate_class), adapter_class): 65 | raise Adapter.InvalidAdapterTypeException( 66 | '{} must be a subclass of {}'.format( 67 | validate_class, 68 | adapter_class.__name__ 69 | ) 70 | ) 71 | 72 | 73 | def get_response_time(chatbot, statement='Hello'): 74 | """ 75 | Returns the amount of time taken for a given 76 | chat bot to return a response. 77 | 78 | :param chatbot: A chat bot instance. 79 | :type chatbot: ChatBot 80 | 81 | :returns: The response time in seconds. 82 | :rtype: float 83 | """ 84 | import time 85 | 86 | start_time = time.time() 87 | 88 | chatbot.get_response(statement) 89 | 90 | return time.time() - start_time 91 | 92 | 93 | def print_progress_bar(description, iteration_counter, total_items, progress_bar_length=20): 94 | """ 95 | Print progress bar 96 | :param description: Training description 97 | :type description: str 98 | 99 | :param iteration_counter: Incremental counter 100 | :type iteration_counter: int 101 | 102 | :param total_items: total number items 103 | :type total_items: int 104 | 105 | :param progress_bar_length: Progress bar length 106 | :type progress_bar_length: int 107 | 108 | :returns: void 109 | :rtype: void 110 | """ 111 | import sys 112 | 113 | percent = float(iteration_counter) / total_items 114 | hashes = '#' * int(round(percent * progress_bar_length)) 115 | spaces = ' ' * (progress_bar_length - len(hashes)) 116 | sys.stdout.write('\r{0}: [{1}] {2}%'.format(description, hashes + spaces, int(round(percent * 100)))) 117 | sys.stdout.flush() 118 | if total_items == iteration_counter: 119 | print('\r') 120 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | coveralls 2 | flake8 3 | nltk>=3.2,<4.0 4 | nose 5 | pint>=0.8.1 6 | pymongo>=3.3,<4.0 7 | twine 8 | twython 9 | spacy>=3.4.1 10 | sphinx>=3.0,<3.1 11 | sphinx_rtd_theme 12 | pyyaml>=5.3,<5.4 13 | git+git://github.com/gunthercox/chatterbot-corpus@master#egg=chatterbot_corpus 14 | https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.1.0/en_core_web_sm-2.1.0.tar.gz#egg=en_core_web_sm 15 | https://github.com/explosion/spacy-models/releases/download/de_core_news_sm-2.1.0/de_core_news_sm-2.1.0.tar.gz#egg=de_core_news_sm 16 | en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.0/en_core_web_sm-3.4.0-py3-none-any.whl 17 | -------------------------------------------------------------------------------- /docs/_includes/python_module_structure.txt: -------------------------------------------------------------------------------- 1 | IronyAdapter/ 2 | |-- README 3 | |-- setup.py 4 | |-- irony_adapter 5 | | |-- __init__.py 6 | | |-- logic.py 7 | |-- tests 8 | |-- |-- __init__.py 9 | |-- |-- test_logic.py -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/github-mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/docs/_static/github-mark.png -------------------------------------------------------------------------------- /docs/_static/so-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/docs/_static/so-icon.png -------------------------------------------------------------------------------- /docs/_static/style.css: -------------------------------------------------------------------------------- 1 | .wy-side-nav-search { 2 | background-color: #300a24; 3 | } 4 | 5 | #mc_embed_signup { 6 | background: transparent; 7 | font: 14px Helvetica,Arial,sans-serif; 8 | } 9 | 10 | /* MailChimp Form Embed Code - Classic - 12/17/2015 v10.7 */ 11 | #mc_embed_signup form { 12 | display: block; 13 | position: relative; 14 | padding: 10px 0 10px 3% 15 | } 16 | #mc_embed_signup h2 { 17 | font-weight: bold; 18 | padding: 0; 19 | margin: 15px 0; 20 | font-size: 1.4em; 21 | } 22 | #mc_embed_signup input { 23 | border: 1px solid #ABB0B2; 24 | border-radius: 3px; 25 | } 26 | #mc_embed_signup input:focus { 27 | border-color: #333; 28 | } 29 | #mc_embed_signup .button { 30 | clear: both; 31 | background-color: #aaa; 32 | border: 0 none; 33 | border-radius: 4px; 34 | transition: all 0.23s ease-in-out 0s; 35 | color: #FFFFFF; 36 | cursor: pointer; 37 | display: inline-block; 38 | font-size: 15px; 39 | font-weight: normal; 40 | height: 32px; 41 | line-height: 32px; 42 | margin: 0 5px 10px 0; 43 | padding: 0 22px; 44 | text-align: center; 45 | text-decoration: none; 46 | vertical-align: top; 47 | white-space: nowrap; 48 | width: auto; 49 | } 50 | #mc_embed_signup .button:hover { 51 | background-color: #777; 52 | } 53 | #mc_embed_signup .small-meta { 54 | font-size: 11px; 55 | } 56 | #mc_embed_signup .nowrap { 57 | white-space: nowrap; 58 | } 59 | #mc_embed_signup .mc-field-group { 60 | clear: left; 61 | position: relative; 62 | width: 96%; 63 | padding-bottom: 3%; 64 | min-height: 50px; 65 | } 66 | #mc_embed_signup .mc-field-group label { 67 | display: block; 68 | margin-bottom: 3px; 69 | } 70 | #mc_embed_signup .mc-field-group input { 71 | display: block; 72 | width: 100%; 73 | padding: 8px 0; 74 | text-indent: 2%; 75 | } 76 | #mc_embed_signup .indicates-required { 77 | text-align: right; 78 | font-size: 11px; 79 | margin-right: 4%; 80 | } 81 | #mc_embed_signup .asterisk { 82 | color: #e85c41; 83 | font-size: 150%; 84 | font-weight: normal; 85 | position: relative; 86 | top: 5px; 87 | } 88 | #mc_embed_signup .clear { 89 | clear: both; 90 | } 91 | #mc_embed_signup .mc-field-group.input-group ul { 92 | margin: 0; 93 | padding: 5px 0; 94 | list-style: none; 95 | } 96 | #mc_embed_signup .mc-field-group.input-group ul li { 97 | display: block; 98 | padding: 3px 0; 99 | margin: 0; 100 | } 101 | #mc_embed_signup .mc-field-group.input-group label { 102 | display: inline; 103 | } 104 | #mc_embed_signup .mc-field-group.input-group input { 105 | display: inline; 106 | width: auto; 107 | border: none; 108 | } 109 | #mc_embed_signup div#mce-responses { 110 | float: left; 111 | top: -1.4em; 112 | padding: 0em .5em 0em .5em; 113 | overflow: hidden; 114 | width: 90%; 115 | margin: 0 5%; 116 | clear: both; 117 | } 118 | #mc_embed_signup div.response { 119 | margin: 1em 0; 120 | padding: 1em .5em .5em 0; 121 | font-weight: bold; 122 | float: left; 123 | top: -1.5em; 124 | z-index: 1; 125 | width: 80%; 126 | } 127 | #mc_embed_signup #mce-error-response { 128 | display: none; 129 | } 130 | #mc_embed_signup #mce-success-response { 131 | color: #529214; 132 | display: none; 133 | } 134 | #mc_embed_signup label.error { 135 | display: block; 136 | float: none; 137 | width: auto; 138 | margin-left: 1.05em; 139 | text-align: left; 140 | padding: .5em 0; 141 | } 142 | 143 | #mc-embedded-subscribe { 144 | clear: both; 145 | width: auto; 146 | display: block; 147 | margin: 1em 0 1em 5%; 148 | } 149 | #mc_embed_signup #num-subscribers { 150 | font-size: 1.1em; 151 | } 152 | #mc_embed_signup #num-subscribers span { 153 | padding: .5em; 154 | border: 1px solid #ccc; 155 | margin-right:.5em; 156 | font-weight:bold; 157 | } 158 | 159 | #mc_embed_signup #mc-embedded-subscribe-form div.mce_inline_error { 160 | display: inline-block; 161 | margin: 2px 0 1em 0; 162 | padding: 5px 10px; 163 | background-color: rgba(255,255,255,0.85); 164 | border-radius: 3px; font-size:14px; 165 | font-weight: normal; z-index:1; color:#e85c41; 166 | } 167 | #mc_embed_signup #mc-embedded-subscribe-form input.mce_inline_error { 168 | border: 2px solid #e85c41; 169 | } 170 | -------------------------------------------------------------------------------- /docs/_templates/footer.html: -------------------------------------------------------------------------------- 1 | {% extends '!footer.html' %} 2 | 3 | {% block extrafooter %} 4 | 56 | 57 | 58 | 71 | {{ super() }} 72 | {% endblock %} 73 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends '!layout.html' %} 2 | 3 | {% set metatags = '' %} 4 | 5 | {% block menu %} 6 | {{ super() }} 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /docs/commands.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Command line tools 3 | ================== 4 | 5 | ChatterBot comes with a few command line tools that can help 6 | 7 | Get the installed ChatterBot version 8 | ==================================== 9 | 10 | If have ChatterBot installed and you want to check what version 11 | you have then you can run the following command. 12 | 13 | .. code-block:: bash 14 | 15 | python -m chatterbot --version 16 | -------------------------------------------------------------------------------- /docs/comparisons.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Comparisons 3 | =========== 4 | 5 | .. _statement-comparison: 6 | 7 | Statement comparison 8 | ==================== 9 | 10 | ChatterBot uses ``Statement`` objects to hold information 11 | about things that can be said. An important part of how a chat bot 12 | selects a response is based on its ability to compare two statements 13 | to each other. There are a number of ways to do this, and ChatterBot 14 | comes with a handful of methods built in for you to use. 15 | 16 | .. automodule:: chatterbot.comparisons 17 | :members: 18 | 19 | Use your own comparison function 20 | ++++++++++++++++++++++++++++++++ 21 | 22 | You can create your own comparison function and use it as long as the function takes two statements 23 | as parameters and returns a numeric value between 0 and 1. A 0 should represent the lowest possible 24 | similarity and a 1 should represent the highest possible similarity. 25 | 26 | .. code-block:: python 27 | 28 | def comparison_function(statement, other_statement): 29 | 30 | # Your comparison logic 31 | 32 | # Return your calculated value here 33 | return 0.0 34 | 35 | Setting the comparison method 36 | ----------------------------- 37 | 38 | To set the statement comparison method for your chat bot, you 39 | will need to pass the ``statement_comparison_function`` parameter 40 | to your chat bot when you initialize it. An example of this 41 | is shown below. 42 | 43 | .. code-block:: python 44 | 45 | from chatterbot import ChatBot 46 | from chatterbot.comparisons import LevenshteinDistance 47 | 48 | chatbot = ChatBot( 49 | # ... 50 | statement_comparison_function=LevenshteinDistance 51 | ) 52 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Contributing to ChatterBot 3 | ========================== 4 | 5 | There are numerous ways to contribute to ChatterBot. All of which are highly encouraged. 6 | 7 | - Contributing bug reports and feature requests 8 | 9 | - Contributing documentation 10 | 11 | - Contributing code for new features 12 | 13 | - Contributing fixes for bugs 14 | 15 | Every bit of help received on this project is highly appreciated. 16 | 17 | 18 | Setting Up a Development Environment 19 | ==================================== 20 | 21 | To contribute to ChatterBot's development, you simply need: 22 | 23 | - Python 24 | 25 | - pip 26 | 27 | - A few python packages: 28 | 29 | .. code-block:: bash 30 | 31 | pip install requirements.txt 32 | pip install dev-requirements.txt 33 | 34 | - A text editor 35 | 36 | 37 | Reporting a Bug 38 | =============== 39 | 40 | If you discover a bug in ChatterBot and wish to report it, please be 41 | sure that you adhere to the following when you report it on GitHub. 42 | 43 | 1. Before creating a new bug report, please search to see if an open or closed report matching yours already exists. 44 | 2. Please include a description that will allow others to recreate the problem you encountered. 45 | 46 | 47 | Requesting New Features 48 | ======================= 49 | 50 | When requesting a new feature in ChatterBot, please make sure to include 51 | the following details in your request. 52 | 53 | 1. Your use case. Describe what you are doing that requires this new functionality. 54 | 55 | 56 | Contributing Documentation 57 | ========================== 58 | 59 | ChatterBot's documentation is written in reStructuredText and is 60 | compiled by Sphinx. The reStructuredText source of the documentation 61 | is located in ``docs/``. 62 | 63 | To build the documentation yourself, run: 64 | 65 | .. code-block:: bash 66 | 67 | sphinx-build ./docs/ ./build/ 68 | 69 | You can then open the index.html file that will be created in the build directory. 70 | 71 | 72 | Contributing Code 73 | ================= 74 | 75 | The development of ChatterBot happens on GitHub. Code contributions should be 76 | submitted there in the form of pull requests. 77 | 78 | Pull requests should meet the following criteria. 79 | 80 | 1. Fix one issue and fix it well. 81 | 2. Do not include extraneous changes that do not relate to the issue being fixed. 82 | 3. Include a descriptive title and description for the pull request. 83 | 4. Have descriptive commit messages. 84 | -------------------------------------------------------------------------------- /docs/conversations.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Conversations 3 | ============= 4 | 5 | ChatterBot supports the ability to have multiple concurrent conversations. 6 | A conversations is where the chat bot interacts with a person, and supporting 7 | multiple concurrent conversations means that the chat bot can have multiple 8 | different conversations with different people at the same time. 9 | 10 | Conversation scope 11 | ------------------ 12 | 13 | If two ``ChatBot`` instances are created, each will have conversations separate from each other. 14 | 15 | An adapter can access any conversation as long as the unique identifier for the conversation is provided. 16 | 17 | Conversation example 18 | -------------------- 19 | 20 | The following example is taken from the Django ``ChatterBotApiView`` built into ChatterBot. 21 | In this method, the unique identifiers for each chat session are being stored in Django's 22 | session objects. This allows different users who interact with the bot through different 23 | web browsers to have separate conversations with the chat bot. 24 | 25 | .. literalinclude:: ../examples/django_app/example_app/views.py 26 | :language: python 27 | :pyobject: ChatterBotApiView.post 28 | :dedent: 4 29 | 30 | 31 | .. _conversation_statements: 32 | 33 | Statements 34 | ========== 35 | 36 | ChatterBot's statement objects represent either an input statement that the 37 | chat bot has received from a user, or an output statement that the chat bot 38 | has returned based on some input. 39 | 40 | .. autoclass:: chatterbot.conversation.Statement 41 | :members: 42 | 43 | .. autoinstanceattribute:: chatterbot.conversation.Statement.confidence 44 | 45 | ChatterBot's logic adapters assign a confidence score to the statement 46 | before it is returned. The confidence score indicates the degree of 47 | certainty with which the chat bot believes this is the correct response 48 | to the given input. 49 | 50 | .. autoinstanceattribute:: chatterbot.conversation.Statement.in_response_to 51 | 52 | The response attribute represents the relationship between two statements. 53 | This value of this field indicates that one statement was issued in response 54 | to another statement. 55 | 56 | 57 | Statement-response relationship 58 | =============================== 59 | 60 | ChatterBot stores knowledge of conversations as statements. Each statement can have any 61 | number of possible responses. 62 | 63 | .. image:: _static/statement-response-relationship.svg 64 | :alt: ChatterBot statement-response relationship 65 | 66 | Each ``Statement`` object has an ``in_response_to`` reference which links the 67 | statement to a number of other statements that it has been learned to be in response to. 68 | The ``in_response_to`` attribute is essentially a reference to all parent statements 69 | of the current statement. 70 | 71 | .. image:: _static/statement-relationship.svg 72 | :alt: ChatterBot statement relationship 73 | 74 | The count of recorded statements with matching, or similar text indicates the number of 75 | times that the statement has been given as a response. This makes it possible for the 76 | chat bot to determine if a particular response is more commonly used than another. 77 | -------------------------------------------------------------------------------- /docs/corpus.rst: -------------------------------------------------------------------------------- 1 | ChatterBot Corpus 2 | ================= 3 | 4 | This is a :term:`corpus` of dialog data that is included in the chatterbot module. 5 | 6 | Additional information about the ``chatterbot-corpus`` module can be found 7 | in the `ChatterBot Corpus Documentation`_. 8 | 9 | Corpus language availability 10 | ---------------------------- 11 | 12 | Corpus data is user contributed, but it is also not difficult to create one if you are familiar with the language. 13 | This is because each corpus is just a sample of various input statements and their responses for the bot to train itself with. 14 | 15 | To explore what languages and collections of corpora are available, 16 | check out the `chatterbot_corpus/data`_ directory in the separate chatterbot-corpus repository. 17 | 18 | .. note:: 19 | If you are interested in contributing content to the corpus, please feel free to 20 | submit a pull request on ChatterBot's corpus GitHub page. Contributions are welcomed! 21 | 22 | https://github.com/gunthercox/chatterbot-corpus 23 | 24 | The ``chatterbot-corpus`` is distributed in its own Python package so that it can 25 | be released and upgraded independently from the ``chatterbot`` package. 26 | 27 | 28 | Exporting your chat bot's database as a training corpus 29 | ------------------------------------------------------- 30 | 31 | Now that you have created your chat bot and sent it out into the world, perhaps 32 | you are looking for a way to share what it has learned with other chat bots? 33 | ChatterBot's training module provides methods that allow you to export the 34 | content of your chat bot's database as a training corpus that can be used to 35 | train other chat bots. 36 | 37 | .. code-block:: python 38 | 39 | chatbot = ChatBot('Export Example Bot') 40 | chatbot.trainer.export_for_training('./export.yml') 41 | 42 | Here is an example: 43 | 44 | .. literalinclude:: ../examples/export_example.py 45 | :language: python 46 | 47 | .. _chatterbot_corpus/data: https://github.com/gunthercox/chatterbot-corpus/tree/master/chatterbot_corpus/data 48 | .. _ChatterBot Corpus Documentation: http://chatterbot-corpus.readthedocs.io/ 49 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Development 3 | =========== 4 | 5 | As the code for ChatterBot is written, the developers attempt to describe 6 | the logic and reasoning for the various decisions that go into creating the 7 | internal structure of the software. This internal documentation is intended 8 | for future developers and maintainers of the project. A majority of this 9 | information is unnecessary for the typical developer using ChatterBot. 10 | 11 | It is not always possible for every idea to be documented. As a result, the 12 | need may arise to question the developers and maintainers of this project 13 | in order to pull concepts from their minds and place them in these documents. 14 | Please pull gently. 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | 19 | contributing 20 | releases 21 | Release Notes 22 | testing 23 | packaging 24 | 25 | Suggested Development Tools 26 | =========================== 27 | 28 | To help developers work with ChatterBot and projects built using it, the 29 | following tools are suggested. Keep in mind that none of these are required, 30 | but this list has been assembled because it is often useful to have a 31 | tool or technology recommended by people who have experience using it. 32 | 33 | Text Editors 34 | ------------ 35 | 36 | Visual Studio Code 37 | ++++++++++++++++++ 38 | 39 | Website: https://code.visualstudio.com/ 40 | 41 | | I find Visual Studio Code to be an optimally light-weight 42 | | and versatile editor. 43 | | 44 | | ~ Gunther Cox 45 | 46 | Database Clients 47 | ---------------- 48 | 49 | Sqlectron 50 | +++++++++ 51 | 52 | | Sadly this cross-platform database client is no longer being maintained 53 | | by it's creator. Regardless, I still use it frequently and I find its 54 | | interface to be highly convenient and user friendly. 55 | | 56 | | ~ Gunther Cox 57 | 58 | Website: https://sqlectron.github.io/ 59 | 60 | Nosqlclient 61 | +++++++++++ 62 | 63 | | This is a very versatile client for Mongo DB, and other non-relational databases. 64 | | 65 | | ~ Gunther Cox 66 | 67 | Website: https://www.nosqlclient.com/ 68 | -------------------------------------------------------------------------------- /docs/django/index.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Django Integration 3 | ================== 4 | 5 | ChatterBot has direct support for integration with Django's ORM. 6 | It is relatively easy to use ChatterBot within your Django application 7 | to create conversational pages and endpoints. 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | settings 13 | views 14 | wsgi 15 | 16 | Install packages 17 | ================ 18 | 19 | Begin by making sure that you have installed both ``django`` and ``chatterbot``. 20 | 21 | .. sourcecode:: sh 22 | 23 | pip install django chatterbot 24 | 25 | For more details on installing Django, see the `Django documentation`_. 26 | 27 | Installed Apps 28 | -------------- 29 | 30 | Add ``chatterbot.ext.django_chatterbot`` to your ``INSTALLED_APPS`` in the 31 | ``settings.py`` file of your Django project. 32 | 33 | .. code-block:: python 34 | 35 | INSTALLED_APPS = ( 36 | # ... 37 | 'chatterbot.ext.django_chatterbot', 38 | ) 39 | 40 | 41 | Migrations 42 | ---------- 43 | 44 | You can run the Django database migrations for your chat bot with the 45 | following command. 46 | 47 | .. sourcecode:: sh 48 | 49 | python manage.py migrate django_chatterbot 50 | 51 | MongoDB and Django 52 | ------------------ 53 | 54 | ChatterBot has a storage adapter for MongoDB but it does not work with Django. 55 | If you want to use MongoDB as your database for Django and your chat bot then 56 | you will need to install a **Django storage backend** such as `Django MongoDB Engine`_. 57 | 58 | The reason this is required is because Django's storage backends are different 59 | and completely separate from ChatterBot's storage adapters. 60 | 61 | .. _Django documentation: https://docs.djangoproject.com/en/dev/intro/install/ 62 | .. _Django MongoDB Engine: https://django-mongodb-engine.readthedocs.io/ 63 | -------------------------------------------------------------------------------- /docs/django/settings.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Chatterbot Django Settings 3 | ========================== 4 | 5 | You can edit the ChatterBot configuration through your Django settings.py file. 6 | 7 | .. code-block:: python 8 | 9 | CHATTERBOT = { 10 | 'name': 'Tech Support Bot', 11 | 'logic_adapters': [ 12 | 'chatterbot.logic.MathematicalEvaluation', 13 | 'chatterbot.logic.TimeLogicAdapter', 14 | 'chatterbot.logic.BestMatch' 15 | ] 16 | } 17 | 18 | Any setting that gets set in the CHATTERBOT dictionary will be passed to the chat bot that powers your django app. 19 | 20 | Additional Django settings 21 | ========================== 22 | 23 | - ``django_app_name`` [default: 'django_chatterbot'] The Django app name to look up the models from. -------------------------------------------------------------------------------- /docs/django/views.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | ChatterBot Django Views 3 | ======================= 4 | 5 | Example API Views 6 | ================= 7 | 8 | ChatterBot's Django example comes with an API view that demonstrates 9 | one way to use ChatterBot to create an conversational API endpoint 10 | for your application. 11 | 12 | The endpoint expects a JSON request in the following format: 13 | 14 | .. code-block:: json 15 | 16 | {"text": "My input statement"} 17 | 18 | 19 | .. literalinclude:: ../../examples/django_app/example_app/views.py 20 | :language: python 21 | :pyobject: ChatterBotApiView 22 | 23 | 24 | .. note:: 25 | 26 | Looking for the full example? Check it out on GitHub: 27 | https://github.com/gunthercox/ChatterBot/tree/master/examples/django_app 28 | -------------------------------------------------------------------------------- /docs/django/wsgi.rst: -------------------------------------------------------------------------------- 1 | Webservices 2 | =========== 3 | 4 | If you want to host your Django app, you need to choose a method through 5 | which it will be hosted. There are a few free services that you can use 6 | to do this such as `Heroku`_ and `PythonAnyWhere`_. 7 | 8 | WSGI 9 | ---- 10 | 11 | A common method for serving Python web applications involves using a 12 | Web Server Gateway Interface (`WSGI`_) package. 13 | 14 | `Gunicorn`_ is a great choice for a WSGI server. They have detailed 15 | documentation and installation instructions on their website. 16 | 17 | Hosting static files 18 | -------------------- 19 | 20 | There are numerous ways to host static files for your Django application. 21 | One extreemly easy way to do this is by using `WhiteNoise`_, a python package 22 | designed to make it possible to serve static files from just about any web application. 23 | 24 | .. _Heroku: https://dashboard.heroku.com/ 25 | .. _PythonAnyWhere: https://www.pythonanywhere.com/details/django_hosting 26 | .. _Gunicorn: http://gunicorn.org/ 27 | .. _WhiteNoise: http://whitenoise.evans.io/en/stable/ 28 | .. _WSGI: http://wsgi.readthedocs.io/en/latest/what.html 29 | -------------------------------------------------------------------------------- /docs/encoding.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Python String Encoding 3 | ====================== 4 | 5 | The Python developer community has published a great article that covers the 6 | details of unicode character processing. 7 | 8 | - Python 3: https://docs.python.org/3/howto/unicode.html 9 | - Python 2: https://docs.python.org/2/howto/unicode.html 10 | 11 | The following notes are intended to help answer some common questions and issues 12 | that developers frequently encounter while learning to properly work with different 13 | character encodings in Python. 14 | 15 | Does ChatterBot handle non-ascii characters? 16 | ============================================ 17 | 18 | ChatterBot is able to handle unicode values correctly. You can pass to it 19 | non-encoded data and it should be able to process it properly 20 | (you will need to make sure that you decode the output that is returned). 21 | 22 | Below is one of ChatterBot's tests from `tests/test_chatbot.py`_, 23 | this is just a simple check that a unicode response can be processed. 24 | 25 | .. code-block:: python 26 | 27 | def test_get_response_unicode(self): 28 | """ 29 | Test the case that a unicode string is passed in. 30 | """ 31 | response = self.chatbot.get_response(u'سلام') 32 | self.assertGreater(len(response.text), 0) 33 | 34 | This test passes Python 3. It also verifies that 35 | ChatterBot *can* take unicode input without issue. 36 | 37 | How do I fix Python encoding errors? 38 | ==================================== 39 | 40 | When working with string type data in Python, it is possible to encounter errors 41 | such as the following. 42 | 43 | .. code-block:: text 44 | 45 | UnicodeDecodeError: 'utf8' codec can't decode byte 0x92 in position 48: invalid start byte 46 | 47 | Depending on what your code looks like, there are a few things that you can do 48 | to prevent errors like this. 49 | 50 | Unicode header 51 | -------------- 52 | 53 | .. code-block:: python 54 | 55 | # -*- coding: utf-8 -*- 56 | 57 | When to use the unicode header 58 | ++++++++++++++++++++++++++++++ 59 | 60 | If your strings use escaped unicode characters (they look like ``u'\u00b0C'``) then 61 | you do not need to add the header. If you use strings like ``'ØÆÅ'`` then you are required 62 | to use the header. 63 | 64 | If you are using this header it must be the first line in your Python file. 65 | 66 | Unicode escape characters 67 | ------------------------- 68 | 69 | .. code-block:: text 70 | 71 | >>> print u'\u0420\u043e\u0441\u0441\u0438\u044f' 72 | Россия 73 | 74 | When to use escape characters 75 | +++++++++++++++++++++++++++++ 76 | 77 | Prefix your strings with the unicode escape character ``u'...'`` when you are 78 | using escaped unicode characters. 79 | 80 | Import unicode literals from future 81 | ----------------------------------- 82 | 83 | .. code-block:: python 84 | 85 | from __future__ import unicode_literals 86 | 87 | When to import unicode literals 88 | +++++++++++++++++++++++++++++++ 89 | 90 | Use this when you need to make sure that Python 3 code also works in Python 2. 91 | 92 | A good article on this can be found here: http://python-future.org/unicode_literals.html 93 | 94 | .. _`tests/test_chatbot.py`: https://github.com/gunthercox/ChatterBot/blob/master/tests/test_chatbot.py 95 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Examples 3 | ======== 4 | 5 | The following examples are available to help you get started with ChatterBot. 6 | 7 | .. note:: 8 | Before you run any example, you will need to install ChatterBot on your system. 9 | See the :ref:`Setup guide ` for instructions. 10 | 11 | Simple Example 12 | ============== 13 | 14 | .. literalinclude:: ../examples/basic_example.py 15 | :language: python 16 | 17 | Terminal Example 18 | ================ 19 | 20 | This example program shows how to create a simple terminal client 21 | that allows you to communicate with your chat bot by typing into 22 | your terminal. 23 | 24 | .. literalinclude:: ../examples/terminal_example.py 25 | :language: python 26 | 27 | Using MongoDB 28 | ============= 29 | 30 | Before you can use ChatterBot's built in adapter for MongoDB, 31 | you will need to `install MongoDB`_. Make sure MongoDB is 32 | running in your environment before you execute your program. 33 | To tell ChatterBot to use this adapter, you will need to set 34 | the `storage_adapter` parameter. 35 | 36 | .. code-block:: python 37 | 38 | storage_adapter="chatterbot.storage.MongoDatabaseAdapter" 39 | 40 | .. literalinclude:: ../examples/terminal_mongo_example.py 41 | :language: python 42 | 43 | Time and Mathematics Example 44 | ============================ 45 | 46 | ChatterBot has natural language evaluation capabilities that 47 | allow it to process and evaluate mathematical and time-based 48 | inputs. 49 | 50 | .. literalinclude:: ../examples/math_and_time.py 51 | :language: python 52 | 53 | Using SQL Adapter 54 | ================= 55 | 56 | ChatterBot data can be saved and retrieved from SQL databases. 57 | 58 | .. literalinclude:: ../examples/memory_sql_example.py 59 | :language: python 60 | 61 | Read only mode 62 | ============== 63 | 64 | Your chat bot will learn based on each new input statement it receives. 65 | If you want to disable this learning feature after your bot has been trained, 66 | you can set `read_only=True` as a parameter when initializing the bot. 67 | 68 | .. code-block:: python 69 | 70 | chatbot = ChatBot("Johnny Five", read_only=True) 71 | 72 | More Examples 73 | ============= 74 | 75 | Even more examples can be found in the ``examples`` directory on GitHub: 76 | https://github.com/gunthercox/ChatterBot/tree/master/examples 77 | 78 | .. _install MongoDB: https://docs.mongodb.com/manual/installation/ 79 | -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Frequently Asked Questions 3 | ========================== 4 | 5 | This document is comprised of questions that are frequently 6 | asked about ChatterBot and chat bots in general. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | encoding 12 | 13 | How do I deploy my chat bot to the web? 14 | --------------------------------------- 15 | 16 | There are a number of excellent web frameworks for creating 17 | Python projects out there. Django and Flask are two excellent 18 | examples of these. ChatterBot is designed to be agnostic to 19 | the platform it is deployed on and it is very easy to get set up. 20 | 21 | To run ChatterBot inside of a web application you just need a way 22 | for your application to receive incoming data and to return data. 23 | You can do this any way you want, HTTP requests, web sockets, etc. 24 | 25 | There are a number of existing examples that show how to do this. 26 | 27 | 1. An example using Django: https://github.com/gunthercox/ChatterBot/tree/master/examples/django_app 28 | 2. An example using Flask: https://github.com/chamkank/flask-chatterbot/blob/master/app.py 29 | 30 | Additional details and recommendations for configuring Django can be found 31 | in the :ref:`Webservices` section of ChatterBot's Django documentation. 32 | 33 | What kinds of machine learning does ChatterBot use? 34 | --------------------------------------------------- 35 | 36 | In brief, ChatterBot uses a number of different machine learning techniques to 37 | generate its responses. The specific algorithms depend on how the chat bot is 38 | used and the settings that it is configured with. 39 | 40 | Here is a general overview of some of the various machine learning techniques 41 | that are employed throughout ChatterBot's codebase. 42 | 43 | 1. Search algorithms 44 | ++++++++++++++++++++ 45 | 46 | Searching is the most rudimentary form of artificial intelligence. To be fair, 47 | there are differences between machine learning and artificial intelligence but 48 | lets avoid those for now and instead focus on the topic of algorithms that make 49 | the chat bot talk intelligently. 50 | 51 | Search is a crucial part of how a chat bot quickly and efficiently retrieves 52 | the possible candidate statements that it can respond with. 53 | 54 | Some examples of attributes that help the chat bot select a response include 55 | 56 | - the similarity of an input statement to known statements 57 | - the frequency in which similar known responses occur 58 | - the likeliness of an input statement to fit into a category that known statements are a part of 59 | 60 | 2. Classification algorithms 61 | ++++++++++++++++++++++++++++ 62 | 63 | Several logic adapters in ChatterBot use `naive Bayesian classification`_ 64 | algorithms to determine if an input statement meets a particular set of 65 | criteria that warrant a response to be generated from that logic adapter. 66 | 67 | .. _naive Bayesian classification: https://en.wikipedia.org/wiki/Naive_Bayes_classifier 68 | -------------------------------------------------------------------------------- /docs/filters.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Filters 3 | ======= 4 | 5 | Filters are an efficient way to create queries that can be passed to ChatterBot's storage adapters. 6 | Filters will reduce the number of statements that a chat bot has to process when it is selecting a response. 7 | 8 | Setting filters 9 | =============== 10 | 11 | .. code-block:: python 12 | 13 | chatbot = ChatBot( 14 | "My ChatterBot", 15 | filters=[filters.get_recent_repeated_responses] 16 | ) 17 | 18 | .. automodule:: chatterbot.filters 19 | :members: 20 | -------------------------------------------------------------------------------- /docs/glossary.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Glossary 3 | ======== 4 | 5 | .. glossary:: 6 | 7 | adapters 8 | A base class that allows a ChatBot instance to execute some kind of functionality. 9 | 10 | logic adapter 11 | An adapter class that allows a ChatBot instance to select a response to 12 | 13 | storage adapter 14 | A class that allows a chat bot to store information somewhere, such as a database. 15 | 16 | corpus 17 | In linguistics, a corpus (plural corpora) or text corpus is a large 18 | and structured set of texts. They are used to do statistical analysis 19 | and hypothesis testing, checking occurrences or validating linguistic 20 | rules within a specific language territory [1]_. 21 | 22 | preprocessors 23 | A member of a list of functions that can be used to modify text 24 | input that the chat bot receives before the text is passed to 25 | the logic adapter for processing. 26 | 27 | statement 28 | A single string of text representing something that can be said. 29 | 30 | search word 31 | A word that is not a stop word and has been trimmed in some way ( 32 | for example through stemming). 33 | 34 | stemming 35 | A process through which a word is reduced into a derivative form. 36 | 37 | stop word 38 | A common word that is often filtered out during the process of 39 | analyzing text. 40 | 41 | response 42 | A single string of text that is uttered as an answer, a reply or 43 | an acknowledgement to a statement. 44 | 45 | untrained instance 46 | An untrained instance of the chat bot has an empty database. 47 | 48 | .. [1] https://en.wikipedia.org/wiki/Text_corpus -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. image:: ../graphics/banner.png 2 | 3 | About ChatterBot 4 | ================ 5 | 6 | ChatterBot is a Python library that makes it easy to generate automated 7 | responses to a user's input. ChatterBot uses a selection of machine learning 8 | algorithms to produce different types of responses. This makes it easy for 9 | developers to create chat bots and automate conversations with users. 10 | For more details about the ideas and concepts behind ChatterBot see the 11 | :ref:`process flow diagram `. 12 | 13 | An example of typical input would be something like this: 14 | 15 | .. code-block:: text 16 | 17 | user: Good morning! How are you doing? 18 | bot: I am doing very well, thank you for asking. 19 | user: You're welcome. 20 | bot: Do you like hats? 21 | 22 | Language Independence 23 | ===================== 24 | 25 | The language independent design of ChatterBot allows it to be trained to speak any language. 26 | Additionally, the machine-learning nature of ChatterBot allows an agent instance to improve 27 | it's own knowledge of possible responses as it interacts with humans and other sources of informative data. 28 | 29 | How ChatterBot Works 30 | ==================== 31 | 32 | ChatterBot is a Python library designed to make it easy to create software that can engage in conversation. 33 | 34 | An :term:`untrained instance` of ChatterBot starts off with no knowledge of how to communicate. 35 | Each time a user enters a :term:`statement`, the library saves the text that they entered and the text 36 | that the statement was in response to. As ChatterBot receives more input the number of responses 37 | that it can reply and the accuracy of each response in relation to the input statement increase. 38 | 39 | The program selects the closest matching :term:`response` by searching for the closest matching known 40 | statement that matches the input, it then chooses a response from the selection of known responses 41 | to that statement. 42 | 43 | .. _process_flow_diagram: 44 | 45 | Process flow diagram 46 | ==================== 47 | 48 | .. image:: _static/chatterbot-process-flow.svg 49 | :alt: ChatterBot process flow diagram 50 | 51 | Contents: 52 | ========= 53 | 54 | .. toctree:: 55 | :maxdepth: 4 56 | 57 | setup 58 | quickstart 59 | tutorial 60 | examples 61 | training 62 | preprocessors 63 | logic/index 64 | storage/index 65 | filters 66 | chatterbot 67 | conversations 68 | comparisons 69 | utils 70 | corpus 71 | django/index 72 | faq 73 | commands 74 | development 75 | glossary 76 | 77 | Report an Issue 78 | =============== 79 | 80 | Please direct all bug reports and feature requests to the project's issue 81 | tracker on `GitHub`_. 82 | 83 | Indices and tables 84 | ================== 85 | 86 | * :ref:`genindex` 87 | * :ref:`modindex` 88 | * :ref:`search` 89 | 90 | .. _GitHub: https://github.com/gunthercox/ChatterBot/issues/ 91 | -------------------------------------------------------------------------------- /docs/logic/index.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Logic Adapters 3 | ============== 4 | 5 | Logic adapters determine the logic for how ChatterBot selects a response to a given input statement. 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | response_selection 11 | create-a-logic-adapter 12 | 13 | The logic adapter that your bot uses can be specified by setting the ``logic_adapters`` parameter 14 | to the import path of the logic adapter you want to use. 15 | 16 | 17 | .. code-block:: python 18 | 19 | chatbot = ChatBot( 20 | "My ChatterBot", 21 | logic_adapters=[ 22 | "chatterbot.logic.BestMatch" 23 | ] 24 | ) 25 | 26 | 27 | It is possible to enter any number of logic adapters for your bot to use. 28 | If multiple adapters are used, then the bot will return the response with 29 | the highest calculated confidence value. If multiple adapters return the 30 | same confidence, then the adapter that is entered into the list first will 31 | take priority. 32 | 33 | .. image:: ../_static/dialog-processing-flow.svg 34 | :alt: ChatterBot dialog processing flow 35 | 36 | 37 | Common logic adapter attributes 38 | ================================= 39 | 40 | Each logic adapter inherits the following attributes and methods. 41 | 42 | .. autoclass:: chatterbot.logic.LogicAdapter 43 | :members: 44 | 45 | 46 | Best Match Adapter 47 | ================== 48 | 49 | .. autofunction:: chatterbot.logic.BestMatch 50 | 51 | The ``BestMatch`` logic adapter selects a response based on the best known match to a given statement. 52 | 53 | How it works 54 | ------------ 55 | 56 | The best match adapter uses a function to compare the input statement to known statements. 57 | Once it finds the closest match to the input statement, it uses another function to select one of the 58 | known responses to that statement. 59 | 60 | Setting parameters 61 | ------------------ 62 | 63 | .. code-block:: python 64 | 65 | chatbot = ChatBot( 66 | "My ChatterBot", 67 | logic_adapters=[ 68 | { 69 | "import_path": "chatterbot.logic.BestMatch", 70 | "statement_comparison_function": chatterbot.comparisons.LevenshteinDistance, 71 | "response_selection_method": chatterbot.response_selection.get_first_response 72 | } 73 | ] 74 | ) 75 | 76 | .. note:: 77 | 78 | The values for ``response_selection_method`` and ``statement_comparison_function`` can be a string 79 | of the path to the function, or a callable. 80 | 81 | See the :ref:`statement-comparison` documentation for the list of functions included with ChatterBot. 82 | 83 | See the :ref:`response-selection` documentation for the list of response selection methods included with ChatterBot. 84 | 85 | 86 | Time Logic Adapter 87 | ================== 88 | 89 | .. autofunction:: chatterbot.logic.TimeLogicAdapter 90 | 91 | The ``TimeLogicAdapter`` identifies statements in which a question about the current time is asked. 92 | If a matching question is detected, then a response containing the current time is returned. 93 | 94 | .. code-block:: text 95 | 96 | User: What time is it? 97 | Bot: The current time is 4:45PM. 98 | 99 | 100 | Mathematical Evaluation Adapter 101 | =============================== 102 | 103 | .. autofunction:: chatterbot.logic.MathematicalEvaluation 104 | 105 | The ``MathematicalEvaluation`` logic adapter checks a given statement to see if 106 | it contains a mathematical expression that can be evaluated. 107 | If one exists, then it returns a response containing the result. 108 | This adapter is able to handle any combination of word and numeric operators. 109 | 110 | .. code-block:: text 111 | 112 | User: What is four plus four? 113 | Bot: (4 + 4) = 8 114 | 115 | 116 | Specific Response Adapter 117 | ========================= 118 | 119 | If the input that the chat bot receives, matches the input text specified 120 | for this adapter, the specified response will be returned. 121 | 122 | .. autofunction:: chatterbot.logic.SpecificResponseAdapter 123 | 124 | Specific response example 125 | ------------------------- 126 | 127 | .. literalinclude:: ../../examples/specific_response_example.py 128 | :language: python 129 | 130 | Low confidence response example 131 | ------------------------------- 132 | 133 | .. literalinclude:: ../../examples/default_response_example.py 134 | :language: python 135 | -------------------------------------------------------------------------------- /docs/logic/response_selection.rst: -------------------------------------------------------------------------------- 1 | ==================================== 2 | How logic adapters select a response 3 | ==================================== 4 | 5 | A typical logic adapter designed to return a response to 6 | an input statement will use two main steps to do this. 7 | The first step involves searching the database for a known 8 | statement that matches or closely matches the input statement. 9 | Once a match is selected, the second step involves selecting a 10 | known response to the selected match. Frequently, there will 11 | be a number of existing statements that are responses to the 12 | known match. 13 | 14 | To help with the selection of the response, several methods 15 | are built into ChatterBot for selecting a response from the 16 | available options. 17 | 18 | .. _response-selection: 19 | 20 | Response selection methods 21 | ========================== 22 | 23 | .. automodule:: chatterbot.response_selection 24 | :members: 25 | 26 | Use your own response selection method 27 | ++++++++++++++++++++++++++++++++++++++ 28 | 29 | You can create your own response selection method and use it as long as the function takes 30 | two parameters (a statements and a list of statements). The method must return a statement. 31 | 32 | .. code-block:: python 33 | 34 | def select_response(statement, statement_list, storage=None): 35 | 36 | # Your selection logic 37 | 38 | return selected_statement 39 | 40 | Setting the response selection method 41 | ===================================== 42 | 43 | To set the response selection method for your chat bot, you 44 | will need to pass the ``response_selection_method`` parameter 45 | to your chat bot when you initialize it. An example of this 46 | is shown below. 47 | 48 | .. code-block:: python 49 | 50 | from chatterbot import ChatBot 51 | from chatterbot.response_selection import get_most_frequent_response 52 | 53 | chatbot = ChatBot( 54 | # ... 55 | response_selection_method=get_most_frequent_response 56 | ) 57 | 58 | Response selection in logic adapters 59 | ==================================== 60 | 61 | When a logic adapter is initialized, the response selection method 62 | parameter that was passed to it can be called using ``self.select_response`` 63 | as shown below. 64 | 65 | .. code-block:: python 66 | 67 | response = self.select_response( 68 | input_statement, 69 | list_of_response_options, 70 | self.chatbot.storage 71 | ) 72 | 73 | 74 | Selecting a response from multiple logic adapters 75 | ================================================= 76 | 77 | The ``generate_response`` method is used to select a single response from the responses 78 | returned by all of the logic adapters that the chat bot has been configured to use. 79 | Each response returned by the logic adapters includes a confidence score that indicates 80 | the likeliness that the returned statement is a valid response to the input. 81 | 82 | Response selection 83 | ++++++++++++++++++ 84 | 85 | The ``generate_response`` will return the response statement that has the greatest 86 | confidence score. The only exception to this is a case where multiple logic adapters 87 | return the same statement and therefore *agree* on that response. 88 | 89 | For this example, consider a scenario where multiple logic adapters are being used. 90 | Assume the following results were returned by a chat bot's logic adapters. 91 | 92 | +------------+--------------+ 93 | | Confidence | Statement | 94 | +============+==============+ 95 | | 0.2 | Good morning | 96 | +------------+--------------+ 97 | | 0.5 | Good morning | 98 | +------------+--------------+ 99 | | 0.7 | Good night | 100 | +------------+--------------+ 101 | 102 | In this case, two of the logic adapters have generated the same result. 103 | When multiple logic adapters come to the same conclusion, that statement 104 | is given priority over another response with a possibly higher confidence score. 105 | The fact that the multiple adapters agreed on a response is a significant 106 | indicator that a particular statement has a greater probability of being 107 | a more accurate response to the input. 108 | 109 | When multiple adapters agree on a response, the greatest confidence score that 110 | was generated for that response will be returned with it. 111 | -------------------------------------------------------------------------------- /docs/packaging.rst: -------------------------------------------------------------------------------- 1 | ================================== 2 | Packaging your code for ChatterBot 3 | ================================== 4 | 5 | There are cases where developers may want to contribute code to ChatterBot but for 6 | various reasons it doesn't make sense or isn't possible to add the code to the 7 | main ChatterBot repository on GitHub. 8 | 9 | Common reasons that code can't be contributed include: 10 | 11 | - Licencing: It may not be possible to contribute code to ChatterBot due to a licencing restriction or a copyright. 12 | - Demand: There needs to be a general demand from the open source community for a particular feature so that there are developers who will want to fix and improve the feature if it requires maintenance. 13 | 14 | In addition, all code should be well documented and thoroughly tested. 15 | 16 | Package directory structure 17 | --------------------------- 18 | 19 | Suppose we want to create a new logic adapter for ChatterBot and add it the 20 | Python Package Index (PyPI) so that other developers can install it and use it. 21 | We would begin doing this by setting up a directory file the following structure. 22 | 23 | .. literalinclude:: _includes/python_module_structure.txt 24 | :caption: Python Module Structure 25 | 26 | More information on creating Python packages can be found here: 27 | https://packaging.python.org/tutorials/distributing-packages/ 28 | 29 | Register on PyPI 30 | ================ 31 | 32 | Create an account: https://pypi.python.org/pypi?%3Aaction=register_form 33 | 34 | Create a ``.pypirc`` configuration file. 35 | 36 | .. code-block:: bash 37 | :caption: .pypirc file contents 38 | 39 | [distutils] 40 | index-servers = 41 | pypi 42 | 43 | [pypi] 44 | username=my_username 45 | password=my_password 46 | 47 | Generate packages 48 | ================= 49 | 50 | .. code-block:: bash 51 | 52 | python setup.py sdist bdist_wheel 53 | 54 | Upload packages 55 | =============== 56 | 57 | The official tool for uploading Python packages is called twine. 58 | You can install twine with pip if you don't already have it installed. 59 | 60 | .. code-block:: bash 61 | 62 | pip install twine 63 | 64 | .. code-block:: bash 65 | 66 | twine upload dist/* 67 | 68 | Install your package locally 69 | ============================ 70 | 71 | .. code-block:: bash 72 | 73 | cd IronyAdapter 74 | pip install . --upgrade 75 | 76 | Using your package 77 | ================== 78 | 79 | If you are creating a module that ChatterBot imports from a dotted module path then you 80 | can set the following in your chat bot. 81 | 82 | .. code-block:: python 83 | 84 | chatbot = ChatBot( 85 | "My ChatBot", 86 | logic_adapters=[ 87 | "irony_adapter.logic.IronyAdapter" 88 | ] 89 | ) 90 | 91 | Testing your code 92 | ================= 93 | 94 | .. code-block:: python 95 | 96 | from unittest import TestCase 97 | 98 | 99 | class IronyAdapterTestCase(TestCase): 100 | """ 101 | Test that the irony adapter allows 102 | the chat bot to understand irony. 103 | """ 104 | 105 | def test_irony(self): 106 | # TODO: Implement test logic 107 | self.assertTrue(response.irony) -------------------------------------------------------------------------------- /docs/preprocessors.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Preprocessors 3 | ============= 4 | 5 | ChatterBot's :term:`preprocessors` are simple functions that modify the input statement 6 | that a chat bot receives before the statement gets processed by the logic adaper. 7 | 8 | Here is an example of how to set preprocessors. The ``preprocessors`` 9 | parameter should be a list of strings of the import paths to your preprocessors. 10 | 11 | .. code-block:: python 12 | 13 | chatbot = ChatBot( 14 | 'Bob the Bot', 15 | preprocessors=[ 16 | 'chatterbot.preprocessors.clean_whitespace' 17 | ] 18 | ) 19 | 20 | Preprocessor functions 21 | ====================== 22 | 23 | ChatterBot comes with several built-in preprocessors. 24 | 25 | .. autofunction:: chatterbot.preprocessors.clean_whitespace 26 | 27 | .. autofunction:: chatterbot.preprocessors.unescape_html 28 | 29 | .. autofunction:: chatterbot.preprocessors.convert_to_ascii 30 | 31 | 32 | Creating new preprocessors 33 | ========================== 34 | 35 | It is simple to create your own preprocessors. A preprocessor is just a function 36 | with a few requirements. 37 | 38 | 1. It must take one parameter, a ``Statement`` instance. 39 | 2. It must return a statement instance. 40 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Quick Start Guide 3 | ================= 4 | 5 | The first thing you'll need to do to get started is install ChatterBot. 6 | 7 | .. code-block:: bash 8 | 9 | pip install chatterbot 10 | 11 | See :ref:`Installation` for options for alternative installation methods. 12 | 13 | Create a new chat bot 14 | ===================== 15 | 16 | .. code-block:: python 17 | 18 | from chatterbot import ChatBot 19 | chatbot = ChatBot("Ron Obvious") 20 | 21 | .. note:: 22 | 23 | The only required parameter for the `ChatBot` is a name. 24 | This can be anything you want. 25 | 26 | Training your ChatBot 27 | ===================== 28 | 29 | After creating a new ChatterBot instance it is also possible to train the bot. 30 | Training is a good way to ensure that the bot starts off with knowledge about 31 | specific responses. The current training method takes a list of statements that 32 | represent a conversation. 33 | Additional notes on training can be found in the :ref:`Training` documentation. 34 | 35 | .. note:: 36 | 37 | Training is not required but it is recommended. 38 | 39 | .. code-block:: python 40 | 41 | from chatterbot.trainers import ListTrainer 42 | 43 | conversation = [ 44 | "Hello", 45 | "Hi there!", 46 | "How are you doing?", 47 | "I'm doing great.", 48 | "That is good to hear", 49 | "Thank you.", 50 | "You're welcome." 51 | ] 52 | 53 | trainer = ListTrainer(chatbot) 54 | 55 | trainer.train(conversation) 56 | 57 | Get a response 58 | ============== 59 | 60 | .. code-block:: python 61 | 62 | response = chatbot.get_response("Good morning!") 63 | print(response) 64 | -------------------------------------------------------------------------------- /docs/releases.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Releasing ChatterBot 3 | ==================== 4 | 5 | ChatterBot follows the following rules when it comes to new versions and updates. 6 | 7 | Versioning 8 | ========== 9 | 10 | ChatterBot follows semantic versioning as a set of guidelines for release versions. 11 | 12 | - **Major** releases (2.0.0, 3.0.0, etc.) are used for large, almost 13 | entirely backwards incompatible changes. 14 | 15 | - **Minor** releases (2.1.0, 2.2.0, 3.1.0, 3.2.0, etc.) are used for 16 | releases that contain small, backwards incompatible changes. Known 17 | backwards incompatibilities will be described in the release notes. 18 | 19 | - **Patch** releases (e.g., 2.1.1, 2.1.2, 3.0.1, 3.0.10, etc.) are used 20 | for releases that contain bug fixes, features and dependency changes. 21 | 22 | 23 | Release Process 24 | =============== 25 | 26 | The following procedure is used to finalize a new version of ChatterBot. 27 | 28 | 1. We make sure that all CI tests on the master branch are passing. 29 | 30 | 2. We tag the release on GitHub. 31 | 32 | 3. A new package is generated from the latest version of the master branch. 33 | 34 | .. code-block:: bash 35 | 36 | python setup.py sdist bdist_wheel 37 | 38 | 4. The Python package files are uploaded to PyPi. 39 | 40 | .. code-block:: bash 41 | 42 | twine upload dist/* 43 | -------------------------------------------------------------------------------- /docs/setup.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | The recommended method for installing ChatterBot is by using `pip`_. 6 | 7 | Installing from PyPi 8 | -------------------- 9 | 10 | If you are just getting started with ChatterBot, it is recommended that you 11 | start by installing the latest version from the Python Package Index (`PyPi`_). 12 | To install ChatterBot from PyPi using pip run the following command in your terminal. 13 | 14 | .. code-block:: bash 15 | 16 | pip install chatterbot 17 | 18 | 19 | Installing from GitHub 20 | ---------------------- 21 | 22 | You can install the latest **development** version of ChatterBot directly from GitHub using ``pip``. 23 | 24 | .. code-block:: bash 25 | 26 | pip install git+git://github.com/gunthercox/ChatterBot.git@master 27 | 28 | 29 | Installing from source 30 | ---------------------- 31 | 32 | 1. Download a copy of the code from GitHub. You may need to install `git`_. 33 | 34 | .. code-block:: bash 35 | 36 | git clone https://github.com/gunthercox/ChatterBot.git 37 | 38 | 2. Install the code you have just downloaded using pip 39 | 40 | .. code-block:: bash 41 | 42 | pip install ./ChatterBot 43 | 44 | 45 | Checking the version of ChatterBot that you have installed 46 | ========================================================== 47 | 48 | If you already have ChatterBot installed and you want to check what version you 49 | have installed you can run the following command. 50 | 51 | .. code-block:: bash 52 | 53 | python -m chatterbot --version 54 | 55 | Upgrading ChatterBot to the latest version 56 | ========================================== 57 | 58 | .. toctree:: 59 | :maxdepth: 4 60 | 61 | upgrading 62 | 63 | .. _git: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git 64 | .. _pip: https://pip.pypa.io/en/stable/installing/ 65 | .. _PyPi: https://pypi.python.org/pypi 66 | -------------------------------------------------------------------------------- /docs/statements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/docs/statements.txt -------------------------------------------------------------------------------- /docs/storage/create-a-storage-adapter.rst: -------------------------------------------------------------------------------- 1 | Creating a new storage adapter 2 | ============================== 3 | 4 | You can write your own storage adapters by creating a new class that 5 | inherits from ``StorageAdapter`` and overrides necessary 6 | methods established in the base ``StorageAdapter`` class. 7 | 8 | You will then need to implement the interface established by the ``StorageAdapter`` class. 9 | 10 | .. literalinclude:: ../../chatterbot/storage/storage_adapter.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/storage/index.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Storage Adapters 3 | ================ 4 | 5 | Storage adapters provide an interface that allows ChatterBot 6 | to connect to different storage technologies. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | text-search 12 | create-a-storage-adapter 13 | 14 | The storage adapter that your bot uses can be specified by setting 15 | the ``storage_adapter`` parameter to the import path of the 16 | storage adapter you want to use. 17 | 18 | .. code-block:: python 19 | 20 | chatbot = ChatBot( 21 | "My ChatterBot", 22 | storage_adapter="chatterbot.storage.SQLStorageAdapter" 23 | ) 24 | 25 | 26 | Common storage adapter attributes 27 | ================================= 28 | 29 | Each storage adapter inherits the following attributes and methods. 30 | 31 | .. autoclass:: chatterbot.storage.StorageAdapter 32 | :members: 33 | 34 | 35 | SQL Storage Adapter 36 | =================== 37 | 38 | .. autoclass:: chatterbot.storage.SQLStorageAdapter 39 | :members: 40 | 41 | MongoDB Storage Adapter 42 | ======================= 43 | 44 | .. note:: 45 | 46 | Before you can use this storage adapter you will need to install 47 | `pymongo`_. Consider adding ``pymongo`` to your project's 48 | ``requirements.txt`` file so you can keep track of your dependencies 49 | and their versions. 50 | 51 | .. autoclass:: chatterbot.storage.MongoDatabaseAdapter 52 | :members: 53 | 54 | Database Migrations 55 | =================== 56 | 57 | Various frameworks such as Django and SQL Alchemy support 58 | functionality that allows revisions to be made to databases 59 | programmatically. This makes it possible for updates and 60 | revisions to structures in the database to be be applied 61 | in consecutive version releases. 62 | 63 | The following explains the included migration process for 64 | each of the databases that ChatterBot comes with support for. 65 | 66 | * Django: Full schema migrations and data migrations will 67 | be included with each release. 68 | * SQL Alchemy: No migrations are currently provided in 69 | releases. If you require migrations between versions 70 | `Alembic`_ is the recommended solution for generating them. 71 | * MongoDB: No migrations are provided. 72 | 73 | .. _Alembic: https://alembic.sqlalchemy.org 74 | .. _pymongo: https://pypi.org/project/pymongo/ 75 | -------------------------------------------------------------------------------- /docs/storage/text-search.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Text Search 3 | =========== 4 | 5 | ChatterBot's storage adapters support text search functionality. 6 | 7 | Text Search Example 8 | =================== 9 | 10 | .. literalinclude:: ../../tests/test_chatbot.py 11 | :language: python 12 | :pyobject: ChatterBotResponseTestCase.test_search_text_results_after_training 13 | 14 | Bigram Text Index 15 | ================= 16 | 17 | Bigram pairs are used for text search 18 | 19 | In addition, the generation of the pairs ensures that there is a smaller number 20 | of possible matches based on the probability of finding two neighboring words 21 | in an existing string that match the search parameter. 22 | 23 | For searches in larger data sets, the bigrams also reduce the number of ``OR`` 24 | comparisons that need to occur on a database level. This will always be a 25 | reduction of ``n - 1`` where ``n`` is the number of search words. 26 | 27 | .. image:: ../_static/bigrams.svg 28 | :alt: ChatterBot bigram generation process 29 | -------------------------------------------------------------------------------- /docs/testing.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Unit Testing 3 | ============ 4 | 5 | *"A true professional does not waste the time and money of other people by handing over software that is not reasonably free of obvious bugs; 6 | that has not undergone minimal unit testing; that does not meet the specifications and requirements; 7 | that is gold-plated with unnecessary features; or that looks like junk."* – Daniel Read 8 | 9 | ChatterBot tests 10 | ---------------- 11 | 12 | ChatterBot's built in tests can be run using nose. 13 | See the `nose documentation`_ for more information. 14 | 15 | .. sourcecode:: sh 16 | 17 | nosetests 18 | 19 | *Note* that nose also allows you to specify individual test cases to run. 20 | For example, the following command will run all tests in the test-module `tests/logic_adapter_tests` 21 | 22 | .. sourcecode:: sh 23 | 24 | nosetests tests/logic_adapter_tests 25 | 26 | Django integration tests 27 | ------------------------ 28 | 29 | Tests for Django integration have been included in the `tests_django` directory and 30 | can be run with: 31 | 32 | .. sourcecode:: sh 33 | 34 | python runtests.py 35 | 36 | Django example app tests 37 | ------------------------ 38 | 39 | Tests for the example Django app can be run with the following command from within the `examples/django_app` directory. 40 | 41 | .. sourcecode:: sh 42 | 43 | python manage.py test 44 | 45 | Benchmark tests 46 | --------------- 47 | 48 | You can run a series of benchmark tests that test a variety of different chat bot configurations for 49 | performance by running the following command. 50 | 51 | .. sourcecode:: sh 52 | 53 | python tests/benchmarks.py 54 | 55 | Running all the tests 56 | --------------------- 57 | 58 | You can run all of ChatterBot's tests with a single command: ``tox``. 59 | 60 | Tox is a tool for managing virtual environments and running tests. 61 | 62 | Installing tox 63 | ++++++++++++++ 64 | 65 | You can install ``tox`` with ``pip``. 66 | 67 | .. code-block:: bash 68 | 69 | pip install tox 70 | 71 | Using tox 72 | +++++++++ 73 | 74 | When you run the ``tox`` command from within the root directory of 75 | the ``ChatterBot`` repository it will run the following tests: 76 | 77 | 1. Tests for ChatterBot's core files. 78 | 2. Tests for ChatterBot's integration with multiple versions of Django. 79 | 3. Tests for each of ChatterBot's example files. 80 | 4. Tests to make sure ChatterBot's documentation builds. 81 | 5. Code style and validation checks (linting). 82 | 6. Benchmarking tests for performance. 83 | 84 | You can run specific tox environments using the ``-e`` flag. 85 | A few examples include: 86 | 87 | .. code-block:: bash 88 | 89 | # Run the documentation tests 90 | tox -e docs 91 | 92 | .. code-block:: bash 93 | 94 | # Run the tests with Django 2.0 95 | tox -e django20 96 | 97 | .. code-block:: bash 98 | 99 | # Run the code linting scripts 100 | tox -e lint 101 | 102 | To see the list of all available environments that you can run tests for: 103 | 104 | .. code-block:: bash 105 | 106 | tox -l 107 | 108 | To run tests for all environments: 109 | 110 | .. code-block:: bash 111 | 112 | tox 113 | 114 | .. _`nose documentation`: https://nose.readthedocs.org/en/latest/ 115 | -------------------------------------------------------------------------------- /docs/upgrading.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Upgrading to Newer Releases 3 | =========================== 4 | 5 | Like any software, changes will be made to ChatterBot over time. 6 | Most of these changes are improvements. Frequently, you don't have 7 | to change anything in your code to benefit from a new release. 8 | 9 | Occasionally there are changes that will require modifications in 10 | your code or there will be changes that make it possible for you 11 | to improve your code by taking advantage of new features. 12 | 13 | To view a record of ChatterBot's history of changes, visit the 14 | releases tab on ChatterBot's GitHub page. 15 | 16 | - https://github.com/gunthercox/ChatterBot/releases 17 | 18 | Use the pip command to upgrade your existing ChatterBot 19 | installation by providing the --upgrade parameter: 20 | 21 | .. code-block:: bash 22 | 23 | pip install chatterbot --upgrade 24 | 25 | Also see :ref:`Versioning` for information about ChatterBot's versioning policy. 26 | -------------------------------------------------------------------------------- /docs/utils.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Utility Methods 3 | =============== 4 | 5 | ChatterBot has a utility module that contains 6 | a collection of miscellaneous but useful functions. 7 | 8 | 9 | Module imports 10 | -------------- 11 | 12 | .. autofunction:: chatterbot.utils.import_module 13 | 14 | 15 | Class initialization 16 | -------------------- 17 | 18 | .. autofunction:: chatterbot.utils.initialize_class 19 | 20 | 21 | ChatBot response time 22 | --------------------- 23 | 24 | .. autofunction:: chatterbot.utils.get_response_time 25 | 26 | 27 | Parsing datetime information 28 | ---------------------------- 29 | 30 | .. autofunction:: chatterbot.parsing.datetime_parsing 31 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/examples/__init__.py -------------------------------------------------------------------------------- /examples/basic_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ListTrainer 3 | 4 | # Create a new chat bot named Charlie 5 | chatbot = ChatBot('Charlie') 6 | 7 | trainer = ListTrainer(chatbot) 8 | 9 | trainer.train([ 10 | "Hi, can I help you?", 11 | "Sure, I'd like to book a flight to Iceland.", 12 | "Your flight has been booked." 13 | ]) 14 | 15 | # Get a response to the input text 'I would like to book a flight.' 16 | response = chatbot.get_response('I would like to book a flight.') 17 | 18 | print(response) 19 | -------------------------------------------------------------------------------- /examples/convert_units.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | bot = ChatBot( 5 | 'Unit Converter', 6 | logic_adapters=[ 7 | 'chatterbot.logic.UnitConversion', 8 | ] 9 | ) 10 | 11 | questions = [ 12 | 'How many meters are in a kilometer?', 13 | 'How many meters are in one inch?', 14 | '0 celsius to fahrenheit', 15 | 'one hour is how many minutes ?' 16 | ] 17 | 18 | # Prints the convertion given the specific question 19 | for question in questions: 20 | response = bot.get_response(question) 21 | print(question + ' - Response: ' + response.text) 22 | -------------------------------------------------------------------------------- /examples/default_response_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ListTrainer 3 | 4 | 5 | # Create a new instance of a ChatBot 6 | bot = ChatBot( 7 | 'Example Bot', 8 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 9 | logic_adapters=[ 10 | { 11 | 'import_path': 'chatterbot.logic.BestMatch', 12 | 'default_response': 'I am sorry, but I do not understand.', 13 | 'maximum_similarity_threshold': 0.90 14 | } 15 | ] 16 | ) 17 | 18 | trainer = ListTrainer(bot) 19 | 20 | # Train the chat bot with a few responses 21 | trainer.train([ 22 | 'How can I help you?', 23 | 'I want to create a chat bot', 24 | 'Have you read the documentation?', 25 | 'No, I have not', 26 | 'This should help get you started: http://chatterbot.rtfd.org/en/latest/quickstart.html' 27 | ]) 28 | 29 | # Get a response for some unexpected input 30 | response = bot.get_response('How do I make an omelette?') 31 | print(response) 32 | -------------------------------------------------------------------------------- /examples/django_app/README.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | ChatterBot Django Example 3 | ========================= 4 | 5 | This is an example Django app that shows how to create a simple chat bot web 6 | app using Django_ and ChatterBot_. 7 | 8 | Documentation 9 | ------------- 10 | 11 | Start the Django app by running `python manage.py runserver 0.0.0.0:8000` 12 | 13 | Further documentation on getting set up with Django and ChatterBot can be 14 | found in the `ChatterBot documentation`_. 15 | 16 | .. _Django: https://www.djangoproject.com 17 | .. _ChatterBot: https://github.com/gunthercox/ChatterBot 18 | .. _ChatterBot documentation: http://chatterbot.readthedocs.io/en/stable/django/index.html 19 | -------------------------------------------------------------------------------- /examples/django_app/example_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/examples/django_app/example_app/__init__.py -------------------------------------------------------------------------------- /examples/django_app/example_app/settings.py: -------------------------------------------------------------------------------- 1 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 2 | import os 3 | 4 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | 6 | 7 | # Quick-start development settings - unsuitable for production 8 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 9 | 10 | # SECURITY WARNING: keep the secret key used in production secret! 11 | SECRET_KEY = 'fsch+6!=q+@ol&%0x!nwdl@48^ixbd4clx5f1i!5n^66y+pmn*' 12 | 13 | # SECURITY WARNING: don't run with debug turned on in production! 14 | DEBUG = True 15 | 16 | ALLOWED_HOSTS = [] 17 | 18 | 19 | # Application definition 20 | 21 | INSTALLED_APPS = ( 22 | 'django.contrib.admin', 23 | 'django.contrib.auth', 24 | 'django.contrib.contenttypes', 25 | 'django.contrib.sessions', 26 | 'django.contrib.messages', 27 | 'django.contrib.staticfiles', 28 | 29 | 'chatterbot.ext.django_chatterbot', 30 | 'example_app', 31 | ) 32 | 33 | # ChatterBot settings 34 | 35 | CHATTERBOT = { 36 | 'name': 'Django ChatterBot Example', 37 | 'django_app_name': 'django_chatterbot' 38 | } 39 | 40 | MIDDLEWARE_CLASSES = ( 41 | 'django.contrib.sessions.middleware.SessionMiddleware', 42 | 'django.middleware.common.CommonMiddleware', 43 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 44 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 45 | 'django.contrib.messages.middleware.MessageMiddleware', 46 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 47 | 'django.middleware.security.SecurityMiddleware', 48 | ) 49 | 50 | ROOT_URLCONF = 'example_app.urls' 51 | 52 | TEMPLATES = [ 53 | { 54 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 55 | 'DIRS': [], 56 | 'APP_DIRS': True, 57 | 'OPTIONS': { 58 | 'context_processors': [ 59 | 'django.template.context_processors.debug', 60 | 'django.template.context_processors.request', 61 | 'django.contrib.auth.context_processors.auth', 62 | 'django.contrib.messages.context_processors.messages', 63 | ], 64 | }, 65 | }, 66 | ] 67 | 68 | WSGI_APPLICATION = 'example_app.wsgi.application' 69 | 70 | 71 | # Database 72 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 73 | 74 | DATABASES = { 75 | 'default': { 76 | 'ENGINE': 'django.db.backends.sqlite3', 77 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 78 | } 79 | } 80 | 81 | 82 | # Internationalization 83 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 84 | 85 | LANGUAGE_CODE = 'en-us' 86 | 87 | TIME_ZONE = 'UTC' 88 | 89 | USE_I18N = True 90 | 91 | USE_L10N = True 92 | 93 | USE_TZ = True 94 | 95 | 96 | # Static files (CSS, JavaScript, Images) 97 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 98 | 99 | STATIC_URL = '/static/' 100 | 101 | STATICFILES_DIRS = ( 102 | os.path.join( 103 | os.path.dirname(__file__), 104 | 'static', 105 | ), 106 | ) 107 | -------------------------------------------------------------------------------- /examples/django_app/example_app/static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* Alternate the background color of the output rows */ 2 | .list-group-item:nth-child(even) { 3 | background-color: #bdf1ac; 4 | } 5 | 6 | .chat-log { 7 | max-height:200px; 8 | overflow-y:scroll; 9 | } -------------------------------------------------------------------------------- /examples/django_app/example_app/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/examples/django_app/example_app/static/favicon.ico -------------------------------------------------------------------------------- /examples/django_app/example_app/static/img/chatterbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/examples/django_app/example_app/static/img/chatterbot.png -------------------------------------------------------------------------------- /examples/django_app/example_app/static/js/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! js-cookie v2.0.3 | MIT */ 2 | !function(a){if("function"==typeof define&&define.amd)define(a);else if("object"==typeof exports)module.exports=a();else{var b=window.Cookies,c=window.Cookies=a(window.jQuery);c.noConflict=function(){return window.Cookies=b,c}}}(function(){function a(){for(var a=0,b={};a1){if(f=a({path:"/"},d.defaults,f),"number"==typeof f.expires){var h=new Date;h.setMilliseconds(h.getMilliseconds()+864e5*f.expires),f.expires=h}try{g=JSON.stringify(e),/^[\{\[]/.test(g)&&(e=g)}catch(i){}return e=encodeURIComponent(String(e)),e=e.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),b=encodeURIComponent(String(b)),b=b.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),b=b.replace(/[\(\)]/g,escape),document.cookie=[b,"=",e,f.expires&&"; expires="+f.expires.toUTCString(),f.path&&"; path="+f.path,f.domain&&"; domain="+f.domain,f.secure?"; secure":""].join("")}b||(g={});for(var j=document.cookie?document.cookie.split("; "):[],k=/(%[0-9A-Z]{2})+/g,l=0;l 3 | 4 | 5 | Django ChatterBot Example 6 | 7 | 8 | 9 | 10 | 11 | {% include 'nav.html' %} 12 | 13 |
14 | 15 |
16 |

Django ChatterBot Example

17 |

18 | This is a web app that allows you to talk to ChatterBot. 19 |

20 | 21 |
22 | 23 |
24 |
25 |
    26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 |
34 | 35 |
36 |
37 | 38 |
39 | 40 |
41 | 42 | 43 | 44 | 45 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /examples/django_app/example_app/templates/nav.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | -------------------------------------------------------------------------------- /examples/django_app/example_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib import admin 3 | from example_app.views import ChatterBotAppView, ChatterBotApiView 4 | 5 | 6 | urlpatterns = [ 7 | url(r'^$', ChatterBotAppView.as_view(), name='main'), 8 | url(r'^admin/', admin.site.urls, name='admin'), 9 | url(r'^api/chatterbot/', ChatterBotApiView.as_view(), name='chatterbot'), 10 | ] 11 | -------------------------------------------------------------------------------- /examples/django_app/example_app/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.views.generic.base import TemplateView 3 | from django.views.generic import View 4 | from django.http import JsonResponse 5 | from chatterbot import ChatBot 6 | from chatterbot.ext.django_chatterbot import settings 7 | 8 | 9 | class ChatterBotAppView(TemplateView): 10 | template_name = 'app.html' 11 | 12 | 13 | class ChatterBotApiView(View): 14 | """ 15 | Provide an API endpoint to interact with ChatterBot. 16 | """ 17 | 18 | chatterbot = ChatBot(**settings.CHATTERBOT) 19 | 20 | def post(self, request, *args, **kwargs): 21 | """ 22 | Return a response to the statement in the posted data. 23 | 24 | * The JSON data should contain a 'text' attribute. 25 | """ 26 | input_data = json.loads(request.body.decode('utf-8')) 27 | 28 | if 'text' not in input_data: 29 | return JsonResponse({ 30 | 'text': [ 31 | 'The attribute "text" is required.' 32 | ] 33 | }, status=400) 34 | 35 | response = self.chatterbot.get_response(input_data) 36 | 37 | response_data = response.serialize() 38 | 39 | return JsonResponse(response_data, status=200) 40 | 41 | def get(self, request, *args, **kwargs): 42 | """ 43 | Return data corresponding to the current conversation. 44 | """ 45 | return JsonResponse({ 46 | 'name': self.chatterbot.name 47 | }) 48 | -------------------------------------------------------------------------------- /examples/django_app/example_app/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_chatterbot project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_app.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /examples/django_app/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_app.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /examples/django_app/requirements.txt: -------------------------------------------------------------------------------- 1 | django>=2.2,<2.3 2 | # chatterbot>=0.8,<1.1 3 | -------------------------------------------------------------------------------- /examples/django_app/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/examples/django_app/tests/__init__.py -------------------------------------------------------------------------------- /examples/django_app/tests/test_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | 6 | class ApiTestCase(TestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.api_url = reverse('chatterbot') 11 | 12 | def test_invalid_text(self): 13 | response = self.client.post( 14 | self.api_url, 15 | data=json.dumps({ 16 | 'type': 'classmethod' 17 | }), 18 | content_type='application/json', 19 | format='json' 20 | ) 21 | 22 | self.assertEqual(response.status_code, 400) 23 | self.assertIn('text', response.json()) 24 | self.assertEqual(['The attribute "text" is required.'], response.json()['text']) 25 | 26 | def test_post(self): 27 | """ 28 | Test that a response is returned. 29 | """ 30 | response = self.client.post( 31 | self.api_url, 32 | data=json.dumps({ 33 | 'text': 'How are you?' 34 | }), 35 | content_type='application/json', 36 | format='json' 37 | ) 38 | 39 | self.assertEqual(response.status_code, 200) 40 | self.assertIn('text', response.json()) 41 | self.assertGreater(len(response.json()['text']), 1) 42 | self.assertIn('in_response_to', response.json()) 43 | 44 | def test_post_unicode(self): 45 | """ 46 | Test that a response is returned. 47 | """ 48 | response = self.client.post( 49 | self.api_url, 50 | data=json.dumps({ 51 | 'text': u'سلام' 52 | }), 53 | content_type='application/json', 54 | format='json' 55 | ) 56 | 57 | self.assertEqual(response.status_code, 200) 58 | self.assertIn('text', response.json()) 59 | self.assertGreater(len(response.json()['text']), 1) 60 | self.assertIn('in_response_to', response.json()) 61 | 62 | def test_escaped_unicode_post(self): 63 | """ 64 | Test that unicode reponce 65 | """ 66 | response = self.client.post( 67 | self.api_url, 68 | data=json.dumps({ 69 | 'text': '\u2013' 70 | }), 71 | content_type='application/json', 72 | format=json 73 | ) 74 | 75 | self.assertEqual(response.status_code, 200) 76 | self.assertIn('text', response.json()) 77 | self.assertIn('in_response_to', response.json()) 78 | 79 | def test_post_tags(self): 80 | post_data = { 81 | 'text': 'Good morning.', 82 | 'tags': [ 83 | 'user:jen@example.com' 84 | ] 85 | } 86 | response = self.client.post( 87 | self.api_url, 88 | data=json.dumps(post_data), 89 | content_type='application/json', 90 | format='json' 91 | ) 92 | 93 | self.assertEqual(response.status_code, 200) 94 | self.assertIn('text', response.json()) 95 | self.assertIn('in_response_to', response.json()) 96 | self.assertIn('tags', response.json()) 97 | self.assertEqual(response.json()['tags'], []) 98 | 99 | def test_get(self): 100 | response = self.client.get(self.api_url) 101 | 102 | self.assertEqual(response.status_code, 200) 103 | 104 | def test_patch(self): 105 | response = self.client.patch(self.api_url) 106 | 107 | self.assertEqual(response.status_code, 405) 108 | 109 | def test_put(self): 110 | response = self.client.put(self.api_url) 111 | 112 | self.assertEqual(response.status_code, 405) 113 | 114 | def test_delete(self): 115 | response = self.client.delete(self.api_url) 116 | 117 | self.assertEqual(response.status_code, 405) 118 | -------------------------------------------------------------------------------- /examples/django_app/tests/test_example.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | 6 | class ViewTestCase(TestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.url = reverse('main') 11 | 12 | def test_get_main_page(self): 13 | """ 14 | Test that the main page can be loaded. 15 | """ 16 | response = self.client.get(self.url) 17 | self.assertEqual(response.status_code, 200) 18 | 19 | 20 | class ApiTestCase(TestCase): 21 | """ 22 | Tests to make sure that the ChatterBot app is 23 | properly working with the Django example app. 24 | """ 25 | 26 | def setUp(self): 27 | super().setUp() 28 | self.api_url = reverse('chatterbot') 29 | 30 | def test_post(self): 31 | """ 32 | Test that a response is returned. 33 | """ 34 | data = { 35 | 'text': 'How are you?' 36 | } 37 | response = self.client.post( 38 | self.api_url, 39 | data=json.dumps(data), 40 | content_type='application/json', 41 | format='json' 42 | ) 43 | 44 | self.assertEqual(response.status_code, 200) 45 | self.assertIn('text', response.json()) 46 | self.assertIn('in_response_to', response.json()) 47 | 48 | def test_post_tags(self): 49 | post_data = { 50 | 'text': 'Good morning.', 51 | 'tags': [ 52 | 'user:jen@example.com' 53 | ] 54 | } 55 | response = self.client.post( 56 | self.api_url, 57 | data=json.dumps(post_data), 58 | content_type='application/json', 59 | format='json' 60 | ) 61 | 62 | self.assertEqual(response.status_code, 200) 63 | self.assertIn('text', response.json()) 64 | self.assertIn('in_response_to', response.json()) 65 | self.assertIn('tags', response.json()) 66 | self.assertEqual(response.json()['tags'], []) 67 | 68 | 69 | class ApiIntegrationTestCase(TestCase): 70 | """ 71 | Test to make sure the ChatterBot API view works 72 | properly with the example Django app. 73 | """ 74 | 75 | def setUp(self): 76 | super().setUp() 77 | self.api_url = reverse('chatterbot') 78 | 79 | def test_get(self): 80 | response = self.client.get(self.api_url) 81 | 82 | self.assertIn('name', response.json()) 83 | -------------------------------------------------------------------------------- /examples/export_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | 4 | ''' 5 | This is an example showing how to create an export file from 6 | an existing chat bot that can then be used to train other bots. 7 | ''' 8 | 9 | chatbot = ChatBot('Export Example Bot') 10 | 11 | # First, lets train our bot with some data 12 | trainer = ChatterBotCorpusTrainer(chatbot) 13 | 14 | trainer.train('chatterbot.corpus.english') 15 | 16 | # Now we can export the data to a file 17 | trainer.export_for_training('./my_export.json') 18 | -------------------------------------------------------------------------------- /examples/learning_feedback_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.conversation import Statement 3 | 4 | """ 5 | This example shows how to create a chat bot that 6 | will learn responses based on an additional feedback 7 | element from the user. 8 | """ 9 | 10 | # Uncomment the following line to enable verbose logging 11 | # import logging 12 | # logging.basicConfig(level=logging.INFO) 13 | 14 | # Create a new instance of a ChatBot 15 | bot = ChatBot( 16 | 'Feedback Learning Bot', 17 | storage_adapter='chatterbot.storage.SQLStorageAdapter' 18 | ) 19 | 20 | 21 | def get_feedback(): 22 | 23 | text = input() 24 | 25 | if 'yes' in text.lower(): 26 | return True 27 | elif 'no' in text.lower(): 28 | return False 29 | else: 30 | print('Please type either "Yes" or "No"') 31 | return get_feedback() 32 | 33 | 34 | print('Type something to begin...') 35 | 36 | # The following loop will execute each time the user enters input 37 | while True: 38 | try: 39 | input_statement = Statement(text=input()) 40 | response = bot.generate_response( 41 | input_statement 42 | ) 43 | 44 | print('\n Is "{}" a coherent response to "{}"? \n'.format( 45 | response.text, 46 | input_statement.text 47 | )) 48 | if get_feedback() is False: 49 | print('please input the correct one') 50 | correct_response = Statement(text=input()) 51 | bot.learn_response(correct_response, input_statement) 52 | print('Responses added to bot!') 53 | 54 | # Press ctrl-c or ctrl-d on the keyboard to exit 55 | except (KeyboardInterrupt, EOFError, SystemExit): 56 | break 57 | -------------------------------------------------------------------------------- /examples/math_and_time.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | bot = ChatBot( 5 | 'Math & Time Bot', 6 | logic_adapters=[ 7 | 'chatterbot.logic.MathematicalEvaluation', 8 | 'chatterbot.logic.TimeLogicAdapter' 9 | ] 10 | ) 11 | 12 | # Print an example of getting one math based response 13 | response = bot.get_response('What is 4 + 9?') 14 | print(response) 15 | 16 | # Print an example of getting one time based response 17 | response = bot.get_response('What time is it?') 18 | print(response) 19 | -------------------------------------------------------------------------------- /examples/memory_sql_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | # Uncomment the following lines to enable verbose logging 4 | # import logging 5 | # logging.basicConfig(level=logging.INFO) 6 | 7 | # Create a new instance of a ChatBot 8 | bot = ChatBot( 9 | 'SQLMemoryTerminal', 10 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 11 | database_uri=None, 12 | logic_adapters=[ 13 | 'chatterbot.logic.MathematicalEvaluation', 14 | 'chatterbot.logic.TimeLogicAdapter', 15 | 'chatterbot.logic.BestMatch' 16 | ] 17 | ) 18 | 19 | # Get a few responses from the bot 20 | 21 | bot.get_response('What time is it?') 22 | 23 | bot.get_response('What is 7 plus 7?') 24 | -------------------------------------------------------------------------------- /examples/specific_response_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | # Create a new instance of a ChatBot 5 | bot = ChatBot( 6 | 'Exact Response Example Bot', 7 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 8 | logic_adapters=[ 9 | { 10 | 'import_path': 'chatterbot.logic.BestMatch' 11 | }, 12 | { 13 | 'import_path': 'chatterbot.logic.SpecificResponseAdapter', 14 | 'input_text': 'Help me!', 15 | 'output_text': 'Ok, here is a link: http://chatterbot.rtfd.org' 16 | } 17 | ] 18 | ) 19 | 20 | # Get a response given the specific input 21 | response = bot.get_response('Help me!') 22 | print(response) 23 | -------------------------------------------------------------------------------- /examples/tagged_dataset_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.conversation import Statement 3 | 4 | 5 | chatbot = ChatBot( 6 | 'Example Bot', 7 | # This database will be a temporary in-memory database 8 | database_uri=None 9 | ) 10 | 11 | label_a_statements = [ 12 | Statement(text='Hello', tags=['label_a']), 13 | Statement(text='Hi', tags=['label_a']), 14 | Statement(text='How are you?', tags=['label_a']) 15 | ] 16 | 17 | label_b_statements = [ 18 | Statement(text='I like dogs.', tags=['label_b']), 19 | Statement(text='I like cats.', tags=['label_b']), 20 | Statement(text='I like animals.', tags=['label_b']) 21 | ] 22 | 23 | chatbot.storage.create_many( 24 | label_a_statements + label_b_statements 25 | ) 26 | 27 | # Return a response from "label_a_statements" 28 | response_from_label_a = chatbot.get_response( 29 | 'How are you?', 30 | additional_response_selection_parameters={ 31 | 'tags': ['label_a'] 32 | } 33 | ) 34 | 35 | # Return a response from "label_b_statements" 36 | response_from_label_b = chatbot.get_response( 37 | 'How are you?', 38 | additional_response_selection_parameters={ 39 | 'tags': ['label_b'] 40 | } 41 | ) 42 | 43 | print('Response from label_a collection:', response_from_label_a.text) 44 | print('Response from label_b collection:', response_from_label_b.text) 45 | -------------------------------------------------------------------------------- /examples/terminal_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | # Uncomment the following lines to enable verbose logging 5 | # import logging 6 | # logging.basicConfig(level=logging.INFO) 7 | 8 | # Create a new instance of a ChatBot 9 | bot = ChatBot( 10 | 'Terminal', 11 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 12 | logic_adapters=[ 13 | 'chatterbot.logic.MathematicalEvaluation', 14 | 'chatterbot.logic.TimeLogicAdapter', 15 | 'chatterbot.logic.BestMatch' 16 | ], 17 | database_uri='sqlite:///database.sqlite3' 18 | ) 19 | 20 | print('Type something to begin...') 21 | 22 | # The following loop will execute each time the user enters input 23 | while True: 24 | try: 25 | user_input = input() 26 | 27 | bot_response = bot.get_response(user_input) 28 | 29 | print(bot_response) 30 | 31 | # Press ctrl-c or ctrl-d on the keyboard to exit 32 | except (KeyboardInterrupt, EOFError, SystemExit): 33 | break 34 | -------------------------------------------------------------------------------- /examples/terminal_mongo_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | # Uncomment the following lines to enable verbose logging 4 | # import logging 5 | # logging.basicConfig(level=logging.INFO) 6 | 7 | # Create a new ChatBot instance 8 | bot = ChatBot( 9 | 'Terminal', 10 | storage_adapter='chatterbot.storage.MongoDatabaseAdapter', 11 | logic_adapters=[ 12 | 'chatterbot.logic.BestMatch' 13 | ], 14 | database_uri='mongodb://localhost:27017/chatterbot-database' 15 | ) 16 | 17 | print('Type something to begin...') 18 | 19 | while True: 20 | try: 21 | user_input = input() 22 | 23 | bot_response = bot.get_response(user_input) 24 | 25 | print(bot_response) 26 | 27 | # Press ctrl-c or ctrl-d on the keyboard to exit 28 | except (KeyboardInterrupt, EOFError, SystemExit): 29 | break 30 | -------------------------------------------------------------------------------- /examples/tkinter_gui.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | import tkinter as tk 3 | try: 4 | import ttk as ttk 5 | import ScrolledText 6 | except ImportError: 7 | import tkinter.ttk as ttk 8 | import tkinter.scrolledtext as ScrolledText 9 | import time 10 | 11 | 12 | class TkinterGUIExample(tk.Tk): 13 | 14 | def __init__(self, *args, **kwargs): 15 | """ 16 | Create & set window variables. 17 | """ 18 | tk.Tk.__init__(self, *args, **kwargs) 19 | 20 | self.chatbot = ChatBot( 21 | "GUI Bot", 22 | storage_adapter="chatterbot.storage.SQLStorageAdapter", 23 | logic_adapters=[ 24 | "chatterbot.logic.BestMatch" 25 | ], 26 | database_uri="sqlite:///database.sqlite3" 27 | ) 28 | 29 | self.title("Chatterbot") 30 | 31 | self.initialize() 32 | 33 | def initialize(self): 34 | """ 35 | Set window layout. 36 | """ 37 | self.grid() 38 | 39 | self.respond = ttk.Button(self, text='Get Response', command=self.get_response) 40 | self.respond.grid(column=0, row=0, sticky='nesw', padx=3, pady=3) 41 | 42 | self.usr_input = ttk.Entry(self, state='normal') 43 | self.usr_input.grid(column=1, row=0, sticky='nesw', padx=3, pady=3) 44 | 45 | self.conversation_lbl = ttk.Label(self, anchor=tk.E, text='Conversation:') 46 | self.conversation_lbl.grid(column=0, row=1, sticky='nesw', padx=3, pady=3) 47 | 48 | self.conversation = ScrolledText.ScrolledText(self, state='disabled') 49 | self.conversation.grid(column=0, row=2, columnspan=2, sticky='nesw', padx=3, pady=3) 50 | 51 | def get_response(self): 52 | """ 53 | Get a response from the chatbot and display it. 54 | """ 55 | user_input = self.usr_input.get() 56 | self.usr_input.delete(0, tk.END) 57 | 58 | response = self.chatbot.get_response(user_input) 59 | 60 | self.conversation['state'] = 'normal' 61 | self.conversation.insert( 62 | tk.END, "Human: " + user_input + "\n" + "ChatBot: " + str(response.text) + "\n" 63 | ) 64 | self.conversation['state'] = 'disabled' 65 | 66 | time.sleep(0.5) 67 | 68 | 69 | gui_example = TkinterGUIExample() 70 | gui_example.mainloop() 71 | -------------------------------------------------------------------------------- /examples/training_example_chatterbot_corpus.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | import logging 4 | 5 | 6 | ''' 7 | This is an example showing how to train a chat bot using the 8 | ChatterBot Corpus of conversation dialog. 9 | ''' 10 | 11 | # Enable info level logging 12 | logging.basicConfig(level=logging.INFO) 13 | 14 | chatbot = ChatBot('Example Bot') 15 | 16 | # Start by training our bot with the ChatterBot corpus data 17 | trainer = ChatterBotCorpusTrainer(chatbot) 18 | 19 | trainer.train( 20 | 'chatterbot.corpus.english' 21 | ) 22 | 23 | # Now let's get a response to a greeting 24 | response = chatbot.get_response('How are you doing today?') 25 | print(response) 26 | -------------------------------------------------------------------------------- /examples/training_example_list_data.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ListTrainer 3 | 4 | 5 | ''' 6 | This is an example showing how to train a chat bot using the 7 | ChatterBot ListTrainer. 8 | ''' 9 | 10 | chatbot = ChatBot('Example Bot') 11 | 12 | # Start by training our bot with the ChatterBot corpus data 13 | trainer = ListTrainer(chatbot) 14 | 15 | trainer.train([ 16 | 'Hello, how are you?', 17 | 'I am doing well.', 18 | 'That is good to hear.', 19 | 'Thank you' 20 | ]) 21 | 22 | # You can train with a second list of data to add response variations 23 | 24 | trainer.train([ 25 | 'Hello, how are you?', 26 | 'I am great.', 27 | 'That is awesome.', 28 | 'Thanks' 29 | ]) 30 | 31 | # Now let's get a response to a greeting 32 | response = chatbot.get_response('How are you doing today?') 33 | print(response) 34 | -------------------------------------------------------------------------------- /examples/training_example_ubuntu_corpus.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example shows how to train a chat bot using the 3 | Ubuntu Corpus of conversation dialog. 4 | """ 5 | import logging 6 | from chatterbot import ChatBot 7 | from chatterbot.trainers import UbuntuCorpusTrainer 8 | 9 | # Enable info level logging 10 | logging.basicConfig(level=logging.INFO) 11 | 12 | chatbot = ChatBot('Example Bot') 13 | 14 | trainer = UbuntuCorpusTrainer(chatbot) 15 | 16 | # Start by training our bot with the Ubuntu corpus data 17 | trainer.train() 18 | 19 | # Now let's get a response to a greeting 20 | response = chatbot.get_response('How are you doing today?') 21 | print(response) 22 | -------------------------------------------------------------------------------- /graphics/README.md: -------------------------------------------------------------------------------- 1 | # ChatterBot Graphics 2 | 3 | Concept art and imagery for ChatterBot was designed by [Griffin Cox](https://github.com/griffincx). 4 | 5 | Files using a `.xcf` format can be viewed and edited using [GIMP](https://www.gimp.org/). 6 | -------------------------------------------------------------------------------- /graphics/ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/graphics/ad.png -------------------------------------------------------------------------------- /graphics/ad.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/graphics/ad.xcf -------------------------------------------------------------------------------- /graphics/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/graphics/banner.png -------------------------------------------------------------------------------- /graphics/chatterbot.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/graphics/chatterbot.xcf -------------------------------------------------------------------------------- /graphics/scan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/graphics/scan.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | mathparse>=0.1,<0.2 2 | python-dateutil>=2.8,<2.9 3 | sqlalchemy>=1.3,<1.4 4 | pytz 5 | coveralls 6 | flake8 7 | nltk>=3.2,<4.0 8 | nose 9 | pint>=0.8.1 10 | pymongo>=3.3,<4.0 11 | twine 12 | twython 13 | spacy>=3.4.1 14 | sphinx>=3.0,<3.1 15 | sphinx_rtd_theme 16 | pyyaml>=5.3,<5.4 17 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This is the test runner for the ChatterBot's Django tests. 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | import django 11 | from django.conf import settings 12 | from django.test.utils import get_runner 13 | 14 | if __name__ == '__main__': 15 | os.environ['DJANGO_SETTINGS_MODULE'] = 'tests_django.test_settings' 16 | django.setup() 17 | TestRunner = get_runner(settings) 18 | test_runner = TestRunner( 19 | verbosity=2 20 | ) 21 | failures = test_runner.run_tests(['tests_django']) 22 | sys.exit(bool(failures)) 23 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | 4 | [bdist_wheel] 5 | universal = 1 6 | 7 | [metadata] 8 | license_file = LICENSE 9 | 10 | [nosetests] 11 | verbosity = 2 12 | nocapture = true 13 | exclude = (?:^tests_django$) 14 | with-coverage = true 15 | cover-package = chatterbot 16 | cover-erase = true 17 | cover-min-percentage = 40 18 | 19 | [flake8] 20 | # H306: imports not in alphabetical order (time, os) 21 | ignore = H306 22 | max_line_length = 175 23 | exclude = .eggs, .git, .tox, build, 24 | 25 | [chatterbot] 26 | version = 1.1.0a7 27 | author = Gunther Cox 28 | email = gunthercx@gmail.com 29 | url = https://github.com/gunthercox/ChatterBot 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | ChatterBot setup file. 4 | """ 5 | import os 6 | import sys 7 | import platform 8 | import configparser 9 | from setuptools import setup 10 | 11 | 12 | if sys.version_info[0] < 3: 13 | raise Exception( 14 | 'You are tying to install ChatterBot on Python version {}.\n' 15 | 'Please install ChatterBot in Python 3 instead.'.format( 16 | platform.python_version() 17 | ) 18 | ) 19 | 20 | config = configparser.ConfigParser() 21 | 22 | current_directory = os.path.dirname(os.path.abspath(__file__)) 23 | config_file_path = os.path.join(current_directory, 'setup.cfg') 24 | 25 | config.read(config_file_path) 26 | 27 | VERSION = config['chatterbot']['version'] 28 | AUTHOR = config['chatterbot']['author'] 29 | AUTHOR_EMAIL = config['chatterbot']['email'] 30 | URL = config['chatterbot']['url'] 31 | 32 | with open('README.md') as f: 33 | LONG_DESCRIPTION = f.read() 34 | 35 | REQUIREMENTS = [] 36 | DEPENDENCIES = [] 37 | 38 | with open('requirements.txt') as requirements: 39 | for requirement in requirements.readlines(): 40 | if requirement.startswith('git+git://'): 41 | DEPENDENCIES.append(requirement) 42 | else: 43 | REQUIREMENTS.append(requirement) 44 | 45 | 46 | setup( 47 | name='ChatterBot', 48 | version=VERSION, 49 | url=URL, 50 | download_url='{}/tarball/{}'.format(URL, VERSION), 51 | project_urls={ 52 | 'Documentation': 'https://chatterbot.readthedocs.io', 53 | }, 54 | description='ChatterBot is a machine learning, conversational dialog engine.', 55 | long_description=LONG_DESCRIPTION, 56 | long_description_content_type='text/markdown', 57 | author=AUTHOR, 58 | author_email=AUTHOR_EMAIL, 59 | packages=[ 60 | 'chatterbot', 61 | 'chatterbot.storage', 62 | 'chatterbot.logic', 63 | 'chatterbot.ext', 64 | 'chatterbot.ext.sqlalchemy_app', 65 | 'chatterbot.ext.django_chatterbot', 66 | 'chatterbot.ext.django_chatterbot.migrations', 67 | ], 68 | package_dir={'chatterbot': 'chatterbot'}, 69 | include_package_data=True, 70 | install_requires=REQUIREMENTS, 71 | dependency_links=DEPENDENCIES, 72 | python_requires='>=3.4, <=3.11', 73 | license='BSD', 74 | zip_safe=True, 75 | platforms=['any'], 76 | keywords=['ChatterBot', 'chatbot', 'chat', 'bot'], 77 | classifiers=[ 78 | 'Development Status :: 4 - Beta', 79 | 'Intended Audience :: Developers', 80 | 'License :: OSI Approved :: BSD License', 81 | 'Environment :: Console', 82 | 'Environment :: Web Environment', 83 | 'Operating System :: OS Independent', 84 | 'Topic :: Software Development :: Libraries :: Python Modules', 85 | 'Topic :: Communications :: Chat', 86 | 'Topic :: Internet', 87 | 'Programming Language :: Python', 88 | 'Programming Language :: Python :: 3', 89 | 'Programming Language :: Python :: 3.4', 90 | 'Programming Language :: Python :: 3.5', 91 | 'Programming Language :: Python :: 3.6', 92 | 'Programming Language :: Python :: 3.7', 93 | 'Programming Language :: Python :: 3.8', 94 | 'Programming Language :: Python :: 3 :: Only', 95 | ], 96 | test_suite='tests' 97 | ) 98 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/tests/__init__.py -------------------------------------------------------------------------------- /tests/base_case.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, SkipTest 2 | from chatterbot import ChatBot 3 | 4 | 5 | class ChatBotTestCase(TestCase): 6 | 7 | def setUp(self): 8 | self.chatbot = ChatBot('Test Bot', **self.get_kwargs()) 9 | 10 | def tearDown(self): 11 | """ 12 | Remove the test database. 13 | """ 14 | self.chatbot.storage.drop() 15 | 16 | def assertIsLength(self, item, length): 17 | """ 18 | Assert that an iterable has the given length. 19 | """ 20 | if len(item) != length: 21 | raise AssertionError( 22 | 'Length {} is not equal to {}'.format(len(item), length) 23 | ) 24 | 25 | def get_kwargs(self): 26 | return { 27 | # Run the test database in-memory 28 | 'database_uri': None, 29 | # Don't execute initialization processes such as downloading required data 30 | 'initialize': False 31 | } 32 | 33 | 34 | class ChatBotMongoTestCase(ChatBotTestCase): 35 | 36 | @classmethod 37 | def setUpClass(cls): 38 | from pymongo.errors import ServerSelectionTimeoutError 39 | from pymongo import MongoClient 40 | 41 | # Skip these tests if a mongo client is not running 42 | try: 43 | client = MongoClient( 44 | serverSelectionTimeoutMS=0.1 45 | ) 46 | client.server_info() 47 | 48 | except ServerSelectionTimeoutError: 49 | raise SkipTest('Unable to connect to Mongo DB.') 50 | 51 | def get_kwargs(self): 52 | kwargs = super().get_kwargs() 53 | kwargs['database_uri'] = 'mongodb://localhost:27017/chatterbot_test_database' 54 | kwargs['storage_adapter'] = 'chatterbot.storage.MongoDatabaseAdapter' 55 | return kwargs 56 | 57 | 58 | class ChatBotSQLTestCase(ChatBotTestCase): 59 | 60 | def get_kwargs(self): 61 | kwargs = super().get_kwargs() 62 | kwargs['storage_adapter'] = 'chatterbot.storage.SQLStorageAdapter' 63 | return kwargs 64 | -------------------------------------------------------------------------------- /tests/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/tests/logic/__init__.py -------------------------------------------------------------------------------- /tests/logic/test_data_cache.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import LogicAdapter 3 | from chatterbot.trainers import ListTrainer 4 | 5 | 6 | class DummyMutatorLogicAdapter(LogicAdapter): 7 | """ 8 | This is a dummy class designed to modify a 9 | the resulting statement before it is returned. 10 | """ 11 | 12 | def process(self, statement, additional_response_selection_parameters=None): 13 | statement.add_tags('pos_tags:NN') 14 | 15 | self.chatbot.storage.update(statement) 16 | statement.confidence = 1 17 | return statement 18 | 19 | 20 | class DataCachingTests(ChatBotTestCase): 21 | 22 | def setUp(self): 23 | super().setUp() 24 | 25 | self.chatbot.logic_adapters = [ 26 | DummyMutatorLogicAdapter(self.chatbot) 27 | ] 28 | 29 | self.trainer = ListTrainer( 30 | self.chatbot, 31 | show_training_progress=False 32 | ) 33 | 34 | self.trainer.train([ 35 | 'Hello', 36 | 'How are you?' 37 | ]) 38 | 39 | def test_additional_attributes_saved(self): 40 | """ 41 | Test that an additional data attribute can be added to the statement 42 | and that this attribute is saved. 43 | """ 44 | self.chatbot.get_response('Hello', conversation='test') 45 | results = list(self.chatbot.storage.filter( 46 | text='Hello', 47 | in_response_to=None, 48 | conversation='test' 49 | )) 50 | 51 | self.assertEqual(len(results), 1) 52 | self.assertIn('pos_tags:NN', results[0].get_tags()) 53 | -------------------------------------------------------------------------------- /tests/logic/test_logic_adapter.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import LogicAdapter 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class LogicAdapterTestCase(ChatBotTestCase): 7 | """ 8 | This test case is for the LogicAdapter base class. 9 | Although this class is not intended for direct use, 10 | this test case ensures that exceptions requiring 11 | basic functionality are triggered when needed. 12 | """ 13 | 14 | def setUp(self): 15 | super().setUp() 16 | self.adapter = LogicAdapter(self.chatbot) 17 | 18 | def test_class_name(self): 19 | """ 20 | Test that the logic adapter can return its own class name. 21 | """ 22 | self.assertEqual(self.adapter.class_name, 'LogicAdapter') 23 | 24 | def test_can_process(self): 25 | """ 26 | This method should return true by default. 27 | """ 28 | self.assertTrue(self.adapter.can_process('')) 29 | 30 | def test_process(self): 31 | with self.assertRaises(LogicAdapter.AdapterMethodNotImplementedError): 32 | self.adapter.process('') 33 | 34 | def test_get_default_response(self): 35 | response = self.adapter.get_default_response(Statement(text='...')) 36 | 37 | self.assertEqual(response.text, '...') 38 | 39 | def test_get_default_response_from_options(self): 40 | self.adapter.default_responses = [ 41 | Statement(text='The default') 42 | ] 43 | response = self.adapter.get_default_response(Statement(text='...')) 44 | 45 | self.assertEqual(response.text, 'The default') 46 | 47 | def test_get_default_response_from_database(self): 48 | self.chatbot.storage.create(text='The default') 49 | 50 | response = self.adapter.get_default_response(Statement(text='...')) 51 | 52 | self.assertEqual(response.text, 'The default') 53 | -------------------------------------------------------------------------------- /tests/logic/test_specific_response.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import SpecificResponseAdapter 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class SpecificResponseAdapterTestCase(ChatBotTestCase): 7 | """ 8 | Test cases for the SpecificResponseAdapter 9 | """ 10 | 11 | def setUp(self): 12 | super().setUp() 13 | self.adapter = SpecificResponseAdapter( 14 | self.chatbot, 15 | input_text='Open sesame!', 16 | output_text='Your sesame seed hamburger roll is now open.' 17 | ) 18 | 19 | def test_exact_match(self): 20 | """ 21 | Test the case that an exact match is given. 22 | """ 23 | statement = Statement(text='Open sesame!') 24 | match = self.adapter.process(statement) 25 | 26 | self.assertEqual(match.confidence, 1) 27 | self.assertEqual(match, self.adapter.response_statement) 28 | 29 | def test_not_exact_match(self): 30 | """ 31 | Test the case that an exact match is not given. 32 | """ 33 | statement = Statement(text='Open says me!') 34 | match = self.adapter.process(statement) 35 | 36 | self.assertEqual(match.confidence, 0) 37 | self.assertEqual(match, self.adapter.response_statement) 38 | -------------------------------------------------------------------------------- /tests/logic/test_time.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import TimeLogicAdapter 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class TimeAdapterTests(ChatBotTestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.adapter = TimeLogicAdapter(self.chatbot) 11 | 12 | def test_positive_input(self): 13 | statement = Statement(text="Do you know what time it is?") 14 | response = self.adapter.process(statement) 15 | 16 | self.assertEqual(response.confidence, 1) 17 | self.assertIn("The current time is ", response.text) 18 | 19 | def test_negative_input(self): 20 | statement = Statement(text="What is an example of a pachyderm?") 21 | response = self.adapter.process(statement) 22 | 23 | self.assertEqual(response.confidence, 0) 24 | self.assertIn("The current time is ", response.text) 25 | -------------------------------------------------------------------------------- /tests/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/tests/storage/__init__.py -------------------------------------------------------------------------------- /tests/storage/test_storage_adapter.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from chatterbot.storage import StorageAdapter 3 | 4 | 5 | class StorageAdapterTestCase(TestCase): 6 | """ 7 | This test case is for the StorageAdapter base class. 8 | Although this class is not intended for direct use, 9 | this test case ensures that exceptions requiring 10 | basic functionality are triggered when needed. 11 | """ 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | cls.adapter = StorageAdapter() 16 | 17 | def test_count(self): 18 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 19 | self.adapter.count() 20 | 21 | def test_filter(self): 22 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 23 | self.adapter.filter() 24 | 25 | def test_remove(self): 26 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 27 | self.adapter.remove('') 28 | 29 | def test_create(self): 30 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 31 | self.adapter.create() 32 | 33 | def test_create_many(self): 34 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 35 | self.adapter.create_many([]) 36 | 37 | def test_update(self): 38 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 39 | self.adapter.update('') 40 | 41 | def test_get_random(self): 42 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 43 | self.adapter.get_random() 44 | 45 | def test_drop(self): 46 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 47 | self.adapter.drop() 48 | 49 | def test_get_model_invalid(self): 50 | with self.assertRaises(AttributeError): 51 | self.adapter.get_model('invalid') 52 | 53 | def test_get_object_invalid(self): 54 | with self.assertRaises(AttributeError): 55 | self.adapter.get_object('invalid') 56 | -------------------------------------------------------------------------------- /tests/test_adapter_validation.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.adapters import Adapter 3 | from tests.base_case import ChatBotTestCase 4 | 5 | 6 | class AdapterValidationTests(ChatBotTestCase): 7 | 8 | def test_invalid_storage_adapter(self): 9 | kwargs = self.get_kwargs() 10 | kwargs['storage_adapter'] = 'chatterbot.logic.LogicAdapter' 11 | with self.assertRaises(Adapter.InvalidAdapterTypeException): 12 | self.chatbot = ChatBot('Test Bot', **kwargs) 13 | 14 | def test_valid_storage_adapter(self): 15 | kwargs = self.get_kwargs() 16 | kwargs['storage_adapter'] = 'chatterbot.storage.SQLStorageAdapter' 17 | try: 18 | self.chatbot = ChatBot('Test Bot', **kwargs) 19 | except Adapter.InvalidAdapterTypeException: 20 | self.fail('Test raised InvalidAdapterException unexpectedly!') 21 | 22 | def test_invalid_logic_adapter(self): 23 | kwargs = self.get_kwargs() 24 | kwargs['logic_adapters'] = ['chatterbot.storage.StorageAdapter'] 25 | with self.assertRaises(Adapter.InvalidAdapterTypeException): 26 | self.chatbot = ChatBot('Test Bot', **kwargs) 27 | 28 | def test_valid_logic_adapter(self): 29 | kwargs = self.get_kwargs() 30 | kwargs['logic_adapters'] = ['chatterbot.logic.BestMatch'] 31 | try: 32 | self.chatbot = ChatBot('Test Bot', **kwargs) 33 | except Adapter.InvalidAdapterTypeException: 34 | self.fail('Test raised InvalidAdapterException unexpectedly!') 35 | 36 | def test_valid_adapter_dictionary(self): 37 | kwargs = self.get_kwargs() 38 | kwargs['storage_adapter'] = { 39 | 'import_path': 'chatterbot.storage.SQLStorageAdapter' 40 | } 41 | try: 42 | self.chatbot = ChatBot('Test Bot', **kwargs) 43 | except Adapter.InvalidAdapterTypeException: 44 | self.fail('Test raised InvalidAdapterException unexpectedly!') 45 | 46 | def test_invalid_adapter_dictionary(self): 47 | kwargs = self.get_kwargs() 48 | kwargs['storage_adapter'] = { 49 | 'import_path': 'chatterbot.logic.BestMatch' 50 | } 51 | with self.assertRaises(Adapter.InvalidAdapterTypeException): 52 | self.chatbot = ChatBot('Test Bot', **kwargs) 53 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from chatterbot import __main__ as main 3 | 4 | 5 | class CommandLineInterfaceTests(TestCase): 6 | """ 7 | Tests for the command line tools that are included with ChatterBot. 8 | """ 9 | 10 | def test_get_chatterbot_version(self): 11 | version = main.get_chatterbot_version() 12 | version_parts = version.split('.') 13 | self.assertEqual(len(version_parts), 3) 14 | self.assertTrue(version_parts[0].isdigit()) 15 | self.assertTrue(version_parts[1].isdigit()) 16 | -------------------------------------------------------------------------------- /tests/test_comparisons.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test ChatterBot's statement comparison algorithms. 3 | """ 4 | 5 | from unittest import TestCase 6 | from chatterbot.conversation import Statement 7 | from chatterbot import comparisons 8 | from chatterbot import languages 9 | 10 | 11 | class LevenshteinDistanceTestCase(TestCase): 12 | 13 | def setUp(self): 14 | super().setUp() 15 | 16 | self.compare = comparisons.LevenshteinDistance( 17 | language=languages.ENG 18 | ) 19 | 20 | def test_levenshtein_distance_statement_false(self): 21 | """ 22 | Falsy values should match by zero. 23 | """ 24 | statement = Statement(text='') 25 | other_statement = Statement(text='Hello') 26 | 27 | value = self.compare(statement, other_statement) 28 | 29 | self.assertEqual(value, 0) 30 | 31 | def test_levenshtein_distance_other_statement_false(self): 32 | """ 33 | Falsy values should match by zero. 34 | """ 35 | statement = Statement(text='Hello') 36 | other_statement = Statement(text='') 37 | 38 | value = self.compare(statement, other_statement) 39 | 40 | self.assertEqual(value, 0) 41 | 42 | def test_levenshtein_distance_statement_integer(self): 43 | """ 44 | Test that an exception is not raised if a statement is initialized 45 | with an integer value as its text attribute. 46 | """ 47 | statement = Statement(text=2) 48 | other_statement = Statement(text='Hello') 49 | 50 | value = self.compare(statement, other_statement) 51 | 52 | self.assertEqual(value, 0) 53 | 54 | def test_exact_match_different_capitalization(self): 55 | """ 56 | Test that text capitalization is ignored. 57 | """ 58 | statement = Statement(text='Hi HoW ArE yOu?') 59 | other_statement = Statement(text='hI hOw are YoU?') 60 | 61 | value = self.compare(statement, other_statement) 62 | 63 | self.assertEqual(value, 1) 64 | 65 | 66 | class SpacySimilarityTests(TestCase): 67 | 68 | def setUp(self): 69 | super().setUp() 70 | 71 | self.compare = comparisons.SpacySimilarity( 72 | language=languages.ENG 73 | ) 74 | 75 | def test_exact_match_different_stopwords(self): 76 | """ 77 | Test sentences with different stopwords. 78 | """ 79 | statement = Statement(text='What is matter?') 80 | other_statement = Statement(text='What is the matter?') 81 | 82 | value = self.compare(statement, other_statement) 83 | 84 | self.assertAlmostEqual(value, 0.9, places=1) 85 | 86 | def test_exact_match_different_capitalization(self): 87 | """ 88 | Test that text capitalization is ignored. 89 | """ 90 | statement = Statement(text='Hi HoW ArE yOu?') 91 | other_statement = Statement(text='hI hOw are YoU?') 92 | 93 | value = self.compare(statement, other_statement) 94 | 95 | self.assertAlmostEqual(value, 0.8, places=1) 96 | 97 | 98 | class JaccardSimilarityTestCase(TestCase): 99 | 100 | def setUp(self): 101 | super().setUp() 102 | 103 | self.compare = comparisons.JaccardSimilarity( 104 | language=languages.ENG 105 | ) 106 | 107 | def test_exact_match_different_capitalization(self): 108 | """ 109 | Test that text capitalization is ignored. 110 | """ 111 | statement = Statement(text='Hi HoW ArE yOu?') 112 | other_statement = Statement(text='hI hOw are YoU?') 113 | 114 | value = self.compare(statement, other_statement) 115 | 116 | self.assertEqual(value, 1) 117 | -------------------------------------------------------------------------------- /tests/test_conversations.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from chatterbot.conversation import Statement 3 | 4 | 5 | class StatementTests(TestCase): 6 | 7 | def setUp(self): 8 | self.statement = Statement(text='A test statement.') 9 | 10 | def test_serializer(self): 11 | data = self.statement.serialize() 12 | self.assertEqual(self.statement.text, data['text']) 13 | -------------------------------------------------------------------------------- /tests/test_examples.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | class ExamplesSmokeTestCase(TestCase): 5 | """ 6 | These are just basic tests that run each example 7 | to make sure no errors are triggered. 8 | """ 9 | 10 | def test_basic_example(self): 11 | from examples import basic_example # NOQA 12 | 13 | def test_convert_units(self): 14 | from examples import convert_units # NOQA 15 | 16 | def test_default_response_example(self): 17 | from examples import default_response_example # NOQA 18 | 19 | def test_export_example(self): 20 | self.skipTest( 21 | 'This is being skipped to avoid creating files during tests.' 22 | ) 23 | 24 | def test_learning_feedback_example(self): 25 | self.skipTest( 26 | 'This is being skipped because it contains ' 27 | 'a while loop in the code body and will not ' 28 | 'terminate on its own.' 29 | ) 30 | 31 | def test_math_and_time(self): 32 | from examples import math_and_time # NOQA 33 | 34 | def test_memory_sql_example(self): 35 | from examples import memory_sql_example # NOQA 36 | 37 | def test_specific_response_example(self): 38 | from examples import specific_response_example # NOQA 39 | 40 | def test_tagged_dataset_example(self): 41 | from examples import tagged_dataset_example # NOQA 42 | 43 | def test_terminal_example(self): 44 | self.skipTest( 45 | 'This is being skipped because it contains ' 46 | 'a while loop in the code body and will not ' 47 | 'terminate on its own.' 48 | ) 49 | 50 | def test_terminal_mongo_example(self): 51 | self.skipTest( 52 | 'This is being skipped so that we do not have ' 53 | 'to check if Mongo DB is running before running ' 54 | 'this test.' 55 | ) 56 | 57 | def test_tkinter_gui(self): 58 | self.skipTest( 59 | 'This is being skipped so that we do not open up ' 60 | 'a GUI during testing.' 61 | ) 62 | 63 | def test_training_example_chatterbot_corpus(self): 64 | from examples import training_example_chatterbot_corpus # NOQA 65 | 66 | def test_training_example_list_data(self): 67 | from examples import training_example_list_data # NOQA 68 | 69 | def test_training_example_ubuntu_corpus(self): 70 | self.skipTest( 71 | 'This test is being skipped because it takes ' 72 | 'hours to download and train from this corpus.' 73 | ) 74 | -------------------------------------------------------------------------------- /tests/test_filters.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotMongoTestCase 2 | from chatterbot import filters 3 | 4 | 5 | class RepetitiveResponseFilterTestCase(ChatBotMongoTestCase): 6 | """ 7 | Test case for the repetitive response filter. 8 | """ 9 | 10 | def test_filter_selection(self): 11 | """ 12 | Test that repetitive responses are filtered out of the results. 13 | """ 14 | from chatterbot.conversation import Statement 15 | from chatterbot.trainers import ListTrainer 16 | 17 | self.chatbot.filters = (filters.get_recent_repeated_responses, ) 18 | 19 | self.trainer = ListTrainer( 20 | self.chatbot, 21 | show_training_progress=False 22 | ) 23 | 24 | self.trainer.train([ 25 | 'Hi', 26 | 'Hello', 27 | 'Hi', 28 | 'Hello', 29 | 'Hi', 30 | 'Hello', 31 | 'How are you?', 32 | 'I am good', 33 | 'Glad to hear', 34 | 'How are you?' 35 | ]) 36 | 37 | statement = Statement(text='Hello', conversation='training') 38 | first_response = self.chatbot.get_response(statement) 39 | second_response = self.chatbot.get_response(statement) 40 | 41 | self.assertEqual('How are you?', first_response.text) 42 | self.assertEqual('Hi', second_response.text) 43 | -------------------------------------------------------------------------------- /tests/test_initialization.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | 3 | 4 | class StringInitializationTestCase(ChatBotTestCase): 5 | 6 | def get_kwargs(self): 7 | return { 8 | 'storage_adapter': 'chatterbot.storage.SQLStorageAdapter', 9 | 'database_uri': None 10 | } 11 | 12 | def test_storage_initialized(self): 13 | from chatterbot.storage import SQLStorageAdapter 14 | self.assertTrue(isinstance(self.chatbot.storage, SQLStorageAdapter)) 15 | 16 | def test_logic_initialized(self): 17 | from chatterbot.logic import BestMatch 18 | self.assertEqual(len(self.chatbot.logic_adapters), 1) 19 | self.assertTrue(isinstance(self.chatbot.logic_adapters[0], BestMatch)) 20 | 21 | 22 | class DictionaryInitializationTestCase(ChatBotTestCase): 23 | 24 | def get_kwargs(self): 25 | return { 26 | 'storage_adapter': { 27 | 'import_path': 'chatterbot.storage.SQLStorageAdapter', 28 | 'database_uri': None 29 | }, 30 | 'logic_adapters': [ 31 | { 32 | 'import_path': 'chatterbot.logic.BestMatch', 33 | }, 34 | { 35 | 'import_path': 'chatterbot.logic.MathematicalEvaluation', 36 | } 37 | ] 38 | } 39 | 40 | def test_storage_initialized(self): 41 | from chatterbot.storage import SQLStorageAdapter 42 | self.assertTrue(isinstance(self.chatbot.storage, SQLStorageAdapter)) 43 | 44 | def test_logic_initialized(self): 45 | from chatterbot.logic import BestMatch 46 | from chatterbot.logic import MathematicalEvaluation 47 | self.assertEqual(len(self.chatbot.logic_adapters), 2) 48 | self.assertTrue(isinstance(self.chatbot.logic_adapters[0], BestMatch)) 49 | self.assertTrue(isinstance(self.chatbot.logic_adapters[1], MathematicalEvaluation)) 50 | -------------------------------------------------------------------------------- /tests/test_languages.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from chatterbot import languages 3 | from unittest import TestCase 4 | 5 | 6 | class LanguageClassTests(TestCase): 7 | 8 | def test_classes_have_correct_attributes(self): 9 | language_classes = languages.get_language_classes() 10 | 11 | for name, obj in language_classes: 12 | self.assertTrue(inspect.isclass(obj)) 13 | self.assertTrue(hasattr(obj, 'ISO_639')) 14 | self.assertTrue(hasattr(obj, 'ISO_639_1')) 15 | self.assertTrue(hasattr(obj, 'ENGLISH_NAME')) 16 | self.assertEqual(name, obj.ISO_639.upper()) 17 | 18 | self.assertEqual(len(language_classes), 402) 19 | -------------------------------------------------------------------------------- /tests/test_preprocessors.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.conversation import Statement 3 | from chatterbot import preprocessors 4 | 5 | 6 | class PreprocessorIntegrationTestCase(ChatBotTestCase): 7 | """ 8 | Make sure that preprocessors work with the chat bot. 9 | """ 10 | 11 | def test_clean_whitespace(self): 12 | self.chatbot.preprocessors = [preprocessors.clean_whitespace] 13 | response = self.chatbot.get_response('Hello, how are you?') 14 | 15 | self.assertEqual(response.text, 'Hello, how are you?') 16 | 17 | 18 | class CleanWhitespacePreprocessorTestCase(ChatBotTestCase): 19 | """ 20 | Make sure that ChatterBot's whitespace removing preprocessor works as expected. 21 | """ 22 | 23 | def test_clean_whitespace(self): 24 | statement = Statement(text='\tThe quick \nbrown fox \rjumps over \vthe \alazy \fdog\\.') 25 | cleaned = preprocessors.clean_whitespace(statement) 26 | normal_text = 'The quick brown fox jumps over \vthe \alazy \fdog\\.' 27 | 28 | self.assertEqual(cleaned.text, normal_text) 29 | 30 | def test_leading_or_trailing_whitespace_removed(self): 31 | statement = Statement(text=' The quick brown fox jumps over the lazy dog. ') 32 | cleaned = preprocessors.clean_whitespace(statement) 33 | normal_text = 'The quick brown fox jumps over the lazy dog.' 34 | 35 | self.assertEqual(cleaned.text, normal_text) 36 | 37 | def test_consecutive_spaces_removed(self): 38 | statement = Statement(text='The quick brown fox jumps over the lazy dog.') 39 | cleaned = preprocessors.clean_whitespace(statement) 40 | normal_text = 'The quick brown fox jumps over the lazy dog.' 41 | 42 | self.assertEqual(cleaned.text, normal_text) 43 | 44 | 45 | class HTMLUnescapePreprocessorTestCase(ChatBotTestCase): 46 | """ 47 | Make sure that ChatterBot's html unescaping preprocessor works as expected. 48 | """ 49 | 50 | def test_html_unescape(self): 51 | 52 | # implicit concatenation 53 | statement = Statement( 54 | text=( 55 | 'The quick brown fox <b>jumps</b> over' 56 | ' the lazy dog.' 57 | ) 58 | ) 59 | 60 | normal_text = ( 61 | 'The quick brown fox jumps over' 62 | ' the lazy dog.' 63 | ) 64 | 65 | cleaned = preprocessors.unescape_html(statement) 66 | 67 | self.assertEqual(cleaned.text, normal_text) 68 | 69 | 70 | class ConvertToASCIIPreprocessorTestCase(ChatBotTestCase): 71 | """ 72 | Make sure that ChatterBot's ASCII conversion preprocessor works as expected. 73 | """ 74 | 75 | def test_convert_to_ascii(self): 76 | statement = Statement(text=u'Klüft skräms inför på fédéral électoral große') 77 | cleaned = preprocessors.convert_to_ascii(statement) 78 | normal_text = 'Kluft skrams infor pa federal electoral groe' 79 | 80 | self.assertEqual(cleaned.text, normal_text) 81 | -------------------------------------------------------------------------------- /tests/test_response_selection.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotSQLTestCase 2 | from chatterbot import response_selection 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class ResponseSelectionTests(ChatBotSQLTestCase): 7 | 8 | def test_get_most_frequent_response(self): 9 | statement_list = [ 10 | Statement(text='What... is your quest?', in_response_to='Hello'), 11 | Statement(text='What... is your quest?', in_response_to='Hello'), 12 | Statement(text='This is a phone.', in_response_to='Hello'), 13 | Statement(text='This is a phone.', in_response_to='Hello'), 14 | Statement(text='This is a phone.', in_response_to='Hello'), 15 | Statement(text='This is a phone.', in_response_to='Hello'), 16 | Statement(text='A what?', in_response_to='Hello'), 17 | Statement(text='A what?', in_response_to='Hello'), 18 | Statement(text='A phone.', in_response_to='Hello') 19 | ] 20 | 21 | for statement in statement_list: 22 | self.chatbot.storage.create( 23 | text=statement.text, 24 | in_response_to=statement.in_response_to 25 | ) 26 | 27 | output = response_selection.get_most_frequent_response( 28 | Statement(text='Hello'), 29 | statement_list, 30 | self.chatbot.storage 31 | ) 32 | 33 | self.assertEqual('This is a phone.', output.text) 34 | 35 | def test_get_first_response(self): 36 | statement_list = [ 37 | Statement(text='What... is your quest?'), 38 | Statement(text='A what?'), 39 | Statement(text='A quest.') 40 | ] 41 | 42 | output = response_selection.get_first_response(Statement(text='Hello'), statement_list) 43 | 44 | self.assertEqual(output.text, 'What... is your quest?') 45 | 46 | def test_get_random_response(self): 47 | statement_list = [ 48 | Statement(text='This is a phone.'), 49 | Statement(text='A what?'), 50 | Statement(text='A phone.') 51 | ] 52 | 53 | output = response_selection.get_random_response(Statement(text='Hello'), statement_list) 54 | 55 | self.assertTrue(output) 56 | -------------------------------------------------------------------------------- /tests/test_turing.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, expectedFailure 2 | 3 | 4 | class TuringTests(TestCase): 5 | 6 | def setUp(self): 7 | from chatterbot import ChatBot 8 | 9 | self.chatbot = ChatBot('Agent Jr.') 10 | 11 | @expectedFailure 12 | def test_ask_name(self): 13 | response = self.chatbot.get_response( 14 | 'What is your name?' 15 | ) 16 | self.assertIn('Agent', response.text) 17 | 18 | @expectedFailure 19 | def test_repeat_information(self): 20 | """ 21 | Test if we can detect any repeat responses from the agent. 22 | """ 23 | self.fail('Condition not met.') 24 | 25 | @expectedFailure 26 | def test_repeat_input(self): 27 | """ 28 | Test what the responses are like if we keep giving the same input. 29 | """ 30 | self.fail('Condition not met.') 31 | 32 | @expectedFailure 33 | def test_contradicting_responses(self): 34 | """ 35 | Test if we can get the agent to contradict themselves. 36 | """ 37 | self.fail('Condition not met.') 38 | 39 | @expectedFailure 40 | def test_mathematical_ability(self): 41 | """ 42 | The math questions inherently suggest that the agent 43 | should get some math problems wrong in order to seem 44 | more human. My view on this is that it is more useful 45 | to have a bot that is good at math, which could just 46 | as easily be a human. 47 | """ 48 | self.fail('Condition not met.') 49 | 50 | @expectedFailure 51 | def test_response_time(self): 52 | """ 53 | Does the agent respond in a realistic amount of time? 54 | """ 55 | self.fail('Condition not met.') 56 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from unittest import TestCase 3 | from chatterbot import utils 4 | 5 | 6 | class UtilityTests(TestCase): 7 | 8 | def test_import_module(self): 9 | datetime = utils.import_module('datetime.datetime') 10 | self.assertTrue(hasattr(datetime, 'now')) 11 | 12 | 13 | class UtilityChatBotTestCase(ChatBotTestCase): 14 | 15 | def test_get_response_time(self): 16 | """ 17 | Test that a response time is returned. 18 | """ 19 | 20 | response_time = utils.get_response_time(self.chatbot) 21 | 22 | self.assertGreater(response_time, 0) 23 | -------------------------------------------------------------------------------- /tests/training/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/tests/training/__init__.py -------------------------------------------------------------------------------- /tests/training/test_chatterbot_corpus_training.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | 4 | 5 | class ChatterBotCorpusTrainingTestCase(ChatBotTestCase): 6 | """ 7 | Test case for training with data from the ChatterBot Corpus. 8 | 9 | Note: This class has a mirror tests_django/integration_tests/ 10 | """ 11 | 12 | def setUp(self): 13 | super().setUp() 14 | self.trainer = ChatterBotCorpusTrainer( 15 | self.chatbot, 16 | show_training_progress=False 17 | ) 18 | 19 | def test_train_with_english_greeting_corpus(self): 20 | self.trainer.train('chatterbot.corpus.english.greetings') 21 | 22 | results = list(self.chatbot.storage.filter(text='Hello')) 23 | 24 | self.assertGreater(len(results), 1) 25 | 26 | def test_train_with_english_greeting_corpus_search_text(self): 27 | self.trainer.train('chatterbot.corpus.english.greetings') 28 | 29 | results = list(self.chatbot.storage.filter(text='Hello')) 30 | 31 | self.assertGreater(len(results), 1) 32 | self.assertEqual(results[0].search_text, 'hello') 33 | 34 | def test_train_with_english_greeting_corpus_search_in_response_to(self): 35 | self.trainer.train('chatterbot.corpus.english.greetings') 36 | 37 | results = list(self.chatbot.storage.filter(in_response_to='Hello')) 38 | 39 | self.assertGreater(len(results), 1) 40 | self.assertEqual(results[0].search_in_response_to, 'hello') 41 | 42 | def test_train_with_english_greeting_corpus_tags(self): 43 | self.trainer.train('chatterbot.corpus.english.greetings') 44 | 45 | results = list(self.chatbot.storage.filter(text='Hello')) 46 | 47 | self.assertGreater(len(results), 1) 48 | statement = results[0] 49 | self.assertEqual(['greetings'], statement.get_tags()) 50 | 51 | def test_train_with_multiple_corpora(self): 52 | self.trainer.train( 53 | 'chatterbot.corpus.english.greetings', 54 | 'chatterbot.corpus.english.conversations', 55 | ) 56 | results = list(self.chatbot.storage.filter(text='Hello')) 57 | 58 | self.assertGreater(len(results), 1) 59 | 60 | def test_train_with_english_corpus(self): 61 | self.trainer.train('chatterbot.corpus.english') 62 | results = list(self.chatbot.storage.filter(text='Hello')) 63 | 64 | self.assertGreater(len(results), 1) 65 | -------------------------------------------------------------------------------- /tests/training/test_training.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.trainers import Trainer 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class TrainingTests(ChatBotTestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | 11 | self.trainer = Trainer(self.chatbot) 12 | 13 | def test_trainer_not_set(self): 14 | with self.assertRaises(Trainer.TrainerInitializationException): 15 | self.trainer.train() 16 | 17 | def test_generate_export_data(self): 18 | self.chatbot.storage.create_many([ 19 | Statement(text='Hello, how are you?'), 20 | Statement(text='I am good.', in_response_to='Hello, how are you?') 21 | ]) 22 | data = self.trainer._generate_export_data() 23 | 24 | self.assertEqual( 25 | [['Hello, how are you?', 'I am good.']], data 26 | ) 27 | -------------------------------------------------------------------------------- /tests_django/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feignbird/ChatterBot-spacy_fixed/3be298db2fe871f9be722f279c4f86c10af8cce3/tests_django/__init__.py -------------------------------------------------------------------------------- /tests_django/base_case.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from django.test import TransactionTestCase 3 | from tests_django import test_settings 4 | 5 | 6 | class ChatterBotTestCase(TransactionTestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.chatbot = ChatBot(**test_settings.CHATTERBOT) 11 | -------------------------------------------------------------------------------- /tests_django/test_chatterbot_corpus_training.py: -------------------------------------------------------------------------------- 1 | from tests_django.base_case import ChatterBotTestCase 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | 4 | 5 | class ChatterBotCorpusTrainingTestCase(ChatterBotTestCase): 6 | """ 7 | Test case for training with data from the ChatterBot Corpus. 8 | 9 | Note: This class has a mirror tests/training_tests/ 10 | """ 11 | 12 | def setUp(self): 13 | super().setUp() 14 | 15 | self.trainer = ChatterBotCorpusTrainer( 16 | self.chatbot, 17 | show_training_progress=False 18 | ) 19 | 20 | def tearDown(self): 21 | super().tearDown() 22 | self.chatbot.storage.drop() 23 | 24 | def test_train_with_english_greeting_corpus(self): 25 | self.trainer.train('chatterbot.corpus.english.greetings') 26 | 27 | results = list(self.chatbot.storage.filter(text='Hello')) 28 | 29 | self.assertGreater(len(results), 1) 30 | 31 | def test_train_with_english_greeting_corpus_tags(self): 32 | self.trainer.train('chatterbot.corpus.english.greetings') 33 | 34 | results = list(self.chatbot.storage.filter(text='Hello')) 35 | 36 | self.assertGreater(len(results), 1) 37 | statement = results[0] 38 | self.assertEqual(['greetings'], statement.get_tags()) 39 | 40 | def test_train_with_multiple_corpora(self): 41 | self.trainer.train( 42 | 'chatterbot.corpus.english.greetings', 43 | 'chatterbot.corpus.english.conversations', 44 | ) 45 | results = list(self.chatbot.storage.filter(text='Hello')) 46 | 47 | self.assertGreater(len(results), 1) 48 | 49 | def test_train_with_english_corpus(self): 50 | self.trainer.train('chatterbot.corpus.english') 51 | results = list(self.chatbot.storage.filter(text='Hello')) 52 | 53 | self.assertGreater(len(results), 1) 54 | -------------------------------------------------------------------------------- /tests_django/test_chatterbot_settings.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.conf import settings 3 | 4 | 5 | class SettingsTestCase(TestCase): 6 | 7 | def test_modified_settings(self): 8 | with self.settings(CHATTERBOT={'name': 'Jim'}): 9 | self.assertIn('name', settings.CHATTERBOT) 10 | self.assertEqual('Jim', settings.CHATTERBOT['name']) 11 | 12 | def test_name_setting(self): 13 | with self.settings(): 14 | self.assertIn('name', settings.CHATTERBOT) 15 | self.assertEqual('Test Django ChatterBot', settings.CHATTERBOT['name']) 16 | -------------------------------------------------------------------------------- /tests_django/test_logic_adapter_integration.py: -------------------------------------------------------------------------------- 1 | from tests_django.base_case import ChatterBotTestCase 2 | from chatterbot.conversation import Statement 3 | 4 | 5 | class LogicIntegrationTestCase(ChatterBotTestCase): 6 | """ 7 | Tests to make sure that logic adapters 8 | function correctly when using Django. 9 | """ 10 | 11 | def setUp(self): 12 | super().setUp() 13 | 14 | self.chatbot.storage.create(text='Default statement') 15 | 16 | def test_best_match(self): 17 | from chatterbot.logic import BestMatch 18 | 19 | adapter = BestMatch(self.chatbot) 20 | 21 | statement1 = self.chatbot.storage.create( 22 | text='Do you like programming?', 23 | conversation='test' 24 | ) 25 | 26 | self.chatbot.storage.create( 27 | text='Yes', 28 | in_response_to=statement1.text, 29 | conversation='test' 30 | ) 31 | 32 | response = adapter.process(statement1) 33 | 34 | self.assertEqual(response.text, 'Yes') 35 | self.assertEqual(response.confidence, 1) 36 | 37 | def test_mathematical_evaluation(self): 38 | from chatterbot.logic import MathematicalEvaluation 39 | 40 | adapter = MathematicalEvaluation(self.chatbot) 41 | 42 | statement = Statement(text='What is 6 + 6?') 43 | 44 | response = adapter.process(statement) 45 | 46 | self.assertEqual(response.text, '6 + 6 = 12') 47 | self.assertEqual(response.confidence, 1) 48 | 49 | def test_time(self): 50 | from chatterbot.logic import TimeLogicAdapter 51 | 52 | adapter = TimeLogicAdapter(self.chatbot) 53 | 54 | statement = Statement(text='What time is it?') 55 | 56 | response = adapter.process(statement) 57 | 58 | self.assertIn('The current time is', response.text) 59 | self.assertEqual(response.confidence, 1) 60 | -------------------------------------------------------------------------------- /tests_django/test_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for when tests are run. 3 | """ 4 | import os 5 | from chatterbot import constants 6 | 7 | DEBUG = True 8 | 9 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 10 | 11 | SECRET_KEY = 'fake-key' 12 | 13 | INSTALLED_APPS = [ 14 | 'django.contrib.auth', 15 | 'django.contrib.contenttypes', 16 | 'django.contrib.sessions', 17 | 'chatterbot.ext.django_chatterbot', 18 | 'tests_django', 19 | ] 20 | 21 | CHATTERBOT = { 22 | 'name': 'Test Django ChatterBot', 23 | 'logic_adapters': [ 24 | { 25 | 'import_path': 'chatterbot.logic.BestMatch', 26 | }, 27 | { 28 | 'import_path': 'chatterbot.logic.MathematicalEvaluation', 29 | } 30 | ], 31 | 'storage_adapter': 'chatterbot.storage.DjangoStorageAdapter', 32 | 'django_app_name': constants.DEFAULT_DJANGO_APP_NAME, 33 | 'initialize': False 34 | } 35 | 36 | MIDDLEWARE_CLASSES = ( 37 | 'django.contrib.sessions.middleware.SessionMiddleware', 38 | 'django.middleware.common.CommonMiddleware', 39 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 40 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 41 | 'django.contrib.messages.middleware.MessageMiddleware', 42 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 43 | 'django.middleware.security.SecurityMiddleware', 44 | ) 45 | 46 | DATABASES = { 47 | 'default': { 48 | 'ENGINE': 'django.db.backends.sqlite3', 49 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 50 | } 51 | } 52 | 53 | # Using the MD5 password hasher improves test performance 54 | PASSWORD_HASHERS = ( 55 | 'django.contrib.auth.hashers.MD5PasswordHasher', 56 | ) 57 | 58 | USE_TZ = True 59 | 60 | LOGGING = { 61 | 'version': 1, 62 | 'disable_existing_loggers': False, 63 | 'handlers': { 64 | 'console': { 65 | 'class': 'logging.StreamHandler', 66 | }, 67 | }, 68 | 'loggers': { 69 | 'django': { 70 | 'handlers': ['console'], 71 | 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 72 | }, 73 | }, 74 | } 75 | -------------------------------------------------------------------------------- /tests_django/test_statement_integration.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from chatterbot.conversation import Statement as StatementObject 3 | from chatterbot.ext.django_chatterbot.models import Statement as StatementModel 4 | 5 | 6 | class StatementIntegrationTestCase(TestCase): 7 | """ 8 | Test case to make sure that the Django Statement model 9 | and ChatterBot Statement object have a common interface. 10 | """ 11 | 12 | def setUp(self): 13 | super().setUp() 14 | 15 | from datetime import datetime 16 | from pytz import UTC 17 | 18 | now = datetime(2020, 2, 15, 3, 14, 10, 0, UTC) 19 | 20 | self.object = StatementObject(text='_', created_at=now) 21 | self.model = StatementModel(text='_', created_at=now) 22 | 23 | # Simulate both statements being saved 24 | self.model.save() 25 | self.object.id = self.model.id 26 | 27 | def test_text(self): 28 | self.assertTrue(hasattr(self.object, 'text')) 29 | self.assertTrue(hasattr(self.model, 'text')) 30 | 31 | def test_in_response_to(self): 32 | self.assertTrue(hasattr(self.object, 'in_response_to')) 33 | self.assertTrue(hasattr(self.model, 'in_response_to')) 34 | 35 | def test_conversation(self): 36 | self.assertTrue(hasattr(self.object, 'conversation')) 37 | self.assertTrue(hasattr(self.model, 'conversation')) 38 | 39 | def test_tags(self): 40 | self.assertTrue(hasattr(self.object, 'tags')) 41 | self.assertTrue(hasattr(self.model, 'tags')) 42 | 43 | def test__str__(self): 44 | self.assertTrue(hasattr(self.object, '__str__')) 45 | self.assertTrue(hasattr(self.model, '__str__')) 46 | 47 | self.assertEqual(str(self.object), str(self.model)) 48 | 49 | def test_add_tags(self): 50 | self.object.add_tags('a', 'b') 51 | self.model.add_tags('a', 'b') 52 | 53 | self.assertIn('a', self.object.get_tags()) 54 | self.assertIn('a', self.model.get_tags()) 55 | 56 | def test_serialize(self): 57 | object_data = self.object.serialize() 58 | model_data = self.model.serialize() 59 | 60 | self.assertEqual(object_data, model_data) 61 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = True 3 | 4 | [testenv] 5 | passenv = DJANGO_SETTINGS_MODULE PYTHONPATH HOME DISPLAY 6 | setenv = PYTHONDONTWRITEBYTECODE=1 7 | deps = 8 | django111: Django>=1.11,<1.12 9 | django20: Django>=2.0,<2.1 10 | -rrequirements.txt 11 | -rdev-requirements.txt 12 | commands_pre = 13 | python -m spacy link en_core_web_sm en 14 | python -m spacy link de_core_news_sm de 15 | 16 | [testenv:docs] 17 | commands = sphinx-build -nW -b html ./docs/ {envtmpdir}/build/ 18 | 19 | [testenv:lint] 20 | deps = flake8 21 | commands_pre = 22 | commands = flake8 23 | 24 | [testenv:test] 25 | commands = 26 | nosetests 27 | 28 | [testenv:django111] 29 | commands = 30 | python setup.py develop --no-deps 31 | python runtests.py 32 | python examples/django_app/manage.py test examples/django_app/ 33 | 34 | [testenv:django20] 35 | commands = 36 | python setup.py develop --no-deps 37 | python runtests.py 38 | python examples/django_app/manage.py test examples/django_app/ --------------------------------------------------------------------------------