We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Report errors while installing default skills
Installing default skills may fail quietly in some conditions, ideally some feedback to the user should be given.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mycroft/skills/main.py`
Content:
```
1 # Copyright 2016 Mycroft AI, Inc.
2 #
3 # This file is part of Mycroft Core.
4 #
5 # Mycroft Core is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Mycroft Core is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Mycroft Core.  If not, see <http://www.gnu.org/licenses/>.
17 
18 
19 import json
20 import os
21 import subprocess
22 import sys
23 import time
24 from os.path import exists, join
25 from threading import Timer
26 
27 from mycroft import MYCROFT_ROOT_PATH
28 from mycroft.configuration import ConfigurationManager
29 from mycroft.lock import Lock  # Creates PID file for single instance
30 from mycroft.messagebus.client.ws import WebsocketClient
31 from mycroft.messagebus.message import Message
32 from mycroft.skills.core import load_skill, create_skill_descriptor, \
33     MainModule, SKILLS_DIR
34 from mycroft.skills.intent_service import IntentService
35 from mycroft.util import connected
36 from mycroft.util.log import getLogger
37 import mycroft.dialog
38 
39 logger = getLogger("Skills")
40 
41 __author__ = 'seanfitz'
42 
43 ws = None
44 loaded_skills = {}
45 last_modified_skill = 0
46 skills_directories = []
47 skill_reload_thread = None
48 skills_manager_timer = None
49 
50 installer_config = ConfigurationManager.instance().get("SkillInstallerSkill")
51 MSM_BIN = installer_config.get("path", join(MYCROFT_ROOT_PATH, 'msm', 'msm'))
52 
53 
54 def connect():
55     global ws
56     ws.run_forever()
57 
58 
59 def install_default_skills(speak=True):
60     if exists(MSM_BIN):
61         p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT,
62                              stdout=subprocess.PIPE, shell=True)
63         t = p.communicate()[0]
64         if t.splitlines()[-1] == "Installed!" and speak:
65             ws.emit(Message("speak", {
66                 'utterance': mycroft.dialog.get("skills updated")}))
67         elif not connected():
68             ws.emit(Message("speak", {
69                 'utterance': mycroft.dialog.get("no network connection")}))
70 
71     else:
72         logger.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
73 
74 
75 def skills_manager(message):
76     global skills_manager_timer, ws
77 
78     if connected():
79         if skills_manager_timer is None:
80             ws.emit(
81                 Message("speak", {'utterance':
82                         mycroft.dialog.get("checking for updates")}))
83 
84         # Install default skills and look for updates via Github
85         logger.debug("==== Invoking Mycroft Skill Manager: " + MSM_BIN)
86         install_default_skills(False)
87 
88     # Perform check again once and hour
89     skills_manager_timer = Timer(3600, _skills_manager_dispatch)
90     skills_manager_timer.daemon = True
91     skills_manager_timer.start()
92 
93 
94 def _skills_manager_dispatch():
95     ws.emit(Message("skill_manager", {}))
96 
97 
98 def _load_skills():
99     global ws, loaded_skills, last_modified_skill, skills_directories, \
100         skill_reload_thread
101 
102     check_connection()
103 
104     # Create skill_manager listener and invoke the first time
105     ws.on('skill_manager', skills_manager)
106     ws.on('mycroft.internet.connected', install_default_skills)
107     ws.emit(Message('skill_manager', {}))
108 
109     # Create the Intent manager, which converts utterances to intents
110     # This is the heart of the voice invoked skill system
111     IntentService(ws)
112 
113     # Create a thread that monitors the loaded skills, looking for updates
114     skill_reload_thread = Timer(0, _watch_skills)
115     skill_reload_thread.daemon = True
116     skill_reload_thread.start()
117 
118 
119 def check_connection():
120     if connected():
121         ws.emit(Message('mycroft.internet.connected'))
122     else:
123         thread = Timer(1, check_connection)
124         thread.daemon = True
125         thread.start()
126 
127 
128 def _get_last_modified_date(path):
129     last_date = 0
130     # getting all recursive paths
131     for root, _, _ in os.walk(path):
132         f = root.replace(path, "")
133         # checking if is a hidden path
134         if not f.startswith(".") and not f.startswith("/."):
135             last_date = max(last_date, os.path.getmtime(path + f))
136 
137     return last_date
138 
139 
140 def _watch_skills():
141     global ws, loaded_skills, last_modified_skill, \
142         id_counter
143 
144     # Scan the file folder that contains Skills.  If a Skill is updated,
145     # unload the existing version from memory and reload from the disk.
146     while True:
147         if exists(SKILLS_DIR):
148             # checking skills dir and getting all skills there
149             list = filter(lambda x: os.path.isdir(
150                 os.path.join(SKILLS_DIR, x)), os.listdir(SKILLS_DIR))
151 
152             for skill_folder in list:
153                 if skill_folder not in loaded_skills:
154                     loaded_skills[skill_folder] = {}
155                 skill = loaded_skills.get(skill_folder)
156                 skill["path"] = os.path.join(SKILLS_DIR, skill_folder)
157                 # checking if is a skill
158                 if not MainModule + ".py" in os.listdir(skill["path"]):
159                     continue
160                 # getting the newest modified date of skill
161                 skill["last_modified"] = _get_last_modified_date(skill["path"])
162                 modified = skill.get("last_modified", 0)
163                 # checking if skill is loaded and wasn't modified
164                 if skill.get(
165                         "loaded") and modified <= last_modified_skill:
166                     continue
167                 # checking if skill was modified
168                 elif skill.get(
169                         "instance") and modified > last_modified_skill:
170                     # checking if skill should be reloaded
171                     if not skill["instance"].reload_skill:
172                         continue
173                     logger.debug("Reloading Skill: " + skill_folder)
174                     # removing listeners and stopping threads
175                     skill["instance"].shutdown()
176                     del skill["instance"]
177                 skill["loaded"] = True
178                 skill["instance"] = load_skill(
179                     create_skill_descriptor(skill["path"]), ws)
180         # get the last modified skill
181         modified_dates = map(lambda x: x.get("last_modified"),
182                              loaded_skills.values())
183         if len(modified_dates) > 0:
184             last_modified_skill = max(modified_dates)
185 
186         # Pause briefly before beginning next scan
187         time.sleep(2)
188 
189 
190 def main():
191     global ws
192     lock = Lock('skills')  # prevent multiple instances of this service
193 
194     # Connect this Skill management process to the websocket
195     ws = WebsocketClient()
196     ConfigurationManager.init(ws)
197 
198     ignore_logs = ConfigurationManager.instance().get("ignore_logs")
199 
200     # Listen for messages and echo them for logging
201     def _echo(message):
202         try:
203             _message = json.loads(message)
204 
205             if _message.get("type") in ignore_logs:
206                 return
207 
208             if _message.get("type") == "registration":
209                 # do not log tokens from registration messages
210                 _message["data"]["token"] = None
211             message = json.dumps(_message)
212         except:
213             pass
214         logger.debug(message)
215 
216     ws.on('message', _echo)
217 
218     # Kick off loading of skills
219     ws.once('open', _load_skills)
220     ws.run_forever()
221 
222 
223 if __name__ == "__main__":
224     try:
225         main()
226     except KeyboardInterrupt:
227         skills_manager_timer.cancel()
228         for skill in loaded_skills:
229             skill.shutdown()
230         if skill_reload_thread:
231             skill_reload_thread.cancel()
232 
233     finally:
234         sys.exit()
235
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mycroft/skills/main.py b/mycroft/skills/main.py
--- a/mycroft/skills/main.py
+++ b/mycroft/skills/main.py
@@ -57,16 +57,25 @@
 
 
 def install_default_skills(speak=True):
+    """
+        Install default skill set using msm.
+
+        Args:
+            speak (optional): Enable response for success. Default True
+    """
     if exists(MSM_BIN):
-        p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT,
-                             stdout=subprocess.PIPE, shell=True)
-        t = p.communicate()[0]
-        if t.splitlines()[-1] == "Installed!" and speak:
+        res = subprocess.call(MSM_BIN + " default", stderr=subprocess.STDOUT,
+                              stdout=subprocess.PIPE, shell=True)
+        if res == 0 and speak:
             ws.emit(Message("speak", {
                 'utterance': mycroft.dialog.get("skills updated")}))
         elif not connected():
             ws.emit(Message("speak", {
                 'utterance': mycroft.dialog.get("no network connection")}))
+        elif res != 0:
+            ws.emit(Message("speak", {
+                'utterance': mycroft.dialog.get(
+                             "sorry I couldn't install default skills")}))
 
     else:
         logger.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)

{"golden_diff": "diff --git a/mycroft/skills/main.py b/mycroft/skills/main.py\n--- a/mycroft/skills/main.py\n+++ b/mycroft/skills/main.py\n@@ -57,16 +57,25 @@\n \n \n def install_default_skills(speak=True):\n+    \"\"\"\n+        Install default skill set using msm.\n+\n+        Args:\n+            speak (optional): Enable response for success. Default True\n+    \"\"\"\n     if exists(MSM_BIN):\n-        p = subprocess.Popen(MSM_BIN + \" default\", stderr=subprocess.STDOUT,\n-                             stdout=subprocess.PIPE, shell=True)\n-        t = p.communicate()[0]\n-        if t.splitlines()[-1] == \"Installed!\" and speak:\n+        res = subprocess.call(MSM_BIN + \" default\", stderr=subprocess.STDOUT,\n+                              stdout=subprocess.PIPE, shell=True)\n+        if res == 0 and speak:\n             ws.emit(Message(\"speak\", {\n                 'utterance': mycroft.dialog.get(\"skills updated\")}))\n         elif not connected():\n             ws.emit(Message(\"speak\", {\n                 'utterance': mycroft.dialog.get(\"no network connection\")}))\n+        elif res != 0:\n+            ws.emit(Message(\"speak\", {\n+                'utterance': mycroft.dialog.get(\n+                             \"sorry I couldn't install default skills\")}))\n \n     else:\n         logger.error(\"Unable to invoke Mycroft Skill Manager: \" + MSM_BIN)\n", "issue": "Report errors while installing default skills\nInstalling default skills may fail quietly in some conditions, ideally some feedback to the user should be given.\n", "before_files": [{"content": "# Copyright 2016 Mycroft AI, Inc.\n#\n# This file is part of Mycroft Core.\n#\n# Mycroft Core is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Mycroft Core is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Mycroft Core.  If not, see <http://www.gnu.org/licenses/>.\n\n\nimport json\nimport os\nimport subprocess\nimport sys\nimport time\nfrom os.path import exists, join\nfrom threading import Timer\n\nfrom mycroft import MYCROFT_ROOT_PATH\nfrom mycroft.configuration import ConfigurationManager\nfrom mycroft.lock import Lock  # Creates PID file for single instance\nfrom mycroft.messagebus.client.ws import WebsocketClient\nfrom mycroft.messagebus.message import Message\nfrom mycroft.skills.core import load_skill, create_skill_descriptor, \\\n    MainModule, SKILLS_DIR\nfrom mycroft.skills.intent_service import IntentService\nfrom mycroft.util import connected\nfrom mycroft.util.log import getLogger\nimport mycroft.dialog\n\nlogger = getLogger(\"Skills\")\n\n__author__ = 'seanfitz'\n\nws = None\nloaded_skills = {}\nlast_modified_skill = 0\nskills_directories = []\nskill_reload_thread = None\nskills_manager_timer = None\n\ninstaller_config = ConfigurationManager.instance().get(\"SkillInstallerSkill\")\nMSM_BIN = installer_config.get(\"path\", join(MYCROFT_ROOT_PATH, 'msm', 'msm'))\n\n\ndef connect():\n    global ws\n    ws.run_forever()\n\n\ndef install_default_skills(speak=True):\n    if exists(MSM_BIN):\n        p = subprocess.Popen(MSM_BIN + \" default\", stderr=subprocess.STDOUT,\n                             stdout=subprocess.PIPE, shell=True)\n        t = p.communicate()[0]\n        if t.splitlines()[-1] == \"Installed!\" and speak:\n            ws.emit(Message(\"speak\", {\n                'utterance': mycroft.dialog.get(\"skills updated\")}))\n        elif not connected():\n            ws.emit(Message(\"speak\", {\n                'utterance': mycroft.dialog.get(\"no network connection\")}))\n\n    else:\n        logger.error(\"Unable to invoke Mycroft Skill Manager: \" + MSM_BIN)\n\n\ndef skills_manager(message):\n    global skills_manager_timer, ws\n\n    if connected():\n        if skills_manager_timer is None:\n            ws.emit(\n                Message(\"speak\", {'utterance':\n                        mycroft.dialog.get(\"checking for updates\")}))\n\n        # Install default skills and look for updates via Github\n        logger.debug(\"==== Invoking Mycroft Skill Manager: \" + MSM_BIN)\n        install_default_skills(False)\n\n    # Perform check again once and hour\n    skills_manager_timer = Timer(3600, _skills_manager_dispatch)\n    skills_manager_timer.daemon = True\n    skills_manager_timer.start()\n\n\ndef _skills_manager_dispatch():\n    ws.emit(Message(\"skill_manager\", {}))\n\n\ndef _load_skills():\n    global ws, loaded_skills, last_modified_skill, skills_directories, \\\n        skill_reload_thread\n\n    check_connection()\n\n    # Create skill_manager listener and invoke the first time\n    ws.on('skill_manager', skills_manager)\n    ws.on('mycroft.internet.connected', install_default_skills)\n    ws.emit(Message('skill_manager', {}))\n\n    # Create the Intent manager, which converts utterances to intents\n    # This is the heart of the voice invoked skill system\n    IntentService(ws)\n\n    # Create a thread that monitors the loaded skills, looking for updates\n    skill_reload_thread = Timer(0, _watch_skills)\n    skill_reload_thread.daemon = True\n    skill_reload_thread.start()\n\n\ndef check_connection():\n    if connected():\n        ws.emit(Message('mycroft.internet.connected'))\n    else:\n        thread = Timer(1, check_connection)\n        thread.daemon = True\n        thread.start()\n\n\ndef _get_last_modified_date(path):\n    last_date = 0\n    # getting all recursive paths\n    for root, _, _ in os.walk(path):\n        f = root.replace(path, \"\")\n        # checking if is a hidden path\n        if not f.startswith(\".\") and not f.startswith(\"/.\"):\n            last_date = max(last_date, os.path.getmtime(path + f))\n\n    return last_date\n\n\ndef _watch_skills():\n    global ws, loaded_skills, last_modified_skill, \\\n        id_counter\n\n    # Scan the file folder that contains Skills.  If a Skill is updated,\n    # unload the existing version from memory and reload from the disk.\n    while True:\n        if exists(SKILLS_DIR):\n            # checking skills dir and getting all skills there\n            list = filter(lambda x: os.path.isdir(\n                os.path.join(SKILLS_DIR, x)), os.listdir(SKILLS_DIR))\n\n            for skill_folder in list:\n                if skill_folder not in loaded_skills:\n                    loaded_skills[skill_folder] = {}\n                skill = loaded_skills.get(skill_folder)\n                skill[\"path\"] = os.path.join(SKILLS_DIR, skill_folder)\n                # checking if is a skill\n                if not MainModule + \".py\" in os.listdir(skill[\"path\"]):\n                    continue\n                # getting the newest modified date of skill\n                skill[\"last_modified\"] = _get_last_modified_date(skill[\"path\"])\n                modified = skill.get(\"last_modified\", 0)\n                # checking if skill is loaded and wasn't modified\n                if skill.get(\n                        \"loaded\") and modified <= last_modified_skill:\n                    continue\n                # checking if skill was modified\n                elif skill.get(\n                        \"instance\") and modified > last_modified_skill:\n                    # checking if skill should be reloaded\n                    if not skill[\"instance\"].reload_skill:\n                        continue\n                    logger.debug(\"Reloading Skill: \" + skill_folder)\n                    # removing listeners and stopping threads\n                    skill[\"instance\"].shutdown()\n                    del skill[\"instance\"]\n                skill[\"loaded\"] = True\n                skill[\"instance\"] = load_skill(\n                    create_skill_descriptor(skill[\"path\"]), ws)\n        # get the last modified skill\n        modified_dates = map(lambda x: x.get(\"last_modified\"),\n                             loaded_skills.values())\n        if len(modified_dates) > 0:\n            last_modified_skill = max(modified_dates)\n\n        # Pause briefly before beginning next scan\n        time.sleep(2)\n\n\ndef main():\n    global ws\n    lock = Lock('skills')  # prevent multiple instances of this service\n\n    # Connect this Skill management process to the websocket\n    ws = WebsocketClient()\n    ConfigurationManager.init(ws)\n\n    ignore_logs = ConfigurationManager.instance().get(\"ignore_logs\")\n\n    # Listen for messages and echo them for logging\n    def _echo(message):\n        try:\n            _message = json.loads(message)\n\n            if _message.get(\"type\") in ignore_logs:\n                return\n\n            if _message.get(\"type\") == \"registration\":\n                # do not log tokens from registration messages\n                _message[\"data\"][\"token\"] = None\n            message = json.dumps(_message)\n        except:\n            pass\n        logger.debug(message)\n\n    ws.on('message', _echo)\n\n    # Kick off loading of skills\n    ws.once('open', _load_skills)\n    ws.run_forever()\n\n\nif __name__ == \"__main__\":\n    try:\n        main()\n    except KeyboardInterrupt:\n        skills_manager_timer.cancel()\n        for skill in loaded_skills:\n            skill.shutdown()\n        if skill_reload_thread:\n            skill_reload_thread.cancel()\n\n    finally:\n        sys.exit()\n", "path": "mycroft/skills/main.py"}], "after_files": [{"content": "# Copyright 2016 Mycroft AI, Inc.\n#\n# This file is part of Mycroft Core.\n#\n# Mycroft Core is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Mycroft Core is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Mycroft Core.  If not, see <http://www.gnu.org/licenses/>.\n\n\nimport json\nimport os\nimport subprocess\nimport sys\nimport time\nfrom os.path import exists, join\nfrom threading import Timer\n\nfrom mycroft import MYCROFT_ROOT_PATH\nfrom mycroft.configuration import ConfigurationManager\nfrom mycroft.lock import Lock  # Creates PID file for single instance\nfrom mycroft.messagebus.client.ws import WebsocketClient\nfrom mycroft.messagebus.message import Message\nfrom mycroft.skills.core import load_skill, create_skill_descriptor, \\\n    MainModule, SKILLS_DIR\nfrom mycroft.skills.intent_service import IntentService\nfrom mycroft.util import connected\nfrom mycroft.util.log import getLogger\nimport mycroft.dialog\n\nlogger = getLogger(\"Skills\")\n\n__author__ = 'seanfitz'\n\nws = None\nloaded_skills = {}\nlast_modified_skill = 0\nskills_directories = []\nskill_reload_thread = None\nskills_manager_timer = None\n\ninstaller_config = ConfigurationManager.instance().get(\"SkillInstallerSkill\")\nMSM_BIN = installer_config.get(\"path\", join(MYCROFT_ROOT_PATH, 'msm', 'msm'))\n\n\ndef connect():\n    global ws\n    ws.run_forever()\n\n\ndef install_default_skills(speak=True):\n    \"\"\"\n        Install default skill set using msm.\n\n        Args:\n            speak (optional): Enable response for success. Default True\n    \"\"\"\n    if exists(MSM_BIN):\n        res = subprocess.call(MSM_BIN + \" default\", stderr=subprocess.STDOUT,\n                              stdout=subprocess.PIPE, shell=True)\n        if res == 0 and speak:\n            ws.emit(Message(\"speak\", {\n                'utterance': mycroft.dialog.get(\"skills updated\")}))\n        elif not connected():\n            ws.emit(Message(\"speak\", {\n                'utterance': mycroft.dialog.get(\"no network connection\")}))\n        elif res != 0:\n            ws.emit(Message(\"speak\", {\n                'utterance': mycroft.dialog.get(\n                             \"sorry I couldn't install default skills\")}))\n\n    else:\n        logger.error(\"Unable to invoke Mycroft Skill Manager: \" + MSM_BIN)\n\n\ndef skills_manager(message):\n    global skills_manager_timer, ws\n\n    if connected():\n        if skills_manager_timer is None:\n            ws.emit(\n                Message(\"speak\", {'utterance':\n                        mycroft.dialog.get(\"checking for updates\")}))\n\n        # Install default skills and look for updates via Github\n        logger.debug(\"==== Invoking Mycroft Skill Manager: \" + MSM_BIN)\n        install_default_skills(False)\n\n    # Perform check again once and hour\n    skills_manager_timer = Timer(3600, _skills_manager_dispatch)\n    skills_manager_timer.daemon = True\n    skills_manager_timer.start()\n\n\ndef _skills_manager_dispatch():\n    ws.emit(Message(\"skill_manager\", {}))\n\n\ndef _load_skills():\n    global ws, loaded_skills, last_modified_skill, skills_directories, \\\n        skill_reload_thread\n\n    check_connection()\n\n    # Create skill_manager listener and invoke the first time\n    ws.on('skill_manager', skills_manager)\n    ws.on('mycroft.internet.connected', install_default_skills)\n    ws.emit(Message('skill_manager', {}))\n\n    # Create the Intent manager, which converts utterances to intents\n    # This is the heart of the voice invoked skill system\n    IntentService(ws)\n\n    # Create a thread that monitors the loaded skills, looking for updates\n    skill_reload_thread = Timer(0, _watch_skills)\n    skill_reload_thread.daemon = True\n    skill_reload_thread.start()\n\n\ndef check_connection():\n    if connected():\n        ws.emit(Message('mycroft.internet.connected'))\n    else:\n        thread = Timer(1, check_connection)\n        thread.daemon = True\n        thread.start()\n\n\ndef _get_last_modified_date(path):\n    last_date = 0\n    # getting all recursive paths\n    for root, _, _ in os.walk(path):\n        f = root.replace(path, \"\")\n        # checking if is a hidden path\n        if not f.startswith(\".\") and not f.startswith(\"/.\"):\n            last_date = max(last_date, os.path.getmtime(path + f))\n\n    return last_date\n\n\ndef _watch_skills():\n    global ws, loaded_skills, last_modified_skill, \\\n        id_counter\n\n    # Scan the file folder that contains Skills.  If a Skill is updated,\n    # unload the existing version from memory and reload from the disk.\n    while True:\n        if exists(SKILLS_DIR):\n            # checking skills dir and getting all skills there\n            list = filter(lambda x: os.path.isdir(\n                os.path.join(SKILLS_DIR, x)), os.listdir(SKILLS_DIR))\n\n            for skill_folder in list:\n                if skill_folder not in loaded_skills:\n                    loaded_skills[skill_folder] = {}\n                skill = loaded_skills.get(skill_folder)\n                skill[\"path\"] = os.path.join(SKILLS_DIR, skill_folder)\n                # checking if is a skill\n                if not MainModule + \".py\" in os.listdir(skill[\"path\"]):\n                    continue\n                # getting the newest modified date of skill\n                skill[\"last_modified\"] = _get_last_modified_date(skill[\"path\"])\n                modified = skill.get(\"last_modified\", 0)\n                # checking if skill is loaded and wasn't modified\n                if skill.get(\n                        \"loaded\") and modified <= last_modified_skill:\n                    continue\n                # checking if skill was modified\n                elif skill.get(\n                        \"instance\") and modified > last_modified_skill:\n                    # checking if skill should be reloaded\n                    if not skill[\"instance\"].reload_skill:\n                        continue\n                    logger.debug(\"Reloading Skill: \" + skill_folder)\n                    # removing listeners and stopping threads\n                    skill[\"instance\"].shutdown()\n                    del skill[\"instance\"]\n                skill[\"loaded\"] = True\n                skill[\"instance\"] = load_skill(\n                    create_skill_descriptor(skill[\"path\"]), ws)\n        # get the last modified skill\n        modified_dates = map(lambda x: x.get(\"last_modified\"),\n                             loaded_skills.values())\n        if len(modified_dates) > 0:\n            last_modified_skill = max(modified_dates)\n\n        # Pause briefly before beginning next scan\n        time.sleep(2)\n\n\ndef main():\n    global ws\n    lock = Lock('skills')  # prevent multiple instances of this service\n\n    # Connect this Skill management process to the websocket\n    ws = WebsocketClient()\n    ConfigurationManager.init(ws)\n\n    ignore_logs = ConfigurationManager.instance().get(\"ignore_logs\")\n\n    # Listen for messages and echo them for logging\n    def _echo(message):\n        try:\n            _message = json.loads(message)\n\n            if _message.get(\"type\") in ignore_logs:\n                return\n\n            if _message.get(\"type\") == \"registration\":\n                # do not log tokens from registration messages\n                _message[\"data\"][\"token\"] = None\n            message = json.dumps(_message)\n        except:\n            pass\n        logger.debug(message)\n\n    ws.on('message', _echo)\n\n    # Kick off loading of skills\n    ws.once('open', _load_skills)\n    ws.run_forever()\n\n\nif __name__ == \"__main__\":\n    try:\n        main()\n    except KeyboardInterrupt:\n        skills_manager_timer.cancel()\n        for skill in loaded_skills:\n            skill.shutdown()\n        if skill_reload_thread:\n            skill_reload_thread.cancel()\n\n    finally:\n        sys.exit()\n", "path": "mycroft/skills/main.py"}]}