From 98a4f5349d230f28a2d99f97c5c758f1ed9e0c65 Mon Sep 17 00:00:00 2001 From: TechieDamien Date: Fri, 12 Nov 2021 11:59:33 +0000 Subject: [PATCH] Added basic entity support --- __init__.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index a4f51ee..4be73a3 100644 --- a/__init__.py +++ b/__init__.py @@ -3,15 +3,73 @@ from opsdroid.matchers import match_rasanlu, match_parse import random class Respond(Skill): + # Constructor gets configs for the skill and applies the decorator + # to match using rasa. def __init__(self, opsdroid, config): super().__init__(opsdroid, config) - self.intent = self.config.get("intent") + self._get_configs() self.respond = match_rasanlu(self.intent)(self.respond) + # This utility function will grab the relevent config variables + # for this skill to function. + def _get_configs(self): + # The intent to match. + self.intent = self.config.get("intent") + # Possible responses given a match. + self.response_options = self.config.get("response_options") + # A bool to check if we want to use entities in our response. + self.use_entities = self.config.get("use_entities", False) + # Fallback responses if we do not get the required entities + # from Rasa. + self.fallback_response_options = self.config.get("fallback_response_options", + ["Sorry, Rasa did not extract sufficient entities to fulfil your request"]) + + + # Extract the entities from the rasanlu response + def _get_entities(self, message): + # This includes data such as the extractor used, which we + # don't need. + raw_entities = message.rasanlu["entities"] + entities = {} + for entity in raw_entities: + entity_type = entity["entity"] + unique_entity_type = self._get_unique_key(entity_type, entities) + entity_value = entity["value"] + entities[unique_entity_type] = entity_value + return entities + + + # Appends the smallest integer to the name of the key in order to + # create a new key that is not already in the dictionary. + def _get_unique_key(self, base_key, dictionary): + counter = 0 + while True: + unique_key_attempt = base_key + str(counter) + if unique_key_attempt in dictionary: + counter += 1 + continue + return unique_key_attempt + + + # Takes the response and attempts to replace all entities in it + # with the extracted entities. Failing that, we will fall back to a + # fallback response. + def _substitute_entities(self, response, entities): + try: + response = response.format(**entities) + except KeyError as e: + chosen_fallback = random.choice(self.fallback_response_options) + return chosen_fallback + return response + + # This line is to add properties to respond on declaration # as doing this in __init__ would cause an exception to be # raised. If you don't believe me, remove it! @match_parse("hgftgyhjknbvhgftyuihjkvhgftyuijkbjhgtyyuijhkjghfdtrytyihujkbvncgfxdtrytuyiuhkbjvbhcfdytryuhjkbvhcfdyrtuyiuhkbhj") async def respond(self, message): - response_options = self.config.get("response_options") - await message.respond(random.choice(response_options)) + chosen_response = random.choice(self.response_options) + if self.use_entities: + entities = self._get_entities(message) + chosen_response = self._substitute_entities(chosen_response, entities) + await message.respond(random.choice(self.response_options))