Skip to content

Commit

Permalink
lang support + refactor for readibility
Browse files Browse the repository at this point in the history
  • Loading branch information
JarbasAl committed Jun 9, 2022
1 parent 0ed6c82 commit 07bb7f1
Showing 1 changed file with 154 additions and 42 deletions.
196 changes: 154 additions & 42 deletions ovos_workshop/skills/ovos.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import time

from copy import copy
from ovos_utils import camel_case_split, get_handler_name
# ensure mycroft can be imported
from ovos_utils import ensure_mycroft_import
Expand All @@ -17,6 +17,7 @@
from ovos_workshop.decorators.killable import killable_event, \
AbortEvent, AbortQuestion
from ovos_workshop.skills.layers import IntentLayers
from ovos_utils.messagebus import dig_for_message, get_message_lang


class OVOSSkill(MycroftSkill):
Expand All @@ -42,6 +43,80 @@ def bind(self, bus):
self.private_settings = PrivateSettings(self.skill_id)
self.intent_layers.bind(self)

# lang support
@property
def lang(self):
"""Get the current active language."""
lang = self.core_lang
message = dig_for_message()
if message:
lang = get_message_lang(message)
return lang.lower()

# new public api, these are private methods in ovos-core
# preference is given to ovos-core code to account for updates
# but most functionality is otherwise duplicated here
# simply with an underscore removed from the name
@property
def core_lang(self):
"""Get the configured default language."""
if hasattr(self, "_core_lang"):
return self._core_lang
return self.config_core.get("lang", "en-us").lower()

@property
def secondary_langs(self):
"""Get the configured secondary languages, mycroft is not
considered to be in these languages but i will load it's resource
files. This provides initial support for multilingual input"""
if hasattr(self, "_secondary_langs"):
return self._secondary_langs
return [l.lower() for l in self.config_core.get('secondary_langs', [])
if l != self.core_lang]

@property
def native_langs(self):
"""Languages natively supported by core
ie, resource files available and explicitly supported
"""
if hasattr(self, "_native_langs"):
return self._native_langs
return [self.core_lang] + self.secondary_langs

@property
def alphanumeric_skill_id(self):
"""skill id converted to only alphanumeric characters
Non alpha-numeric characters are converted to "_"
Returns:
(str) String of letters
"""
if hasattr(self, "_alphanumeric_skill_id"):
return self._alphanumeric_skill_id
return ''.join(c if c.isalnum() else '_'
for c in str(self.skill_id))

@property
def resources(self):
"""Instantiates a ResourceFileLocator instance when needed.
a new instance is always created to ensure self.lang
reflects the active language and not the default core language
"""
if hasattr(self, "_resources"):
return self._resources
return self.load_lang(self.root_dir, self.lang)

def load_lang(self, root_directory=None, lang=None):
"""Instantiates a ResourceFileLocator instance when needed.
a new instance is always created to ensure lang
reflects the active language and not the default core language
"""
if hasattr(self, "_load_lang"):
return self._load_lang(root_directory, lang)
raise RuntimeError("this functionality is only available in "
"ovos-core and the mark2")

#
def voc_match(self, *args, **kwargs):
try:
return super().voc_match(*args, **kwargs)
Expand Down Expand Up @@ -79,66 +154,103 @@ def register_intent_layer(self, layer_name, intent_list):
name = intent_file
self.intent_layers.update_layer(layer_name, [name])

# this method can probably use a better refactor, we are only changing one
# of the internal callbacks
def add_event(self, name, handler, handler_info=None, once=False):
# killable_events support
def send_stop_signal(self, stop_event=None):
msg = dig_for_message() or Message("mycroft.stop")
# stop event execution
if stop_event:
self.bus.emit(msg.forward(stop_event))
# stop TTS
self.bus.emit(msg.forward("mycroft.audio.speech.stop"))
# Tell ovos-core to stop recording (not in mycroft-core)
self.bus.emit(msg.forward('recognizer_loop:record_stop'))
# STT might continue recording and screw up the next get_response
self.bus.emit(msg.forward('mycroft.mic.mute'))
time.sleep(0.5) # if TTS had not yet started
self.bus.emit(msg.forward("mycroft.audio.speech.stop"))
time.sleep(1.5) # the silence from muting should make STT stop recording
self.bus.emit(msg.forward('mycroft.mic.unmute'))

# these methods are copied from ovos-core for compat with mycroft-core
def _on_event_start(self, message, handler_info, skill_data):
"""Indicate that the skill handler is starting."""
if handler_info:
# Indicate that the skill handler is starting if requested
msg_type = handler_info + '.start'
message.context["skill_id"] = self.skill_id
self.bus.emit(message.forward(msg_type, skill_data))

def _on_event_end(self, message, handler_info, skill_data):
"""Store settings and indicate that the skill handler has completed
"""
if self.settings != self._initial_settings:
try: # ovos-core
self.settings.store()
self._initial_settings = copy(self.settings)
except: # mycroft-core
from mycroft.skills.settings import save_settings
save_settings(self.settings_write_path, self.settings)
self._initial_settings = dict(self.settings)
if handler_info:
msg_type = handler_info + '.complete'
message.context["skill_id"] = self.skill_id
self.bus.emit(message.forward(msg_type, skill_data))

def _on_event_error(self, error, message, handler_info, skill_data, speak_errors):
"""Speak and log the error."""
# Convert "MyFancySkill" to "My Fancy Skill" for speaking
handler_name = camel_case_split(self.name)
msg_data = {'skill': handler_name}
speech = dialog.get('skill.error', self.lang, msg_data)
if speak_errors:
self.speak(speech)
LOG.exception(error)
# append exception information in message
skill_data['exception'] = repr(error)
if handler_info:
# Indicate that the skill handler errored
msg_type = handler_info + '.error'
message = message or Message("")
message.context["skill_id"] = self.skill_id
self.bus.emit(message.forward(msg_type, skill_data))

def add_event(self, name, handler, handler_info=None, once=False, speak_errors=True):
"""Create event handler for executing intent or other event.
Arguments:
Args:
name (string): IntentParser name
handler (func): Method to call
handler_info (string): Base message when reporting skill event
handler status on messagebus.
once (bool, optional): Event handler will be removed after it has
been run once.
speak_errors (bool, optional): Determines if an error dialog should be
spoken to inform the user whenever
an exception happens inside the handler
"""
skill_data = {'name': get_handler_name(handler)}

def on_error(e):
"""Speak and log the error."""
if not isinstance(e, AbortEvent):
# Convert "MyFancySkill" to "My Fancy Skill" for speaking
handler_name = camel_case_split(self.name)
msg_data = {'skill': handler_name}
msg = dialog.get('skill.error', self.lang, msg_data)
self.speak(msg)
LOG.exception(msg)
else:
def on_error(error, message):
if isinstance(error, AbortEvent):
LOG.info("Skill execution aborted")
# append exception information in message
skill_data['exception'] = repr(e)
self._on_event_end(message, handler_info, skill_data)
return
self._on_event_error(error, message, handler_info, skill_data, speak_errors)

def on_start(message):
"""Indicate that the skill handler is starting."""
if handler_info:
# Indicate that the skill handler is starting if requested
msg_type = handler_info + '.start'
message.context["skill_id"] = self.skill_id
self.bus.emit(message.forward(msg_type, skill_data))
self._on_event_start(message, handler_info, skill_data)

def on_end(message):
"""Store settings and indicate that the skill handler has completed
"""
if self.settings != self._initial_settings:
try: # ovos-core
self.settings.store()
except: # mycroft-core
from mycroft.skills.settings import save_settings
save_settings(self.settings_write_path, self.settings)
self._initial_settings = dict(self.settings)
if handler_info:
msg_type = handler_info + '.complete'
message.context["skill_id"] = self.skill_id
self.bus.emit(message.forward(msg_type, skill_data))
self._on_event_end(message, handler_info, skill_data)

wrapper = create_wrapper(handler, self.skill_id,
on_start, on_end, on_error)
wrapper = create_wrapper(handler, self.skill_id, on_start, on_end,
on_error)
return self.events.add(name, wrapper, once)

def __handle_stop(self, _):
self.bus.emit(Message(self.skill_id + ".stop",
context={"skill_id": self.skill_id}))
super().__handle_stop(_)
def __handle_stop(self, message):
self.bus.emit(message.forward(self.skill_id + ".stop",
context={"skill_id": self.skill_id}))
super().__handle_stop(message)

# abort get_response gracefully
def _wait_response(self, is_cancel, validator, on_fail, num_retries):
Expand Down

0 comments on commit 07bb7f1

Please sign in to comment.