util.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. # Natural Language Toolkit: Chatbot Utilities
  2. #
  3. # Copyright (C) 2001-2019 NLTK Project
  4. # Authors: Steven Bird <stevenbird1@gmail.com>
  5. # URL: <http://nltk.org/>
  6. # For license information, see LICENSE.TXT
  7. # Based on an Eliza implementation by Joe Strout <joe@strout.net>,
  8. # Jeff Epler <jepler@inetnebr.com> and Jez Higgins <jez@jezuk.co.uk>.
  9. from __future__ import print_function
  10. import re
  11. import random
  12. from six.moves import input
  13. reflections = {
  14. "i am": "you are",
  15. "i was": "you were",
  16. "i": "you",
  17. "i'm": "you are",
  18. "i'd": "you would",
  19. "i've": "you have",
  20. "i'll": "you will",
  21. "my": "your",
  22. "you are": "I am",
  23. "you were": "I was",
  24. "you've": "I have",
  25. "you'll": "I will",
  26. "your": "my",
  27. "yours": "mine",
  28. "you": "me",
  29. "me": "you",
  30. }
  31. class Chat(object):
  32. def __init__(self, pairs, reflections={}):
  33. """
  34. Initialize the chatbot. Pairs is a list of patterns and responses. Each
  35. pattern is a regular expression matching the user's statement or question,
  36. e.g. r'I like (.*)'. For each such pattern a list of possible responses
  37. is given, e.g. ['Why do you like %1', 'Did you ever dislike %1']. Material
  38. which is matched by parenthesized sections of the patterns (e.g. .*) is mapped to
  39. the numbered positions in the responses, e.g. %1.
  40. :type pairs: list of tuple
  41. :param pairs: The patterns and responses
  42. :type reflections: dict
  43. :param reflections: A mapping between first and second person expressions
  44. :rtype: None
  45. """
  46. self._pairs = [(re.compile(x, re.IGNORECASE), y) for (x, y) in pairs]
  47. self._reflections = reflections
  48. self._regex = self._compile_reflections()
  49. def _compile_reflections(self):
  50. sorted_refl = sorted(self._reflections.keys(), key=len, reverse=True)
  51. return re.compile(
  52. r"\b({0})\b".format("|".join(map(re.escape, sorted_refl))), re.IGNORECASE
  53. )
  54. def _substitute(self, str):
  55. """
  56. Substitute words in the string, according to the specified reflections,
  57. e.g. "I'm" -> "you are"
  58. :type str: str
  59. :param str: The string to be mapped
  60. :rtype: str
  61. """
  62. return self._regex.sub(
  63. lambda mo: self._reflections[mo.string[mo.start() : mo.end()]], str.lower()
  64. )
  65. def _wildcards(self, response, match):
  66. pos = response.find('%')
  67. while pos >= 0:
  68. num = int(response[pos + 1 : pos + 2])
  69. response = (
  70. response[:pos]
  71. + self._substitute(match.group(num))
  72. + response[pos + 2 :]
  73. )
  74. pos = response.find('%')
  75. return response
  76. def respond(self, str):
  77. """
  78. Generate a response to the user input.
  79. :type str: str
  80. :param str: The string to be mapped
  81. :rtype: str
  82. """
  83. # check each pattern
  84. for (pattern, response) in self._pairs:
  85. match = pattern.match(str)
  86. # did the pattern match?
  87. if match:
  88. resp = random.choice(response) # pick a random response
  89. resp = self._wildcards(resp, match) # process wildcards
  90. # fix munged punctuation at the end
  91. if resp[-2:] == '?.':
  92. resp = resp[:-2] + '.'
  93. if resp[-2:] == '??':
  94. resp = resp[:-2] + '?'
  95. return resp
  96. # Hold a conversation with a chatbot
  97. def converse(self, quit="quit"):
  98. user_input = ""
  99. while user_input != quit:
  100. user_input = quit
  101. try:
  102. user_input = input(">")
  103. except EOFError:
  104. print(user_input)
  105. if user_input:
  106. while user_input[-1] in "!.":
  107. user_input = user_input[:-1]
  108. print(self.respond(user_input))