def __init__(self): self._logger = logging.getLogger('ProjectAlice') self._logger.info('Starting Project Alice initializer') self._confsFile = Path(commons.rootDir(), 'config.py') self._confsSample = Path(commons.rootDir(), 'configSample.py') self._initFile = Path('/boot/ProjectAlice.yaml') self._latest = 1.03
def runCmd(self, cmd: str, services: list = None): if not Path(commons.rootDir() + '/assistant').exists(): self._logger.warning( f'[{self.name}] Assistant not yet existing, shouldn\'t handle Snips for now' ) return if not services: services = self._snipsServices for service in services: if (service == 'snips-asr' and not isinstance(self.ASRManager.asr, SnipsASR)) or ( service == 'snips-tts' and not isinstance(self.TTSManager.tts, SnipsTTS)): continue result = subprocess.run(['sudo', 'systemctl', cmd, service], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: self._logger.info(f"[{self.name}] Service {service} {cmd}'ed") elif result.returncode == 5: pass else: self._logger.info( f"[{self.name}] Tried to {cmd} the {service} service but it returned with return code {result.returncode}" )
def uploadToNewDevice(self, uid: str): directory = Path(commons.rootDir(), 'trained/hotwords') for fiile in directory: if (directory / fiile).is_file(): continue self._upload(directory / fiile, uid)
def onStartListening(self, session: DialogSession): if isinstance(self._asr, SnipsASR): return else: start = time.time() result = self._asr.onListen() end = time.time() processing = float(end - start) if result: # Stop listener as fast as possible self.MqttManager.publish(topic='hermes/asr/stopListening', payload={'sessionId': session.sessionId, 'siteId': session.siteId}) result = self.LanguageManager.sanitizeNluQuery(result) self._logger.debug('[{}] - {} output: "{}"'.format(self.NAME, self._asr.__class__.__name__, result)) supportedIntents = session.intentFilter or self.ModuleManager.supportedIntents intentFilter = [intent.justTopic for intent in supportedIntents if isinstance(intent, Intent) and not intent.protected] # Add Global Intents intentFilter.append(Intent('GlobalStop').justTopic) self.MqttManager.publish(topic='hermes/asr/textCaptured', payload={'sessionId': session.sessionId, 'text': result, 'siteId': session.siteId, 'likelihood': 1, 'seconds': processing}) self.MqttManager.publish(topic='hermes/nlu/query', payload={'id':session.sessionId, 'input': result, 'intentFilter': intentFilter, 'sessionId': session.sessionId}) else: self.MqttManager.publish(topic='hermes/nlu/intentNotRecognized') self.MqttManager.playSound( soundFile=Path(commons.rootDir(), 'assistant/custom_dialogue/sound/error.wav'), sessionId=uuid.uuid4(), absolutePath=True, siteId=session.siteId )
def _loadTTS(self, tts: str, user: User = None) -> TTSEnum: try: tts = TTSEnum(tts) except: tts = TTSEnum.SNIPS if tts == TTSEnum.SNIPS: from core.voice.model.SnipsTTS import SnipsTTS self._tts = SnipsTTS(user) elif tts == TTSEnum.PICO: self._tts = PicoTTS(user) elif tts == TTSEnum.MYCROFT: if not Path(Path(commons.rootDir()).parent, 'mimic/voices').is_dir(): self._logger.warning( '[{}] Trying to use Mycroft as TTS but files not available, falling back to picotts' .format(self.NAME)) self._tts = PicoTTS(user) tts = TTSEnum.PICO else: from core.voice.model.MycroftTTS import MycroftTTS self._tts = MycroftTTS(user) elif tts == TTSEnum.AMAZON: from core.voice.model.AmazonTTS import AmazonTTS self._tts = AmazonTTS(user) elif tts == TTSEnum.GOOGLE: from core.voice.model.GoogleTTS import GoogleTTS self._tts = GoogleTTS(user) else: from core.voice.model.SnipsTTS import SnipsTTS self._tts = SnipsTTS(user) return tts
def _addWakewordToSnips(self, path: Path): # TODO unhardcode sensitivity models: list = self.ConfigManager.getSnipsConfiguration( 'snips-hotword', 'model', createIfNotExist=True) if not isinstance(models, list): models = list() wakewordName = path.name add = True copy = models.copy() for i, model in enumerate(copy): if wakewordName in model: models.pop(i) elif '/snips_hotword=' in model: add = False if add: models.append( str( Path(commons.rootDir(), 'trained/hotwords/snips_hotword=0.53'))) models.append('{}=0.52'.format(str(path))) self.ConfigManager.updateSnipsConfiguration('snips-hotword', 'model', models, restartSnips=True) self._upload(path)
def loadModuleConfigurations(self, module: str = ''): self._logger.info('[{}] Loading module configurations'.format( self.name)) # Iterate through all modules declared in global config file for moduleName in self._aliceConfigurations['modules']: if module and moduleName != module: continue moduleConfigFile = Path(commons.rootDir(), 'modules', moduleName, 'config.json') moduleConfigFileExists = moduleConfigFile.exists() if not self._aliceConfigurations['modules'][moduleName][ 'active'] or not moduleConfigFileExists: self._modulesConfigurations[moduleName] = { **self._aliceConfigurations['modules'][moduleName] } continue try: self._logger.info( '- Loading config file for module {}'.format(moduleName)) with open(moduleConfigFile) as jsonFile: self._modulesConfigurations[moduleName] = { **json.load(jsonFile), **self._aliceConfigurations['modules'][moduleName] } except json.decoder.JSONDecodeError: self._logger.error( '- Error in config file for module {}'.format(moduleName))
def onMessage(self, intent: str, session: DialogSession): if intent == self._INTENT_ANSWER_HEADS_OR_TAIL: coin = random.choice(['heads', 'tails']) SuperManager.getInstance().mqttManager.playSound( soundFile=os.path.join(commons.rootDir(), 'modules', 'Minigames', 'sounds', 'coinflip'), sessionId='coinflip', siteId=session.siteId, absolutePath=True) redQueen = SuperManager.getInstance( ).moduleManager.getModuleInstance('RedQueen') redQueen.changeRedQueenStat('happiness', 5) if session.slotValue('HeadsOrTails') == coin: result = 'flipACoinUserWins' redQueen.changeRedQueenStat('frustration', 1) else: result = 'flipACoinUserLooses' redQueen.changeRedQueenStat('frustration', -5) redQueen.changeRedQueenStat('hapiness', 5) SuperManager.getInstance().mqttManager.continueDialog( sessionId=session.sessionId, text=SuperManager.getInstance().talkManager.randomTalk( talk=result, module='Minigames').format( SuperManager.getInstance().languageManager. getTranslations(module='Minigames', key=coin, toLang=SuperManager.getInstance(). languageManager.activeLanguage)[0]), intentFilter=[self._INTENT_ANSWER_YES_OR_NO], previousIntent=self._INTENT_PLAY_GAME, customData={'askRetry': True})
def langSwitch(self, newLang: str, siteId: str): self.publish(topic='hermes/asr/textCaptured', payload={'siteId': siteId}) subprocess.run( [commons.rootDir() + '/system/scripts/langSwitch.sh', newLang]) self.ThreadManager.doLater(interval=3, func=self._confirmLangSwitch, args=[newLang, siteId])
def numberIntent(self, intent: str, session: DialogSession): number = int(session.slotValue('Number')) if number == self._number: score = round(time.time() - self._start) m, s = divmod(score, 60) scoreFormatted = SuperManager.getInstance( ).languageManager.getTranslations( module='Minigames', key='minutesAndSeconds')[0].format(round(m), round(s)) SuperManager.getInstance().mqttManager.playSound( soundFile=os.path.join(commons.rootDir(), 'modules', 'Minigames', 'sounds', 'applause'), siteId=session.siteId, absolutePath=True) SuperManager.getInstance().mqttManager.endDialog( sessionId=session.sessionId, text=SuperManager.getInstance().talkManager.randomTalk( 'guessTheNumberCorrect', 'Minigames').format(self._number, self._number)) textType = 'guessTheNumberScore' if session.user != 'unknown' and SuperManager.getInstance( ).moduleManager.getModuleInstance('Minigames').checkAndStoreScore( user=session.user, score=score, biggerIsBetter=False): textType = 'guessTheNumberNewHighscore' SuperManager.getInstance().mqttManager.say( client=session.siteId, text=SuperManager.getInstance().talkManager.randomTalk( textType, 'Minigames').format(scoreFormatted), canBeEnqueued=True) SuperManager.getInstance().mqttManager.ask( text=SuperManager.getInstance().talkManager.randomTalk( 'playAgain', 'Minigames'), intentFilter=[self._INTENT_ANSWER_YES_OR_NO], previousIntent=self._INTENT_PLAY_GAME, customData={ 'speaker': session.user, 'askRetry': True }) return textType = 'guessTheNumberLess' if number < self._number: textType = 'guessTheNumberMore' SuperManager.getInstance().mqttManager.continueDialog( sessionId=session.sessionId, text=SuperManager.getInstance().talkManager.randomTalk( textType, 'Minigames'), intentFilter=[self._INTENT_ANSWER_NUMBER], previousIntent=intent)
def onStart(self): super().onStart() if not self.ConfigManager.getAliceConfigByName('webInterfaceActive'): self._logger.info( f'[{self.name}] Web interface is disabled by settings') else: langFile = Path( commons.rootDir(), f'core/interface/languages/{self.LanguageManager.activeLanguage.lower()}.json' ) if not langFile.exists(): self._logger.warning( f'[{self.name}] Lang "{self.LanguageManager.activeLanguage.lower()}" not found, falling back to "en"' ) langFile = Path(commons.rootDir(), 'core/interface/languages/en.json') else: self._logger.info( f'[{self.name}] Loaded interface in "{self.LanguageManager.activeLanguage.lower()}"' ) with langFile.open('r') as f: self._langData = json.load(f) for view in self._VIEWS: view.register(self.app) self.ThreadManager.newThread( name='WebInterface', target=self.app.run, kwargs={ 'debug': True, 'port': int( self.ConfigManager.getAliceConfigByName( 'webInterfacePort')), 'host': commons.getLocalIp(), 'use_reloader': False })
def playSound(self, soundFile: str, sessionId: str = '', absolutePath: bool = False, siteId: str = 'default', root: str = '', uid: str = ''): """ Plays a sound :param uid: a unique id for that sound :param absolutePath: bool :param sessionId: int a session id :param soundFile: str The sound file name :param siteId: int Where to play the sound :param root: If different from default """ if not root: root = Path(commons.rootDir(), 'system/sounds') root = Path(root) if not uid: uid = str(uuid.uuid4()) if not sessionId: sessionId = str(uuid.uuid4()) if siteId == 'all': deviceList = self.DeviceManager.getDevicesByType( 'AliceSatellite', connectedOnly=True) deviceList.append('default') for device in deviceList: device = device.replace('@mqtt', '') self.playSound(sessionId=sessionId, soundFile=soundFile, absolutePath=absolutePath, siteId=device, root=root) else: if ' ' in siteId: siteId = siteId.replace(' ', '_') soundFile = Path( soundFile).with_suffix('.wav') if absolutePath else Path( root, soundFile).with_suffix('.wav') if not soundFile.exists(): self._logger.error( "Sound file {} doesn't exist".format(soundFile)) return self._mqttClient.publish( 'hermes/audioServer/{}/playBytes/{}'.format(siteId, uid), payload=bytearray(soundFile.read_bytes()))
def removeModule(self, moduleName: str): if not moduleName in self._modules: return else: self.configureModuleIntents(moduleName, False) self.ConfigManager.removeModule(moduleName) del self._modules[moduleName] shutil.rmtree(Path(commons.rootDir(), 'modules', moduleName)) # TODO Samkilla cleaning self.SnipsConsoleManager.doDownload()
def playSound(self, soundFilename: str, location: Path = None, sessionId: str = '', siteId: str = constants.DEFAULT_SITE_ID, uid: str = '', suffix: str = '.wav'): if not sessionId: sessionId = str(uuid.uuid4()) if not uid: uid = str(uuid.uuid4()) if not location: location = Path(commons.rootDir()) / 'system' / 'sounds' elif not str(location).startswith('/'): location = Path(commons.rootDir()) / location if siteId == 'all': deviceList = self.DeviceManager.getDevicesByType( 'AliceSatellite', connectedOnly=True) deviceList.append(constants.DEFAULT_SITE_ID) for device in deviceList: device = device.replace('@mqtt', '') self.playSound(soundFilename, location, sessionId, device, uid) else: if ' ' in siteId: siteId = siteId.replace(' ', '_') soundFile = Path(location / soundFilename).with_suffix(suffix) if not soundFile.exists(): self._logger.error( f"[{self.name}] Sound file {soundFile} doesn't exist") return self._mqttClient.publish(constants.TOPIC_PLAY_BYTES.format( siteId, uid), payload=bytearray(soundFile.read_bytes()))
def changeFeedbackSound(self, inDialog: bool, siteId: str = 'all'): if not Path(commons.rootDir(), 'assistant').exists(): return # Unfortunately we can't yet get rid of the feedback sound because Alice hears herself finishing the sentence and capturing part of it if inDialog: state = '_ask' else: state = '' subprocess.run(['sudo', 'ln', '-sfn', f'{commons.rootDir()}/system/sounds/{self.LanguageManager.activeLanguage}/start_of_input{state}.wav', f'{commons.rootDir()}/assistant/custom_dialogue/sound/start_of_input.wav']) subprocess.run(['sudo', 'ln', '-sfn', f'{commons.rootDir()}/system/sounds/{self.LanguageManager.activeLanguage}/error{state}.wav', f'{commons.rootDir()}/assistant/custom_dialogue/sound/error.wav'])
def finalizeWakeword(self): self._logger.info('[{}] Finalyzing wakeword'.format(self.name)) self._state = WakewordManagerState.FINALIZING config = { 'hotword_key': self._wakeword.username.lower(), 'kind': 'personal', 'dtw_ref': 0.22, 'from_mfcc': 1, 'to_mfcc': 13, 'band_radius': 10, 'shift': 10, 'window_size': 10, 'sample_rate': 16000, 'frame_length_ms': 25.0, 'frame_shift_ms': 10.0, 'num_mfcc': 13, 'num_mel_bins': 13, 'mel_low_freq': 20, 'cepstral_lifter': 22.0, 'dither': 0.0, 'window_type': 'povey', 'use_energy': False, 'energy_floor': 0.0, 'raw_energy': True, 'preemphasis_coefficient': 0.97, 'model_version': 1 } path = Path(commons.rootDir(), 'trained/hotwords', self.wakeword.username.lower()) if path.exists(): self._logger.warning( '[{}] Destination directory for new wakeword already exists, deleting' .format(self.name)) shutil.rmtree(path) path.mkdir() (path / 'config.json').write_text(json.dumps(config, indent=4)) for i in range(1, 4): shutil.move(Path(tempfile.gettempdir(), '{}.wav'.format(i)), path / '{}.wav'.format(i)) self._addWakewordToSnips(path) self.ThreadManager.newThread(name='SatelliteWakewordUpload', target=self._upload, args=[path, self._wakeword.username], autostart=True) self._state = WakewordManagerState.IDLE
def onStart(self): super().onStart() self._userEmail = self.ConfigManager.getAliceConfigByName( 'snipsConsoleLogin') self._userPassword = self.ConfigManager.getAliceConfigByName( 'snipsConsolePassword') self._mainProcessor = MainProcessor(self) self.initActions() self._loadDialogTemplateMapsInConfigManager() path = Path(commons.rootDir(), 'var/assistants', self.LanguageManager.activeLanguage) if not path.exists() or not [x for x in path.iterdir() if x.is_dir()]: self.sync()
def onSay(self, session: DialogSession): super().onSay(session) if not self._text: return if not self._cacheFile.exists(): subprocess.run([ 'snips-makers-tts', '--output', self._cacheFile, 'file://{}/var/voices/cmu_{}_{}.flitevox'.format( commons.rootDir(), SuperManager.getInstance().languageManager. activeCountryCode.lower(), self._voice), self._text ]) self._speak(file=self._cacheFile, session=session)
def __init__(self): super().__init__() os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = str( Path(commons.rootDir(), 'credentials/googlecredentials.json')) self._client = speech.SpeechClient() self._config = types.RecognitionConfig( encoding=enums.RecognitionConfig.AudioEncoding.LINEAR16, sample_rate_hertz=SuperManager.getInstance( ).configManager.getAliceConfigByName('micSampleRate'), language_code=SuperManager.getInstance( ).languageManager.activeLanguageAndCountryCode) self._capableOfArbitraryCapture = True self._streamingConfig = types.StreamingRecognitionConfig( config=self._config, single_utterance=True, interim_results=False)
def _writeToModuleConfigurationFile(moduleName: str, confs: dict): """ Saaves the given configuration into config.py of the Module :param moduleName: the targeted module :param confs: the dict to save """ # Don't store "active", "version", "author", "conditions" value in module config file misterProper = ['active', 'version', 'author', 'conditions'] confsCleaned = { key: value for key, value in confs.items() if key not in misterProper } moduleConfigFile = Path(commons.rootDir(), 'modules', moduleName, 'config.json') moduleConfigFile.write_text(json.dumps(confsCleaned, indent=4))
def _checkForModuleInstall(self): self.ThreadManager.newTimer(interval=10, func=self._checkForModuleInstall, autoStart=True) root = Path(commons.rootDir(), 'system/moduleInstallTickets') files = [f for f in root.iterdir() if f.suffix == '.install'] if self._busyInstalling.isSet() or \ not self.InternetManager.online or \ not files or \ self.ThreadManager.getEvent('SnipsAssistantDownload').isSet(): return if files: self._logger.info( f'[{self.name}] Found {len(files)} install ticket(s)') self._busyInstalling.set() modulesToBoot = list() try: modulesToBoot = self._installModules(files) except Exception as e: self._logger.error( f'[{self.name}] Error checking for module install: {e}') finally: if modulesToBoot: for moduleName, info in modulesToBoot.items(): self._modules = self._loadModuleList( moduleToLoad=moduleName, isUpdate=info['update']) try: self.LanguageManager.loadStrings( moduleToLoad=moduleName) self.TalkManager.loadTalks(moduleToLoad=moduleName) except: pass try: self.SamkillaManager.sync(moduleFilter=modulesToBoot) except Exception as esamk: self._logger.error( f'[{self.name}] Failed syncing with remote snips console {esamk}' ) raise self._busyInstalling.clear()
def checkForModuleUpdates(self): if self.ConfigManager.getAliceConfigByName('stayCompletlyOffline'): return self._logger.info(f'[{self.name}] Checking for module updates') if not self.InternetManager.online: self._logger.info(f'[{self.name}] Not connected...') return availableModules = self.ConfigManager.modulesConfigurations i = 0 for moduleName in self._modules: try: if moduleName not in availableModules: continue req = requests.get( f'https://raw.githubusercontent.com/project-alice-powered-by-snips/ProjectAliceModules/master/PublishedModules/{availableModules[moduleName]["author"]}/{moduleName}/{moduleName}.install' ) remoteFile = req.json() if float(remoteFile['version']) > float( availableModules[moduleName]['version']): i += 1 if not self.ConfigManager.getAliceConfigByName( 'moduleAutoUpdate'): if moduleName in self._modules: self._modules[moduleName][ 'instance'].updateAvailable = True elif moduleName in self._deactivatedModules: self._deactivatedModules[moduleName][ 'instance'].updateAvailable = True else: moduleFile = Path(commons.rootDir(), 'system/moduleInstallTickets', moduleName + '.install') moduleFile.write_text(json.dumps(remoteFile)) except Exception as e: self._logger.warning( f'[{self.name}] Error checking updates for module "{moduleName}": {e}' ) self._logger.info(f'[{self.name}] Found {i} module update(s)')
def _speakOnSonos(self, text, client): if text == '': return subprocess.call([ 'sudo', Path(commons.rootDir(), '/system/scripts/snipsSuperTTS.sh'), Path('/share/tmp.wav'), 'amazon', self.LanguageManager.activeLanguage, 'US', 'Joanna', 'FEMALE', text, '22050' ]) sonosModule = self.ModuleManager.getModuleInstance('Sonos') if sonosModule: sonosModule.aliceSpeak(client) else: self._logger.error( 'Tried to speak on Sonos but Sonos module is disabled or missing' )
def start(self, session: DialogSession): super().start(session) SuperManager.getInstance().mqttManager.playSound( soundFile=os.path.join(commons.rootDir(), 'modules', 'Minigames', 'sounds', 'rollADice'), sessionId='rollADice', siteId=session.siteId, absolutePath=True) redQueen = SuperManager.getInstance().moduleManager.getModuleInstance( 'RedQueen') redQueen.changeRedQueenStat('happiness', 5) SuperManager.getInstance().mqttManager.endDialog( sessionId=session.sessionId, text=SuperManager.getInstance().talkManager.randomTalk( talk='rollADiceResult', module='Minigames').format(random.randint(1, 6)))
def checkForModuleUpdates(self): if not self.ConfigManager.getAliceConfigByName('moduleAutoUpdate'): return self._logger.info('[{}] Checking for module updates'.format(self.name)) if not self.InternetManager.online: self._logger.info('[{}] Not connected...'.format(self.name)) return self._busyInstalling.set() availableModules = self.ConfigManager.modulesConfigurations i = 0 for moduleName in self._modules: try: if moduleName not in availableModules: continue req = requests.get( 'https://raw.githubusercontent.com/project-alice-powered-by-snips/ProjectAliceModules/master/PublishedModules/{0}/{1}/{1}.install' .format(availableModules[moduleName]['author'], moduleName)) remoteFile = json.loads(req.content.decode()) if float(remoteFile['version']) > float( availableModules[moduleName]['version']): i += 1 moduleFile = Path(commons.rootDir(), 'system/moduleInstallTickets', moduleName + '.install') moduleFile.write_text(json.dumps(remoteFile)) self._modules[moduleName]['instance'].active = False except Exception as e: self._logger.warning( '[{}] Error checking updates for module "{}": {}'.format( self.name, moduleName, e)) self._logger.info('[{}] Found {} module update(s)'.format( self.name, i)) self._busyInstalling.clear()
def loadModuleCommands(self, moduleName: str) -> bool: commandsMountpoint = Path(commons.rootDir(), 'modules', moduleName, 'console') for commandFile in commandsMountpoint.glob('*Command.py'): commandClassFile = commandFile.with_suffix( '').absolute().as_posix() try: commandImport = importlib.import_module( 'modules.{}.console.{}'.format(moduleName, commandClassFile)) klass = getattr(commandImport, commandClassFile) instance = klass() self.add(instance) return True except Exception: pass return False
def onModuleInstalled(self): service = f"""\ [Unit] Description=zigbee2mqtt After=network.target [Service] ExecStart=/usr/bin/npm start WorkingDirectory=/opt/zigbee2mqtt StandardOutput=inherit StandardError=inherit Restart=always User={getpass.getuser()} [Install] WantedBy=multi-user.target""" filepath = Path(commons.rootDir(), 'zigbee2mqtt.service') filepath.write_text(service) subprocess.run(['sudo', 'mv', str(filepath), '/etc/systemd/system/zigbee2mqtt.service']) subprocess.run(['sudo', 'systemctl', 'daemon-reload'])
def onMessage(self, intent: str, session: DialogSession): if intent == self._INTENT_ANSWER_ROCK_PAPER_OR_SCISSORS: choices = ['rock', 'paper', 'scissors'] me = random.choice(choices) SuperManager.getInstance().mqttManager.playSound( soundFile=os.path.join(commons.rootDir(), 'modules', 'Minigames', 'sounds', 'drum_suspens'), siteId=session.siteId, absolutePath=True) redQueen = SuperManager.getInstance( ).moduleManager.getModuleInstance('RedQueen') redQueen.changeRedQueenStat('happiness', 5) player = session.slotValue('RockPaperOrScissors') # tie if player == me: result = 'rockPaperScissorsTie' # player wins elif choices[choices.index(player) - 1] == me: result = 'rockPaperScissorsWins' redQueen.changeRedQueenStat('frustration', 2) # alice wins else: result = 'rockPaperScissorsLooses' redQueen.changeRedQueenStat('frustration', -5) redQueen.changeRedQueenStat('happiness', 5) SuperManager.getInstance().mqttManager.continueDialog( sessionId=session.sessionId, text=SuperManager.getInstance().talkManager.randomTalk( talk=result, module='Minigames').format( SuperManager.getInstance().languageManager. getTranslations(module='Minigames', key=me, toLang=SuperManager.getInstance(). languageManager.activeLanguage)[0]), intentFilter=[self._INTENT_ANSWER_YES_OR_NO], previousIntent=self._INTENT_PLAY_GAME, customData={'askRetry': True})
class SyslogView(View): LOGS = Path(commons.rootDir(), 'var', 'logs', 'logs.log') def __init__(self): super().__init__() self._counter = 0 def index(self): return render_template('syslog.html', langData=self._langData) def update(self): return jsonify(data=self._getData()) def refresh(self): self._counter = 0 return self.update() def _getData(self) -> list: data = self.LOGS.open('r').readlines() ret = data[self._counter:] self._counter = len(data) return ['] -'.join(line.split('] -')[1:]) for line in ret]
def _loadModuleList(self, moduleToLoad: str = '', isUpdate: bool = False): if moduleToLoad: modules = self._modules.copy() else: modules = dict() availableModules = self.ConfigManager.modulesConfigurations availableModules = collections.OrderedDict( sorted(availableModules.items())) if Customisation.MODULE_NAME in availableModules: customisationModule = availableModules.pop( Customisation.MODULE_NAME) availableModules[Customisation.MODULE_NAME] = customisationModule for moduleName, module in availableModules.items(): if moduleToLoad and moduleName != moduleToLoad: continue conditionName = '' conditionValue = '' try: if not module['active']: if moduleName in self.NEEDED_MODULES: self._logger.info( "Module {} marked as disable but it shouldn't be". format(moduleName)) SuperManager.getInstance().onStop() break else: self._logger.info( 'Module {} is disabled'.format(moduleName)) continue if 'conditions' in module: for conditionName, conditionValue in module[ 'conditions'].items(): if conditionName == 'lang' and self.LanguageManager.activeLanguage not in conditionValue: raise ModuleNotConditionCompliant elif conditionName == 'online': if conditionValue and self.ConfigManager.getAliceConfigByName( 'stayCompletlyOffline'): raise ModuleNotConditionCompliant elif not conditionValue and not self.ConfigManager.getAliceConfigByName( 'stayCompletlyOffline'): raise ModuleNotConditionCompliant elif conditionName == 'module': for requiredModule in conditionValue: if requiredModule[ 'name'] in availableModules and not availableModules[ requiredModule['name']]['active']: raise ModuleNotConditionCompliant elif requiredModule[ 'name'] not in availableModules: self._logger.info( '[{}] Module {} has another module as dependency, adding download' .format(self.name, moduleName)) subprocess.run([ 'wget', requiredModule['url'], '-O', Path( commons.rootDir(), 'system/moduleInstallTickets/{}.install' .format(requiredModule['name'])) ]) elif conditionName == 'notModule': for excludedModule in conditionValue: author, name = excludedModule.split('/') if name in availableModules and availableModules[ name][ 'author'] == author and availableModules[ name]['active']: raise ModuleNotConditionCompliant elif conditionName == 'asrArbitraryCapture': if conditionValue and not self.ASRManager.asr.capableOfArbitraryCapture: raise ModuleNotConditionCompliant elif conditionName == 'activeManager': for manager in conditionValue: if not manager: continue man = SuperManager.getInstance().getManager( manager) if not man or not man.isActive: raise ModuleNotConditionCompliant if ' ' in moduleName: name = commons.toCamelCase(moduleName) else: name = moduleName moduleInstance = self.importFromModule(moduleName=name, isUpdate=isUpdate) if moduleInstance: modules[moduleInstance.name] = {'instance': moduleInstance} except ModuleStartingFailed as e: self._logger.warning('[{}] Failed loading module: {}'.format( self.name, e)) continue except ModuleNotConditionCompliant: self._logger.info( '[{}] Module {} does not comply to "{}" condition, required "{}"' .format(self.name, moduleName, conditionName, conditionValue)) continue except Exception as e: self._logger.warning( '[{}] Something went wrong loading a module: {}'.format( self.name, e)) continue # noinspection PyTypeChecker return collections.OrderedDict(sorted(modules.items()))