files: # Copyright 2017 Mycroft AI Inc.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  import imp
  import operator
  import sys
  import time
  import csv
  import inspect
  from inspect import getargspec
  from datetime import datetime, timedelta
  
  import abc
  import re
  from adapt.intent import Intent, IntentBuilder
  from os.path import join, abspath, dirname, basename, exists
  from threading import Event
  
  from mycroft.api import DeviceApi
  from mycroft.audio import wait_while_speaking
  from mycroft.client.enclosure.api import EnclosureAPI
  from mycroft.configuration import Configuration
  from mycroft.dialog import DialogLoader
  from mycroft.filesystem import FileSystemAccess
  from mycroft.messagebus.message import Message
  from mycroft.metrics import report_metric, report_timing, Stopwatch
  from mycroft.skills.settings import SkillSettings
  from mycroft.skills.skill_data import (load_vocabulary, load_regex, to_letters,
                                         munge_regex, munge_intent_parser)
  from mycroft.util import resolve_resource_file
  from mycroft.util.log import LOG
  # python 2+3 compatibility
  from past.builtins import basestring
  
  MainModule = '__init__'
  
  
  def dig_for_message():
      """
          Dig Through the stack for message.
      """
      stack = inspect.stack()
      # Limit search to 10 frames back
      stack = stack if len(stack) < 10 else stack[:10]
      local_vars = [frame[0].f_locals for frame in stack]
      for l in local_vars:
          if 'message' in l and isinstance(l['message'], Message):
              return l['message']
  
  
  def unmunge_message(message, skill_id):
      """Restore message keywords by removing the Letterified skill ID.
  
      Args:
          message (Message): Intent result message
          skill_id (int): skill identifier
  
      Returns:
          Message without clear keywords
      """
      if isinstance(message, Message) and isinstance(message.data, dict):
          skill_id = to_letters(skill_id)
          for key in message.data:
              if key[:len(skill_id)] == skill_id:
                  new_key = key[len(skill_id):]
                  message.data[new_key] = message.data.pop(key)
  
      return message
  
  
  def open_intent_envelope(message):
      """ Convert dictionary received over messagebus to Intent. """
      intent_dict = message.data
      return Intent(intent_dict.get('name'),
                    intent_dict.get('requires'),
                    intent_dict.get('at_least_one'),
                    intent_dict.get('optional'))
  
  
  def load_skill(skill_descriptor, emitter, skill_id, BLACKLISTED_SKILLS=None):
      """
          load skill from skill descriptor.
  
          Args:
              skill_descriptor: descriptor of skill to load
              emitter:          messagebus emitter
              skill_id:         id number for skill
          Returns:
              MycroftSkill: the loaded skill or None on failure
      """
      BLACKLISTED_SKILLS = BLACKLISTED_SKILLS or []
      try:
          LOG.info("ATTEMPTING TO LOAD SKILL: " + skill_descriptor["name"] +
                   " with ID " + str(skill_id))
          if skill_descriptor['name'] in BLACKLISTED_SKILLS:
              LOG.info("SKILL IS BLACKLISTED " + skill_descriptor["name"])
              return None
          skill_module = imp.load_module(
              skill_descriptor["name"] + MainModule, *skill_descriptor["info"])
          if (hasattr(skill_module, 'create_skill') and
                  callable(skill_module.create_skill)):
              # v2 skills framework
              skill = skill_module.create_skill()
              skill.settings.allow_overwrite = True
              skill.settings.load_skill_settings_from_file()
              skill.bind(emitter)
              skill.skill_id = skill_id
              skill.load_data_files(dirname(skill_descriptor['info'][1]))
              # Set up intent handlers
              skill.initialize()
              skill._register_decorated()
              LOG.info("Loaded " + skill_descriptor["name"])
  
              # The very first time a skill is run, speak the intro
              first_run = skill.settings.get("__mycroft_skill_firstrun", True)
              if first_run:
                  LOG.info("First run of " + skill_descriptor["name"])
                  skill.settings["__mycroft_skill_firstrun"] = False
                  skill.settings.store()
                  intro = skill.get_intro_message()
                  if intro:
                      skill.speak(intro)
              return skill
          else:
              LOG.warning(
                  "Module %s does not appear to be skill" % (
                      skill_descriptor["name"]))
      except:
          LOG.error(
              "Failed to load skill: " + skill_descriptor["name"],
              exc_info=True)
      return None
  
  
  def create_skill_descriptor(skill_folder):
      info = imp.find_module(MainModule, [skill_folder])
      return {"name": basename(skill_folder), "info": info}
  
  
  def get_handler_name(handler):
      """
          Return name (including class if available) of handler
          function.
  
          Args:
              handler (function): Function to be named
  
          Returns: handler name as string
      """
      name = ''
      if '__self__' in dir(handler) and 'name' in dir(handler.__self__):
          name += handler.__self__.name + '.'
      name += handler.__name__
      return name
  
  
  def intent_handler(intent_parser):
      """ Decorator for adding a method as an intent handler. """
  
      def real_decorator(func):
          # Store the intent_parser inside the function
          # This will be used later to call register_intent
          if not hasattr(func, 'intents'):
              func.intents = []
          func.intents.append(intent_parser)
          return func
  
      return real_decorator
  
  
  def intent_file_handler(intent_file):
      """ Decorator for adding a method as an intent file handler. """
  
      def real_decorator(func):
          # Store the intent_file inside the function
          # This will be used later to call register_intent_file
          if not hasattr(func, 'intent_files'):
              func.intent_files = []
          func.intent_files.append(intent_file)
          return func
  
      return real_decorator
  
  
  #######################################################################
  # MycroftSkill base class
  #######################################################################
  class MycroftSkill(object):
      """
      Abstract base class which provides common behaviour and parameters to all
      Skills implementation.
      """
  
      def __init__(self, name=None, emitter=None):
          self.name = name or self.__class__.__name__
          # Get directory of skill
          self._dir = dirname(abspath(sys.modules[self.__module__].__file__))
  
          self.bind(emitter)
          self.config_core = Configuration.get()
          self.config = self.config_core.get(self.name)
          self.dialog_renderer = None
          self.vocab_dir = None
          self.root_dir = None
          self.file_system = FileSystemAccess(join('skills', self.name))
          self.registered_intents = []
          self.log = LOG.create_logger(self.name)
          self.reload_skill = True  # allow reloading
          self.events = []
          self.skill_id = 0
  
      @property
      def location(self):
          """ Get the JSON data struction holding location information. """
          # TODO: Allow Enclosure to override this for devices that
          # contain a GPS.
          return self.config_core.get('location')
  
      @property
      def location_pretty(self):
          """ Get a more 'human' version of the location as a string. """
          loc = self.location
          if type(loc) is dict and loc["city"]:
              return loc["city"]["name"]
          return None
  
      @property
      def location_timezone(self):
          """ Get the timezone code, such as 'America/Los_Angeles' """
          loc = self.location
          if type(loc) is dict and loc["timezone"]:
              return loc["timezone"]["code"]
          return None
  
      @property
      def lang(self):
          return self.config_core.get('lang')
  
      @property
      def settings(self):
          """ Load settings if not already loaded. """
          try:
              return self._settings
          except:
              self._settings = SkillSettings(self._dir, self.name)
              return self._settings
  
      def bind(self, emitter):
          """ Register emitter with skill. """
          if emitter:
              self.emitter = emitter
              self.enclosure = EnclosureAPI(emitter, self.name)
              self.__register_stop()
  
      def __register_stop(self):
          self.stop_time = time.time()
          self.stop_threshold = self.config_core.get("skills").get(
              'stop_threshold')
          self.add_event('mycroft.stop', self.__handle_stop, False)
  
      def detach(self):
          for (name, intent) in self.registered_intents:
              name = str(self.skill_id) + ':' + name
              self.emitter.emit(Message("detach_intent", {"intent_name": name}))
  
      def initialize(self):
          """
          Invoked after the skill is fully constructed and registered with the
          system.  Use to perform any final setup needed for the skill.
          """
          pass
  
      def get_intro_message(self):
          """
          Get a message to speak on first load of the skill.  Useful
          for post-install setup instructions.
  
          Returns:
              str: message that will be spoken to the user
          """
          return None
  
      def converse(self, utterances, lang="en-us"):
          """
              Handle conversation. This method can be used to override the normal
              intent handler after the skill has been invoked once.
  
              To enable this override thise converse method and return True to
              indicate that the utterance has been handled.
  
              Args:
                  utterances (list): The utterances from the user
                  lang:       language the utterance is in
  
              Returns:    True if an utterance was handled, otherwise False
          """
          return False
  
      def __get_response(self):
          """
          Helper to get a reponse from the user
  
          Returns:
              str: user's response or None on a timeout
          """
          event = Event()
  
          def converse(utterances, lang="en-us"):
              converse.response = utterances[0] if utterances else None
              event.set()
              return True
  
          # install a temporary conversation handler
          self.make_active()
          converse.response = None
          default_converse = self.converse
          self.converse = converse
          event.wait(15)  # 10 for listener, 5 for SST, then timeout
          self.converse = default_converse
          return converse.response
  
      def get_response(self, dialog='', data=None, announcement='',
                       validator=None, on_fail=None, num_retries=-1):
          """
          Prompt user and wait for response
  
          The given dialog or announcement will be spoken, the immediately
          listen and return user response.  The response can optionally be
          validated.
  
          Example:
              color = self.get_response('ask.favorite.color')
  
          Args:
              dialog (str): Announcement dialog to read to the user
              data (dict): Data used to render the dialog
              announcement (str): Literal string (overrides dialog)
              validator (any): Function with following signature
                  def validator(utterance):
                      return utterance != "red"
              on_fail (any): Dialog or function returning literal string
                             to speak on invalid input.  For example:
                  def on_fail(utterance):
                      return "nobody likes the color red, pick another"
              num_retries (int): Times to ask user for input, -1 for infinite
                  NOTE: User can not respond and timeout or say "cancel" to stop
  
          Returns:
              str: User's reply or None if timed out or canceled
          """
          data = data or {}
  
          def get_announcement():
              return announcement or self.dialog_renderer.render(dialog, data)
  
          if not get_announcement():
              raise ValueError('announcement or dialog message required')
  
          def on_fail_default(utterance):
              fail_data = data.copy()
              fail_data['utterance'] = utterance
              if on_fail:
                  return self.dialog_renderer.render(on_fail, fail_data)
              else:
                  return get_announcement()
  
          # TODO: Load with something like mycroft.dialog.get_all()
          cancel_voc = 'text/' + self.lang + '/cancel.voc'
          with open(resolve_resource_file(cancel_voc)) as f:
              cancel_words = list(filter(bool, f.read().split('\n')))
  
          def is_cancel(utterance):
              return utterance in cancel_words
  
          def validator_default(utterance):
              # accept anything except 'cancel'
              return not is_cancel(utterance)
  
          validator = validator or validator_default
          on_fail_fn = on_fail if callable(on_fail) else on_fail_default
  
          self.speak(get_announcement(), expect_response=True)
          wait_while_speaking()
          num_fails = 0
          while True:
              response = self.__get_response()
  
              if response is None:
                  # if nothing said, prompt one more time
                  num_none_fails = 1 if num_retries < 0 else num_retries
                  if num_fails >= num_none_fails:
                      return None
              else:
                  if validator(response):
                      return response
  
                  # catch user saying 'cancel'
                  if is_cancel(response):
                      return None
  
              num_fails += 1
              if 0 < num_retries < num_fails:
                  return None
  
              line = on_fail_fn(response)
              self.speak(line, expect_response=True)
  
      def report_metric(self, name, data):
          """
          Report a skill metric to the Mycroft servers
  
          Args:
              name (str): Name of metric. Must use only letters and hyphens
              data (dict): JSON dictionary to report. Must be valid JSON
          """
          report_metric(basename(self.root_dir) + ':' + name, data)
  
      def send_email(self, title, body):
          """
          Send an email to the registered user's email
  
          Args:
              title (str): Title of email
              body  (str): HTML body of email. This supports
                           simple HTML like bold and italics
          """
          DeviceApi().send_email(title, body, basename(self.root_dir))
  
      def make_active(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
          """
          self.emitter.emit(Message('active_skill_request',
                                    {"skill_id": self.skill_id}))
  
      def _register_decorated(self):
          """
          Register all intent handlers that have been decorated with an intent.
  
          Looks for all functions that have been marked by a decorator
          and read the intent data from them
          """
          for attr_name in dir(self):
              method = getattr(self, attr_name)
  
              if hasattr(method, 'intents'):
                  for intent in getattr(method, 'intents'):
                      self.register_intent(intent, method)
  
              if hasattr(method, 'intent_files'):
                  for intent_file in getattr(method, 'intent_files'):
                      self.register_intent_file(intent_file, method)
  
      def translate(self, text, data=None):
          """
          Load a translatable single string resource
  
          The string is loaded from a file in the skill's dialog subdirectory
            'dialog/<lang>/<text>.dialog'
          The string is randomly chosen from the file and rendered, replacing
          mustache placeholders with values found in the data dictionary.
  
          Args:
              text (str): The base filename  (no extension needed)
              data (dict, optional): a JSON dictionary
  
          Returns:
              str: A randomly chosen string from the file
          """
          return self.dialog_renderer.render(text, data or {})
  
      def translate_namedvalues(self, name, delim=None):
          """
          Load translation dict containing names and values.
  
          This loads a simple CSV from the 'dialog' folders.
          The name is the first list item, the value is the
          second.  Lines prefixed with # or // get ignored
  
          Args:
              name (str): name of the .value file, no extension needed
              delim (char): delimiter character used, default is ','
  
          Returns:
              dict: name and value dictionary, or [] if load fails
          """
  
          delim = delim or ','
          result = {}
          if not name.endswith(".value"):
              name += ".value"
  
          try:
              with open(join(self.root_dir, 'dialog', self.lang, name)) as f:
                  reader = csv.reader(f, delimiter=delim)
                  for row in reader:
                      # skip blank or comment lines
                      if not row or row[0].startswith("#"):
                          continue
                      if len(row) != 2:
                          continue
  
                      result[row[0]] = row[1]
  
              return result
          except Exception:
              return {}
  
      def translate_template(self, template_name, data=None):
          """
          Load a translatable template
  
          The strings are loaded from a template file in the skill's dialog
          subdirectory.
            'dialog/<lang>/<template_name>.template'
          The strings are loaded and rendered, replacing mustache placeholders
          with values found in the data dictionary.
  
          Args:
              template_name (str): The base filename (no extension needed)
              data (dict, optional): a JSON dictionary
  
          Returns:
              list of str: The loaded template file
  
  
  [... the rest is omitted ...]
files: mycroft/skills/core.py
files: # Copyright 2017 Mycroft AI Inc.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  import imp
  import operator
  import sys
  import time
  import csv
  import inspect
  from functools import wraps
  from inspect import getargspec
  from datetime import datetime, timedelta
  
  import abc
  import re
  from adapt.intent import Intent, IntentBuilder
  from os.path import join, abspath, dirname, basename, exists
  from threading import Event
  
  from mycroft.api import DeviceApi
  from mycroft.audio import wait_while_speaking
  from mycroft.client.enclosure.api import EnclosureAPI
  from mycroft.configuration import Configuration
  from mycroft.dialog import DialogLoader
  from mycroft.filesystem import FileSystemAccess
  from mycroft.messagebus.message import Message
  from mycroft.metrics import report_metric, report_timing, Stopwatch
  from mycroft.skills.settings import SkillSettings
  from mycroft.skills.skill_data import (load_vocabulary, load_regex, to_letters,
                                         munge_regex, munge_intent_parser)
  from mycroft.util import resolve_resource_file
  from mycroft.util.log import LOG
  # python 2+3 compatibility
  from past.builtins import basestring
  
  MainModule = '__init__'
  
  
  def dig_for_message():
      """
          Dig Through the stack for message.
      """
      stack = inspect.stack()
      # Limit search to 10 frames back
      stack = stack if len(stack) < 10 else stack[:10]
      local_vars = [frame[0].f_locals for frame in stack]
      for l in local_vars:
          if 'message' in l and isinstance(l['message'], Message):
              return l['message']
  
  
  def unmunge_message(message, skill_id):
      """Restore message keywords by removing the Letterified skill ID.
  
      Args:
          message (Message): Intent result message
          skill_id (int): skill identifier
  
      Returns:
          Message without clear keywords
      """
      if isinstance(message, Message) and isinstance(message.data, dict):
          skill_id = to_letters(skill_id)
          for key in message.data:
              if key[:len(skill_id)] == skill_id:
                  new_key = key[len(skill_id):]
                  message.data[new_key] = message.data.pop(key)
  
      return message
  
  
  def open_intent_envelope(message):
      """ Convert dictionary received over messagebus to Intent. """
      intent_dict = message.data
      return Intent(intent_dict.get('name'),
                    intent_dict.get('requires'),
                    intent_dict.get('at_least_one'),
                    intent_dict.get('optional'))
  
  
  def load_skill(skill_descriptor, emitter, skill_id, BLACKLISTED_SKILLS=None):
      """
          load skill from skill descriptor.
  
          Args:
              skill_descriptor: descriptor of skill to load
              emitter:          messagebus emitter
              skill_id:         id number for skill
          Returns:
              MycroftSkill: the loaded skill or None on failure
      """
      BLACKLISTED_SKILLS = BLACKLISTED_SKILLS or []
      try:
          LOG.info("ATTEMPTING TO LOAD SKILL: " + skill_descriptor["name"] +
                   " with ID " + str(skill_id))
          if skill_descriptor['name'] in BLACKLISTED_SKILLS:
              LOG.info("SKILL IS BLACKLISTED " + skill_descriptor["name"])
              return None
          skill_module = imp.load_module(
              skill_descriptor["name"] + MainModule, *skill_descriptor["info"])
          if (hasattr(skill_module, 'create_skill') and
                  callable(skill_module.create_skill)):
              # v2 skills framework
              skill = skill_module.create_skill()
              skill.settings.allow_overwrite = True
              skill.settings.load_skill_settings_from_file()
              skill.bind(emitter)
              skill.skill_id = skill_id
              skill.load_data_files(dirname(skill_descriptor['info'][1]))
              # Set up intent handlers
              skill.initialize()
              skill._register_decorated()
              LOG.info("Loaded " + skill_descriptor["name"])
  
              # The very first time a skill is run, speak the intro
              first_run = skill.settings.get("__mycroft_skill_firstrun", True)
              if first_run:
                  LOG.info("First run of "+skill_descriptor["name"])
                  skill.settings["__mycroft_skill_firstrun"] = False
                  skill.settings.store()
                  intro = skill.get_intro_message()
                  if intro:
                      skill.speak(intro)
              return skill
          else:
              LOG.warning(
                  "Module %s does not appear to be skill" % (
                      skill_descriptor["name"]))
      except:
          LOG.error(
              "Failed to load skill: " + skill_descriptor["name"],
              exc_info=True)
      return None
  
  
  def create_skill_descriptor(skill_folder):
      info = imp.find_module(MainModule, [skill_folder])
      return {"name": basename(skill_folder), "info": info}
  
  
  def get_handler_name(handler):
      """
          Return name (including class if available) of handler
          function.
  
          Args:
              handler (function): Function to be named
  
          Returns: handler name as string
      """
      name = ''
      if '__self__' in dir(handler) and 'name' in dir(handler.__self__):
          name += handler.__self__.name + '.'
      name += handler.__name__
      return name
  
  
  # Lists used when adding skill handlers using decorators
  _intent_list = []
  _intent_file_list = []
  
  
  def intent_handler(intent_parser):
      """ Decorator for adding a method as an intent handler. """
  
      def real_decorator(func):
          @wraps(func)
          def handler_method(*args, **kwargs):
              return func(*args, **kwargs)
  
          _intent_list.append((intent_parser, func))
          return handler_method
  
      return real_decorator
  
  
  def intent_file_handler(intent_file):
      """ Decorator for adding a method as an intent file handler. """
  
      def real_decorator(func):
          @wraps(func)
          def handler_method(*args, **kwargs):
              return func(*args, **kwargs)
  
          _intent_file_list.append((intent_file, func))
          return handler_method
  
      return real_decorator
  
  
  #######################################################################
  # MycroftSkill base class
  #######################################################################
  class MycroftSkill(object):
      """
      Abstract base class which provides common behaviour and parameters to all
      Skills implementation.
      """
  
      def __init__(self, name=None, emitter=None):
          self.name = name or self.__class__.__name__
          # Get directory of skill
          self._dir = dirname(abspath(sys.modules[self.__module__].__file__))
  
          self.bind(emitter)
          self.config_core = Configuration.get()
          self.config = self.config_core.get(self.name)
          self.dialog_renderer = None
          self.vocab_dir = None
          self.root_dir = None
          self.file_system = FileSystemAccess(join('skills', self.name))
          self.registered_intents = []
          self.log = LOG.create_logger(self.name)
          self.reload_skill = True  # allow reloading
          self.events = []
          self.skill_id = 0
  
      @property
      def location(self):
          """ Get the JSON data struction holding location information. """
          # TODO: Allow Enclosure to override this for devices that
          # contain a GPS.
          return self.config_core.get('location')
  
      @property
      def location_pretty(self):
          """ Get a more 'human' version of the location as a string. """
          loc = self.location
          if type(loc) is dict and loc["city"]:
              return loc["city"]["name"]
          return None
  
      @property
      def location_timezone(self):
          """ Get the timezone code, such as 'America/Los_Angeles' """
          loc = self.location
          if type(loc) is dict and loc["timezone"]:
              return loc["timezone"]["code"]
          return None
  
      @property
      def lang(self):
          return self.config_core.get('lang')
  
      @property
      def settings(self):
          """ Load settings if not already loaded. """
          try:
              return self._settings
          except:
              self._settings = SkillSettings(self._dir, self.name)
              return self._settings
  
      def bind(self, emitter):
          """ Register emitter with skill. """
          if emitter:
              self.emitter = emitter
              self.enclosure = EnclosureAPI(emitter, self.name)
              self.__register_stop()
  
      def __register_stop(self):
          self.stop_time = time.time()
          self.stop_threshold = self.config_core.get("skills").get(
              'stop_threshold')
          self.add_event('mycroft.stop', self.__handle_stop, False)
  
      def detach(self):
          for (name, intent) in self.registered_intents:
              name = str(self.skill_id) + ':' + name
              self.emitter.emit(Message("detach_intent", {"intent_name": name}))
  
      def initialize(self):
          """
          Invoked after the skill is fully constructed and registered with the
          system.  Use to perform any final setup needed for the skill.
          """
          pass
  
      def get_intro_message(self):
          """
          Get a message to speak on first load of the skill.  Useful
          for post-install setup instructions.
  
          Returns:
              str: message that will be spoken to the user
          """
          return None
  
      def converse(self, utterances, lang="en-us"):
          """
              Handle conversation. This method can be used to override the normal
              intent handler after the skill has been invoked once.
  
              To enable this override thise converse method and return True to
              indicate that the utterance has been handled.
  
              Args:
                  utterances (list): The utterances from the user
                  lang:       language the utterance is in
  
              Returns:    True if an utterance was handled, otherwise False
          """
          return False
  
      def __get_response(self):
          """
          Helper to get a reponse from the user
  
          Returns:
              str: user's response or None on a timeout
          """
          event = Event()
  
          def converse(utterances, lang="en-us"):
              converse.response = utterances[0] if utterances else None
              event.set()
              return True
  
          # install a temporary conversation handler
          self.make_active()
          converse.response = None
          default_converse = self.converse
          self.converse = converse
          event.wait(15)  # 10 for listener, 5 for SST, then timeout
          self.converse = default_converse
          return converse.response
  
      def get_response(self, dialog='', data=None, announcement='',
                       validator=None, on_fail=None, num_retries=-1):
          """
          Prompt user and wait for response
  
          The given dialog or announcement will be spoken, the immediately
          listen and return user response.  The response can optionally be
          validated.
  
          Example:
              color = self.get_response('ask.favorite.color')
  
          Args:
              dialog (str): Announcement dialog to read to the user
              data (dict): Data used to render the dialog
              announcement (str): Literal string (overrides dialog)
              validator (any): Function with following signature
                  def validator(utterance):
                      return utterance != "red"
              on_fail (any): Dialog or function returning literal string
                             to speak on invalid input.  For example:
                  def on_fail(utterance):
                      return "nobody likes the color red, pick another"
              num_retries (int): Times to ask user for input, -1 for infinite
                  NOTE: User can not respond and timeout or say "cancel" to stop
  
          Returns:
              str: User's reply or None if timed out or canceled
          """
          data = data or {}
  
          def get_announcement():
              return announcement or self.dialog_renderer.render(dialog, data)
  
          if not get_announcement():
              raise ValueError('announcement or dialog message required')
  
          def on_fail_default(utterance):
              fail_data = data.copy()
              fail_data['utterance'] = utterance
              if on_fail:
                  return self.dialog_renderer.render(on_fail, fail_data)
              else:
                  return get_announcement()
  
          # TODO: Load with something like mycroft.dialog.get_all()
          cancel_voc = 'text/' + self.lang + '/cancel.voc'
          with open(resolve_resource_file(cancel_voc)) as f:
              cancel_words = list(filter(bool, f.read().split('\n')))
  
          def is_cancel(utterance):
              return utterance in cancel_words
  
          def validator_default(utterance):
              # accept anything except 'cancel'
              return not is_cancel(utterance)
  
          validator = validator or validator_default
          on_fail_fn = on_fail if callable(on_fail) else on_fail_default
  
          self.speak(get_announcement(), expect_response=True)
          wait_while_speaking()
          num_fails = 0
          while True:
              response = self.__get_response()
  
              if response is None:
                  # if nothing said, prompt one more time
                  num_none_fails = 1 if num_retries < 0 else num_retries
                  if num_fails >= num_none_fails:
                      return None
              else:
                  if validator(response):
                      return response
  
                  # catch user saying 'cancel'
                  if is_cancel(response):
                      return None
  
              num_fails += 1
              if 0 < num_retries < num_fails:
                  return None
  
              line = on_fail_fn(response)
              self.speak(line, expect_response=True)
  
      def report_metric(self, name, data):
          """
          Report a skill metric to the Mycroft servers
  
          Args:
              name (str): Name of metric. Must use only letters and hyphens
              data (dict): JSON dictionary to report. Must be valid JSON
          """
          report_metric(basename(self.root_dir) + ':' + name, data)
  
      def send_email(self, title, body):
          """
          Send an email to the registered user's email
  
          Args:
              title (str): Title of email
              body  (str): HTML body of email. This supports
                           simple HTML like bold and italics
          """
          DeviceApi().send_email(title, body, basename(self.root_dir))
  
      def make_active(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
          """
          self.emitter.emit(Message('active_skill_request',
                                    {"skill_id": self.skill_id}))
  
      def _register_decorated(self):
          """
          Register all intent handlers that have been decorated with an intent.
          """
          global _intent_list, _intent_file_list
          for intent_parser, handler in _intent_list:
              self.register_intent(intent_parser, handler, need_self=True)
          for intent_file, handler in _intent_file_list:
              self.register_intent_file(intent_file, handler, need_self=True)
          _intent_list = []
          _intent_file_list = []
  
      def translate(self, text, data=None):
          """
          Load a translatable single string resource
  
          The string is loaded from a file in the skill's dialog subdirectory
            'dialog/<lang>/<text>.dialog'
          The string is randomly chosen from the file and rendered, replacing
          mustache placeholders with values found in the data dictionary.
  
          Args:
              text (str): The base filename  (no extension needed)
              data (dict, optional): a JSON dictionary
  
          Returns:
              str: A randomly chosen string from the file
          """
          return self.dialog_renderer.render(text, data or {})
  
      def translate_namedvalues(self, name, delim=None):
          """
          Load translation dict containing names and values.
  
          This loads a simple CSV from the 'dialog' folders.
          The name is the first list item, the value is the
          second.  Lines prefixed with # or // get ignored
  
          Args:
              name (str): name of the .value file, no extension needed
              delim (char): delimiter character used, default is ','
  
          Returns:
              dict: name and value dictionary, or [] if load fails
          """
  
          delim = delim or ','
          result = {}
          if not name.endswith(".value"):
              name += ".value"
  
          try:
              with open(join(self.root_dir, 'dialog', self.lang, name)) as f:
                  reader = csv.reader(f, delimiter=delim)
                  for row in reader:
                      # skip blank or comment lines
                      if not row or row[0].startswith("#"):
                          continue
                      if len(row) != 2:
                          continue
  
                      result[row[0]] = row[1]
  
              return result
          except Exception:
              return {}
  
      def translate_template(self, template_name, data=None):
          """
          Load a translatable template
  
          The strings are loaded from a template file in the skill's dialog
          subdirectory.
            'dialog/<lang>/<template_name>.template'
          The strings are loaded and rendered, replacing mustache placeholders
          with values found in the data dictionary.
  
          Args:
              template_name (str): The base filename (no extension needed)
              data (dict, optional): a JSON dictionary
  
          Returns:
              list of str: The loaded template file
          """
          return self.__translate_file(template_name + '.template', data)
  
      def translate
  
  [... the rest is omitted ...]
files: mycroft/skills/core.py