def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) self._account_id = profile.get(['cereproc-tts', 'account_id']) if not self._account_id: raise ValueError("Cereproc account ID not configured!") self._password = profile.get(['cereproc-tts', 'password']) if not self._password: raise ValueError("Cereproc password not configured!") language = profile.get(['language'], 'en-US') voice = profile.get(['cereproc-tts', 'voice']) if voice is None: for name, lang in VOICES: if language == lang: voice = name break else: if not any(voice == name for name, language in VOICES): voice = None if voice is None: raise ValueError('Invalid voice!') self._voice = voice self._soapclient = suds.client.Client( "https://cerevoice.com/soap/soap_1_1.php?WSDL")
def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) client_id = profile.get(['mstranslator-tts', 'client_id']) if not client_id: raise ValueError('Microsoft Translator client ID not configured!') client_secret = profile.get(['mstranslator-tts', 'client_secret']) if not client_secret: raise ValueError( 'Microsoft Translator client secret not configured!') language = profile.get(['language'], 'en-US') self._mstranslator = mstranslator.Translator(client_id, client_secret) available_languages = self._mstranslator.get_langs(speakable=True) for lang in (language.lower(), language.lower().split('-')[0]): if lang in available_languages: self._language = lang break else: raise ValueError("Language '%s' not supported" % language) best_quality = profile.get(['mstranslator-tts', 'best_quality']) self._kwargs = {'format': 'audio/wav', 'best_quality': best_quality}
def __init__(self, *args, **kwargs): super(MPDControlPlugin, self).__init__(*args, **kwargs) self._logger = logging.getLogger(__name__) server = profile.get(['mpdclient', 'server'], 'localhost') try: port = int(profile.get(['mpdclient', 'port'], 6600)) except ValueError: port = 6600 self._logger.warning( "Configured port is invalid, using %d instead", port ) password = profile.get(['mpdclient', 'password'], '') self._autoplay = profile.get(['mpdclient', 'autoplay'], False) # In reticent mode Naomi is quieter. self._reticient = profile.get_profile_flag( ['mpdclient', 'reticient'], False ) self._music = mpdclient.MPDClient( server=server, port=port, password=password )
def send_email(SUBJECT, BODY, TO): """Sends an HTML email.""" USERNAME = profile.get_profile_password(['email', 'username']) SENDER = profile.get(['keyword'], ['Naomi'])[0] FROM = profile.get_profile_password(['email', 'address']) PASSWORD = profile.get_profile_password(['email', 'password']) SERVER = profile.get(['email', 'smtp', 'server']) PORT = profile.get(['email', 'smtp', 'port'], 587) msg = MIMEMultipart() msg['From'] = "{} <{}>".format(SENDER, FROM) msg['To'] = TO msg['Subject'] = SUBJECT msg.attach(MIMEText(BODY.encode('UTF-8'), 'html', 'UTF-8')) logging.info('using %s, and %s as port', SERVER, PORT) session = smtplib.SMTP(SERVER, PORT) session.starttls() session.login(USERNAME, PASSWORD) session.sendmail(msg['From'], msg['To'], msg.as_string()) session.quit() logging.info('Successful.')
def check_smtp_config(): success = True USERNAME = profile.get_profile_password(['email', 'username']) if (not USERNAME): logging.info('Email username not set') success = False PASSWORD = profile.get_profile_password(['email', 'password']) if (not PASSWORD): logging.info('Email password not set') success = False SERVER = profile.get(['email', 'smtp', 'server']) if (not SERVER): logging.info('Email smtp server not set') success = False PORT = profile.get(['email', 'smtp', 'port'], 587) try: session = smtplib.SMTP(SERVER, PORT) session.starttls() session.login(USERNAME, PASSWORD) session.quit() except TimeoutError: logging.info('SMTP connection timed out (check server name)') success = False except imaplib.IMAP4.error as e: if hasattr(e, 'args'): logging.info(e.args[0]) success = False return success
def check_imap_config(): success = True USERNAME = profile.get_profile_password(['email', 'username']) if (not USERNAME): logging.info('Email username not set') success = False PASSWORD = profile.get_profile_password(['email', 'password']) if (not PASSWORD): logging.info('Email password not set') success = False SERVER = profile.get(['email', 'imap', 'server']) if (not SERVER): logging.info('Email imap server not set') success = False PORT = profile.get(['email', 'imap', 'port'], 993) try: conn = imaplib.IMAP4_SSL(SERVER, PORT) conn.login(USERNAME, PASSWORD) conn.logout() except TimeoutError: logging.info('IMAP connection timed out (check server name)') success = False except imaplib.IMAP4.error as e: if hasattr(e, 'args'): logging.info(e.args[0]) success = False return success
def __init__(self, *args, **kwargs): self._logger = logging.getLogger(__name__) plugin.TTSPlugin.__init__(self, *args, **kwargs) self.server = profile.get( ['mary-tts', 'server'], 'marytts.phonetik.uni-muenchen.de' ) try: port = int(profile.get(['mary-tts', 'port'])) except(TypeError, ValueError): port = 59125 self.port = port self.netloc = '{server}:{port}'.format( server=self.server, port=self.port ) self.session = requests.Session() available_voices = self.get_voices() orig_language = profile.get(['language'], 'en-US') language = orig_language.replace('-', '_') if language not in available_voices: language = language.split('_')[0] if language not in available_voices: raise ValueError( "Language '{}' ('{}') not supported".format( language, orig_language ) ) self.language = language self._logger.info('Available voices: %s', ', '.join( available_voices[language])) voice = profile.get(['mary-tts', 'voice']) if voice is not None and voice in available_voices[language]: self.voice = voice else: self.voice = available_voices[language][0] if voice is not None: self._logger.info( "Voice '{}' not found, using '{}' instead.".format( voice, self.voice ) )
def __init__(self, *args, **kwargs): plugin.STTPlugin.__init__(self, *args, **kwargs) self.resource_file = paths.PLUGIN_PATH + "/stt/snowboy-stt/common.res" self.model = profile.get(['snowboy', 'model']) self.sensitivity = profile.get(['snowboy', 'sensitivity'], "0.5") self.detector = snowboydetect.SnowboyDetect( resource_filename=self.resource_file, model_str=self.model) self.detector.SetAudioGain(1) self.detector.SetSensitivity(self.sensitivity)
def __init__(self, *args, **kwargs): """ Create Plugin Instance """ plugin.STTPlugin.__init__(self, *args, **kwargs) self._logger = logging.getLogger(__name__) self.token = profile.get(['witai-stt', 'access_token']) language = profile.get(['language'], 'en-US') if language.split('-')[0] not in SUPPORTED_LANG: raise ValueError('Language {} is not supported.'.format( language.split('-')[0]))
def __init__(self, *args, **kwargs): language = profile.get(['language'], 'en-US') plugin.STTPlugin.__init__(self, *args, **kwargs) if language not in SUPPORTED_LANGUAGES: raise ValueError("Language '%s' not supported" % language) self.language = language plugin.STTPlugin.__init__(self, *args, **kwargs) self._token = None self.app_key = profile.get(['att-stt', 'app_key']) self.app_secret = profile.get(['att-stt', 'app_secret'])
def handle(self, text, mic): """ Responds to user-input, typically speech text, with a summary of the day's top news headlines, sending them to the user over email if desired. Arguments: text -- user-input, typically transcribed speech mic -- used to interact with the user (for both input and output) """ _ = self.gettext mic.say(self.gettext("Pulling up the news...")) lang = profile.get(['language'], 'en-US').split('-')[0] articles = get_top_articles(language=lang, num_headlines=5) if len(articles) == 0: mic.say( _("Sorry, I'm unable to get the latest headlines right now.")) return del articles[0] # fixing "This RSS feed URL is deprecated" text = _('These are the current top headlines...') text += ' ' text += '... '.join('%d) %s' % (i, a.title) for i, a in enumerate(articles, start=1)) mic.say(text) email = profile.get(['email', 'address']) if not email: return if profile.get_profile_flag(['allows_email'], False): mic.say(_('Would you like me to send you these articles?')) answers = mic.active_listen() if any( self.gettext('YES').upper() in answer.upper() for answer in answers): mic.say(self.gettext("Sure, just give me a moment.")) SUBJECT = self.gettext("Your Top Headlines") email_text = self.make_email_text(articles) email_sent = app_utils.email_user(SUBJECT=SUBJECT, BODY=email_text) if email_sent: mic.say(_("Okay, I've sent you an email.")) else: mic.say( _("Sorry, I'm having trouble sending you these articles." )) else: mic.say(self.gettext("Okay, I will not send any articles."))
def get_plugin_phrases(self, passive_listen=False): phrases = [] # include the keyword, otherwise if (passive_listen): keywords = profile.get(["keyword"]) if not (isinstance(keywords, list)): keywords = [keywords] phrases.extend([word.upper() for word in keywords]) # Include any custom phrases (things you say to Naomi # that don't match plugin phrases. Otherwise, there is # a high probability that something you say will be # interpreted as a command. For instance, the # "check_email" plugin has only "EMAIL" and "INBOX" as # standard phrases, so every time I would say # "Naomi, check email" Naomi would hear "NAOMI SHUT EMAIL" # and shut down. custom_standard_phrases_file = paths.data( "standard_phrases", "{}.txt".format(profile.get(['language'], 'en-US'))) if (os.path.isfile(custom_standard_phrases_file)): with open(custom_standard_phrases_file, mode='r') as f: for line in f: phrase = line.strip() if phrase: phrases.append(phrase) # for plugin in self._plugins: for intent in self.intent_map['intents']: if ('templates' in self.intent_map['intents'][intent]): templates = self.intent_map['intents'][intent]['templates'] keywords_list = [keyword for keyword in self.keywords] # print("Keywords: {}".format(keywords_list)) for keyword in keywords_list: # This will not replace keywords that do not have a list associated with them, like regex and open keywords # print("Replacing {} with words from {} in templates".format(keyword,keywords[keyword])) if (keyword[:len(intent) + 1] == "{}_".format(intent)): short_keyword = self.keywords[keyword]['name'] for template in templates: # print("Checking template: {} for keyword {}".format(template,short_keyword)) if (to_keyword(short_keyword) in template): templates.extend([ template.replace(to_keyword(short_keyword), word.upper()) for word in self.keywords[keyword]['words'] ]) # Now that we have expanded every instance of keyword in templates, delete any template that still contains keyword templates = [ template for template in templates if not to_keyword(short_keyword) in template ] phrases.extend(templates) return sorted(phrases)
def __init__(self, *args, **kwargs): plugin.STTPlugin.__init__(self, *args, **kwargs) # FIXME: get init args from config self._endpoint_url = 'https://stream.watsonplatform.net/speech-to-text/api/v1/recognize' self._username = None self._password = None self._model = 'en-US_BroadbandModel' self._http = requests.Session() self.username = profile.get(['watson_stt', 'username']) if not self.username: raise ValueError("Watson STT username missing!") self.password = profile.get(['watson_stt', 'password']) if not self.password: raise ValueError("Watson STT password missing!")
def __init__(self, *args, **kwargs): global warning_msg super(ControlLEDPlugin, self).__init__(*args, **kwargs) try: # Buster seems to see an arduino as an ACM device, not # a USB device. # There are a lot of things that can go wrong here, including # having something else (like a 3D printer or something) # already using the ttyACM0 device, in which case it might be # ttyACM1 # This should probably be a property that the user can set in # the profile.yml self._SER = serial.Serial( port=profile.get(['Control LED', 'device'], '/dev/ttyACM0'), baudrate=9600, timeout=0 ) except serial.serialutil.SerialException as e: warning_msg = e.args[1] try: self._SER = serial.Serial( port='/dev/ttyUSB0', baudrate=9600, timeout=0 ) except Exception as e: self._SER = SimulatedSerial() if not self._SER: raise serial.serialutil.SerialException(warning_msg)
def __get_translations(self): language = profile.get(['language'], 'en-US') if language not in self.__translations: raise ValueError('Unsupported Language!') return self.__translations[language]
def get_timezone(): """ Returns the pytz timezone for a given profile. Arguments: None """ return timezone(profile.get(['timezone']))
def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) orig_language = profile.get(['language'], 'en-US') language = orig_language.split('-')[0] available_voices = self.get_voices() matching_voices = [ v for v in available_voices if v.language.startswith(language) ] if len(matching_voices) == 0: raise ValueError("Language '%s' ('%s') not supported" % (language, orig_language)) self._logger.info('Available voices: %s', ', '.join(v.name for v in matching_voices)) voice = profile.get(['espeak-tts', 'voice']) if voice is not None and len( [v for v in matching_voices if v.name == voice]) > 0: self.voice = voice else: if voice is not None: self._logger.warning( "Voice '%s' is not available for language '%s'!", self.voice, language) self.voice = matching_voices[0].name self._logger.info("Using voice '%s'.", self.voice) try: pitch_adjustment = int( profile.get(['espeak-tts', 'pitch_adjustment'])) except (TypeError, ValueError): pitch_adjustment = 40 self.pitch_adjustment = pitch_adjustment try: words_per_minute = int( profile.get(['espeak-tts', 'words_per_minute'])) except (TypeError, ValueError): words_per_minute = 160 self.words_per_minute = words_per_minute
def _regenerate_config(self): phrases = [] phrases.extend(profile.get_profile_var(["keyword"], "Naomi")) self._config = types.RecognitionConfig( encoding=enums.RecognitionConfig.AudioEncoding.LINEAR16, language_code=profile.get(['language'], 'en-US'), speech_contexts=[speech.types.SpeechContext( phrases=phrases)] if len(phrases) else None, model="command_and_search")
def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) language = profile.get(['language'], 'en-US') available_languages = self.get_languages() if language not in available_languages: raise ValueError("Language '%s' not supported" % language) self._language = language
def compile_vocabulary(directory, phrases): """ Compiles the vocabulary to the Pocketsphinx format by creating a languagemodel and a dictionary. Arguments: phrases -- a list of phrases that this vocabulary will contain """ print("phrases: {}".format(phrases)) logger = logging.getLogger(__name__) languagemodel_path = get_languagemodel_path(directory) dictionary_path = get_dictionary_path(directory) executable = profile.get(['pocketsphinx', 'phonetisaurus_executable'], 'phonetisaurus-g2p') nbest = profile.get(['pocketsphinx', 'nbest'], 3) fst_model = profile.get(['pocketsphinx', 'fst_model']) fst_model_alphabet = profile.get(['pocketsphinx', 'fst_model_alphabet'], 'arpabet') if not fst_model: raise ValueError('FST model not specified!') if not os.path.exists(fst_model): raise OSError('FST model {} does not exist!'.format(fst_model)) g2pconverter = PhonetisaurusG2P(executable, fst_model, fst_model_alphabet=fst_model_alphabet, nbest=nbest) logger.debug('Languagemodel path: %s' % languagemodel_path) logger.debug('Dictionary path: %s' % dictionary_path) text = " ".join([("<s> %s </s>" % phrase.upper()) for phrase in phrases]) # There's some strange issue when text2idngram sometime can't find any # input (although it's there). For a reason beyond me, this can be fixed # by appending a space char to the string. text += ' ' logger.debug('Compiling languagemodel...') vocabulary = compile_languagemodel(text, languagemodel_path) logger.debug('Starting dictionary...') compile_dictionary(g2pconverter, vocabulary, dictionary_path)
def __init__(self, *args, **kwargs): plugin.STTPlugin.__init__(self, *args, **kwargs) vocabulary_path = self.compile_vocabulary( juliusvocab.compile_vocabulary) self._dfa_file = juliusvocab.get_dfa_path(vocabulary_path) self._dict_file = juliusvocab.get_dict_path(vocabulary_path) hmmdefs = profile.get( ['julius', 'hmmdefs'], "/usr/share/voxforge/julius/acoustic_model_files/hmmdefs") tiedlist = profile.get( ['julius', 'tiedlist'], "/usr/share/voxforge/julius/acoustic_model_files/tiedlist") self._hmmdefs = hmmdefs self._tiedlist = tiedlist # Inital test run: we run this command once to log errors/warnings cmd = [ 'julius', '-input', 'stdin', '-dfa', self._dfa_file, '-v', self._dict_file, '-h', self._hmmdefs, '-hlist', self._tiedlist, '-forcedict' ] cmd = [str(x) for x in cmd] self._logger.debug('Executing: %r', cmd) with tempfile.SpooledTemporaryFile() as out_f: with tempfile.SpooledTemporaryFile() as f: with tempfile.SpooledTemporaryFile() as err_f: subprocess.call(cmd, stdin=f, stdout=out_f, stderr=err_f) out_f.seek(0) for line in out_f.read().splitlines(): line = line.strip() if len(line) > 7 and line[:7].upper() == 'ERROR: ': if not line[7:].startswith('adin_'): self._logger.error(line[7:]) elif len(line) > 9 and line[:9].upper() == 'WARNING: ': self._logger.warning(line[9:]) elif len(line) > 6 and line[:6].upper() == 'STAT: ': self._logger.debug(line[6:])
def play_fp(self, fp, *args, **kwargs): if('chunksize' in kwargs): chunksize = kwargs['chunksize'] else: chunksize = profile.get(['audio','output_chunksize'], 1024) if('add_padding' in kwargs): add_padding = kwargs['add_padding'] else: add_padding = profile.get(['audio','output_padding'], False) pause = profile.get(['audio', 'output_pause'], 0) w = wave.open(fp, 'rb') channels = w.getnchannels() samplewidth = w.getsampwidth() bits = w.getsampwidth() * 8 rate = w.getframerate() with self.open_stream( bits, channels, rate, chunksize=chunksize ) as stream: data = w.readframes(chunksize) datalen = len(data) if add_padding and datalen > 0 and datalen < (chunksize * samplewidth): data += b'\00' * (chunksize * samplewidth - datalen) datalen = len(data) while data: # Check to see if we need to stop if(hasattr(self, "stop")): del self.stop break stream.write(data) data = w.readframes(chunksize) datalen = len(data) if add_padding and datalen > 0 and datalen < (chunksize * samplewidth): data += b'\00' * (chunksize * samplewidth - datalen) datalen = len(data) # pause before closing the stream (reduce clipping) if(pause > 0): time.sleep(pause) w.close()
def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) # FIXME: get init args from config self._logger = logging.getLogger(__name__) self._endpoint_url = 'https://stream.watsonplatform.net/text-to-speech/api/v1/synthesize' self._username = None self._password = None self._http = requests.Session() self.username = profile.get(['watson-tts', 'username']) if not self.username: raise ValueError("Username account for Watson TTS missing!") self.password = profile.get(['watson-tts', 'password']) if not self.password: raise ValueError("Password for Watson TTS missing!") self._voice = profile.get(['watson-tts', 'voice']) if not self._voice: raise ValueError("No voice selected!")
def __init__(self, *args, **kwargs): self._logger = logging.getLogger(__name__) translations = i18n.parse_translations(paths.data('locale')) translator = i18n.GettextMixin(translations, profile.get_profile()) _ = translator.gettext plugin.STTPlugin.__init__(self, *args, **kwargs) self._http = requests.Session() self._url = profile.get(['kaldigstserver-stt', 'url'], defaultKaldiServer)
def handle(self, text, mic): """ Responds to user-input, typically speech text, with a summary of the day's top news headlines, sending them to the user over email if desired. Arguments: intent -- intentparser result with the following layout: intent['action'] = the action to take when the intent is activated intent['input'] = the original words intent['matches'] = dictionary of lists with matching elements, each with a list of the actual words matched intent['score'] = how confident Naomi is that it matched the correct intent. mic -- used to interact with the user (for both input and output) """ _ = self.gettext num_headlines = profile.get(['hacker-news', 'num-headlines'], 4) mic.say( _("Getting the top {} stories from Hacker News...").format( num_headlines)) articles = get_top_articles(num_headlines=num_headlines) if len(articles) == 0: mic.say(" ".join([ _("Sorry, I'm unable to get the top stories from"), _("Hacker News right now.") ])) return text = _('These are the current top stories... ') text += '... '.join(f'{i}) {a.title}' for i, a in enumerate(articles, start=1)) mic.say(text) if profile.get_profile_flag(['allows_email']): mic.say(_('Would you like me to send you these articles?')) answers = mic.active_listen() if any(_('YES').upper() in answer.upper() for answer in answers): mic.say(_("Sure, just give me a moment.")) email_text = self.make_email_text(articles) email_sent = app_utils.email_user( SUBJECT=_("Top Stories from Hacker News"), BODY=email_text) if email_sent: mic.say(_("Okay, I've sent you an email.")) else: mic.say( _("Sorry, I'm having trouble sending you these articles." )) else: mic.say(_("Okay, I will not send any articles."))
def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) self._logger = logging.getLogger(__name__) self._logger.warning( "This TTS plugin doesn't have multilanguage support!") voice = profile.get(['flite-tts', 'voice'], 'slt') self._logger.info("Voice: {}".format(voice)) voices = self.get_voices() if not voice or voice not in voices: self._logger.info("Voice {} not in Voices {}".format( voice, voices)) voice = '' self.voice = voice
def __init__(self, *args, **kwargs): super(JokePlugin, self).__init__(*args, **kwargs) language = profile.get(['language'], 'en-US') try: self._jokes = get_jokes(language) except IOError as e: if e.errno == 2: self._jokes = [] else: raise e if len(self._jokes) == 0: raise ValueError('Unsupported language!')
def add_intents(self, intents): for intent in intents: # this prevents collisions between intents intent_base = intent intent_inc = 0 locale = profile.get("language") while intent in self.intent_map['intents']: intent_inc += 1 intent = "{}{}".format(intent_base, intent_inc) if ('locale' in intents[intent_base]): # If the selected locale is not available, try matching just # the language ("en-US" -> "en") if (locale not in intents[intent_base]['locale']): for language in intents[intent_base]['locale']: if (language[:2] == locale[:2]): locale = language break while intent in self.intent_map['intents']: intent_inc += 1 intent = "{}{}".format(intent_base, intent_inc) self.intent_map['intents'][intent] = { 'action': intents[intent_base]['action'], 'name': intent_base, 'templates': [] } templates = intents[intent_base]['locale'][locale]['templates'] if ('keywords' in intents[intent_base]['locale'][locale]): for keyword in intents[intent_base]['locale'][locale][ 'keywords']: keyword_token = "{}_{}".format(intent, keyword) # print("Keyword_token: {}".format(keyword_token)) self.keywords[keyword_token] = { 'words': intents[intent_base]['locale'][locale]['keywords'] [keyword], 'name': keyword } # print("Adding keyword '{}': {}".format(keyword_token,intents[intent_base]['keywords'][keyword])) # map the keywords into the intents templates = [ t.replace(keyword, keyword_token) for t in templates ] self.container.add_entity( keyword_token, intents[intent_base]['locale'][locale] ['keywords'][keyword]) self.intent_map['intents'][intent]['templates'] = templates self.container.add_intent(intent, templates)
def handle(self, intent, mic): """ Responds to user-input, typically speech text, with a summary of the user's Facebook notifications, including a count and details related to each individual notification. Arguments: text -- user-input, typically transcribed speech mic -- used to interact with the user (for both input and output) """ _ = self.gettext oauth_access_token = profile.get(['keys', 'FB_TOKEN']) graph = facebook.GraphAPI(oauth_access_token) try: results = graph.request("me/notifications") except facebook.GraphAPIError: mic.say("".join([ _("I have not been authorized to query your Facebook. If"), _("you would like to check your notifications in the"), _("future, please visit the Naomi dashboard.") ])) return except Exception: mic.say(_("I apologize, I can't access Facebook at the moment.")) if not len(results['data']): mic.say(_("You have no Facebook notifications.")) return updates = [] for notification in results['data']: updates.append(notification['title']) count = len(results['data']) if count == 0: mic.say(_("You have no Facebook notifications.")) elif count == 1: mic.say(_("You have one Facebook notification.")) else: mic.say(_("You have {} Facebook notifications.").format(count)) if count > 0: mic.say("%s." % " ".join(updates)) return
def __init__(self, *args, **kwargs): plugin.TTSPlugin.__init__(self, *args, **kwargs) access_key = profile.get(['ivona-tts', 'access_key']) if not access_key: raise ValueError("Ivona access key not configured!") secret_key = profile.get(['ivona-tts', 'secret_key']) if not secret_key: raise ValueError("Ivona secret key not configured!") region = profile.get(['ivona-tts', 'region']) voice = profile.get(['ivona-tts', 'voice']) speech_rate = profile.get(['ivona-tts', 'speech_rate']) try: sentence_break = int(profile.get(['ivona-tts', 'sentence_break'])) except (TypeError, ValueError): sentence_break = None language = profile.get(['language'], "en-US") self._pyvonavoice = pyvona.Voice(access_key, secret_key) self._pyvonavoice.codec = "mp3" if region is not None: self._pyvonavoice.region = region # Use an appropriate voice for the chosen language try: all_voices = json.loads(self._pyvonavoice.list_voices())["Voices"] except TypeError: all_voices = self._pyvonavoice.list_voices()["Voices"] suitable_voices = [v for v in all_voices if v["Language"] == language] if len(suitable_voices) == 0: raise ValueError("Language '%s' not supported" % language) else: if voice is not None and len([v for v in suitable_voices if v["Name"] == voice]) > 0: # Use voice from config self._pyvonavoice.voice_name = voice else: # Use any voice for that language voice = suitable_voices[0]["Name"] self._pyvonavoice.voice_name = voice if speech_rate is not None: self._pyvonavoice.speech_rate = speech_rate if sentence_break is not None: self._pyvonavoice.sentence_break = sentence_break