From 676f9a50646c5bbc0947851d3291c507d705b4b6 Mon Sep 17 00:00:00 2001 From: jarbasal Date: Thu, 21 Oct 2021 12:38:58 +0100 Subject: [PATCH] active skill events - enhanced version of mycroft-core/pull/1468 - adds `self.activate()` and `self.deactivate()` in MycroftSkill class - adds "intent.service.skills.deactivated" event when IntentService removes a skill from active list - adds "intent.service.skills.activated" event when IntentService adds a skill to active list - adds `def handle_deactivate(self, message):` callback in MycroftSkill class - adds `def handle_activate(self, message):` callback in MycroftSkill class - prepares `self.make_active()` for deprecation --- mycroft/skills/fallback_skill.py | 2 +- mycroft/skills/intent_service.py | 32 +++++++++- mycroft/skills/mycroft_skill/mycroft_skill.py | 63 +++++++++++++++++-- 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/mycroft/skills/fallback_skill.py b/mycroft/skills/fallback_skill.py index b8418904bdc3..6d60220659f4 100644 --- a/mycroft/skills/fallback_skill.py +++ b/mycroft/skills/fallback_skill.py @@ -153,7 +153,7 @@ def register_fallback(self, handler, priority): def wrapper(*args, **kwargs): if handler(*args, **kwargs): - self.make_active() + self.activate() return True return False diff --git a/mycroft/skills/intent_service.py b/mycroft/skills/intent_service.py index bef1e01de135..c276d04566bc 100644 --- a/mycroft/skills/intent_service.py +++ b/mycroft/skills/intent_service.py @@ -28,6 +28,7 @@ ) from mycroft.skills.intent_service_interface import open_intent_envelope from mycroft.skills.permissions import ConverseMode, ConverseActivationMode +from mycroft.messagebus.message import Message def _get_message_lang(message): @@ -110,6 +111,11 @@ def __init__(self, bus): self.bus.on('mycroft.speech.recognition.unknown', self.reset_converse) self.bus.on('mycroft.skills.loaded', self.update_skill_name_dict) + self.bus.on('intent.service.skills.activate', + self.handle_activate_skill_request) + self.bus.on('intent.service.skills.deactivate', + self.handle_deactivate_skill_request) + # TODO backwards compat, deprecate self.bus.on('active_skill_request', self.handle_activate_skill_request) self.active_skills = [] # [skill_id , timestamp] @@ -211,6 +217,21 @@ def handle_activate_skill_request(self, message): self.add_active_skill(skill_id) self._consecutive_activations[skill_id] += 1 + # converse handling + + def handle_activate_skill_request(self, message): + self.add_active_skill(message.data['skill_id']) + + def handle_deactivate_skill_request(self, message): + # TODO imperfect solution - only a skill can deactivate itself + # someone can forge this message and emit it raw, but in ovos-core all + # skill message should have skill_id in context, so let's make sure + # this doesnt happen accidentally + skill_id = message.data['skill_id'] + source_skill = message.context.get("skill_id") or skill_id + if skill_id == source_skill: + self.remove_active_skill(message.data['skill_id']) + def reset_converse(self, message): """Let skills know there was a problem with speech recognition""" lang = _get_message_lang(message) @@ -273,6 +294,9 @@ def remove_active_skill(self, skill_id): for skill in self.active_skills: if skill[0] == skill_id: self.active_skills.remove(skill) + self.bus.emit( + Message("intent.service.skills.deactivated", + {"skill_id": skill_id})) if skill_id in self._consecutive_activations: self._consecutive_activations[skill_id] = 0 @@ -288,9 +312,15 @@ def add_active_skill(self, skill_id): # search the list for an existing entry that already contains it # and remove that reference if skill_id != '': - self.remove_active_skill(skill_id) + # do not call remove method to not send deactivate bus event! + for skill in self.active_skills: + if skill[0] == skill_id: + self.active_skills.remove(skill) # add skill with timestamp to start of skill_list self.active_skills.insert(0, [skill_id, time.time()]) + self.bus.emit( + Message("intent.service.skills.activated", + {"skill_id": skill_id})) else: LOG.warning('Skill ID was empty, won\'t add to list of ' 'active skills.') diff --git a/mycroft/skills/mycroft_skill/mycroft_skill.py b/mycroft/skills/mycroft_skill/mycroft_skill.py index 3bf5e5cf65a5..d298cd47f025 100644 --- a/mycroft/skills/mycroft_skill/mycroft_skill.py +++ b/mycroft/skills/mycroft_skill/mycroft_skill.py @@ -412,6 +412,14 @@ def stop_is_implemented(): 'mycroft.skills.settings.changed', self.handle_settings_change ) + self.events.add(f"{self.skill_id}.deactivate", + self.handle_deactivate) + self.events.add("intent.service.skills.deactivated", + self._handle_skill_deactivated) + self.events.add(f"{self.skill_id}.activate", + self.handle_activate) + self.events.add("intent.service.skills.activated", + self._handle_skill_activated) def handle_settings_change(self, message): """Update settings if the remote settings changes apply to this skill. @@ -462,6 +470,50 @@ def get_intro_message(self): """ return None + # converse handling + def _handle_skill_activated(self, message): + """ intent service activated a skill + if it was this skill fire the skill activation event""" + if message.data.get("skill_id") == self.skill_id: + self.bus.emit(message.forward(f"{self.skill_id}.activate")) + + def handle_activate(self, message): + """ skill is now considered active by the intent service + converse method will be called, skills might want to prepare/resume + """ + + def _handle_skill_deactivated(self, message): + """ intent service deactivated a skill + if it was this skill fire the skill deactivation event""" + if message.data.get("skill_id") == self.skill_id: + self.bus.emit(message.forward(f"{self.skill_id}.deactivate")) + + def handle_deactivate(self, message): + """ skill is no longer considered active by the intent service + converse method will not be called, skills might want to reset state here + """ + + def activate(self): + """Bump skill to active_skill list in intent_service. + This enables converse method to be called even without skill being + used in last 5 minutes. + """ + msg = dig_for_message() or Message("") + if "skill_id" not in msg.context: + msg.context["skill_id"] = self.skill_id + self.bus.emit(msg.forward("intent.service.skills.activate", + data={"skill_id": self.skill_id})) + + def deactivate(self): + """remove skill from active_skill list in intent_service. + This stops converse method from being called + """ + msg = dig_for_message() or Message("") + if "skill_id" not in msg.context: + msg.context["skill_id"] = self.skill_id + self.bus.emit(msg.forward(f"intent.service.skills.deactivate", + data={"skill_id": self.skill_id})) + def converse(self, message=None): """Handle conversation. @@ -505,7 +557,7 @@ def converse(utterances, lang=None): return True # install a temporary conversation handler - self.make_active() + self.activate() converse.response = None default_converse = self.converse self.converse = converse @@ -776,12 +828,11 @@ def make_active(self): This enables converse method to be called even without skill being used in last 5 minutes. + + deprecated: use self.activate() instead """ - msg = dig_for_message() or Message("") - if "skill_id" not in msg.context: - msg.context["skill_id"] = self.skill_id - self.bus.emit(msg.forward('active_skill_request', - {'skill_id': self.skill_id})) + # TODO deprecate, backwards compat + self.activate() def _handle_collect_resting(self, message=None): """Handler for collect resting screen messages.