def start_sip(self): if self.sip is not None: self.sip.quit() sleep(0.5) self.sip = BareSIP(self.settings["user"], self.settings["password"], self.settings["gateway"], block=False, debug=self.settings["debug"]) self.sip.handle_incoming_call = self.handle_incoming_call self.sip.handle_call_ended = self.handle_call_ended self.sip.handle_login_failure = self.handle_login_failure self.sip.handle_login_success = self.handle_login_success self.sip.handle_call_established = self.handle_call_established
from baresipy import BareSIP from time import sleep gateway = "your_sip.gateway.net" user = "******" pswd = "your_password" debug = False b = BareSIP(user, pswd, gateway, debug=debug) to = "*****@*****.**" speech = "this is jarbas personal assistant speaking. this was a test" audio = "examples/acdc.mp3" b.call(to) while b.running: sleep(0.5) if b.call_established: b.send_dtmf("123") b.speak(speech) b.speak("Goodbye") # b.send_audio(audio) b.hang() b.quit()
class SIPSkill(FallbackSkill): def __init__(self): super(SIPSkill, self).__init__(name='SIPSkill') # skill settings defaults if "intercept_allowed" not in self.settings: self.settings["intercept_allowed"] = True if "confirm_operations" not in self.settings: self.settings["confirm_operations"] = True if "debug" not in self.settings: self.settings["debug"] = True if "priority" not in self.settings: self.settings["priority"] = 50 if "timeout" not in self.settings: self.settings["timeout"] = 15 # auto answer incoming calls if "auto_answer" not in self.settings: self.settings["auto_answer"] = False if "auto_reject" not in self.settings: self.settings["auto_reject"] = False if "auto_speech" not in self.settings: self.settings["auto_speech"] = "I am busy, try again later" # web ui contacts management self.settings["add_contact"] = False self.settings["delete_contact"] = False if "contact_name" not in self.settings: self.settings["contact_name"] = None if "contact_address" not in self.settings: self.settings["contact_address"] = None # sip creds if "user" not in self.settings: self.settings["user"] = None if "gateway" not in self.settings: self.settings["gateway"] = None if "password" not in self.settings: self.settings["password"] = None # sipxcom integration if "sipxcom_user" not in self.settings: self.settings["sipxcom_user"] = None if "sipxcom_gateway" not in self.settings: self.settings["sipxcom_gateway"] = None if "sipxcom_password" not in self.settings: self.settings["sipxcom_password"] = None if "sipxcom_sync" not in self.settings: self.settings["sipxcom_sync"] = False # events self.settings_change_callback = self._on_web_settings_change self.namespace = self.__class__.__name__.lower() self.skill_name = "Voice Over IP" # state trackers self._converse_keepalive = None self.on_hold = False self.muted = False self.intercepting_utterances = False self._old_settings = dict(self.settings) self.sip = None self.say_vocab = None self.cb = None self.contacts = ContactList("mycroft_sip") def initialize(self): self.register_fallback(self.handle_fallback, int(self.settings["priority"])) self._converse_keepalive = create_daemon(self.converse_keepalive) say_voc = self.find_resource('and_say.voc', 'vocab') if say_voc: # load vocab and flatten into a simple list # TODO sort by length self.say_vocab = list(chain(*read_vocab_file(say_voc))) self.start_sip() if self.settings["sipxcom_sync"]: self.sipxcom_sync() # Register GUI Events self.handle_gui_state("Clear") self.gui.register_handler("voip.jarbas.acceptCall", self.accept_call) self.gui.register_handler("voip.jarbas.hangCall", self.hang_call) self.gui.register_handler("voip.jarbas.muteCall", self.mute_call) self.gui.register_handler("voip.jarbas.unmuteCall", self.unmute_call) self.gui.register_handler("voip.jarbas.callContact", self.handle_call_contact_from_gui) self.gui.register_handler("voip.jarbas.updateConfig", self.handle_config_from_gui) self.add_event('skill--voip.jarbasskills.home', self.show_homescreen) def _on_web_settings_change(self): # TODO settings should be uploaded to backend when changed inside # skill, but this functionality is gone, # the issue here is with Selene, if anyone thinks of a clever # workaround let me know, currently this is WONTFIX, problem # is on mycroft side if self.settings["delete_contact"] and self.settings["contact_name"] \ != self._oldsettings["contact_name"]: self.delete_contact(self.settings["contact_name"]) self.settings["delete_contact"] = False elif self.settings["add_contact"] and self.settings["contact_name"] \ != self._oldsettings["contact_name"]: self.add_new_contact(self.settings["contact_name"], self.settings["contact_address"]) self.settings["add_contact"] = False if self.settings["auto_reject"]: self.settings["auto_answer"] = False elif self.settings["auto_answer"]: self.settings["auto_reject"] = False if self.settings["auto_speech"] != \ self._old_settings["auto_speech"]: self.speak_dialog("accept_all", {"speech": self.settings["auto_speech"]}) if self.settings["sipxcom_sync"]: self.sipxcom_sync() if self.sip is None: if self.settings["gateway"]: self.start_sip() else: self.speak_dialog("credentials_missing") else: for k in ["user", "password", "gateway"]: if self.settings[k] != self._old_settings[k]: self.speak_dialog("sip_restart") self.sip.quit() self.sip = None self.intercepting_utterances = False # just in case self.start_sip() break self._old_settings = dict(self.settings) def start_sip(self): if self.sip is not None: self.sip.quit() sleep(0.5) self.sip = BareSIP(self.settings["user"], self.settings["password"], self.settings["gateway"], block=False, debug=self.settings["debug"]) self.sip.handle_incoming_call = self.handle_incoming_call self.sip.handle_call_ended = self.handle_call_ended self.sip.handle_login_failure = self.handle_login_failure self.sip.handle_login_success = self.handle_login_success self.sip.handle_call_established = self.handle_call_established def get_intro_message(self): # welcome dialog on skill install self.speak_dialog("intro", {"skill_name": self.skill_name}) # SIP def _wait_until_call_established(self): while not self.sip.call_established: sleep(0.5) # TODO timeout in case of errors def accept_call(self): self.sip.accept_call() self.handle_gui_state("Connected") def hang_call(self): self.sip.hang() self.intercepting_utterances = False def mute_call(self): self.gui["call_muted"] = True self.sip.mute_mic() def unmute_call(self): self.gui["call_muted"] = False self.sip.unmute_mic() def add_new_contact(self, name, address, prompt=False): name = name.replace("_", " ").replace("-", " ").strip() address = address.strip() contact = self.contacts.get_contact(name) # new address if contact is None: self.log.info("Adding new contact {name}:{address}".format( name=name, address=address)) self.contacts.add_contact(name, address) self.speak_dialog("contact_added", {"contact": name}, wait=True) # update contact (address exist) else: contact = self.contacts.search_contact(address) or contact if prompt and \ (name != contact["name"] or address != contact["url"]): if self.ask_yesno("update_confirm", data={"contact": name}) == "no": return self.log.info("Updating contact {name}:{address}".format( name=name, address=address)) if name != contact["name"]: # new name (unique ID) self.contacts.remove_contact(contact["name"]) self.contacts.add_contact(name, address) self.speak_dialog("contact_updated", {"contact": name}, wait=True) elif address != contact["url"]: # new address self.contacts.update_contact(name, address) self.speak_dialog("contact_updated", {"contact": name}, wait=True) def delete_contact(self, name, prompt=False): name = name.replace("_", " ").replace("-", " ").strip() if self.contacts.get_contact(name): if prompt: if self.ask_yesno("delete_confirm", data={"contact": name}) == "no": return self.log.info("Deleting contact {name}".format(name=name)) self.contacts.remove_contact(name) self.speak_dialog("contact_deleted", {"contact": name}) def speak_and_hang(self, speech): self._wait_until_call_established() self.sip.mute_mic() self.sip.speak(speech) self.hang_call() def handle_call_established(self): self.handle_gui_state("Connected") if self.cb is not None: self.cb() self.cb = None def handle_login_success(self): pass #self.speak_dialog("sip_login_success") def handle_login_failure(self): self.log.error("Log in failed!") self.sip.quit() self.sip = None self.intercepting_utterances = False # just in case if self.settings["user"] is not None and \ self.settings["gateway"] is not None and \ self.settings["password"] is not None: self.speak_dialog("sip_login_fail") else: self.speak_dialog("credentials_missing") def handle_incoming_call(self, number): if number.startswith("sip:"): number = number[4:] if self.settings["auto_answer"]: self.accept_call() self._wait_until_call_established() self.sip.speak(self.settings["auto_speech"]) self.hang_call() self.log.info("Auto answered call") return if self.settings["auto_reject"]: self.log.info("Auto rejected call") self.hang_call() return contact = self.contacts.search_contact(number) if contact: self.gui["currentContact"] = contact["name"] self.handle_gui_state("Incoming") self.speak_dialog("incoming_call", {"contact": contact["name"]}, wait=True) else: self.gui["currentContact"] = "Unknown" self.handle_gui_state("Incoming") self.speak_dialog("incoming_call_unk", wait=True) self.intercepting_utterances = True def handle_call_ended(self, reason): self.handle_gui_state("Hang") self.log.info("Call ended") self.log.debug("Reason: " + reason) self.intercepting_utterances = False self.speak_dialog("call_ended", {"reason": reason}) self.on_hold = False self.muted = False # intents def handle_utterance(self, utterance): # handle both fallback and converse stage utterances # control ongoing calls here if self.intercepting_utterances: if self.voc_match(utterance, 'reject'): self.hang_call() self.speak_dialog("call_rejected") elif self.muted or self.on_hold: # allow normal mycroft interaction in these cases only return False elif self.voc_match(utterance, 'accept'): speech = None if self.say_vocab and self.voc_match(utterance, 'and_say'): for word in self.say_vocab: if word in utterance: speech = utterance.split(word)[1] break # answer call self.accept_call() if speech: self.speak_and_hang(speech) else: # User 2 User pass elif self.voc_match(utterance, 'hold_call'): self.on_hold = True self.sip.hold() self.speak_dialog("call_on_hold") elif self.voc_match(utterance, 'mute'): self.muted = True self.sip.mute_mic() self.speak_dialog("call_muted") # if in call always intercept utterance / assume false activation return True return False @intent_file_handler("restart.intent") def handle_restart(self, message): if self.sip is not None: self.sip.stop() self.sip = None self.handle_login(message) @intent_file_handler("login.intent") def handle_login(self, message): if self.sip is None: if self.settings["gateway"]: self.speak_dialog("sip_login", {"gateway": self.settings["gateway"]}) self.start_sip() else: self.speak_dialog("credentials_missing") else: self.speak_dialog("sip_running") if self.ask_yesno("want_restart") == "yes": self.handle_restart(message) @intent_file_handler("call.intent") def handle_call_contact(self, message): name = message.data["contact"] self.log.debug("Placing call to " + name) contact = self.contacts.get_contact(name) if contact is not None: self.gui["currentContact"] = name self.speak_dialog("calling", {"contact": name}, wait=True) self.intercepting_utterances = True address = contact["url"] self.sip.call(address) else: self.speak_dialog("no_such_contact", {"contact": name}) @intent_file_handler("call_and_say.intent") def handle_call_contact_and_say(self, message): utterance = message.data["speech"] def cb(): self.speak_and_hang(utterance) self.cb = cb self.handle_call_contact(message) @intent_file_handler("resume_call.intent") @intent_file_handler("unmute.intent") def handle_resume(self, message): # TODO can both happen at same time ? if self.on_hold: self.on_hold = False self.speak_dialog("resume_call", wait=True) self.sip.resume() elif self.muted: self.muted = False self.speak_dialog("unmute_call", wait=True) self.sip.unmute_mic() else: self.speak_dialog("no_call") @intent_file_handler("reject_all.intent") def handle_auto_reject(self, message): self.settings["auto_reject"] = True self.settings["auto_answer"] = False self.speak_dialog("rejecting_all") @intent_file_handler("answer_all.intent") def handle_auto_answer(self, message): self.settings["auto_answer"] = True self.settings["auto_reject"] = False self.speak_dialog("accept_all", {"speech": self.settings["auto_speech"]}) @intent_file_handler("answer_all_and_say.intent") def handle_auto_answer_with(self, message): self.settings["auto_speech"] = message.data["speech"] self.handle_auto_answer(message) @intent_file_handler("contacts_list.intent") def handle_list_contacts(self, message): self.gui["contactListModel"] = self.contacts.list_contacts() self.handle_gui_state("Contacts") users = self.contacts.list_contacts() self.speak_dialog("contacts_list") for user in users: self.speak(user["name"]) @intent_file_handler("contacts_number.intent") def handle_number_of_contacts(self, message): users = self.contacts.list_contacts() self.speak_dialog("contacts_number", {"number": len(users)}) @intent_file_handler("disable_auto.intent") def handle_no_auto_answering(self, message): self.settings["auto_answer"] = False self.settings["auto_reject"] = False self.speak_dialog("no_auto") @intent_file_handler("call_status.intent") def handle_status(self, message): if self.sip is not None: self.speak_dialog("call_status", {"status": self.sip.call_status}) else: self.speak_dialog("sip_not_running") # sipxcom intents @intent_file_handler("sipxcom_sync.intent") def handle_syncs(self, message): self.sipxcom_sync() def sipxcom_sync(self): try: sipxcom = SipXCom(self.settings["sipxcom_user"], self.settings["sipxcom_password"], self.settings["sipxcom_gateway"]) if sipxcom.check_auth(): contacts = sipxcom.get_contacts(True) for c in contacts: self.add_new_contact(c["name"], c["url"], prompt=True) else: self.speak_dialog("sipxcom_badcreds") except Exception as e: self.speak_dialog("sipxcom_sync_error") self.log.exception(e) # converse def converse_keepalive(self): while True: if self.settings["intercept_allowed"]: # avoid converse timed_out self.make_active() time.sleep(60) def converse(self, utterances, lang="en-us"): if self.settings["intercept_allowed"] and utterances is not None: self.log.debug( "{name}: Intercept stage".format(name=self.skill_name)) return self.handle_utterance(utterances[0]) return False # fallback def handle_fallback(self, message): utterance = message.data["utterance"] self.log.debug("{name}: Fallback stage".format(name=self.skill_name)) return self.handle_utterance(utterance) # shutdown def stop_converse(self): if self._converse_keepalive is not None and \ self._converse_keepalive.running: self._converse_keepalive.join(2) def shutdown(self): if self.sip is not None: self.sip.quit() self.stop_converse() super(SIPSkill, self).shutdown() # Handle GUI States Centerally def handle_gui_state(self, state): self.gui["call_muted"] = False if state == "Hang": self.gui["pageState"] = "Disconnected" self.gui.show_page("voipLoader.qml", override_idle=True) time.sleep(5) self.gui["currentContact"] = "Unknown" self.gui.clear() self.enclosure.display_manager.remove_active() self.bus.emit(Message("mycroft.mark2.reset_idle")) elif state == "Clear": self.gui["currentContact"] = "Unknown" self.gui.clear() self.enclosure.display_manager.remove_active() self.bus.emit(Message("mycroft.mark2.reset_idle")) else: self.gui["pageState"] = state self.gui.show_page("voipLoader.qml", override_idle=True) # Handle GUI Show Home @intent_file_handler("show_home.intent") def show_homescreen(self): self.handle_gui_state("Homescreen") # Handle Config From GUI def handle_config_from_gui(self, message): if message.data["type"] is not "SipXCom": self.settings["user"] = message.data["username"] self.settings["gateway"] = message.data["gateway"] self.settings["password"] = message.data["password"] else: self.settings["sipxcom_user"] = message.data["username"] self.settings["sipxcom_gateway"] = message.data["gateway"] self.settings["sipxcom_password"] = message.data["password"] self.sip.quit() self.sip = None self.handle_restart({}) # Handle Contact Calling From GUI def handle_call_contact_from_gui(self, message): if self.sip is not None: self.handle_gui_state("Outgoing") self.handle_call_contact(message) else: self.handle_call_failure_gui() # Handle Failure def handle_call_failure_gui(self): self.handle_gui_state("Failed") sleep(3) self.handle_gui_state("Clear")
class SIPSkill(FallbackSkill): def __init__(self): super(SIPSkill, self).__init__(name='SIPSkill') # skill settings defaults if "intercept_allowed" not in self.settings: self.settings["intercept_allowed"] = True if "confirm_operations" not in self.settings: self.settings["confirm_operations"] = True if "debug" not in self.settings: self.settings["debug"] = True if "priority" not in self.settings: self.settings["priority"] = 50 if "timeout" not in self.settings: self.settings["timeout"] = 15 # auto answer incoming calls if "auto_answer" not in self.settings: self.settings["auto_answer"] = False if "auto_reject" not in self.settings: self.settings["auto_reject"] = False if "auto_speech" not in self.settings: self.settings["auto_speech"] = "I am busy, try again later" # web ui contacts management self.settings["add_contact"] = False self.settings["delete_contact"] = False if "contact_name" not in self.settings: self.settings["contact_name"] = None if "contact_address" not in self.settings: self.settings["contact_address"] = None # sip creds if "user" not in self.settings: self.settings["user"] = None if "gateway" not in self.settings: self.settings["gateway"] = None if "password" not in self.settings: self.settings["password"] = None # events self.settings_change_callback = self._on_web_settings_change self.namespace = self.__class__.__name__.lower() self.skill_name = "Voice Over IP" # state trackers self._converse_keepalive = None self.on_hold = False self.muted = False self.intercepting_utterances = False self._old_settings = dict(self.settings) self.sip = None self.say_vocab = None self.cb = None self.contacts = ContactList("mycroft_sip") def initialize(self): self.register_fallback(self.handle_fallback, int(self.settings["priority"])) self._converse_keepalive = create_daemon(self.converse_keepalive) say_voc = self.find_resource('and_say.voc', 'vocab') if say_voc: # load vocab and flatten into a simple list self.say_vocab = list(chain(*read_vocab_file(say_voc))) self.start_sip() def _on_web_settings_change(self): try: if self.settings["add_contact"]: if self.settings["contact_name"]: self.add_new_contact(self.settings["contact_name"], self.settings["contact_address"]) self.settings["add_contact"] = False if self.settings["delete_contact"]: if self.settings["contact_name"]: self.delete_contact(self.settings["contact_name"]) self.settings["delete_contact"] = False if self.sip is None: self.start_sip() else: for k in ["user", "password", "gateway"]: if self.settings[k] != self._old_settings[k]: self.speak_dialog("sip_restart") self.sip.quit() self.sip = None self.intercepting_utterances = False # just in case break self._old_settings = dict(self.settings) except Exception as e: self.log.exception(e) def start_sip(self): if self.sip is not None: self.sip.quit() sleep(0.5) self.sip = BareSIP(self.settings["user"], self.settings["password"], self.settings["gateway"], block=False, debug=self.settings["debug"]) self.sip.handle_incoming_call = self.handle_incoming_call self.sip.handle_call_ended = self.handle_call_ended self.sip.handle_login_failure = self.handle_login_failure self.sip.handle_login_success = self.handle_login_success self.sip.handle_call_established = self.handle_call_established def get_intro_message(self): # welcome dialog on skill install self.speak_dialog("intro", {"skill_name": self.skill_name}) # SIP def handle_call_established(self): if self.cb is not None: self.cb() self.cb = None def handle_login_success(self): self.speak_dialog("sip_login_success") def handle_login_failure(self): self.log.error("Log in failed!") self.sip.quit() self.sip = None self.intercepting_utterances = False # just in case if self.settings["user"] is not None and \ self.settings["gateway"] is not None and \ self.settings["password"] is not None: self.speak_dialog("sip_login_fail") else: self.speak_dialog("credentials_missing") def handle_incoming_call(self, number): if self.settings["auto_answer"]: self.accept_call() self.sip.speak(self.settings["auto_speech"]) self.hang_call() self.log.info("Auto answered call") return if self.settings["auto_reject"]: self.log.info("Auto rejected call") self.hang_call() return contact = self.contacts.search_contact(number) if contact: self.speak_dialog("incoming_call", {"contact": contact["name"]}, wait=True) else: self.speak_dialog("incoming_call", {"contact": number}, wait=True) self.speak_dialog("incoming_call_unk", wait=True) self.intercepting_utterances = True def handle_call_ended(self, reason): self.log.info("Call ended") self.log.debug("Reason: " + reason) self.intercepting_utterances = False not_errors = ["hanged up"] if reason.lower().strip() not in not_errors: sleep(1) self.speak_dialog("call_ended", {"reason": reason}) self.on_hold = False self.muted = False def accept_call(self): self.sip.accept_call() def hang_call(self): self.sip.hang() self.intercepting_utterances = False self.speak_dialog("call_finished") def add_new_contact(self, name, address, prompt=False): contact = self.contacts.get_contact(name) # new address if contact is None: self.log.info("Adding new contact {name}:{address}".format( name=name, address=address)) self.contacts.add_contact(name, address) self.speak_dialog("contact_added", {"contact": name}) # update contact (address exist) else: contact = self.contacts.search_contact(address) if prompt: if self.ask_yesno(self, "update_confirm", data={"contact": name}) == "no": return self.log.info("Updating contact {name}:{address}".format( name=name, address=address)) if name != contact["name"]: # new name (unique ID) self.contacts.remove_contact(contact["name"]) self.contacts.add_contact(name, address) else: # new address self.contacts.update_contact(name, address) self.speak_dialog("contact_updated", {"contact": name}) def delete_contact(self, name, prompt=False): if self.contacts.get_contact(name): if prompt: if self.ask_yesno(self, "delete_confirm", data={"contact": name}) == "no": return self.log.info("Deleting contact {name}".format(name=name)) self.contacts.remove_contact(name) self.speak_dialog("contact_deleted", {"contact": name}) # intents def handle_utterance(self, utterance): # handle both fallback and converse stage utterances # control ongoing calls here if self.intercepting_utterances: if self.voc_match(utterance, 'reject'): self.hang_call() self.speak_dialog("call_rejected") elif self.muted or self.on_hold: # allow normal mycroft interaction in these cases only return False elif self.voc_match(utterance, 'accept'): speech = None if self.say_vocab and self.voc_match(utterance, 'and_say'): for word in self.say_vocab: if word in utterance: speech = utterance.split(word)[1] break # answer call self.accept_call() if speech: # TTS in voice call and hang while not self.sip.call_established: sleep(0.5) # TODO timeout in case of errors self.sip.speak(speech) self.hang_call() else: # User 2 User pass elif self.voc_match(utterance, 'hold_call'): self.on_hold = True self.sip.hold() self.speak_dialog("call_on_hold") elif self.voc_match(utterance, 'mute'): self.muted = True self.sip.mute() self.speak_dialog("call_muted") # if in call always intercept utterance / assume false activation return True return False @intent_file_handler("restart.intent") def handle_restart(self, message): if self.sip is not None: self.sip.stop() self.sip = None self.handle_login(message) @intent_file_handler("login.intent") def handle_login(self, message): if self.sip is None: if self.settings["gateway"]: self.speak_dialog("sip_login", {"gateway": self.settings["gateway"]}) self.start_sip() else: self.speak_dialog("credentials_missing") else: self.speak_dialog("sip_running") if self.ask_yesno("want_restart") == "yes": self.handle_restart(message) @intent_file_handler("call.intent") def handle_call_contact(self, message): name = message.data["contact"] self.log.debug("Placing call to " + name) contact = self.contacts.get_contact(name) if contact is not None: self.speak_dialog("calling", {"contact": name}, wait=True) self.intercepting_utterances = True address = contact["url"] self.sip.call(address) else: self.speak_dialog("no_such_contact", {"contact": name}) @intent_file_handler("call_and_say.intent") def handle_call_contact_and_say(self, message): utterance = message.data["speech"] def cb(): self.sip.speak(utterance) self.hang_call() self.cb = cb self.handle_call_contact(message) @intent_file_handler("resume_call.intent") @intent_file_handler("unmute.intent") def handle_resume(self, message): # TODO can both happen at same time ? if self.on_hold: self.on_hold = False self.speak_dialog("resume_call", wait=True) self.sip.resume() elif self.muted: self.muted = False self.speak_dialog("unmute_call", wait=True) self.sip.unmute() else: self.speak_dialog("no_call") @intent_file_handler("reject_all.intent") def handle_auto_reject(self, message): # TODO dont allow overwrite from web ui self.settings["auto_reject"] = True self.settings["auto_answer"] = False self.speak_dialog("rejecting_all") @intent_file_handler("answer_all.intent") def handle_auto_answer(self, message): # TODO dont allow overwrite from web ui self.settings["auto_answer"] = True self.settings["auto_reject"] = False self.speak_dialog("accept_all", {"speech": self.settings["auto_speech"]}) @intent_file_handler("answer_all_and_say.intent") def handle_auto_answer_with(self, message): self.settings["auto_speech"] = message.data["speech"] self.handle_auto_answer(message) @intent_file_handler("contacts_list.intent") def handle_list_contacts(self, message): users = self.contacts.list_contacts() self.speak_dialog("contacts_list") for user in users: self.speak(user["name"]) @intent_file_handler("contacts_number.intent") def handle_number_of_contacts(self, message): users = self.contacts.list_contacts() self.speak_dialog("contacts_number", {"number": len(users)}) # converse def converse_keepalive(self): while True: if self.settings["intercept_allowed"]: # avoid converse timed_out self.make_active() time.sleep(60) def converse(self, utterances, lang="en-us"): if self.settings["intercept_allowed"]: self.log.debug( "{name}: Intercept stage".format(name=self.skill_name)) return self.handle_utterance(utterances[0]) return False # fallback def handle_fallback(self, message): utterance = message.data["utterance"] self.log.debug("{name}: Fallback stage".format(name=self.skill_name)) return self.handle_utterance(utterance) # shutdown def stop_converse(self): if self._converse_keepalive is not None and \ self._converse_keepalive.running: self._converse_keepalive.join(2) def shutdown(self): if self.sip is not None: self.sip.quit() self.stop_converse() super(SIPSkill, self).shutdown()