def create_reminder(hour, minute, am_pm, message, to_about, timer: str = None) -> NoReturn: """Creates the lock file necessary to set a reminder. Args: hour: Hour of reminder time. minute: Minute of reminder time. am_pm: AM/PM of reminder time. message: Message to be reminded for. to_about: remind to or remind about as said in phrase. timer: Number of minutes/hours to reminder. """ if not os.path.isdir('reminder'): os.mkdir('reminder') pathlib.Path( os.path.join( "reminder", f"{hour}_{minute}_{am_pm}|{message.replace(' ', '_')}.lock") ).touch() if timer: logger.info( f"Reminder created for '{message}' at {hour}:{minute} {am_pm}") speaker.speak( text=f"{random.choice(conversation.acknowledgement)}! " f"I will remind you {to_about} {message}, after {timer}.") else: speaker.speak( text=f"{random.choice(conversation.acknowledgement)}! " f"I will remind you {to_about} {message}, at {hour}:{minute} {am_pm}." )
def ip_info(phrase: str) -> None: """Gets IP address of the host machine. Args: phrase: Takes the spoken phrase an argument and tells the public IP if requested. """ if "public" in phrase.lower(): if not internet_checker(): speaker.speak( text=f"You are not connected to the internet {env.title}!") return if ssid := get_ssid(): ssid = f"for the connection {ssid} " else: ssid = "" output = None try: output = f"My public IP {ssid}is " \ f"{json.load(urllib.request.urlopen(url='https://ipinfo.io/json')).get('ip')}" except (urllib.error.HTTPError, urllib.error.URLError) as error: logger.error(error) try: output = output or f"My public IP {ssid}is " \ f"{json.loads(urllib.request.urlopen(url='http://ip.jsontest.com').read()).get('ip')}" except (urllib.error.HTTPError, urllib.error.URLError) as error: logger.error(error) if not output: output = f"I was unable to fetch the public IP {env.title}!"
def reminder(phrase: str) -> None: """Passes hour, minute, am/pm and reminder message to Reminder class which initiates a thread for reminder. Args: phrase: Takes the voice recognized statement as argument and extracts the time and message from it. """ message = re.search(' to (.*) at ', phrase) or re.search(' about (.*) at ', phrase) or \ re.search(' to (.*) after ', phrase) or re.search(' about (.*) after ', phrase) or \ re.search(' to (.*) in ', phrase) or re.search(' about (.*) in ', phrase) or \ re.search(' to (.*)', phrase) or re.search(' about (.*)', phrase) if not message: speaker.speak( text= 'Reminder format should be::Remind me to do something, at some time.' ) return to_about = 'about' if 'about' in phrase else 'to' if 'minute' in phrase: if minutes := support.extract_nos(input_=phrase, method=int): min_ = 'minutes' if minutes > 1 else 'minute' hour, minute, am_pm = ( datetime.now() + timedelta(minutes=minutes)).strftime("%I %M %p").split() create_reminder(hour=hour, minute=minute, am_pm=am_pm, message=message.group(1).strip(), timer=f"{minutes} {min_}", to_about=to_about) return
def sentry_mode() -> NoReturn: """Listens forever and invokes ``initiator()`` when recognized. Stops when ``restart`` table has an entry. See Also: - Gets invoked only when run from Mac-OS older than 10.14. - A regular listener is used to convert audio to text. - The text is then condition matched for wake-up words. - Additional wake words can be passed in a list as an env var ``LEGACY_KEYWORDS``. """ try: while True: sys.stdout.write("\rSentry Mode") if wake_word := listener.listen(timeout=10, phrase_limit=2.5, sound=False, stdout=False): support.flush_screen() if any(word in wake_word.lower() for word in env.legacy_keywords): playsound(sound=indicators.acknowledgement, block=False) if phrase := listener.listen(timeout=env.timeout, phrase_limit=env.phrase_limit, sound=False): initiator(phrase=phrase, should_return=True) speaker.speak(run=True) if flag := support.check_restart(): logger.info(f"Restart condition is set to {flag[0]} by {flag[1]}") if flag[1] == "OFFLINE": stop_processes() for _handler in logger.handlers: if isinstance(_handler, logging.FileHandler): logger.removeHandler(hdlr=_handler) handler = custom_handler() logger.info(f"Switching to {handler.baseFilename}") logger.addHandler(hdlr=handler) shared.processes = start_processes() else: stop_processes(func_name=flag[1]) shared.processes[flag[1]] = start_processes(flag[1])
def shutdown(proceed: bool = False) -> None: """Gets confirmation and turns off the machine. Args: proceed: Boolean value whether to get confirmation. Raises: StopSignal: To stop Jarvis' PID. """ if not proceed: speaker.speak( text= f"{random.choice(conversation.confirmation)} turn off the machine?", run=True) converted = listener.listen(timeout=3, phrase_limit=3) else: converted = 'yes' if converted and any(word in converted.lower() for word in keywords.ok): stop_terminals() if env.macos: subprocess.call( ['osascript', '-e', 'tell app "System Events" to shut down']) else: os.system("shutdown /s /t 1") raise StopSignal else: speaker.speak(text=f"Machine state is left intact {env.title}!") return
def start(self) -> NoReturn: """Runs ``audio_stream`` in a forever loop and calls ``initiator`` when the phrase ``Jarvis`` is heard.""" try: while True: sys.stdout.write("\rSentry Mode") if not self.audio_stream: self.open_stream() pcm = struct.unpack_from("h" * self.detector.frame_length, self.audio_stream.read(num_frames=self.detector.frame_length, exception_on_overflow=False)) self.recorded_frames.append(pcm) if self.detector.process(pcm=pcm) >= 0: playsound(sound=indicators.acknowledgement, block=False) self.close_stream() if phrase := listener.listen(timeout=env.timeout, phrase_limit=env.phrase_limit, sound=False): initiator(phrase=phrase, should_return=True) speaker.speak(run=True) if flag := support.check_restart(): logger.info(f"Restart condition is set to {flag[0]} by {flag[1]}") if flag[1] == "OFFLINE": stop_processes() for _handler in logger.handlers: if isinstance(_handler, logging.FileHandler): logger.removeHandler(hdlr=_handler) handler = custom_handler() logger.info(f"Switching to {handler.baseFilename}") logger.addHandler(hdlr=handler) shared.processes = start_processes() else: stop_processes(func_name=flag[1]) shared.processes[flag[1]] = start_processes(flag[1]) if flag := support.check_stop(): logger.info(f"Stopper condition is set to {flag[0]} by {flag[1]}") self.stop() terminator()
def volume(phrase: str = None, level: int = None) -> None: """Controls volume from the numbers received. Defaults to 50%. See Also: SetVolume for Windows: https://rlatour.com/setvol/ Args: phrase: Takes the phrase spoken as an argument. level: Level of volume to which the system has to set. """ if not level: if 'mute' in phrase.lower(): level = 0 elif 'max' in phrase.lower() or 'full' in phrase.lower(): level = 100 else: level = support.extract_nos(input_=phrase, method=int) if level is None: level = env.volume support.flush_screen() if env.macos: os.system(f'osascript -e "set Volume {round((8 * level) / 100)}"') else: os.system(f'SetVol.exe {level}') support.flush_screen() if phrase: speaker.speak(text=f"{random.choice(conversation.acknowledgement)}!")
def kill_alarm(phrase: str) -> None: """Removes lock file to stop the alarm which rings only when the certain lock file is present. Args: phrase: Takes the voice recognized statement as argument and extracts time from it. """ word = 'timer' if 'timer' in phrase else 'alarm' alarm_state = support.lock_files(alarm_files=True) if not alarm_state: speaker.speak(text=f"You have no {word}s set {env.title}!") elif len(alarm_state) == 1: hour, minute, am_pm = alarm_state[0][0:2], alarm_state[0][3:5], alarm_state[0][6:8] os.remove(f"alarm/{alarm_state[0]}") speaker.speak(text=f"Your {word} at {hour}:{minute} {am_pm} has been silenced {env.title}!") else: speaker.speak(text=f"Your {word}s are at {', and '.join(alarm_state).replace('.lock', '')}. " f"Please let me know which {word} you want to remove.", run=True) if not (converted := listener.listen(timeout=3, phrase_limit=4)): return alarm_time = converted.split()[0] am_pm = converted.split()[-1] if ":" in converted: hour = int(alarm_time.split(":")[0]) minute = int(alarm_time.split(":")[-1]) else: hour = int(alarm_time.split()[0]) minute = 0 hour, minute = f"{hour:02}", f"{minute:02}" am_pm = str(am_pm).replace('a.m.', 'AM').replace('p.m.', 'PM') if os.path.exists(f'alarm/{hour}_{minute}_{am_pm}.lock'): os.remove(f"alarm/{hour}_{minute}_{am_pm}.lock") speaker.speak(text=f"Your {word} at {hour}:{minute} {am_pm} has been silenced {env.title}!") else: speaker.speak(text=f"I wasn't able to find your {word} at {hour}:{minute} {am_pm}. Try again.")
def restart(ask: bool = True) -> None: """Restart triggers ``restart.py`` which in turn starts Jarvis after 5 seconds. Warnings: - | Restarts the machine without approval when ``uptime`` is more than 2 days as the confirmation is requested | in `system_vitals <https://thevickypedia.github.io/Jarvis/#executors.system.system_vitals>`__. - This is done ONLY when the system vitals are read, and the uptime is more than 2 days. Args: ask: Boolean flag to get confirmation from user. Raises: StopSignal: To stop Jarvis' PID. """ if ask: speaker.speak( text=f"{random.choice(conversation.confirmation)} restart your " f"{shared.hosted_device.get('device')}?", run=True) converted = listener.listen(timeout=3, phrase_limit=3) else: converted = 'yes' if any(word in converted.lower() for word in keywords.ok): stop_terminals() if env.macos: subprocess.call( ['osascript', '-e', 'tell app "System Events" to restart']) else: os.system("shutdown /r /t 1") raise StopSignal else: speaker.speak(text=f"Machine state is left intact {env.title}!") return
def alpha(text: str) -> bool: """Uses wolfram alpha API to fetch results for uncategorized phrases heard. Args: text: Takes the voice recognized statement as argument. Notes: Handles broad ``Exception`` clause raised when Full Results API did not find an input parameter while parsing. Returns: bool: Boolean True if wolfram alpha API is unable to fetch consumable results. References: `Error 1000 <https://products.wolframalpha.com/show-steps-api/documentation/#:~:text=(Error%201000)>`__ """ if not env.wolfram_api_key: return False alpha_client = wolframalpha.Client(app_id=env.wolfram_api_key) try: res = alpha_client.query(text) except Exception: # noqa return False if res['@success'] == 'false': return False else: try: response = next(res.results).text.splitlines()[0] response = re.sub(r'(([0-9]+) \|)', '', response).replace(' |', ':').strip() if response == '(no data available)': return False speaker.speak(text=response) return True except (StopIteration, AttributeError): return False
def renew() -> None: """Keeps listening and sends the response to ``conditions()`` function. Notes: - This function runs only for a minute. - split_phrase(converted) is a condition so that, loop breaks when if sleep in ``conditions()`` returns True. """ speaker.speak(run=True) waiter = 0 while waiter < 12: waiter += 1 if waiter == 1: converted = listener.listen(timeout=3, phrase_limit=5) else: converted = listener.listen(timeout=3, phrase_limit=5, sound=False) if not converted: continue remove = ['buddy', 'jarvis', 'hey', 'hello'] converted = ' '.join( [i for i in converted.split() if i.lower() not in remove]) if converted: if split_phrase( phrase=converted ): # should_return flag is not passed which will default to False break # split_phrase() returns a boolean flag from conditions. conditions return True only for sleep elif any(word in converted.lower() for word in remove): continue speaker.speak(run=True)
def locate_places(phrase: str = None) -> None: """Gets location details of a place. Args: phrase: Takes the phrase spoken as an argument. """ place = support.get_capitalized(phrase=phrase) if phrase else None # if no words found starting with an upper case letter, fetches word after the keyword 'is' eg: where is Chicago if not place: keyword = "is" before_keyword, keyword, after_keyword = phrase.partition(keyword) place = after_keyword.replace(" in", "").strip() if not place: if shared.called_by_offline: speaker.speak(text=f"I need a location to get you the details {env.title}!") return speaker.speak(text="Tell me the name of a place!", run=True) if not (converted := listener.listen(timeout=3, phrase_limit=4)) or "exit" in converted or "quit" in converted \ or "Xzibit" in converted: return place = support.get_capitalized(phrase=converted) if not place: keyword = "is" before_keyword, keyword, after_keyword = converted.partition(keyword) place = after_keyword.replace(" in", "").strip()
def restart_control(phrase: str = None, quiet: bool = False) -> NoReturn: """Controls the restart functions based on the user request. Args: phrase: Takes the phrase spoken as an argument. quiet: Take a boolean flag to restart without warning. """ if phrase and ('pc' in phrase.lower() or 'computer' in phrase.lower() or 'machine' in phrase.lower()): logger.info( f'JARVIS::Restart for {shared.hosted_device.get("device")} has been requested.' ) restart() else: caller = sys._getframe(1).f_code.co_name # noqa logger.info(f'Called by {caller}') if quiet: # restarted due internal errors logger.info(f"Restarting {caller}") elif shared.called_by_offline: # restarted via automator logger.info("Restarting all background processes!") caller = "OFFLINE" else: speaker.speak( text= "I didn't quite get that. Did you mean restart your computer?") return with db.connection: cursor = db.connection.cursor() cursor.execute("INSERT INTO restart (flag, caller) VALUES (?,?);", (True, caller)) cursor.connection.commit()
def delete_todo() -> None: """Drops the table ``tasks`` from the database.""" with tdb.connection: cursor = tdb.connection.cursor() cursor.execute('DROP TABLE IF EXISTS tasks') cursor.connection.commit() speaker.speak( text=f"I've dropped the table: tasks from the database {env.title}.")
def repeat() -> NoReturn: """Repeats whatever is heard.""" speaker.speak(text="Please tell me what to repeat.", run=True) if keyword := listener.listen(timeout=3, phrase_limit=10): if 'exit' in keyword or 'quit' in keyword or 'Xzibit' in keyword: pass else: speaker.speak(text=f"I heard {keyword}")
def time_travel() -> None: """Triggered only from ``initiator()`` to give a quick update on the user's daily routine.""" part_day = support.part_of_day() speaker.speak(text=f"Good {part_day} {env.name}!") if part_day == 'Night': if event := support.celebrate(): speaker.speak(text=f'Happy {event}!') return
def flip_a_coin() -> NoReturn: """Says ``heads`` or ``tails`` from a random choice.""" playsound(sound=indicators.coin, block=True) if not shared.called_by_offline else None speaker.speak( text= f"""{random.choice(['You got', 'It landed on', "It's"])} {random.choice(['heads', 'tails'])} {env.title}""" )
def delete_todo_items() -> None: """Deletes items from an existing to-do list.""" speaker.speak(text=f"Which one should I remove {env.title}?", run=True) if not (item := listener.listen(timeout=3, phrase_limit=5) ) or 'exit' in item or 'quit' in item or 'Xzibit' in item: speaker.speak( text=f'Your to-do list has been left intact {env.title}.') return
def add_todo() -> None: """Adds new items to the to-do list.""" speaker.speak(text=f"What's your plan {env.title}?", run=True) if not (item := listener.listen(timeout=3, phrase_limit=5) ) or 'exit' in item or 'quit' in item or 'Xzibit' in item: speaker.speak( text=f'Your to-do list has been left intact {env.title}.') return
def initialize() -> None: """Awakens from sleep mode. ``greet_check`` is to ensure greeting is given only for the first function call.""" if shared.greeting: speaker.speak(text="What can I do for you?") else: speaker.speak(text=f'Good {support.part_of_day()}.') shared.greeting = True renew()
def pc_sleep() -> NoReturn: """Locks the host device using osascript and reduces brightness to bare minimum.""" Thread(target=decrease_brightness).start() # os.system("""osascript -e 'tell app "System Events" to sleep'""") # requires restarting Jarvis manually os.system( """osascript -e 'tell application "System Events" to keystroke "q" using {control down, command down}'""" ) if not (shared.called['report'] or shared.called['time_travel']): speaker.speak(text=random.choice(conversation.acknowledgement))
def speed_test() -> None: """Initiates speed test and says the ping rate, download and upload speed. References: Number of threads per core: https://psutil.readthedocs.io/en/latest/#psutil.cpu_count """ if not (st := internet_checker()): speaker.speak( text=f"You're not connected to the internet {env.title}!") return
def sprint_name() -> NoReturn: """Generates a random sprint name.""" response = requests.get(url="https://sprint-name-gen.herokuapp.com/") if not response.ok: speaker.speak( text= "I wasn't able to get a sprint name sir! Why not name it, Jarvis failed?" ) return soup = BeautifulSoup(response.content, 'html.parser') name = soup.find('span', {'class': 'sprint-name'}).text speaker.speak(text=name)
def location() -> NoReturn: """Gets the user's current location.""" try: with open(fileio.location) as file: current_location = yaml.load(stream=file, Loader=yaml.FullLoader) except yaml.YAMLError as error: logger.error(error) speaker.speak(text=f"I'm sorry {env.title}! I wasn't able to get the location details. Please check the logs.") return speaker.speak(text=f"I'm at {current_location.get('address', {}).get('road', '')} - " f"{current_location.get('address', {}).get('city', '')} " f"{current_location.get('address', {}).get('state', '')} - " f"in {current_location.get('address', {}).get('country', '')}")
def wikipedia_() -> None: """Gets any information from wikipedia using its API.""" speaker.speak(text="Please tell the keyword.", run=True) if keyword := listener.listen(timeout=3, phrase_limit=5): if any(word in keyword.lower() for word in keywords.exit_): return else: sys.stdout.write( f"\rGetting your info from Wikipedia API for {keyword}") try: result = wikipedia.summary(keyword) except wikipedia.DisambiguationError as error: sys.stdout.write(f"\r{error}") speaker.speak( text= f"Your keyword has multiple results {env.title}. {' '.join(error.options)}" "Please pick one and try again.") if shared.called_by_offline: return speaker.speak(run=True) if not (keyword1 := listener.listen(timeout=3, phrase_limit=5)): return result = wikipedia.summary(keyword1) except wikipedia.PageError: speaker.speak( text= f"I'm sorry {env.title}! I didn't get a response for the phrase: {keyword}." ) return
def news(news_source: str = 'fox') -> None: """Says news around the user's location. Args: news_source: Source from where the news has to be fetched. Defaults to ``fox``. """ if not env.news_api: logger.warning("News apikey not found.") support.no_env_vars() return sys.stdout.write(f'\rGetting news from {news_source} news.') news_client = NewsApiClient(api_key=env.news_api) try: all_articles = news_client.get_top_headlines( sources=f'{news_source}-news') except newsapi_exception.NewsAPIException: speaker.speak( text=f"I wasn't able to get the news {env.title}! " "I think the News API broke, you may try after sometime.") return speaker.speak(text="News around you!") speaker.speak(text=' '.join( [article['title'] for article in all_articles['articles']])) if shared.called_by_offline: return if shared.called['report'] or shared.called['time_travel']: speaker.speak(run=True)
def google_search(phrase: str = None) -> None: """Opens up a Google search for the phrase received. If nothing was received, gets phrase from user. Args: phrase: Takes the phrase spoken as an argument. """ phrase = phrase.split('for')[-1] if 'for' in phrase else None if not phrase: speaker.speak(text="Please tell me the search phrase.", run=True) if (converted := listener.listen(timeout=3, phrase_limit=5)) or 'exit' in converted or 'quit' in converted or \ 'xzibit' in converted or 'cancel' in converted: return else: phrase = converted.lower()
def avail_check(function_to_call: Callable) -> NoReturn: """Speaks an error message if any of the lights aren't reachable. Args: function_to_call: Takes the function/method that has to be called as an argument. """ status = ThreadPool(processes=1).apply_async(func=thread_worker, args=[function_to_call]) speaker.speak(run=True) if failed := status.get(timeout=5): plural_ = "lights aren't available right now!" if failed > 1 else "light isn't available right now!" speaker.speak( text= f"I'm sorry sir! {support.number_to_words(input_=failed, capitalize=True)} {plural_}" )
def robinhood() -> None: """Gets investment details from robinhood API.""" if not all([env.robinhood_user, env.robinhood_pass, env.robinhood_qr]): logger.warning("Robinhood username, password or QR code not found.") support.no_env_vars() return sys.stdout.write("\rGetting your investment details.") rh = Robinhood() rh.login(username=env.robinhood_user, password=env.robinhood_pass, qr_code=env.robinhood_qr) raw_result = rh.positions() result = raw_result["results"] stock_value = watcher(rh, result) speaker.speak(text=stock_value)
def sleep_control(phrase: str) -> bool: """Controls whether to stop listening or to put the host device on sleep. Args: phrase: Takes the phrase spoken as an argument. """ phrase = phrase.lower() if 'pc' in phrase or 'computer' in phrase or 'imac' in phrase or \ 'screen' in phrase: pc_sleep() if env.macos else support.missing_windows_features() else: speaker.speak( text=f"Activating sentry mode, enjoy yourself {env.title}!") if shared.greeting: shared.greeting = False return True