mycroft-core/mycroft/skills/main.py

144 lines
4.5 KiB
Python

# Copyright 2016 Mycroft AI, Inc.
#
# This file is part of Mycroft Core.
#
# Mycroft Core is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Mycroft Core is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mycroft Core. If not, see <http://www.gnu.org/licenses/>.
import json
import sys
import time
from threading import Timer
import os
from os.path import expanduser, exists
from mycroft.configuration import ConfigurationManager
from mycroft.messagebus.client.ws import WebsocketClient
from mycroft.skills.core import load_skills, THIRD_PARTY_SKILLS_DIR, \
load_skill, create_skill_descriptor, MainModule
from mycroft.util.log import getLogger
logger = getLogger("Skills")
__author__ = 'seanfitz'
ws = None
loaded_skills = {}
last_modified_skill = 0
skills_directories = []
def connect():
global ws
ws.run_forever()
def load_watch_skills():
global ws, loaded_skills, last_modified_skill, skills_directories
skills_directories = [os.path.dirname(os.path.abspath(__file__))]
skills_directories = skills_directories + THIRD_PARTY_SKILLS_DIR
try:
config = ConfigurationManager.get().get("skills")
ini_third_party_skills_dir = expanduser(config.get("directory"))
if ini_third_party_skills_dir and exists(ini_third_party_skills_dir):
skills_directories.append(ini_third_party_skills_dir)
except AttributeError as e:
logger.warning(e.message)
timer = Timer(0, watch_skills)
timer.daemon = True
timer.start()
def clear_skill_events(instance):
global ws
events = ws.emitter._events
instance_events = []
for event in events:
e = ws.emitter._events[event]
if len(e) > 0 and e[0].func_closure and isinstance(
e[0].func_closure[1].cell_contents, instance.__class__):
instance_events.append(event)
for event in instance_events:
del events[event]
def watch_skills():
global ws, loaded_skills, last_modified_skill, skills_directories
for dir in skills_directories:
if exists(dir):
list = filter(lambda x: os.path.isdir(os.path.join(dir, x)),
os.listdir(dir))
for skill_folder in list:
if not skill_folder in loaded_skills:
loaded_skills[skill_folder] = {}
skill = loaded_skills.get(skill_folder)
skill["path"] = os.path.join(dir, skill_folder)
if not MainModule + ".py" in os.listdir(skill["path"]):
continue
skill["last_modified"] = max(
os.path.getmtime(root) for root, _, _ in
os.walk(skill["path"]))
if skill.get("instance") and skill.get("last_modified",
0) <= last_modified_skill:
continue
elif skill.get("instance") and skill.get("last_modified",
0) > last_modified_skill:
logger.debug("Reloading Skill: " + skill_folder)
skill["instance"].shutdown()
clear_skill_events(skill["instance"])
del skill["instance"]
skill["instance"] = load_skill(
create_skill_descriptor(skill["path"]), ws)
last_modified_skill = max(
map(lambda x: x.get("last_modified"), loaded_skills.values()))
time.sleep(2)
watch_skills()
def main():
global ws
ws = WebsocketClient()
ConfigurationManager.init(ws)
def echo(message):
try:
_message = json.loads(message)
if _message.get("type") == "registration":
# do not log tokens from registration messages
_message["data"]["token"] = None
message = json.dumps(_message)
except:
pass
logger.debug(message)
ws.on('message', echo)
ws.once('open', load_watch_skills)
ws.run_forever()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
for skill in loaded_skills:
skill.shutdown()
finally:
sys.exit()