util.py 3.8 KB

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