123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- # Natural Language Toolkit: Chatbot Utilities
- #
- # Copyright (C) 2001-2019 NLTK Project
- # Authors: Steven Bird <stevenbird1@gmail.com>
- # URL: <http://nltk.org/>
- # For license information, see LICENSE.TXT
- # Based on an Eliza implementation by Joe Strout <joe@strout.net>,
- # Jeff Epler <jepler@inetnebr.com> and Jez Higgins <jez@jezuk.co.uk>.
- from __future__ import print_function
- import re
- import random
- from six.moves import input
- reflections = {
- "i am": "you are",
- "i was": "you were",
- "i": "you",
- "i'm": "you are",
- "i'd": "you would",
- "i've": "you have",
- "i'll": "you will",
- "my": "your",
- "you are": "I am",
- "you were": "I was",
- "you've": "I have",
- "you'll": "I will",
- "your": "my",
- "yours": "mine",
- "you": "me",
- "me": "you",
- }
- class Chat(object):
- def __init__(self, pairs, reflections={}):
- """
- Initialize the chatbot. Pairs is a list of patterns and responses. Each
- pattern is a regular expression matching the user's statement or question,
- e.g. r'I like (.*)'. For each such pattern a list of possible responses
- is given, e.g. ['Why do you like %1', 'Did you ever dislike %1']. Material
- which is matched by parenthesized sections of the patterns (e.g. .*) is mapped to
- the numbered positions in the responses, e.g. %1.
- :type pairs: list of tuple
- :param pairs: The patterns and responses
- :type reflections: dict
- :param reflections: A mapping between first and second person expressions
- :rtype: None
- """
- self._pairs = [(re.compile(x, re.IGNORECASE), y) for (x, y) in pairs]
- self._reflections = reflections
- self._regex = self._compile_reflections()
- def _compile_reflections(self):
- sorted_refl = sorted(self._reflections.keys(), key=len, reverse=True)
- return re.compile(
- r"\b({0})\b".format("|".join(map(re.escape, sorted_refl))), re.IGNORECASE
- )
- def _substitute(self, str):
- """
- Substitute words in the string, according to the specified reflections,
- e.g. "I'm" -> "you are"
- :type str: str
- :param str: The string to be mapped
- :rtype: str
- """
- return self._regex.sub(
- lambda mo: self._reflections[mo.string[mo.start() : mo.end()]], str.lower()
- )
- def _wildcards(self, response, match):
- pos = response.find('%')
- while pos >= 0:
- num = int(response[pos + 1 : pos + 2])
- response = (
- response[:pos]
- + self._substitute(match.group(num))
- + response[pos + 2 :]
- )
- pos = response.find('%')
- return response
- def respond(self, str):
- """
- Generate a response to the user input.
- :type str: str
- :param str: The string to be mapped
- :rtype: str
- """
- # check each pattern
- for (pattern, response) in self._pairs:
- match = pattern.match(str)
- # did the pattern match?
- if match:
- resp = random.choice(response) # pick a random response
- resp = self._wildcards(resp, match) # process wildcards
- # fix munged punctuation at the end
- if resp[-2:] == '?.':
- resp = resp[:-2] + '.'
- if resp[-2:] == '??':
- resp = resp[:-2] + '?'
- return resp
- # Hold a conversation with a chatbot
- def converse(self, quit="quit"):
- user_input = ""
- while user_input != quit:
- user_input = quit
- try:
- user_input = input(">")
- except EOFError:
- print(user_input)
- if user_input:
- while user_input[-1] in "!.":
- user_input = user_input[:-1]
- print(self.respond(user_input))
|