diff --git a/platypush/message/event/assistant/__init__.py b/platypush/message/event/assistant/__init__.py index 592fd4fc04..431f997141 100644 --- a/platypush/message/event/assistant/__init__.py +++ b/platypush/message/event/assistant/__init__.py @@ -1,9 +1,9 @@ import re import sys -from typing import Optional, Union +from typing import Any, Mapping, Optional, Union from platypush.context import get_plugin -from platypush.message.event import Event +from platypush.message.event import Event, EventMatchResult from platypush.plugins.assistant import AssistantPlugin from platypush.utils import get_plugin_name_by_class @@ -102,6 +102,23 @@ class ResponseEvent(AssistantEvent): ) +class ResponseEndEvent(ConversationEndEvent): + """ + Event triggered when a response has been rendered on the assistant. + """ + + def __init__(self, *args, with_follow_on_turn: bool = False, **kwargs): + """ + :param with_follow_on_turn: Set to true if the conversation expects a + user follow-up, false otherwise. + """ + super().__init__( + *args, + with_follow_on_turn=with_follow_on_turn, + **kwargs, + ) + + class NoResponseEvent(ConversationEndEvent): """ Event triggered when a conversation ends with no response @@ -205,6 +222,42 @@ class SpeechRecognizedEvent(AssistantEvent): return result +class IntentMatchedEvent(AssistantEvent): + """ + Event triggered when an intent is matched by a speech command. + """ + + def __init__( + self, *args, intent: str, slots: Optional[Mapping[str, Any]] = None, **kwargs + ): + """ + :param intent: The intent that has been matched. + :param slots: The slots extracted from the intent, as a key-value mapping. + """ + super().__init__(*args, intent=intent, slots=slots or {}, **kwargs) + + def _matches_argument( + self, argname, condition_value, event_args, result: EventMatchResult + ): + if argname != 'slots': + return super()._matches_argument( + argname, condition_value, event_args, result + ) + + event_slots = set(event_args.get(argname, {}).items()) + slots = set(self.args.get(argname, {}).items()) + + # All the slots in the condition must be in the event + if slots.difference(event_slots) == 0: + result.is_match = True + result.score += 1 + else: + result.is_match = False + result.score = 0 + + return result + + class HotwordDetectedEvent(AssistantEvent): """ Event triggered when a custom hotword is detected diff --git a/platypush/plugins/assistant/__init__.py b/platypush/plugins/assistant/__init__.py index 6146039362..ad0cc1065a 100644 --- a/platypush/plugins/assistant/__init__.py +++ b/platypush/plugins/assistant/__init__.py @@ -242,7 +242,9 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC): tts.say(text=text, **self.tts_plugin_args) def _on_response_render_end(self): - pass + from platypush.message.event.assistant import ResponseEndEvent + + self._send_event(ResponseEndEvent) def _on_hotword_detected(self, hotword: Optional[str]): from platypush.message.event.assistant import HotwordDetectedEvent @@ -256,6 +258,11 @@ class AssistantPlugin(Plugin, AssistantEntityManager, ABC): self._last_query = phrase self._send_event(SpeechRecognizedEvent, phrase=phrase) + def _on_intent_matched(self, intent: str, slots: Optional[Dict[str, Any]] = None): + from platypush.message.event.assistant import IntentMatchedEvent + + self._send_event(IntentMatchedEvent, intent=intent, slots=slots) + def _on_alarm_start(self): from platypush.message.event.assistant import AlarmStartedEvent