diff --git a/.gitignore b/.gitignore index e8a81d67b..9d1792882 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ myinfo\.csv .vscode .buildozer .mypy_cache +bin diff --git a/.travis.yml b/.travis.yml index 8719897cb..42679568d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,15 @@ language: python -matrix: +jobs: cache: pip include: - name: "Python 3.7 on Linux" os: linux dist: xenial - sudo: true python: 3.7 env: TOXENV=py37 - name: "Python 3.7 on MacOS" os: osx language: generic - sudo: required env: TOXENV=py37 - name: "Python 3.7 on Windows" os: windows diff --git a/README.md b/README.md index 803910bed..b504a7486 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Jarvis is a simple personal assistant for Linux, MacOS and Windows which works o ## Getting Started -In order to start Jarvis just clone [this repository](https://github.com/sukeesh/Jarvis.git) repository and run `python installer`. +In order to start Jarvis just clone [this repository](https://github.com/sukeesh/Jarvis.git) and run `python installer`. Run **Jarvis** from anywhere by command `jarvis` @@ -29,8 +29,8 @@ You can start by typing `help` within the Jarvis command line to check what Jarv - PRs are accepted!! - We follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) guidelines. Before making a PR, make sure that your code is according to PEP 8 standards. -- If you have some ideas for new features and you don't have time to implement them please open an issue with the tag new_feature -- Please don't forget to comment (document) your code +- If you have some ideas for new features and you don't have time to implement them please open an issue with the tag new_feature. +- Please don't forget to comment (document) your code! @@ -100,4 +100,4 @@ See also the list of [contributors](https://github.com/sukeesh/Jarvis/graphs/con ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/buildozer.spec b/buildozer.spec new file mode 100644 index 000000000..4739b320e --- /dev/null +++ b/buildozer.spec @@ -0,0 +1,319 @@ +[app] +title = Jarvis +package.name = Jarvis +package.domain = org.jarvis + +source.dir = jarviscli + + +# (list) Source files to include (let empty to include all the files) +source.include_exts = py,png,jpg,kv,atlas + +# (list) List of inclusions using pattern matching +#source.include_patterns = assets/*,images/*.png + +# (list) Source files to exclude (let empty to not exclude anything) +#source.exclude_exts = spec + +# (list) List of directory to exclude (let empty to not exclude anything) +source.exclude_dirs = tests, bin, env + +# (list) List of exclusions using pattern matching +#source.exclude_patterns = license,images/*/*.jpg + +# (str) Application versioning (method 1) +version = 0.1 + +# (str) Application versioning (method 2) +# version.regex = __version__ = ['"](.*)['"] +# version.filename = %(source.dir)s/main.py + +# (list) Application requirements +# comma separated e.g. requirements = sqlite3,kivy +requirements = python3,kivy,pluginmanager,colorama,requests,python-dateutil + +# (str) Custom source folders for requirements +# Sets custom source for any requirements with recipes +# requirements.source.kivy = ../../kivy + +# (list) Garden requirements +#garden_requirements = + +# (str) Presplash of the application +#presplash.filename = %(source.dir)s/data/presplash.png + +# (str) Icon of the application +#icon.filename = %(source.dir)s/data/icon.png + +# (str) Supported orientation (one of landscape, sensorLandscape, portrait or all) +orientation = all + +# (list) List of service to declare +#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY + +# +# OSX Specific +# + +# +# author = © Copyright Info + +# change the major version of python used by the app +osx.python_version = 3 + +# Kivy version to use +osx.kivy_version = 1.9.1 + +# +# Android specific +# + +# (bool) Indicate if the application should be fullscreen or not +fullscreen = 0 + +# (string) Presplash background color (for new android toolchain) +# Supported formats are: #RRGGBB #AARRGGBB or one of the following names: +# red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray, +# darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy, +# olive, purple, silver, teal. +#android.presplash_color = #FFFFFF + +# (list) Permissions +#android.permissions = INTERNET + +# (int) Target Android API, should be as high as possible. +#android.api = 27 + +# (int) Minimum API your APK will support. +android.minapi = 21 + +# (int) Android SDK version to use +#android.sdk = 20 + +# (str) Android NDK version to use +#android.ndk = 19b + +# (int) Android NDK API to use. This is the minimum API your app will support, it should usually match android.minapi. +android.ndk_api = 21 + +# (bool) Use --private data storage (True) or --dir public storage (False) +#android.private_storage = True + +# (str) Android NDK directory (if empty, it will be automatically downloaded.) +#android.ndk_path = + +# (str) Android SDK directory (if empty, it will be automatically downloaded.) +#android.sdk_path = + +# (str) ANT directory (if empty, it will be automatically downloaded.) +#android.ant_path = + +# (bool) If True, then skip trying to update the Android sdk +# This can be useful to avoid excess Internet downloads or save time +# when an update is due and you just want to test/build your package +# android.skip_update = False + +# (bool) If True, then automatically accept SDK license +# agreements. This is intended for automation only. If set to False, +# the default, you will be shown the license when first running +# buildozer. +# android.accept_sdk_license = False + +# (str) Android entry point, default is ok for Kivy-based app +#android.entrypoint = org.renpy.android.PythonActivity + +# (str) Android app theme, default is ok for Kivy-based app +# android.apptheme = "@android:style/Theme.NoTitleBar" + +# (list) Pattern to whitelist for the whole project +#android.whitelist = + +# (str) Path to a custom whitelist file +#android.whitelist_src = + +# (str) Path to a custom blacklist file +#android.blacklist_src = + +# (list) List of Java .jar files to add to the libs so that pyjnius can access +# their classes. Don't add jars that you do not need, since extra jars can slow +# down the build process. Allows wildcards matching, for example: +# OUYA-ODK/libs/*.jar +#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar + +# (list) List of Java files to add to the android project (can be java or a +# directory containing the files) +#android.add_src = + +# (list) Android AAR archives to add (currently works only with sdl2_gradle +# bootstrap) +#android.add_aars = + +# (list) Gradle dependencies to add (currently works only with sdl2_gradle +# bootstrap) +#android.gradle_dependencies = + +# (list) add java compile options +# this can for example be necessary when importing certain java libraries using the 'android.gradle_dependencies' option +# see https://developer.android.com/studio/write/java8-support for further information +# android.add_compile_options = "sourceCompatibility = 1.8", "targetCompatibility = 1.8" + +# (list) Gradle repositories to add {can be necessary for some android.gradle_dependencies} +# please enclose in double quotes +# e.g. android.gradle_repositories = "maven { url 'https://kotlin.bintray.com/ktor' }" +#android.add_gradle_repositories = + +# (list) packaging options to add +# see https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html +# can be necessary to solve conflicts in gradle_dependencies +# please enclose in double quotes +# e.g. android.add_packaging_options = "exclude 'META-INF/common.kotlin_module'", "exclude 'META-INF/*.kotlin_module'" +#android.add_gradle_repositories = + +# (list) Java classes to add as activities to the manifest. +#android.add_activites = com.example.ExampleActivity + +# (str) OUYA Console category. Should be one of GAME or APP +# If you leave this blank, OUYA support will not be enabled +#android.ouya.category = GAME + +# (str) Filename of OUYA Console icon. It must be a 732x412 png image. +#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png + +# (str) XML file to include as an intent filters in tag +#android.manifest.intent_filters = + +# (str) launchMode to set for the main activity +#android.manifest.launch_mode = standard + +# (list) Android additional libraries to copy into libs/armeabi +#android.add_libs_armeabi = libs/android/*.so +#android.add_libs_armeabi_v7a = libs/android-v7/*.so +#android.add_libs_arm64_v8a = libs/android-v8/*.so +#android.add_libs_x86 = libs/android-x86/*.so +#android.add_libs_mips = libs/android-mips/*.so + +# (bool) Indicate whether the screen should stay on +# Don't forget to add the WAKE_LOCK permission if you set this to True +#android.wakelock = False + +# (list) Android application meta-data to set (key=value format) +#android.meta_data = + +# (list) Android library project to add (will be added in the +# project.properties automatically.) +#android.library_references = + +# (list) Android shared libraries which will be added to AndroidManifest.xml using tag +#android.uses_library = + +# (str) Android logcat filters to use +#android.logcat_filters = *:S python:D + +# (bool) Copy library instead of making a libpymodules.so +#android.copy_libs = 1 + +# (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86, x86_64 +android.arch = armeabi-v7a + +# +# Python for android (p4a) specific +# + +# (str) python-for-android fork to use, defaults to upstream (kivy) +#p4a.fork = kivy + +# (str) python-for-android branch to use, defaults to master +#p4a.branch = master + +# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) +#p4a.source_dir = + +# (str) The directory in which python-for-android should look for your own build recipes (if any) +#p4a.local_recipes = + +# (str) Filename to the hook for p4a +#p4a.hook = + +# (str) Bootstrap to use for android builds +p4a.bootstrap = sdl2 + +# (int) port number to specify an explicit --port= p4a argument (eg for bootstrap flask) +#p4a.port = + + +# +# iOS specific +# + +# (str) Path to a custom kivy-ios folder +#ios.kivy_ios_dir = ../kivy-ios +# Alternately, specify the URL and branch of a git checkout: +ios.kivy_ios_url = https://github.com/kivy/kivy-ios +ios.kivy_ios_branch = master + +# Another platform dependency: ios-deploy +# Uncomment to use a custom checkout +#ios.ios_deploy_dir = ../ios_deploy +# Or specify URL and branch +ios.ios_deploy_url = https://github.com/phonegap/ios-deploy +ios.ios_deploy_branch = 1.7.0 + +# (str) Name of the certificate to use for signing the debug version +# Get a list of available identities: buildozer ios list_identities +#ios.codesign.debug = "iPhone Developer: ()" + +# (str) Name of the certificate to use for signing the release version +#ios.codesign.release = %(ios.codesign.debug)s + + +[buildozer] + +# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) +log_level = 2 + +# (int) Display warning if buildozer is run as root (0 = False, 1 = True) +warn_on_root = 1 + +# (str) Path to build artifact storage, absolute or relative to spec file +# build_dir = ./.buildozer + +# (str) Path to build output (i.e. .apk, .ipa) storage +# bin_dir = ./bin + +# ----------------------------------------------------------------------------- +# List as sections +# +# You can define all the "list" as [section:key]. +# Each line will be considered as a option to the list. +# Let's take [app] / source.exclude_patterns. +# Instead of doing: +# +#[app] +#source.exclude_patterns = license,data/audio/*.wav,data/images/original/* +# +# This can be translated into: +# +#[app:source.exclude_patterns] +#license +#data/audio/*.wav +#data/images/original/* +# + + +# ----------------------------------------------------------------------------- +# Profiles +# +# You can extend section / key with a profile +# For example, you want to deploy a demo version of your application without +# HD content. You could first change the title to add "(demo)" in the name +# and extend the excluded directories to remove the HD content. +# +#[app@demo] +#title = My Application (demo) +# +#[app:source.exclude_patterns@demo] +#images/hd/* +# +# Then, invoke the command line with the "demo" profile: +# +#buildozer --profile demo android debug diff --git a/doc/TESTING.md b/doc/TESTING.md index 428138340..b402b56d8 100644 --- a/doc/TESTING.md +++ b/doc/TESTING.md @@ -19,7 +19,7 @@ class XXXTest(PluginTest): self.test.run(TEST_STRING) # verify that code works - self.assertEqual(self.history_say.last_text, EXPECTED_OUTPUT) + self.assertEqual(self.history_say().last_text(), EXPECTED_OUTPUT) if __name__ == '__main__': diff --git a/icons/default.ico b/icons/default.ico new file mode 100644 index 000000000..4953ba9e6 Binary files /dev/null and b/icons/default.ico differ diff --git a/icons/warn.ico b/icons/warn.ico new file mode 100644 index 000000000..f739cebbb Binary files /dev/null and b/icons/warn.ico differ diff --git a/installer/helper.py b/installer/helper.py index 945daf138..2c31e7c6f 100644 --- a/installer/helper.py +++ b/installer/helper.py @@ -62,7 +62,7 @@ def log(msg): print('msg:') try: print(str(msg)) - except: + except BaseException: print('msg unprintable') print('-----------------------------') diff --git a/installer/optional.py b/installer/optional.py index f8335210f..2cfcb9304 100644 --- a/installer/optional.py +++ b/installer/optional.py @@ -54,6 +54,14 @@ } +Fasttext = { + "name": "Fasttext language recognition", + "pip": ['fasttext'], + "description": "Fasttext is a text classification library capable of detecting 176 languages.", + "instructions": "https://github.com/facebookresearch/fastText/#requirements" +} + + NativeNotification = { "name": "Notification", "executable": ['notify-send'], @@ -123,7 +131,7 @@ } -OPTIONAL_REQUIREMENTS = [PortAudio, RequestsSecurity, FFMPEG, ESPEAK, WKHTMLTOPDF] +OPTIONAL_REQUIREMENTS = [PortAudio, RequestsSecurity, FFMPEG, ESPEAK, WKHTMLTOPDF, Fasttext] if not sys.platform == "darwin": diff --git a/installer/requirements.txt b/installer/requirements.txt index d27338d03..8bbd36187 100644 --- a/installer/requirements.txt +++ b/installer/requirements.txt @@ -1,5 +1,5 @@ akinator.py -archey4; sys_platform != 'darwin' +archey4==4.6.0.post1; sys_platform != 'darwin' beautifulsoup4 colorama distro @@ -17,6 +17,7 @@ psutil; sys_platform == 'win32' pycricbuzz pydoc-markdown pydub +pyjokes pync==1.6.1; sys_platform == 'darwin' pypiwin32; sys_platform == 'win32' pyreadline; sys_platform == 'win32' @@ -24,13 +25,19 @@ python-dateutil pytimeparse pyttsx3 == 2.71; sys_platform != 'darwin' pywin32; sys_platform == 'win32' +random-word requests[security] speedtest-cli sympy tabulate termdown opencv-python +whois wikipedia +win10toast; sys_platform == 'win32' gtts playsound windows-curses; sys_platform == 'win32' +Image +img2pdf +pdf2image diff --git a/installer/steps/e_launcher.py b/installer/steps/e_launcher.py index 0807dcc62..721b44ef8 100644 --- a/installer/steps/e_launcher.py +++ b/installer/steps/e_launcher.py @@ -19,9 +19,9 @@ section("Write Jarvis starter") JARVIS_MACRO = """\ - #!/bin/bash - source {PATH}/env/bin/activate - python {PATH}/jarviscli "$@" +#!/bin/bash +source "{PATH}/env/bin/activate" +python "{PATH}/jarviscli" "$@" """ fw = open('jarvis', 'w') diff --git a/jarviscli/__main__.py b/jarviscli/__main__.py index 102d45be4..bdd63b329 100644 --- a/jarviscli/__main__.py +++ b/jarviscli/__main__.py @@ -1,28 +1,4 @@ -# -*- coding: utf-8 -*- -import sys - -import colorama - -import ui.CmdInterpreter -from jarvis import Jarvis -from language import default - - -def check_python_version(): - return sys.version_info[0] == 3 - - -def main(): - language_parser = default.DefaultLanguageParser() - jarvis = Jarvis(language_parser) - cmd_interpreter = ui.CmdInterpreter.CmdInterpreter(jarvis) - - command = " ".join(sys.argv[1:]).strip() - cmd_interpreter.executor(command) - +import main if __name__ == '__main__': - if check_python_version(): - main() - else: - print("Sorry! Only Python 3 supported.") + main.main_cli() diff --git a/jarviscli/api.py b/jarviscli/api.py index a163d6c87..d8b322ed9 100644 --- a/jarviscli/api.py +++ b/jarviscli/api.py @@ -9,7 +9,7 @@ class DummyIO: def say(self, text, color=''): - pass + print(text) def inptu(self, prompt="", color=""): pass @@ -44,6 +44,8 @@ def __init__(self): if not self.speech_rate: self.speech_rate = 120 + self.io = DummyIO() + # what if the platform does not have any engines, travis doesn't have sapi5 acc to me try: gtts_status = self.get_data('gtts_status') @@ -52,7 +54,6 @@ def __init__(self): self.say("Voice not supported", self, Fore.RED) self.say(str(e), self, Fore.RED) - self.io = DummyIO() def say(self, text, color="", speak=True): """ @@ -262,3 +263,6 @@ def spinner_stop(self, message="Task executed successfully! ", color=Fore.GREEN) def is_spinner_running(self): return self.spinner_running + + def get_server(self): + return self.io.cmd_interpreter._jarvis.server diff --git a/jarviscli/data/LICENSE b/jarviscli/data/LICENSE new file mode 100644 index 000000000..5b9e22bca --- /dev/null +++ b/jarviscli/data/LICENSE @@ -0,0 +1,27 @@ +Unlike the rest of this project, personality_questions.tsv is adpated from Open Extended Jungian Type Scales 1.2 +at https://openpsychometrics.org/tests/OJTS/development/#liscmark,licensed, and is licensed under the Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International License. + +this applys to lid.176.ftz + +MIT License + +Copyright (c) 2016-present, Facebook, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/jarviscli/data/code_to_lang.json b/jarviscli/data/code_to_lang.json new file mode 100644 index 000000000..c6f6f5299 --- /dev/null +++ b/jarviscli/data/code_to_lang.json @@ -0,0 +1 @@ +{"af": "Afrikaans", "als": "Alemannic", "am": "Amharic", "an": "Aragonese", "ar": "Arabic", "arz": "Egyptian Arabic", "as": "Assamese", "ast": "Asturian", "av": "Avaric", "az": "Azerbaijani", "azb": "South Azerbaijani", "ba": "Bashkir", "bar": "Bavarian", "bcl": "Bikol", "be": "Belarusian", "bg": "Bulgarian", "bh": "Bihari languages", "bn": "Bengali", "bo": "Tibetan", "bpy": "Bishnupriya Manipuri", "br": "Breton", "bs": "Bosnian", "bxr": "Russia Buriat", "ca": "Catalan, Valencian", "cbk": "Zamboanga Chavacano", "ce": "Chechen", "ceb": "Cebuano", "ckb": "Central Kurdish", "co": "Corsican", "cs": "Czech", "cv": "Chuvash", "cy": "Welsh", "da": "Danish", "de": "German", "diq": "Dimli", "dsb": "Lower Sorbian", "dty": "Dotyali", "dv": "Divehi / Maldivian", "el": "Greek", "eml": "Emiliano-Romagnolo", "en": "English", "eo": "Esperanto", "es": "Spanish", "et": "Estonian", "eu": "Basque", "fa": "Persian", "fi": "Finnish", "fr": "French", "frr": "Northern Frisian", "fy": "Western Frisian", "ga": "Irish", "gd": "Gaelic", "gl": "Galician", "gn": "Guarani", "gom": "Goan Konkani", "gu": "Gujarati", "gv": "Manx", "he": "Hebrew", "hi": "Hindi", "hif": "Fiji Hindi", "hr": "Croatian", "hsb": "Fiji Hindi", "ht": "Haitian", "hu": "Hungarian", "hy": "Armenian", "ia": "Interlingua (International Auxiliary Language Association)", "id": "Indonesian", "ie": "Interlingue, Occidental", "ilo": "Iloko", "io": "Ido", "is": "Icelandic", "it": "Italian", "ja": "Japanese", "jbo": "Lojban", "jv": "Javanese", "ka": "Georgian", "kk": "Kazakh", "km": "Central Khmer", "kn": "Kannada", "ko": "Korean", "krc": "Karachay-Balkar", "ku": "Kurdish", "kv": "Komi", "kw": "Cornish", "ky": "Kirghiz", "la": "Latin", "lb": "Luxembourgish", "lez": "Lezghian", "li": "Limburgan", "lmo": "Lombard", "lo": "Lao", "lrc": "Northern Luri", "lt": "Lithuanian", "lv": "Latvian", "mai": "Maithili", "mg": "Malagasy", "mhr": "Eastern Mari", "min": "Minangkabau", "mk": "Macedonian", "ml": "Malayalam", "mn": "Mongolian", "mr": "Marathi", "mrj": "Western Mari", "ms": "Malay", "mt": "Maltese", "mwl": "Mirandese", "my": "Burmese", "myv": "Erzya", "mzn": "Mazanderani", "nah": "Nahuatl languages", "nap": "Neapolitan", "nds": "Low German", "ne": "Nepali", "new": "Nepal Bhasa, Newari", "nl": "Dutch", "nn": "Norwegian Nynorsk", "no": "Norwegian", "oc": "Occitan (post 1500)", "or": "Oriya", "os": "Ossetian", "pa": "Panjabi", "pam": "Kapampangan", "pfl": "Pfaelzisch", "pl": "Polish", "pms": "Piemontese", "pnb": "Western Panjabi", "ps": "Pushto", "pt": "Portuguese", "qu": "Quechua", "rm": "Romansh", "ro": "Romanian, Moldavian", "ru": "Russian", "rue": "Rusyn", "sa": "Sanskrit", "sah": "Yakut", "sc": "Sardinian", "scn": "Sicilian", "sco": "Scots", "sd": "Sindhi", "sh": "Serbo-Croatian", "si": "Sinhala", "sk": "Slovak", "sl": "Slovenian", "so": "Somali", "sq": "Albanian", "sr": "Serbian", "su": "Sundanese", "sv": "Swedish", "sw": "Swahili", "ta": "Tamil", "te": "Telugu", "tg": "Tajik", "th": "Thai", "tk": "Turkmen", "tl": "Tagalog", "tr": "Turkish", "tt": "Tatar", "tyv": "Tuvinian", "ug": "Uighur", "uk": "Ukrainian", "ur": "Urdu", "uz": "Uzbek", "vec": "Venetian", "vep": "Veps", "vi": "Vietnamese", "vls": "Vlaams", "vo": "Volap\u00fck", "wa": "Walloon", "war": "Waray (Philippines)", "wuu": "Wu Chinese", "xal": "Kalmyk, Oirat", "xmf": "Mingrelian", "yi": "Yiddish", "yo": "Yoruba", "yue": "Yue Chinese", "zh": "Chinese"} \ No newline at end of file diff --git a/jarviscli/data/lid.176.ftz b/jarviscli/data/lid.176.ftz new file mode 100644 index 000000000..1fb85b357 Binary files /dev/null and b/jarviscli/data/lid.176.ftz differ diff --git a/jarviscli/data/personality_questions.tsv b/jarviscli/data/personality_questions.tsv new file mode 100644 index 000000000..dea2318b8 --- /dev/null +++ b/jarviscli/data/personality_questions.tsv @@ -0,0 +1,32 @@ +makes lists relies on memory +sceptical wants to believe +bored by time alone needs time alone +accepts things as they are unsatisfied with the ways things are +keeps a clean room just puts stuff where ever +thinks "robotic" is an insult strives to have a mechanical mind +energetic mellow +prefer to take multiple choice test prefer essay answers +chaotic organized +easily hurt thick-skinned +works best in groups works best alone +focused on the present focused on the future +plans far ahead plans at the last minute +wants people's respect wants their love +gets worn out by parties gets fired up by parties +fits in stands out +keeps options open commits +wants to be good at fixing things wants to be good at fixing people +talks more listens more +when describing an event, will tell people what happened when describing an event, will tell people what it meant +gets work done right away procrastinates +follows the heart follows the head +stays at home goes out on the town +wants the big picture wants the details +improvises prepares +bases morality on justice bases morality on compassion +finds it difficult to yell very loudly yelling to others when they are far away comes naturally +theoretical empirical +works hard plays hard +uncomfortable with emotions values emotions +likes to perform in front of other people avoids public speaking +likes to know "who?", "what?", "when?" likes to know "why?" diff --git a/jarviscli/data/pi.txt b/jarviscli/data/pi.txt new file mode 100644 index 000000000..141a93674 --- /dev/null +++ b/jarviscli/data/pi.txt @@ -0,0 +1 @@ +3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745530506820349625245174939965143142980919065925093722169646151570985838741059788595977297549893016175392846813826868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388439045124413654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767889525213852254995466672782398645659611635488623057745649803559363456817432411251507606947945109659609402522887971089314566913686722874894056010150330861792868092087476091782493858900971490967598526136554978189312978482168299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610213596953623144295248493718711014576540359027993440374200731057853906219838744780847848968332144571386875194350643021845319104848100537061468067491927819119793995206141966342875444064374512371819217999839101591956181467514269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672218256259966150142150306803844773454920260541466592520149744285073251866600213243408819071048633173464965145390579626856100550810665879699816357473638405257145910289706414011097120628043903975951567715770042033786993600723055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816909152801735067127485832228718352093539657251210835791513698820914442100675103346711031412671113699086585163983150197016515116851714376576183515565088490998985998238734552833163550764791853589322618548963213293308985706420467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325974636673058360414281388303203824903758985243744170291327656180937734440307074692112019130203303801976211011004492932151608424448596376698389522868478312355265821314495768572624334418930396864262434107732269780280731891544110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201855810072936065987648611791045334885034611365768675324944166803962657978771855608455296541266540853061434443185867697514566140680070023787765913440171274947042056223053899456131407112700040785473326993908145466464588079727082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923099079654737612551765675135751782966645477917450112996148903046399471329621073404375189573596145890193897131117904297828564750320319869151402870808599048010941214722131794764777262241425485454033215718530614228813758504306332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120918076383271664162748888007869256029022847210403172118608204190004229661711963779213375751149595015660496318629472654736425230817703675159067350235072835405670403867435136222247715891504953098444893330963408780769325993978054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229246543668009806769282382806899640048243540370141631496589794092432378969070697794223625082216889573837986230015937764716512289357860158816175578297352334460428151262720373431465319777741603199066554187639792933441952154134189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759882816133231666365286193266863360627356763035447762803504507772355471058595487027908143562401451718062464362679456127531813407833033625423278394497538243720583531147711992606381334677687969597030983391307710987040859133746414428227726346594704745878477872019277152807317679077071572134447306057007334924369311383504931631284042512192565179806941135280131470130478164378851852909285452011658393419656213491434159562586586557055269049652098580338507224264829397285847831630577775606888764462482468579260395352773480304802900587607582510474709164396136267604492562742042083208566119062545433721315359584506877246029016187667952406163425225771954291629919306455377991403734043287526288896399587947572917464263574552540790914513571113694109119393251910760208252026187985318877058429725916778131496990090192116971737278476847268608490033770242429165130050051683233643503895170298939223345172201381280696501178440874519601212285993716231301711444846409038906449544400619869075485160263275052983491874078668088183385102283345085048608250393021332197155184306354550076682829493041377655279397517546139539846833936383047461199665385815384205685338621867252334028308711232827892125077126294632295639898989358211674562701021835646220134967151881909730381198004973407239610368540664319395097901906996395524530054505806855019567302292191393391856803449039820595510022635353619204199474553859381023439554495977837790237421617271117236434354394782218185286240851400666044332588856986705431547069657474585503323233421073015459405165537906866273337995851156257843229882737231989875714159578111963583300594087306812160287649628674460477464915995054973742562690104903778198683593814657412680492564879855614537234786733039046883834363465537949864192705638729317487233208376011230299113679386270894387993620162951541337142489283072201269014754668476535761647737946752004907571555278196536213239264061601363581559074220202031872776052772190055614842555187925303435139844253223415762336106425063904975008656271095359194658975141310348227693062474353632569160781547818115284366795706110861533150445212747392454494542368288606134084148637767009612071512491404302725386076482363414334623518975766452164137679690314950191085759844239198629164219399490723623464684411739403265918404437805133389452574239950829659122850855582157250310712570126683024029295252201187267675622041542051618416348475651699981161410100299607838690929160302884002691041407928862150784245167090870006992821206604183718065355672525325675328612910424877618258297651579598470356222629348600341587229805349896502262917487882027342092222453398562647669149055628425039127577102840279980663658254889264880254566101729670266407655904290994568150652653053718294127033693137851786090407086671149655834343476933857817113864558736781230145876871266034891390956200993936103102916161528813843790990423174733639480457593149314052976347574811935670911013775172100803155902485309066920376719220332290943346768514221447737939375170344366199104033751117354719185504644902636551281622882446257591633303910722538374218214088350865739177150968288747826569959957449066175834413752239709683408005355984917541738188399944697486762655165827658483588453142775687900290951702835297163445621296404352311760066510124120065975585127617858382920419748442360800719304576189323492292796501987518721272675079812554709589045563579212210333466974992356302549478024901141952123828153091140790738602515227429958180724716259166854513331239480494707911915326734302824418604142636395480004480026704962482017928964766975831832713142517029692348896276684403232609275249603579964692565049368183609003238092934595889706953653494060340216654437558900456328822505452556405644824651518754711962184439658253375438856909411303150952617937800297412076651479394259029896959469955657612186561967337862362561252163208628692221032748892186543648022967807057656151446320469279068212073883778142335628236089632080682224680122482611771858963814091839036736722208883215137556003727983940041529700287830766709444745601345564172543709069793961225714298946715435784687886144458123145935719849225284716050492212424701412147805734551050080190869960330276347870810817545011930714122339086639383395294257869050764310063835198343893415961318543475464955697810382930971646514384070070736041123735998434522516105070270562352660127648483084076118301305279320542746286540360367453286510570658748822569815793678976697422057505968344086973502014102067235850200724522563265134105592401902742162484391403599895353945909440704691209140938700126456001623742880210927645793106579229552498872758461012648369998922569596881592056001016552563756785667227966198857827948488558343975187445455129656344348039664205579829368043522027709842942325330225763418070394769941597915945300697521482933665556615678736400536665641654732170439035213295435291694145990416087532018683793702348886894791510716378529023452924407736594956305100742108714261349745956151384987137570471017879573104229690666702144986374645952808243694457897723300487647652413390759204340196340391147320233807150952220106825634274716460243354400515212669324934196739770415956837535551667302739007497297363549645332888698440611964961627734495182736955882207573551766515898551909866653935494810688732068599075407923424023009259007017319603622547564789406475483466477604114632339056513433068449539790709030234604614709616968868850140834704054607429586991382966824681857103188790652870366508324319744047718556789348230894310682870272280973624809399627060747264553992539944280811373694338872940630792615959954626246297070625948455690347119729964090894180595343932512362355081349490043642785271383159125689892951964272875739469142725343669415323610045373048819855170659412173524625895487301676002988659257866285612496655235338294287854253404830833070165372285635591525347844598183134112900199920598135220511733658564078264849427644113763938669248031183644536985891754426473998822846218449008777697763127957226726555625962825427653183001340709223343657791601280931794017185985999338492354956400570995585611349802524990669842330173503580440811685526531170995708994273287092584878944364600504108922669178352587078595129834417295351953788553457374260859029081765155780390594640873506123226112009373108048548526357228257682034160504846627750450031262008007998049254853469414697751649327095049346393824322271885159740547021482897111777923761225788734771881968254629812686858170507402725502633290449762778944236216741191862694396506715157795867564823993917604260176338704549901761436412046921823707648878341968968611815581587360629386038101712158552726683008238340465647588040513808016336388742163714064354955618689641122821407533026551004241048967835285882902436709048871181909094945331442182876618103100735477054981596807720094746961343609286148494178501718077930681085469000944589952794243981392135055864221964834915126390128038320010977386806628779239718014613432445726400973742570073592100315415089367930081699805365202760072774967458400283624053460372634165542590276018348403068113818551059797056640075094260878857357960373245141467867036880988060971642584975951380693094494015154222219432913021739125383559150310033303251117491569691745027149433151558854039221640972291011290355218157628232831823425483261119128009282525619020526301639114772473314857391077758744253876117465786711694147764214411112635835538713610110232679877564102468240322648346417663698066378576813492045302240819727856471983963087815432211669122464159117767322532643356861461865452226812688726844596844241610785401676814208088502800541436131462308210259417375623899420757136275167457318918945628352570441335437585753426986994725470316566139919996826282472706413362221789239031760854289437339356188916512504244040089527198378738648058472689546243882343751788520143956005710481194988423906061369573423155907967034614914344788636041031823507365027785908975782727313050488939890099239135033732508559826558670892426124294736701939077271307068691709264625484232407485503660801360466895118400936686095463250021458529309500009071510582362672932645373821049387249966993394246855164832611341461106802674466373343753407642940266829738652209357016263846485285149036293201991996882851718395366913452224447080459239660281715655156566611135982311225062890585491450971575539002439315351909021071194573002438801766150352708626025378817975194780610137150044899172100222013350131060163915415895780371177927752259787428919179155224171895853616805947412341933984202187456492564434623925319531351033114763949119950728584306583619353693296992898379149419394060857248639688369032655643642166442576079147108699843157337496488352927693282207629472823815374099615455987982598910937171262182830258481123890119682214294576675807186538065064870261338928229949725745303328389638184394477077940228435988341003583854238973542439564755568409522484455413923941000162076936368467764130178196593799715574685419463348937484391297423914336593604100352343777065888677811394986164787471407932638587386247328896456435987746676384794665040741118256583788784548581489629612739984134427260860618724554523606431537101127468097787044640947582803487697589483282412392929605829486191966709189580898332012103184303401284951162035342801441276172858302435598300320420245120728725355811958401491809692533950757784000674655260314461670508276827722235341911026341631571474061238504258459884199076112872580591139356896014316682831763235673254170734208173322304629879928049085140947903688786878949305469557030726190095020764334933591060245450864536289354568629585313153371838682656178622736371697577418302398600659148161640494496501173213138957470620884748023653710311508984279927544268532779743113951435741722197597993596852522857452637962896126915723579866205734083757668738842664059909935050008133754324546359675048442352848747014435454195762584735642161981340734685411176688311865448937769795665172796623267148103386439137518659467300244345005449953997423723287124948347060440634716063258306498297955101095418362350303094530973358344628394763047756450150085075789495489313939448992161255255977014368589435858775263796255970816776438001254365023714127834679261019955852247172201777237004178084194239487254068015560359983905489857235467456423905858502167190313952629445543913166313453089390620467843877850542393905247313620129476918749751910114723152893267725339181466073000890277689631148109022097245207591672970078505807171863810549679731001678708506942070922329080703832634534520380278609905569001341371823683709919495164896007550493412678764367463849020639640197666855923356546391383631857456981471962108410809618846054560390384553437291414465134749407848844237721751543342603066988317683310011331086904219390310801437843341513709243530136776310849135161564226984750743032971674696406665315270353254671126675224605511995818319637637076179919192035795820075956053023462677579439363074630569010801149427141009391369138107258137813578940055995001835425118417213605572752210352680373572652792241737360575112788721819084490061780138897107708229310027976659358387589093956881485602632243937265624727760378908144588378550197028437793624078250527048758164703245812908783952324532378960298416692254896497156069811921865849267704039564812781021799132174163058105545988013004845629976511212415363745150056350701278159267142413421033015661653560247338078430286552572227530499988370153487930080626018096238151613669033411113865385109193673938352293458883225508870645075394739520439680790670868064450969865488016828743437861264538158342807530618454859037982179945996811544197425363443996029025100158882721647450068207041937615845471231834600726293395505482395571372568402322682130124767945226448209102356477527230820810635188991526928891084555711266039650343978962782500161101532351605196559042118449499077899920073294769058685778787209829013529566139788848605097860859570177312981553149516814671769597609942100361835591387778176984587581044662839988060061622984861693533738657877359833616133841338536842119789389001852956919678045544828584837011709672125353387586215823101331038776682721157269495181795897546939926421979155233857662316762754757035469941489290413018638611943919628388705436777432242768091323654494853667680000010652624854730558615989991401707698385483188750142938908995068545307651168033373222651756622075269517914422528081651716677667279303548515420402381746089232839170327542575086765511785939500279338959205766827896776445318404041855401043513483895312013263783692835808271937831265496174599705674507183320650345566440344904536275600112501843356073612227659492783937064784264567633881880756561216896050416113903906396016202215368494109260538768871483798955999911209916464644119185682770045742434340216722764455893301277815868695250694993646101756850601671453543158148010545886056455013320375864548584032402987170934809105562116715468484778039447569798042631809917564228098739987669732376957370158080682290459921236616890259627304306793165311494017647376938735140933618332161428021497633991898354848756252987524238730775595559554651963944018218409984124898262367377146722606163364329640633572810707887581640438148501884114318859882769449011932129682715888413386943468285900666408063140777577257056307294004929403024204984165654797367054855804458657202276378404668233798528271057843197535417950113472736257740802134768260450228515797957976474670228409995616015691089038458245026792659420555039587922981852648007068376504183656209455543461351341525700659748819163413595567196496540321872716026485930490397874895890661272507948282769389535217536218507962977851461884327192232238101587444505286652380225328438913752738458923844225354726530981715784478342158223270206902872323300538621634798850946954720047952311201504329322662827276321779088400878614802214753765781058197022263097174950721272484794781695729614236585957820908307332335603484653187302930266596450137183754288975579714499246540386817992138934692447419850973346267933210726868707680626399193619650440995421676278409146698569257150743157407938053239252394775574415918458215625181921552337096074833292349210345146264374498055961033079941453477845746999921285999993996122816152193148887693880222810830019860165494165426169685867883726095877456761825072759929508931805218729246108676399589161458550583972742098090978172932393010676638682404011130402470073508578287246271349463685318154696904669686939254725194139929146524238577625500474852954768147954670070503479995888676950161249722820403039954632788306959762493615101024365553522306906129493885990157346610237122354789112925476961760050479749280607212680392269110277722610254414922157650450812067717357120271802429681062037765788371669091094180744878140490755178203856539099104775941413215432844062503018027571696508209642734841469572639788425600845312140659358090412711359200419759851362547961606322887361813673732445060792441176399759746193835845749159880976674470930065463424234606342374746660804317012600520559284936959414340814685298150539471789004518357551541252235905906872648786357525419112888773717663748602766063496035367947026923229718683277173932361920077745221262475186983349515101986426988784717193966497690708252174233656627259284406204302141137199227852699846988477023238238400556555178890876613601304770984386116870523105531491625172837327286760072481729876375698163354150746088386636406934704372066886512756882661497307886570156850169186474885416791545965072342877306998537139043002665307839877638503238182155355973235306860430106757608389086270498418885951380910304235957824951439885901131858358406674723702971497850841458530857813391562707603563907639473114554958322669457024941398316343323789759556808568362972538679132750555425244919435891284050452269538121791319145135009938463117740179715122837854601160359554028644059024964669307077690554810288502080858008781157738171917417760173307385547580060560143377432990127286772530431825197579167929699650414607066457125888346979796429316229655201687973000356463045793088403274807718115553309098870255052076804630346086581653948769519600440848206596737947316808641564565053004988161649057883115434548505266006982309315777650037807046612647060214575057932709620478256152471459189652236083966456241051955105223572397395128818164059785914279148165426328920042816091369377737222999833270820829699557377273756676155271139225880552018988762011416800546873655806334716037342917039079863965229613128017826797172898229360702880690877686605932527463784053976918480820410219447197138692560841624511239806201131845412447820501107987607171556831540788654390412108730324020106853419472304766667217498698685470767812051247367924791931508564447753798537997322344561227858432968466475133365736923872014647236794278700425032555899268843495928761240075587569464137056251400117971331662071537154360068764773186755871487839890810742953094106059694431584775397009439883949144323536685392099468796450665339857388878661476294434140104988899316005120767810358861166020296119363968213496075011164983278563531614516845769568710900299976984126326650234771672865737857908574664607722834154031144152941880478254387617707904300015669867767957609099669360755949651527363498118964130433116627747123388174060373174397054067031096767657486953587896700319258662594105105335843846560233917967492678447637084749783336555790073841914731988627135259546251816043422537299628632674968240580602964211463864368642247248872834341704415734824818333016405669596688667695634914163284264149745333499994800026699875888159350735781519588990053951208535103572613736403436753471410483601754648830040784641674521673719048310967671134434948192626811107399482506073949507350316901973185211955263563258433909982249862406703107683184466072912487475403161796994113973877658998685541703188477886759290260700432126661791922352093822787888098863359911608192353555704646349113208591897961327913197564909760001399623444553501434642686046449586247690943470482932941404111465409239883444351591332010773944111840741076849810663472410482393582740194493566516108846312567852977697346843030614624180358529331597345830384554103370109167677637427621021370135485445092630719011473184857492331816720721372793556795284439254815609137281284063330393735624200160456645574145881660521666087387480472433912129558777639069690370788285277538940524607584962315743691711317613478388271941686066257210368513215664780014767523103935786068961112599602818393095487090590738613519145918195102973278755710497290114871718971800469616977700179139196137914171627070189584692143436967629274591099400600849835684252019155937037010110497473394938778859894174330317853487076032219829705797511914405109942358830345463534923498268836240433272674155403016195056806541809394099820206099941402168909007082133072308966211977553066591881411915778362729274615618571037217247100952142369648308641025928874579993223749551912219519034244523075351338068568073544649951272031744871954039761073080602699062580760202927314552520780799141842906388443734996814582733720726639176702011830046481900024130835088465841521489912761065137415394356572113903285749187690944137020905170314877734616528798482353382972601361109845148418238081205409961252745808810994869722161285248974255555160763716750548961730168096138038119143611439921063800508321409876045993093248510251682944672606661381517457125597549535802399831469822036133808284993567055755247129027453977621404931820146580080215665360677655087838043041343105918046068008345911366408348874080057412725867047922583191274157390809143831384564241509408491339180968402511639919368532255573389669537490266209232613188558915808324555719484538756287861288590041060060737465014026278240273469625282171749415823317492396835301361786536737606421667781377399510065895288774276626368418306801908046098498094697636673356622829151323527888061577682781595886691802389403330764419124034120223163685778603572769415417788264352381319050280870185750470463129333537572853866058889045831114507739429352019943219711716422350056440429798920815943071670198574692738486538334361457946341759225738985880016980147574205429958012429581054565108310462972829375841611625325625165724980784920998979906200359365099347215829651741357984910471116607915874369865412223483418877229294463351786538567319625598520260729476740726167671455736498121056777168934849176607717052771876011999081441130586455779105256843048114402619384023224709392498029335507318458903553971330884461741079591625117148648744686112476054286734367090466784686702740918810142497111496578177242793470702166882956108777944050484375284433751088282647719785400065097040330218625561473321177711744133502816088403517814525419643203095760186946490886815452856213469883554445602495566684366029221951248309106053772019802183101032704178386654471812603971906884623708575180800353270471856594994761242481109992886791589690495639476246084240659309486215076903149870206735338483495508363660178487710608098042692471324100094640143736032656451845667924566695510015022983307984960799498824970617236744936122622296179081431141466094123415935930958540791390872083227335495720807571651718765994498569379562387555161757543809178052802946420044721539628074636021132942559160025707356281263873310600589106524570802447493754318414940148211999627645310680066311838237616396631809314446712986155275982014514102756006892975024630401735148919457636078935285550531733141645705049964438909363084387448478396168405184527328840323452024705685164657164771393237755172947951261323982296023945485797545865174587877133181387529598094121742273003522965080891777050682592488223221549380483714547816472139768209633205083056479204820859204754998573203888763916019952409189389455767687497308569559580106595265030362661597506622250840674288982659075106375635699682115109496697445805472886936310203678232501823237084597901115484720876182124778132663304120762165873129708112307581598212486398072124078688781145016558251361789030708608701989758898074566439551574153631931919810705753366337380382721527988493503974800158905194208797113080512339332219034662499171691509485414018710603546037946433790058909577211808044657439628061867178610171567409676620802957665770512912099079443046328929473061595104309022214393718495606340561893425130572682914657832933405246350289291754708725648426003496296116541382300773133272983050016025672401418515204189070115428857992081219844931569990591820118197335001261877280368124819958770702075324063612593134385955425477819611429351635612234966615226147353996740515849986035529533292457523888101362023476246690558164389678630976273655047243486430712184943734853006063876445662721866617012381277156213797461498613287441177145524447089971445228856629424402301847912054784985745216346964489738920624019435183100882834802492490854030778638751659113028739587870981007727182718745290139728366148421428717055317965430765045343246005363614726181809699769334862640774351999286863238350887566835950972655748154319401955768504372480010204137498318722596773871549583997184449072791419658459300839426370208756353982169620553248032122674989114026785285996734052420310917978999057188219493913207534317079800237365909853755202389116434671855829068537118979526262344924833924963424497146568465912489185566295893299090352392333336474352037077010108438800329075983421701855422838616172104176030116459187805393674474720599850235828918336929223373239994804371084196594731626548257480994825099918330069765693671596893644933488647442135008407006608835972350395323401795825570360169369909886711321097988970705172807558551912699306730992507040702455685077867906947661262980822516331363995211709845280926303759224267425755998928927837047444521893632034894155210445972618838003006776179313813991620580627016510244588692476492468919246121253102757313908404700071435613623169923716948481325542009145304103713545329662063921054798243921251725401323149027405858920632175894943454890684639931375709103463327141531622328055229729795380188016285907357295541627886764982741861642187898857410716490691918511628152854867941736389066538857642291583425006736124538491606741373401735727799563410433268835695078149313780073623541800706191802673285511919426760912210359874692411728374931261633950012395992405084543756985079570462226646190001035004901830341535458428337643781119885563187777925372011667185395418359844383052037628194407615941068207169703022851522505731260930468984234331527321313612165828080752126315477306044237747535059522871744026663891488171730864361113890694202790881431194487994171540421034121908470940802540239329429454938786402305129271190975135360009219711054120966831115163287054230284700731206580326264171161659576132723515666625366727189985341998952368848309993027574199164638414270779887088742292770538912271724863220288984251252872178260305009945108247835729056919885554678860794628053712270424665431921452817607414824038278358297193010178883456741678113989547504483393146896307633966572267270433932167454218245570625247972199786685427989779923395790575818906225254735822052364248507834071101449804787266919901864388229323053823185597328697809222535295910173414073348847610055640182423921926950620831838145469839236646136398910121021770959767049083050818547041946643713122996923588953849301363565761861060622287055994233716310212784574464639897381885667462608794820186474876727272220626764653380998019668836809941590757768526398651462533363124505364026105696055131838131742611844201890888531963569869627950367384243130113317533053298020166888174813429886815855778103432317530647849832106297184251843855344276201282345707169885305183261796411785796088881503296022907056144762209150947390359466469162353968092013945781758910889319921122600739281491694816152738427362642980982340632002440244958944561291670495082358124873917996486411334803247577752197089327722623494860150466526814398770516153170266969297049283162855042128981467061953319702695072143782304768752802873541261663917082459251700107141808548006369232594620190022780874098597719218051585321473926532515590354102092846659252999143537918253145452905984158176370589279069098969111643811878094353715213322614436253144901274547726957393934815469163116249288735747188240715039950094467319543161938554852076657388251396391635767231510055560372633948672082078086537349424401157996675073607111593513319591971209489647175530245313647709420946356969822266737752099451684506436238242118535348879893956731878066061078854400055082765703055874485418057788917192078814233511386629296671796434687600770479995378833878703487180218424373421122739402557176908196030920182401884270570460926225641783752652633583242406612533115294234579655695025068100183109004112453790153329661569705223792103257069370510908307894799990049993953221536227484766036136776979785673865846709366795885837887956259464648913766521995882869338018360119323685785585581955560421562508836502033220245137621582046181067051953306530606065010548871672453779428313388716313955969058320834168984760656071183471362181232462272588419902861420872849568796393254642853430753011052857138296437099903569488852851904029560473461311382638788975517885604249987483163828040468486189381895905420398898726506976202019955484126500053944282039301274816381585303964399254702016727593285743666616441109625663373054092195196751483287348089574777752783442210910731113518280460363471981856555729571447476825528578633493428584231187494400032296906977583159038580393535213588600796003420975473922967333106493956018122378128545843176055617338611267347807458506760630482294096530411183066710818930311088717281675195796753471885372293096161432040063813224658411111577583585811350185690478153689381377184728147519983505047812977185990847076219746058874232569958288925350419379582606162118423687685114183160683158679946016520577405294230536017803133572632670547903384012573059123396018801378254219270947673371919872873852480574212489211834708766296672072723256505651293331260595057777275424712416483128329820723617505746738701282095755443059683955556868611883971355220844528526400812520276655576774959696266126045652456840861392382657685833846984997787267065551918544686984694784957346226062942196245570853712727765230989554501930377321666491825781546772920052126671434632096378918523232150189761260343736840671941930377468809992968775824410478781232662531818459604538535438391144967753128642609252115376732588667226040425234910870269580996475958057946639734190640100363619040420331135793365424263035614570090112448008900208014780566037101541223288914657223931450760716706435568274377439657890679726874384730763464516775621030986040927170909512808630902973850445271828927496892121066700816485833955377359191369501531620189088874842107987068991148046692706509407620465027725286507289053285485614331608126930056937854178610969692025388650345771831766868859236814884752764984688219497397297077371871884004143231276365048145311228509900207424092558592529261030210673681543470152523487863516439762358604191941296976904052648323470099111542426012734380220893310966863678986949779940012601642276092608234930411806438291383473546797253992623387915829984864592717340592256207491053085315371829116816372193951887009577881815868504645076993439409874335144316263303172477474868979182092394808331439708406730840795893581089665647758599055637695252326536144247802308268118310377358870892406130313364773710116282146146616794040905186152603600925219472188909181073358719641421444786548995285823439470500798303885388608310357193060027711945580219119428999227223534587075662469261776631788551443502182870266856106650035310502163182060176092179846849368631612937279518730789726373537171502563787335797718081848784588665043358243770041477104149349274384575871071597315594394264125702709651251081155482479394035976811881172824721582501094960966253933953809221955919181885526780621499231727631632183398969380756168559117529984501320671293924041445938623988093812404521914848316462101473891825101090967738690664041589736104764365000680771056567184862814963711188321924456639458144914861655004956769826903089111856879869294705135248160917432430153836847072928989828460222373014526556798986277679680914697983782687643115988321090437156112997665215396354644208691975673700057387649784376862876817924974694384274652563163230055513041742273416464551278127845777724575203865437542828256714128858345444351325620544642410110379554641905811686230596447695870540721419852121067343324107567675758184569906930460475227701670056845439692340417110898889934163505851578873534308155208117720718803791040469830695786854739376564336319797868036718730796939242363214484503547763156702553900654231179201534649779290662415083288583952905426376876689688050333172278001858850697362324038947004718976193473443084374437599250341788079722358591342458131440498477017323616947197657153531977549971627856631190469126091825912498903676541769799036237552865263757337635269693443544004730671988689019681474287677908669796885225016369498567302175231325292653758964151714795595387842784998664563028788319620998304945198743963690706827626574858104391122326187940599415540632701319898957037611053236062986748037791537675115830432084987209202809297526498125691634250005229088726469252846661046653921714820801305022980526378364269597337070539227891535105688839381132497570713310295044303467159894487868471164383280506925077662745001220035262037094660234146489983902525888301486781621967751945831677187627572005054397944124599007711520515461993050983869825428464072555409274031325716326407929341833421470904125425335232480219322770753555467958716383587501815933871742360615511710131235256334858203651461418700492057043720182617331947157008675785393360786227395581857975872587441025420771054753612940474601000940954449596628814869159038990718659805636171376922272907641977551777201042764969496110562205925024202177042696221549587264539892276976603105249808557594716310758701332088614632664125911486338812202844406941694882615295776253250198703598706743804698219420563812558334364219492322759372212890564209430823525440841108645453694049692714940033197828613181861888111184082578659287574263844500599442295685864604810330153889114994869354360302218109434667640000223625505736312946262960961987605642599639461386923308371962659547392346241345977957485246478379807956931986508159776753505539189911513352522987361127791827485420086895396583594219633315028695611920122988898870060799927954111882690230789131076036176347794894320321027733594169086500719328040171638406449878717537567811853213284082165711075495282949749362146082155832056872321855740651610962748743750980922302116099826330339154694946444910045152809250897450748967603240907689836529406579201983152654106581368237919840906457124689484702093577611931399802468134052003947819498662026240089021501661638135383815150377350229660746279529103840686855690701575166241929872444827194293310048548244545807188976330032325258215812803274679620028147624318286221710543528983482082734516801861317195933247110746622285087106661177034653528395776259977446721857158161264111432717943478859908928084866949141390977167369002777585026866465405659503948678411107901161040085727445629384254941675946054871172359464291058509099502149587931121961359083158826206823321561530868337308381732793281969838750870834838804638847844188400318471269745437093732983624028751979208023218787448828728437273780178270080587824107493575148899789117397461293203510814327032514090304874622629423443275712600866425083331876886507564292716055252895449215376517514921963671810494353178583834538652556566406572513635750643532365089367904317025978781771903148679638408288102094614900797151377170990619549696400708676671023300486726314755105372317571143223174114116806228642063889062101923552235467116621374996932693217370431059872250394565749246169782609702533594750209138366737728944386964000281103440260847128990007468077648440887113413525033678773167977093727786821661178653442317322646378476978751443320953400016506921305464768909850502030150448808342618452087305309731894929164253229336124315143065782640702838984098416029503092418971209716016492656134134334222988279099217860426798124572853458013382609958771781131021673402565627440072968340661984806766158050216918337236803990279316064204368120799003162644491461902194582296909921227885539487835383056468648816555622943156731282743908264506116289428035016613366978240517701552196265227254558507386405852998303791803504328767038092521679075712040612375963276856748450791511473134400018325703449209097124358094479004624943134550289006806487042935340374360326258205357901183956490893543451013429696175452495739606214902887289327925206965353863964432253883275224996059869747598823299162635459733244451637553343774929289905811757863555556269374269109471170021654117182197505198317871371060510637955585889055688528879890847509157646390746936198815078146852621332524738376511929901561091897779220087057933964638274906806987691681974923656242260871541761004306089043779766785196618914041449252704808819714988015420577870065215940092897776013307568479669929554336561398477380603943688958876460549838714789684828053847017308711177611596635050399793438693391197898871091565417091330826076474063057114110988393880954814378284745288383680794188843426662220704387228874139478010177213922819119923654055163958934742639538248296090369002883593277458550608013179884071624465639979482757836501955142215513392819782269842786383916797150912624105487257009240700454884856929504481107380879965474815689139353809434745569721289198271770207666136024895814681191336141212587838955773571949863172108443989014239484966592517313881716026632619310653665350414730708044149391693632623737677770958503132559900957627319573086480424677012123270205337426670531424482081681303063973787366424836725398374876909806021827857862165127385635132901489035098832706172589325753639939790557291751600976154590447716922658063151110280384360173747421524760851520990161585823125715907334217365762671423904782795872815050956330928026684589376496497702329736413190609827406335310897924642421345837409011693919642504591288134034988106354008875968200544083643865166178805576089568967275315380819420773325979172784376256611843198910250074918290864751497940031607038455494653859460274524474668123146879434416109933389089926384118474252570445725174593257389895651857165759614812660203107976282541655905060424791140169579003383565748692528007430256234194982864679144763227740055294609039401775363356554719310001754300475047191448998410400158679461792416100164547165513370740739502604427695385538343975505488710997852054011751697475813449260794336895437832211724506873442319898788441285420647428097356258070669831069799352606933921356858813912148073547284632277849080870024677763036055512323866562951788537196730346347012229395816067925091532174890308408865160611190114984434123501246469280288059961342835118847154497712784733617662850621697787177438243625657117794500644777183702219991066950216567576440449979407650379999548450027106659878136038023141268369057831904607927652972776940436130230517870805465115424693952651271010529270703066730244471259739399505146284047674313637399782591845411764133279064606365841529270190302760173394748669603486949765417524293060407270050590395031485229213925755948450788679779252539317651564161971684435243697944473559642606333910551268260615957262170366985064732812667245219890605498802807828814297963366967441248059821921463395657457221022986775997467381260693670691340815594120161159601902377535255563006062479832612498812881929373434768626892192397778339107331065882568137771723283153290825250927330478507249771394483338925520811756084529665905539409655685417060011798572938139982583192936791003918440992865756059935989100029698644609747147184701015312837626311467742091455740418159088000649432378558393085308283054760767995243573916312218860575496738322431956506554608528812019023636447127037486344217272578795034284863129449163184753475314350413920961087960577309872013524840750576371992536504709085825139368634638633680428917671076021111598288755399401200760139470336617937153963061398636554922137415979051190835882900976566473007338793146789131814651093167615758213514248604422924453041131606527009743300884990346754055186406773426035834096086055337473627609356588531097609942383473822220872924644976845605795625167655740884103217313456277358560523582363895320385340248422733716391239732159954408284216666360232965456947035771848734420342277066538373875061692127680157661810954200977083636043611105924091178895403380214265239489296864398089261146354145715351943428507213534530183158756282757338982688985235577992957276452293915674775666760510878876484534936360682780505646228135988858792599409464460417052044700463151379754317371877560398159626475014109066588661621800382669899619655805872086397211769952194667898570117983324406018115756580742841829106151939176300591943144346051540477105700543390001824531177337189558576036071828605063564799790041397618089553636696031621931132502238517916720551806592635180362512145759262383693482226658955769946604919381124866090997981285718234940066155521961122072030922776462009993152442735894887105766238946938894464950939603304543408421024624010487233287500817491798755438793873814398942380117627008371960530943839400637561164585609431295175977139353960743227924892212670458081833137641658182695621058728924477400359470092686626596514220506300785920024882918608397437323538490839643261470005324235406470420894992102504047267810590836440074663800208701266642094571817029467522785400745085523777208905816839184465928294170182882330149715542352359117748186285929676050482038643431087795628929254056389466219482687110428281638939757117577869154301650586029652174595819888786804081103284327398671986213062055598552660364050462821523061545944744899088390819997387474529698107762014871340001225355222466954093152131153379157980269795557105085074738747507580687653764457825244326380461430428892359348529610582693821034980004052484070844035611678171705128133788057056434506161193304244407982603779511985486945591520519600930412710072778493015550388953603382619293437970818743209499141595933963681106275572952780042548630600545238391510689989135788200194117865356821491185282078521301255185184937115034221595422445119002073935396274002081104655302079328672547405436527175958935007163360763216147258154076420530200453401835723382926619153083540951202263291650544261236191970516138393573266937601569144299449437448568097756963031295887191611292946818849363386473927476012269641588489009657170861605981472044674286642087653347998582220906198021732116142304194777549907387385679411898246609130916917722742072333676350326783405863019301932429963972044451792881228544782119535308989101253429755247276357302262813820918074397486714535907786335301608215599113141442050914472935350222308171936635093468658586563148555758624478186201087118897606529698992693281787055764351433820601410773292610634315253371822433852635202177354407152818981376987551575745469397271504884697936195004777209705617939138289898453274262272886471088832701737232588182446584362495805925603381052156062061557132991560848920643403033952622634514542836786982880742514225674518061841495646861116354049718976821542277224794740335715274368194098920501136534001238467142965518673441537416150425632567134302476551252192180357801692403266995417460875924092070046693403965101781348578356944407604702325407555577647284507518268904182939661133101601311190773986324627782190236506603740416067249624901374332172464540974129955705291424382080760983648234659738866913499197840131080155813439791948528304367390124820824448141280954437738983200598649091595053228579145768849625786658859991798675205545580990045564611787552493701245532171701942828846174027366499784755082942280202329012216301023097721515694464279098021908266898688342630716092079140851976952355534886577434252775311972474308730436195113961190800302558783876442060850447306312992778889427291897271698905759252446796601897074829609491906487646937027507738664323919190422542902353189233772931667360869962280325571853089192844038050710300647768478632431910002239297852553723755662136447400967605394398382357646069924652600890906241059042154539279044115295803453345002562441010063595300395988644661695956263518780606885137234627079973272331346939714562855426154676506324656766202792452085813477176085216913409465203076733918411475041401689241213198268815686645614853802875393311602322925556189410429953356400957864953409351152664540244187759493169305604486864208627572011723195264050230997745676478384889734643172159806267876718380052476968840849891850861490034324034767426862459523958903585821350064509981782446360873177543788596776729195261112138591947254514003011805034378752776644027626189410175768726804281766238606804778852428874302591452470739505465251353394595987896197789110418902929438185672050709646062635417329446495766126519534957018600154126239622864138977967333290705673769621564981845068422636903678495559700260798679962610190393312637685569687670292953711625280055431007864087289392257145124811357786276649024251619902774710903359333093049483805978566288447874414698414990671237647895822632949046798120899848571635710878311918486302545016209298058292083348136384054217200561219893536693713367333924644161252231969434712064173754912163570085736943973059797097197266666422674311177621764030686813103518991122713397240368870009968629225464650063852886203938005047782769128356033725482557939129852515068299691077542576474883253414121328006267170940090982235296579579978030182824284902214707481111240186076134151503875698309186527806588966823625239378452726345304204188025084423631903833183845505223679923577529291069250432614469501098610888999146585518818735825281643025209392852580779697376208456374821144339881627100317031513344023095263519295886806908213558536801610002137408511544849126858412686958991741491338205784928006982551957402018181056412972508360703568510553317878408290000415525118657794539633175385320921497205266078312602819611648580986845875251299974040927976831766399146553861089375879522149717317281315179329044311218158710235187407572221001237687219447472093493123241070650806185623725267325407333248757544829675734500193219021991199607979893733836732425761039389853492787774739805080800155447640610535222023254094435677187945654304067358964910176107759483645408234861302547184764851895758366743997915085128580206078205544629917232020282229148869593997299742974711553718589242384938558585954074381048826246487880533042714630119415898963287926783273224561038521970111304665871005000832851773117764897352309266612345888731028835156264460236719966445547276083101187883891511493409393447500730258558147561908813987523578123313422798665035227253671712307568610450045489703600795698276263923441071465848957802414081584052295369374997106655948944592462866199635563506526234053394391421112718106910522900246574236041300936918892558657846684612156795542566054160050712766417660568742742003295771606434486062012398216982717231978268166282499387149954491373020518436690767235774000539326626227603236597517189259018011042903842741855078948874388327030632832799630072006980122443651163940869222207453202446241211558043545420642151215850568961573564143130688834431852808539759277344336553841883403035178229462537020157821573732655231857635540989540332363823192198921711774494694036782961859208034038675758341115188241774391450773663840718804893582568685420116450313576333555094403192367203486510105610498727264721319865434354504091318595131451812764373104389725070049819870521762724940652146199592321423144397765467083517147493679861865527917158240806510637995001842959387991583501715807598837849622573985121298103263793762183224565942366853767991131401080431397323354490908249104991433258432988210339846981417157560108297065830652113470768036806953229719905999044512090872757762253510409023928887794246304832803191327104954785991801969678353214644411892606315266181674431935508170818754770508026540252941092182648582138575266881555841131985600221351588872103656960875150631875330029421186822218937755460272272912905042922597877106678738400006167721546384412923711935218284998243509208918016855727981564218581911974909857305703326676464607287574305653726027689823732597450844796495456480307715981539558277791393736017174229960273531027687194494449179397851446315973144353518504914139415573293820485421235081739125497498193087143966151329420459193801062314217741991840601803479498876910515579055548069538785400664533759818628464199052204528033062636956264909108276271159038569950512465299960628554438383303276385998007929228466595035512112452840875162290602620118577753137479493620554964010730013488531507354873539056029089335264007132747326219603117734339436733857591245081493357369116645412817881714540230547506671365182582848980995121391939956332413365567770980030819102720409971486874181346670060940510214626902804491596465453301077546954130887141653125448130611924078211886900560277818242350226961893443525476335735364856193632544177566139817039306328721669057222597452091929172621998444096461582694563802395028371216864465617852355651641277128269186886155727162014749340522769465957121983149433816221140069363074304441732847861017777438379770372317952554341072234455125555899986461838767649039724611679590181000350989286412041951635511087632042676129798265294258829511412758412627327907988075597518515768412647422094797218433093529726652100156625145529947451276315509176367302594621329301904028379542463232585503010967069227202270748634190054383026506812141421350571541750575086399076739463351462090828889349383764393992569006040673114220933121959362029829723511632593867722414779116295727807523950562515816031333593823115005186268905306583681299881086632632719806112715488587980934879129137074982305759290918629391950147211975860672700925477180257503377307993971345395326461952699965963856549175904583335857991020127132045839032008538788816336376851820837278851311752277696097879621423721625452145912818317982160441113116714069148271709810154577819392023115638719508050246797257924976057726259133285597263712112019057207714091486450740949267180358151575715140503976109638467555692989703835473141002238025834687673501297754132795320609711545064842121859364909979177668747744818828706323155158650328981642282882327468661065927321979071623846421534898524762167890502609980452664839295423572873439776804957740914495383915755654854590589764951985138010079580107837599457752991967005476022525520344539887125387801719607181640781248478472579124078245443616823452395706895142722697504318736332630111030534233358216093331912188066082683414289104151732472160533558499932245487307788229052523242348615315209769384610425828497149634753418375620030149157032796853018686315724884015266398356895636346574353217834931998255421173084677452970858395076164582296303244243282377374505170285606980678895217681981567107816334052667595394249262807569683261074953233905362230908070814559198373553777487420290390181429373115293346444681512129450975965343062842153194457271186149000176505581770953024688752632501197052094761594167687277844720001927891372518416228577837922844390843011811214963664246590336341945406571835447719124466212593926566203068885200555991212353637182269225317814587925937504414489339816086579008761650246351970458288954817937566810464746141051424988702521399368705093723054477341126413548928068410591077166778212383328102621855877513127211793444482014404257450830639447383637939062830089733062413806145894142276947479316657176231824721683506780764875734204915576282175839729751344789906965895325489403356156131674032764724692125057591162515296545685446334981143176702572956618447754874693784642337372389819206620485118943788682248072793520225017965453437572741639107919729529508129429222053477173041844779156739917384183117103625243957161527146690058147000026330104526435478659032907332054683388720787354447626479252976901709120078741837367350877133769776834963442524199499513883150748775374338494582597655609965559543180409201784971846854973706962120885243770138537576814166327224126344239821529416453780004925072627651507890850712659970367087266927643083772296859851691223050374627443108529343052730788652839773352460174635277032059381791253969156210636376258829375713738407544064689647831007045806134467312715911946084359358259877828352665311510650416232953290477721740835593497237585521380483050900096466760883015406128243087406455944318534137552201663058121110334531207450868243394321590435944303124312274713858420303901060709403152355561727679941600203939750998976293353258555756248089966918298642226775023601932579747267425782111197347094023574572222712125268523842958742735015636600931880454933389897415714905441825597380808715652814301026704602843168192303925352977957658624143927015497408792731310516361191375770089295648233236482982630246079758757677453771601024908046243018565241617566556001608591215345562676021926899828553778725831451440826545834844094784631787773747946535801699607794055687011923286080411309046293508718271259346687127666948738998245985277864995691654640294589350649643358098247659651651420909867552038083092032304873427034682887516040715466538346196112230137594515792526967436425319273900360386082364507626988274976187235754767628899507521148048525279508450339585708381304769378813211236742813194879502280663201700224603319896719706491637411758548518784840120548446725888514015627250198217190669608126277854859648183696214107217142149863619187747545096503089570994709343378569816744658282679119406119560378453978558392407612763441057667510243075598145527861678159496570625597550743065210853015979080733437360794328667578905334836695554868039134337201564988342208933999716414797469386969054800891930671380571715058573071488156499207140867582596028760564597824237702424698053280566327870419267684671162668794634869504645074202193739452592626686135529406247813612062026364981999994984051438682852589563422643287076632993048917234007254717641886853513723326678779217383475414800228033929973579361524127558295692768372312347989894462743304545667900620324205163962825884430854383072014956721064605332385372031432421126074244858450945804940818209276391400085404220235562602185643489941454399504109805918179488826280520664410863190016885681551692294862030107388971810077092905904807490924271410189335428184299959881696609938369616443815288772140852680887574882932587358099056707558170179491619061140019085537448827262009366856044755965574764856740081773817033073803054769736097865438593821872205839023444435088674998665060406458743460053318274362961778625180818931443632512051070946908135864405192295129324500788333987884293393424351263433652043858129128343452973086529097833006712617981303167943855357262969987403595704584522308563900989131794759487521263970783759448611394519602867512105616389760088800927461158608002078033415914517970730368351969777660763737853330120241201120469886092093390853657732223924124490515327809509558664594776344822699860748132973026309750288121035177231244650953496536930900186377640940943498373132513218620802148099226855029484546618147155574447096695301776904342720318927706047177845279391604722815343798035396798614243709566832214914654380145938292773933960327540480095522318166673803571839327570771420467238386246178039762923771312095807893638414479298025880655221292620936239306373134966401866195108115834711733120258058667276399927635790780638188130691563662741254312595899361196476261014055635033995231403231138196562363271989618372548453337020625634642239527669435683767613687119629218187545760816170530315907288287007123136663087227549186613957737305460659974378109876498024140112421427736680827513909593134041558262667895108467761186659576601659981780894149857549762843878561002637965431783136340251358141611519020964991335487331311150227006819301359295959716401971960536250335584799809634887180391116128135959685654788683258564378961731597620024196215528962979048198221994622694871374624447290934564700285376949588595916067892824910544125159963007813683674902093749157328962700286568293444313423473512392982591667395034259958689706972673325827359031212887466604514614878503461428277659916080903986525757172630818334944418201935333850712923457743755793440621787113300631060033240539916936826037461766385657588775802012293663532702671006812618251729146082025418928859352444910701382062115538277935652969145765020486432828655579347072096348073726921411868954673227677513356901901537236690368653891612916888878764075254934942497334271811788927599315967193547589880979245252623636590363200708544407845447973482918020820449266706344204375553250505275228337788870408040335319234076856301093477721256390886404131010738178533383160381352808281190408325644018420537467929926220376987180180611226244909092426419858208617511771137890516091403815750033664241560952163281971223350231674226005679412814062172196418427057843289598028823350598282081966662490358577899403331522748177769528436816300885317696947836905806710648280835980466988410981351586549069333195223943632879239905348109878302745001720654336990661177845543646877236318444647680691428280045510746866453928053994091087549391660957316197150331669683099294663491427987808422572206971488755806374803088629951184731871247772919100702275888934869394562895158029653721504096031077612898312635899648934102470360366450586872875890514068412381242473863854279082827338279733268855049358743031602747490631295723497426112215174171531336186224109138695006888358989623492763173164783400774608866555987333821138299287769114954921841920877716060684728746736818861675072210172611038306717878566948129487850489430630861699487987031605158841082823512741535385133658953329486294944950618685147791058046960390693726626703865129052011378108586161888869479576074135855345851517680519733344334952301203957707396237713160302428872005373209982530089776189731298178819446717311606472314762484575519287327828251271824468078242152164695678192940982389262849437602488522790036202193866964822156280936053731780408637272684266964219299468192149087017075333610947913818040632873875938482695355830773957614479972700034728801827852813895032179863452161110666088393140532269449054555278678944175792024400214507801920998044613825478058580484424164047750315360549065914300781583724301231375115622840158386442708907182848167575271238467824595343344496220100960710513706084618011875431207254913349942476171156333214089346091565615506003173842187015702261031019166038870646614388977363187809407115275281746895764015810470169652475577408916445686777171585005832699434016772021567677240681283665652641229824394651331973591997094032759385026695574702318132032437164205861410336065245369391600506449530601612678226489424373971667176612310489750318857321655549883421218028469125290861014855278152776256237504563757694977343368460156077270355096290493924870884062810679436224187047470083688426710225583024035998416459511224852726336326451140173952480861946358407837535568856223171155209472230654370926067973510005655493812245754837285457117973936157561676416928958052572975223385586113883221711073622658162188424431788574887981090266537934266642169909140565364322493013348679881548866286650523469972355747384248305904236771432787923164224038777643301926001922847783138376325361210253369358126240868666997382759773656822279072158324788886423693463961643633087301398142114303060087306661648036789840913359262934023043249749268878316436026810113095707161419128306865773235326396536773903176613613159655535849993986005651559219367599777179330197446881483711032065036931928945214026509154651843099365534933371834252984336799159394174662239003895276738133306177476295749438687169784537672194935065908757119177208754771071899379608947745126547575018711948707387367858902006173733210756933022163206284320656711920969505857611739616323262177089454262146098584102378132158177276022227381334954104810030732751077999489919779638835307344434575329759142637684054422647842160631227696469671564739990437159033239065607266441164386054048388471619121090087010191307260710441141432419767968285478855247794764818029597360494397004795960402927462992035720997619501403483153809477146010563334469988208221205872815107291829712119178764248803546723169165418522567292344291871281632325969654135485895771332083399112887759172261152733790103413620856145779923987783250835507301998184590259583559892605532996737704917224549353296833000022301815172265757875240588322490858212800897479093261007625787704286560069961762121768454789964407050662417102133274867962374302291553582007801411653480656474882306150033920689837947662550365498228053296628621179306284301704924023019857199789488368971830438051821744191476604297524372516834354112170386313794114220952958857980601529387527537990309388716835720957607152219002793792927863036372687658226812419933848081660216037221547101430073775377926990695871212892880190520316012858618254944133538207848834653116326504076424283908701210151942319616522684220037112304643006734420647477180213530701240988603533991526679238711017062218658835737812109351797756044256346949997872511254408545222748109148743072598696020402759411789425812818821599523596589791811440776533543217575952555361581280011638467203193465072968079907939637149617743121194020212975731251652537680173591015573381537720019524445436200718484756634154074423286210609976132434875488474345396659813387174660930205350702719529839432714253711557666000257844230310734295515339450604862227649666876240793243531929926392537310768921353525723210808898193391686682789482811704726245019484097009757609209837240900747179733407881418251958425980962417476101382526439551352593118850456362641883003385396524359974169313228947198783084276004013680747039040972384739458348961865397905941185993103561684368692194853820557803957738813606795499000851232594425297244866667668346414021899159445653094234406506678519484177667794704720419588220432953803263105374948831221803912796784461001397267538921951191178365876625280836900532490045974109470687729123282143046353372835199536482743258331191444590178096077828835837301118575436599589827245319253105881150263075425714939430244539318701799236081666113054262539958338979429716020703387678150330102801200959972522222808014235710947603519255444349299867678178910455590630159538097618759203589373419789623589311259839025983102671933041892151096891562250696591198283234555030590817307351955037216658702880539921385760370353771051780212801295668419841403628727256232144287543022109094727210734741349755141907370433182766261772759968888260272252471336833534528166927795913288613817663498577289369009657495622871030243625907724122190943008717556926257580657099120166596224360802428700245473620363948412559548817272724736534677836472019183039987176270375157246499222894679323226936191776416146187956139566995677830682903165896994307673335082349907906241002025061340573443006957454746821756904416515406365846804636926212742110753990421887161276177870142588648257752238891845995233762923779155857445494773612955259522265786364621183775984737003479714082069941455807190802135907322692331008317595106590191212947954086036407573587502058902087045796700070552625058114206639074592152733094068236494415908910092202966805233252661989113118420162916310768940847235643668081821686572196882683584027855007828040434537101836510969517823357430305048526537380735310741859177056103973950626403554422751561011072617793706347238049906669221619711942591204450846417463835899382399465173955090008594799901360266742614942900664671150671754221770387745076735637421547829059110126191575558702389570014051178226469899449179083017954758767601680941001358376135785913569244556477644641786671153919513576961048649224900834467154863830544779143300976804868783481846727337584368927243104474068076852786255851650920882638132336231487333367147645204508766276149503899495048095604609896043291233583488599902945264002849942808786240398118148847673012167541611066299955536681931232874257020637383520200868636913117334697317412191536332467453256308713473027921749562270146873258678917345583799643513588009593508775563562488104938529990076751355135277924124292774885658885665132473025147102105753525165118148509027504768455182520963318990685276144351382136621523688905787866994322888160283774820355060160298940091197138501798716836337441392759736440170070147637066557035043381211135764150184518214136198234951596010647527125759351853043328755377830575095674254426847122196187091785607839361445113833356491032564057338986671781239722375193164306170138595394743678433926709867124522111896908402363274114966012434830989299417380305884171666130730400675883804321115553794406054977217059428215148861656727712409033877277456290971101348851843741186956554497457368452180669829110450580042998879538990278043835962824094218605562877884288021275538848037286400194416142574999042720095952046541705981049899675045119364711727722204361026140797508096869751766002371877483480161203102346805671126447661237476278521902412025699435347162266608936752198331118135111465038548950251206557726361454736044268594980743969323312971273771573470997139522911826534851555871373366291202427143025037632695013509116129529937858646813072264860082708813335381937036825988678933212383270532976258573827900978264605455985551318366888446282651337984916678394097613537662517982582496634587719501243840403591408492097337546424744881761840700235695801774101776969250778148933866725578985645898510568919609243988415692806969833522402256345704973122452693541938370048431833571965166267215755241934019330990183193091965829209696562476676836596470195957547393455143374137087615173236772042273856742791706982045499530959188724349395240944416789988463198455048523936629720797774528143994182567894577957125524268260899408633173715388962628896294021121088844273765686245276121303710173007851357154045330415079594477761435974378037424366469732471384104921243141389035790924160364063140381498314819052517209371039640268089948325722979545640427017577229041732347960736187878899133183058430693948259613187138164234672187308451338772190869751049428437693250249816566738162606159417682525099937416728839517440669325496534031014522253161890092353764863784828813442098700480962271712264074895719390029185733074601043607291909457679946149292904279816877294264877299528584346477753869069501489841339245403941446802636254021186143170312511175776428299146445334089209769616990983726523617687456058947049681701369749095230720826828878907301900182534258053434217059287139317379931424108526473909482845964180936141384758311361305761084623668372376959134926158245162215521348792441450417568480641206365201703863301295327776990231186480200675569056822950163549319923059142463962170253297475731140942201801993680350264956369558664259067626856873721103391567938398957655651931778830002416135395624377778408017488193730950206999008908993280883974303677365955248913001566332940779071396154645340887915103006513219344866732482759079468078798194250195826223203951312520141099605312606965554042486705499867869230217469890095478507256729787947698888310934874644264007181831603316555115342761556224054744733780492462149521332585276988473362691826491743389878247892784689188280546699823036899397834137475870258057163494135684339293960681920617733317917382085624364336353598634944968907810640196740744365836670715869245211829978938040771375012908586465789057714268335827689785547176871844277261205092664861020515356428406323684818072879407171279668200607275595559040402331787494473464547606281895415121391629184442976510669479693540168660100551960776873353965116149309375709685545593815137895690392510149532656281470119983269922000663928753747131352364215892651262040728877165783584052196460541054354436421665622445650429990102565869272791427529311720827939377513261060528812353734510683729398935808712438693859343891757133763007203197608166044646839377258069092372975234867029169104263692620901996052041210240776481903160140858635584276095370865581642739953493465463145040401995285372520049578052546562511541092524379913262627136090994029022620628367521323050651839340574501120993414649184333236465693717259144893241590062420206128857329261335968087265000456282845575745965921205303413101118275013069615098355156320043107846019065654938065425252291619918199596027523277022498557388248998827074659363557685825605180689642853768507720122203479209939361792682065901421656159253067379445689490708532635681968318617722682499114726157320358076462981162440133167378927886892290325933498617970219949819257396176730758344170985592221701718257127775344915082052784309046194608352174020058386728497094110232669539214454610662150064106747402070091899119513764669044812672536915371622907913854039375600778351533741677479421003840023089518509945487790393461222208650601605003517762648316111533255877050735412792499098593734737870811942530551214369797499149518605359204038302357163527276308746932196221900642608861836761033460022554774778136410126919065696864950126883762969072339612762872230411418136100602640440300359969889199458273976241146137448040596970625767647237660655416185746905272292382282751867991569833907476711461030227766060200612468764777288190967916133540198814027579921741676787992316039635694928515136336472195406111717673873725557285229400543617851765023075446938693078734991103521825329297260445532107978877114498988709115112372506042387537348412570860640690520584521227545338480082053024504565176695185769132000428167580549248117805198326460324457928297301291053183856368212062155312886685649565126138922613670640939533345705269869596923503530942245438652786776730275404027022463844835532399147513634410440500923303612714960813554905315390210022995957565837053812619656831442860579566966221547216956208700137277685369608407048333251327931122325071486302069512453950037357233468070946564830892098015348787056334910923660575540508641115214414814346304372732710450277686619531078583233348578402971609252153260925589326556006721243594642550659967717703884453961816328796144608177892721718369088801267782074301064225246348074543004764928855534090621851536543554741254761527697726677697727770583158014121856880117050283652755432148034880044429799980621579045641619572127845089284898064264974270905791290692178072987694779751124473059914060506299468942809310342164166299356148281309988707452927160484336308184041264696379258430941854422163590845761460785585624738149314270782662151855416038702068769804617474008083243436653823545551094494984310934947599446726736653525176627067721941831919771963780157021699336750837600571634546436717767233875886434056448715669643210412825956453498413884128904206820470076155969168430389993483667935425492103281133631847225923055543830582069416756299920133731754891220372303490726810685344540359935618235763128377676406310131253352121419946118693508331765878520471123643312267651299641713252175135532618676819423387903654689080018271352835848884441117612341011799187092365071848578562210211040097769944531217950224795780695065329659403839873699072407976790408267940076187295478359634927939045769736616434053597922192858705749574816966940623342726197335181366260637359825755524965098072601236682836059283418558480269584137725589708837899429105498003311138846034019391661221866960584915714857335682861495000190975911252188003964197621635593757437180114805594422987304181968080856472657135476128316292004498803154021055305970766663627493283089168809323592900817874119857383171926167288349184024297212904349655269427264025596414635259143484006758676903503823205729341329815935330444464968294413673234421583807616948312193331198190610961429522015361702985751055943264614685054526849757648078080092213358113781977492717685450755383287688744745915937311624706010912446098294248412875202244625944776387494919978404468292573609685345498432665368628444893657041118177938064416165312236002149187687694673984075171763075168498563592014868929431059402024579696229245666448819675762943495353263821716133957577907663707645695702597388004384158058943361371065518599876007549241872117148892952217377211460811543449826654798725800566747240511220073834592715757277152185899469481179406444663994323700442911407472181802248258377360173466853007449855647154200361235933973129144585915228874087195087086322188372882628228846318437172619033057771476515641438223067918473860391476831081413582757558536435977216500282778037134228696887873497950960311088991961433866640684506974207877002805093672033872326296378560386532164323488155575570184690890746478791224363755566686780676105449550172607911429308312857612544819444494732448190937953690082063846316782250648095318104065702543276043857035059228189198780658654121842992172737209551032422510797180778330426090867942734289557355592527238055114404380012390416877164451802264916816419274011064516224311017000566911217331894234005479596846698042980173625704067332821299621536848814041021944634246462207455756439604529853130714090846084996537678037932018991408658146621753193376659701143306086250098295669176388460567629729314649114937046244693519840395344491351411936679333019366176636525551491749823079870722808608596261126605042892969665356525166888855721122768027727437089173896397722575648905334010388559311256799915165890250164869614272070059160561661597024519890518329692789355503039346812197615821839804839605625230914626384473862960398489243861872985077759287927220685548072104978176532862101874767668972488411395603494803767270363169210073508340738652616845074824964485974281349364803724261167042668708319250409976153190768557703274217850100064419841242073964001396036015838105659284136845741191027364202741637234882145241013477165296031284086584197879511165115298278146203791398550063999603265912485253084936903131301007999771913622308660110999291428712493885416120380204113401888872196934779044975274542880728035093058287544207551348166609278793535665212556201399882496284787262144323628536765025914504683776352825876521391564809721419296755493843755826002531685363567313792624758780494459441834291727569883762262618463654527434976624111384513054814498363117897844897320767195087841586188796929558197332506999514026015116755297505754378102422389579257865621284327312022007167305740692868693639301867659582513264991459502609170693475194089753574640168308117988464524736189560564794263580705625632811892696630264795359510971276591362331808669215357886078127599105371714022045061860753748663063505914839164676567232057145168861707909846959322367249467375830996070425892204815507991327520885837811176852142693347869218952406226579210436203488529262679840139532164587911515790504605797108389833718640380244175113472264725470107947939969535546696197267632552299146549334996632341859514503609803440922122067125676987234279407088570704742931733291885238967219713539244924261786411886377909628144869178694681775917171506691114800207594320120619696377951032270890295660855622254526026104607361313688690092817210681986185537809820184711541636303262656992834241550236009780464171085255376127289053350455061356841437758544296779770146602943876872251153638011917581540281208182556064854107879335989210644272448986189616294134180012951306836386092941000831366733721530083526962357371753307386533382048421903081864491840937239440334052449095545580164064607615810103017674884750176619086929460987692016912021816882910408707095609514704169211470274133900522533408348128703530310239196999785974139085936054335996970756044601342424536824960987725813110247327985620721265724990034682938868723048955622532044636026398542252584164643242716114198178024825955635449072192265838636626637508359443148776351561457107455280161596770484427141944351832756984075526779264112617652506159652354571879566731709133193587616282559207830801852068901515047133403861003100559148178521103847545429333891884441205179439699701941126951195265649195941899754183932346474242907027188752235343936736336632003072327470374071239825620246626519740901997624520561985576257600087081730832883443818310700545144935458854226785785519153722923795554943334101744201696000906964156127322977702212179518683763590822551288164700219923488640439591530184640047143211863606225270115411222838027785389110984902013427410141215597699654388771974853764311582298385331230717511329619045590079380642766958190148426279912217929479873489018684716765038273285520590829845298062592503521284519259279865935061329619467962523739725655841578537445675589980324054921869628884903325608514553443916602262577755129162007727968526293879375304541810807292858919897153817973434961872329276147478501926114504132748732429705834084711123337462746172746265824153242710593225062553023147387592517247873228814914559156050363345754242337791603749525024930223514819613811625639114156103268449580725082734317659440540982697652693445798634797097431244982719331138638731596363612186234972614095560799206283169994200720548115253533939460768500199098865538614334957816500899616490796781429011483876456821749140756237676184537751440314754112067601607264605568592577993220703373333989163695043466906948284366299800374145276277165476238255461708831898108688068478537055364804693509588180253605297407935386765111950793732820831462689600710751755206144337841145499501364324463281933463890509365457145069008644834401804283633905135781572739733345372842633721740657757710798305175557210367959769018899584941301959995730179012401939086813565855396619413717944876320798688003716073032205474235722668968018821234243918859841689722776521940324932273147936692340048489760590379580946960417542796137825537812239476461478329269765451622902817011004378460387565441517394339600489153188175766505009516974024156447712936566142539493688842305174001299205568542898538979426699567770270891465137368922061044154816621568042198384767308717875902792091759006952734566820265133731115180001814341209626016586298210766635233617740078377834237091526440630540718078433580610729611055500204151316963730468492133568372654003075098290893646120478911147530370498939528334578240828173864413227100029683119402033234564208264732762338302946393789983758365545599193408662350909679611340048670271231765266637107787251118603540375544874186935197336566217723592293967764632515620234875701137957120962377234313702120310049651521119760131764194082034373485128526029133349151250831198028501778557107253731491392157091051309650598859999315608636554774035518981667335358800482146650997414337611827777233519107412175728415925808725913150746060256349037772633739144613770380213183474473011130326702969173350477016321066162278300272692833655840117914194478087482533607144032962522857750098085996090409363126356213281620714534061042241120830100085872642521122624801426475194261843258533867538740547434910727100497542811594660171361225904401589916002298278017960351940800465135347526987776095278399843680869089891978396935321799801391354425527179102253970108106321430485113782914985113819691430434975001899806816444121232733283071928243624067331965546926778511931527751134464689055042481133614349846048490512583456832664415284897139723760403282126602535166939140820499473204860216277597917712347510975024030789357599377150950217516935558270725339118923340702238320775858021371747783787783910152341320984894234596136923404979982793041444631627072147961174569757196812392919137409829258055619552074342432959828989805292333664154192563673806894942014712413405250722040617943552525552250087487900865683145428351677505422948032747830440564385815919526667582829297052261276287110401348017872248017896840524079243605827424674430767216452703134513541676496689012747868010102951338626986497482121186290403376915685762406992963724930972016287072001898354236903641492702369619385473724803298550451120891928798298744678641291594175316756025334353106267452545071141814832398806072971402347255207134907983989823552687239509093656678789923837125789762487559904432288953883773173489411227570714109597900479193010467407504114353817824646307959895556389918847737813413470702467473621120489862269918885174562517325193413520381158633501239130544419100736284475675141610504109735058527620444891909789019843154852805339857778443139338839943104444656692445508859463140817512203313906815965925105468580131338381521764182104334297888261196304431113887962587460902261309008499754303957712432306169062629194039214397402708947776637024881554993224588259790206312574369109463932528062416424768684954553249380176393716156368478598237159023854212658406153672286071317026747401311452610637653833903159219434698176053583803106128878520515469336392410884676320095670897183674905781630851581381619668822220475704375906143380407258538620835651769984267745231958241826836982701602374149383634966293515768540613973427464708996856181701605511048809715548591186171896680259735417054239851355600187203350790609464212711439931960465274240508822253597734815191354385712532585404939460108657937980586201433660788252197178090258173708709164604527279771535099103407364250203863867182205228796944583876529479510486607173902293274554267856697768659399234168341222746630150621553205026553414609952493560508549217565491348309589065361756938176374736441833789742297007035452066631709296075919896277324230902523974438610142630986877339138825186843165010279649114977375828889134503411488659486702154921010843280807834280894172980089832975369406449699031253998639195816014689952208806622854084148642747862819755466292788146216071713818801808405720847158689068369193933818642784545379567192723979723646516675920110579956639625985355127635587681402134098290162968734298507924718460568748283313812591619624761569028759010727331032991406238646083333786382579263023915900035576090324772813388873391780969666014696150317542267511259933155296742133363002229649064809345820081810618021002276645804002782133367585730190113717546727630590443531313190360924890972464279284555499134900051802957070829190525567818899138996251386623193800536113462242946102489540724048571232566288889317221164329478161905548680549434410340906807160880282279596869501336438142682521704728708630101373011552368614169083756757476372397631857570381094433905645644685241830281481079983769185121272019350440418046047216269394457883770901059746932197205581140787759897720720096893822493032368305158626572811146379969831375179376232151112523497343052406221052442343537329056551634066695061658928782187077567941760807129737813351871179316500331555238224877306534441794534153952024244497034101208740721881093882681675120422994049481794494727328947701115741394412284555218284249222406587526891722727806071167540469730080370396187877966948825556146743843925701158295466613586786718976612973112672000729715536130275035561678177654422874421147298816148027052438068176535732755786025058470840132088379328160087690813004924914736825170353822196190390149995234953871059973511434782923394991879366086923013755963685323738067035911442432685615121094042595826393016780171286692392832310576588517140202111969570647998140315056330451415644146231637638099044028162569175764891425697141635984393174332702378123369380430128926263753826677950341693343236075002481757418087503884750949394548962097404854426356371649959499209808842947903636662975260032438563529458447289445471662092974954966168774141208821304770228161164560440072363515811497297392189667373826472047226422212420165601502849713063327958143025160136948255670147809357908896571349261581613469018069650895563101212184918058479227206918716963163300448580201028606578585912699746376617414639341595695395542033146280265189511679380745733157598460861737026878676029436777805002446733913324316698803540732323882818475010516413311895370364884226902704780527424906034920829547550540034571601840725745369381455311753542107265578356154998744474804273234578800618731493415660463529797794550753593047956872093167245365472083816858556060438019770307642460834898761013457093948770029461757920619525492557571090385251714885252656710453498134198033906415298763436954202560802776144219143189213939088345431317696851018401038444723489488695209819435319065065553546173358140455448378847525262539496658699920584176527801253410338964698186424300341467913806190280596078548880107897055169462152287730901044674624979799926271209516847795684825833414022664772108433624375937416105367340419547389641978954253350363018614009515347669614762556518738232924685473569358028960115367917873035531593783630822486151777705415775765617593585120166929431111388635821596676188303261041646517148469793854226216871614001223782137797741312689772667129920259220174087700769562834739322010881593562862819285635718933849588506038531581797606794798408783609759601497334205727046035217906056476032855692762734951822032361441125841824262477120120357763888959743182328278713146080535335744942976217967890345681698895535185044783256163807094769516990862471000197488092050095219436323787197648703392238115403634754886268459561597551937654101150140670012269274743938885899438597302454148010612359080362745852884935632515853843832424932526660875889083187007091002373771065769850564339288543376583425967506537150053335144899082938877373520514593330496265314151413861244379358850709446880454869753581702129084907873478068143663233228194158273456713564431715379678180581958524648400840329099819437817181773023170039897330504953873561162610239994332597801268934326055847102787649010709234438846340117355568659035852449193701810416262085042992586974358170981338940459344719374938776242324098528327622666049423851297094532455862521036008292866497241749191419889661295580767709795947953060131191590117739431042090490794244488685130868444937059090260061206494257447103535476578592427081304106185462198818300906345881870387558562749115873754210646679513464875867715438380185213482819158124625993351601989355951679689328522058247994210345127158771633452229954188396804488355297533612868372259353900792016669413390911687588039888288692160023732573615882071635162713328105181876021048521806755266486739089009071951380586267351243122156916379022773287054108420378415256832887180469879525130732663402785190594173389203585403956770356113293544825856282876106106982297214209619935093313121711878910787668720445488760894101747986471378824621539559333332755620094395804345379197822805903959599274369137937786649409640487778417483364326840262829324062600819080818043909145563519368560630450891422896452199877988493474777291327972660276584016678901364905087411421268619698620441269652829810870454798615595453380212011556469799767857389201862435993267776894540605082188382279098336271671244900267611784982643770330020818445900097172352043319947082420987715144497510170556430295428218196700092025156158441742059336581481349026931115170938722600264586305613256057925609273322655793462808056834439213736884056504343073965740610177793701414246154930707413608054421002956000956635889778992676305177187819437067614982175641865901161608654086353915130392013168057690341725964536923508064174465623515239290504094799531840748621512105618338545661766526063937136588025216662235761322019417013726649660732520107719479312652827633024138051649071745659648537483546691945235803153019691604809946068149040378198297323609300871357607986214254220964190043679054790499300783724215819545354183711293686584305538427176280352791288211293083515756565999447417884383815651484342298587042455924346932952328218035083337262837918302165918361815542171574484657784201343299825945668845582661719790121808494803324487872581837748055222681510113717453684178702802744524429054745182346749195641885512444213377835214238659799259882032870851093383868299065719946149062902574276860388505110326385445404191849588665385450405713236296810691468148478696591668618427567984600418687622980555629630459532279230516167215919686758495236352989357885077460815373214546429847923105116763577494946229525694976603594739624309953433104049942096778838270027144784940690370732491064441516960532565605867787574174721108274357743151940607579835636291433263978122189462874477981198072256467146640548501310096567863148800903037493388753641831651349825466946733161181233648543976493250261795493572043054021829748712511074040116114058999110930624923128131163405492625713567218186289327861388337180285350565035919527414008695109261675414767926680321092374670872136062783329223864136195941213392780361182763241060047409711110481400036233427145144833346416754663546997314947566434236594934968458845515241507563766050866328274247941360628760412906449138285194564026431532258586240431418386695906332450630003922131926476259626915109044576953014440546180378575030366862124622786397527466678701210033929848733750144756003221006223580293437749550320370127384681630610265703008722754629667968808905871276763610662257223522297392064430935243272281008599730951325286306011054979156447918450046180467624089289256809129305929606423570210615246462050232489665939873249339673769520239917608984745718435319366465291258480644801965201628387951894993367592414856261369959453072872545324632915291101287637706055706095313775277518679232921349552451330898679691651290738413021675732386375758200803635757280027544903279530799007994425411087256931880146679355958346764328688769666100973957499678365933978463469599489506104903836474095046952260638580467580730699122904740898791668721171475276447116044019527181695082897335371485309289370463844208932997711258568408466083399340456890267875160087754612679880154658565220612109534907967073655397025761994313766399606060611064069593308281718764260435734253617569437848484952501082664883951597004905983808121052211110919433239511360514464598342107990580820937164645231277040231600721385437234612672609978703856570919985075956346132484601884098501942876879022687345565005191215465440638292538512763176639220509383452043007730170299403626154340013227639109129883278639204123004455516840548898090807791746360924393349126411642400938807463566072623366958427645836982687348158819610585718357674620096505260659292635482914990457683072108932458570737016607173981944850288426039636607460311847862258310565808708703055675958613417007454029656876347741764310517510367328692455585820823720386017817394051751304379948688223200443780431031709210342616749980000730160948145863744887785222730763304953839443453827706087607635420984450083062476302535727810327834617669705442871553153400164970766571959850417481990872014908756860377835919947193433527729472855379257876848323011018593658007172911869676176550537750302930338307064489128114120255061508964110076238245744886551825810581403453201247547232690875475070785776597325428444593530449920700145387489482265564422236963655441942254413382122254774975354946248276805333369832841561386923634433585538684711114304982483989918031654586382893537991305352228334301379533729540162576232280811384994918761441413229337671065634925288145282395062090223578766846501166600973827536604054469416534222390521083145858470355293522199282727605748212660652913855303455497445514703449394868634294596584310241907859236802245607639367841662705185551787029040735573046206396924533077957822459497104201880430001838814290081730394505073427870131244668600927785818110409115117293748736278878749074652855654347488868310641100510230208751077689187815256227352515503795324448577872776170019648537035551676552091193393437628662846198440262952521836785223674751088097815070989784130862458815226609635514018744958369269177990471207264949057372642860052114035812310760066995185361248627467563758962252991164960668765082617341784847893372950567390078786179253514406210453662506404637288156982323175005962610809219552111508593029556549675388626129723399146283584760486276270273097392020014322487075823373549152460856082103288829741839064788699232736913600488374366152235170584377055452108155133612621429118156153017588825735948925071088792621286413924433093837973338678061317952373152667738208580247014335270092438032669517421195076708843263464427491275589077468635821621660427413151702124585860562336314931646469139465624974717419583542186077487110573384584336899396459137406033821593522435947516262391886853078228217639832373061802042465604775279431047961897242995330297924974816840528937910449470045908649918727273454135081019838818646736093925719305119686456018557824502182310658894379865224320506773799661969554724405859224179530068204517953700434724517628935667705084902131077366257516973355274623029430312035962609534235743972496592110106578178261087453188748031874308235736991951563409571627009924449297491054898515196586647401482251063353679497371425102293418825851173719944991150975837461301055050641977215319293548753711916302620303285886585284801935092258757755974252765840117213423236480840271433563675420463751825525249443296570438613878659019657388028684018940876728167141370336617326501205786539157807030887142615190750014925761129276751930967284539711602136063030905422439663206743235827978893323244057791992784846333397777376559018705748068286783479656241461028995084873996929707504327530299728722973279344429886464127253481606037797072982991730292963086958019963124133049393504933254123550710544611825911411164545347103298810478440677801380771314654000993863064812666143308582068113958383191695455582594268957698414288937434670841079463189325391069639557807060212459748982935646135607889834724199794785643620420946134123876131988653523583129968622689486084084566556068769545012744866314050547353517468730098063227804689122468214608067276277084024022661554850240089528916571176174390203375848778429112896232470591918746910420058483261406773337510271956539946971625172483122306339193287079838007484857265161234349332733566644733585564302352808839243482787608861649432893991663992104883078477770480457284914563033532650700295889062659154985094079727675671297950100982294762289618915914415200322838787734851309790810191292672271037788980539641563623641691549857684083984688616843754070651210390625061281076637990479088796747780697384731704752534421563903872012388063236880370179493089549007763315230635483742568166533616066419800301882871237674818983302468363714883092592833759022789425880600872860388591688497306939480205112217663591382515242786700944069423551202015683777788518246700256517085092496237477268136942843500629388144299879053010562173754591826799732177350293689280652100253962688074980926434580116557158867004435039765053234782873273688408635400027406767838219635222265392909398073673913640828987220177767471681181958561337215831190546829360832369761134502817578302029348459829250008956826302712632958662921476531422333517930933879513570953463771836840924444220963193312956203055755173400679737406141621079236334238056468500920371671526425563718538895714164197723874226105966673969971731681694154350952831935564177056686222152179911513556397071433128936575538446483262012064243380169558626985610224606460693307938478588143674070005997697036490192733288261353293631124036506986521606389872502672380874033967443978302582968942568967418643361349794752455262914265228424192430833881035800537870239995421721136865502753413622116931406946695131869281025747959856051450050217159133177516099578655519818861932112821107094422872404424811534060558959583558152320121846058205635926993034788511320686266275887714460359966561084307256965005630644891875994665967728471715395736121081808415472731426617489331341746326623542220726001460127012069346395205644455432916629866607830890681187900908152950636267820756143888157813511346953663038784120923469428687308393204323338727754968052103028215443247233888452153437272501285897476914608083144041258681815400491877722878698018534545370065266556491709154295227567092222174741120627206566229898060328916720687436549482461086973672255474048128892424718543236057534116728507575520571311566979545848873987422281358879858407831350605482905514827852948911219053831956242287194847594078593980479010941940706717644390327307121358873850499936388382055016834027774960702768448802819122206368886368110435695293006521955282615269912716372773884189932871305634646882273982887631986457098363089177864870866761854856800476725526754147428510281458074031529921978145577568436811101853174981670164266478840902626828244482580275320945499151045185177165463118049045679857132575281179136562781581112888165622858760308759749638494352756766121689592614850307853620452745077529506310124803418045840594329260798544356200937080918215239203717906781219922804960697382387433126267303067959439609549571895772179155973005886936468455766760924509060882022122357192545367151918348725874239194108904441159599327600445065562064611646556654875942473692523369559930303550958176261762318495619064948396730020377638743693439998294302091470736189479326927624451865602395590537051289781634554233201149759948962784243274837880327014186769526211809750064051497558896502930048676052080104915378854139094245316917199876289412772211294645682948602814931815602496778879498137772162293594378110044480607976724292762495107841534464291508427645200020427694706980417758322090970202916573472515829046309103590378429775726517208772447409522671663060054697163879431711968734846887381866567512792985750163634113146275304990191356468238043299706957701507893377286580357127909137674208056554936246464126002437968454377733902647251281941632007684873625176406596754069362175887930785591647877727473927200291034294956244766130820072925073452917076422662104767303786316995423745511745652202278332409680352466766319086101120674585628731741351116229207886513294124481547162818207987716834634132236223411778823102765982510935889235916205510876329808799316517252893800123781743489683215159056249334737020683223210011863739577056747386710217321237522432524162635803437625360680866916357159455152781780392177432282343663377281118639051189307590166665074295275838400854463541931719053136365972490515840910658220181473479902235906713814690511605192230126948231611341743994471483304086248426913950233671341242512386402665725813094396762193965540738652422989787978219863791829970955792474732030323911641044590690797786231551834959303530592378981751589145765040802510947912342175848284188195013854616568030175503558005494489488487135160537559340234574897951660244233832140603009593710558845705251570426628460035440282367876855098267816176552037579565548167789603892749835560879154117774942357340076416109329400389998219926725708695732606877497422480202330752518765025596842076069322998858757989889646074438178817008154889522651672283404527721910699141576463948523112679473086580319507645519767562895742888179681209002638714525785831527761510908863174024369568056787301523542780479341426649522383370711751126537550394237209878466804913947344653071407962259728713050307725871487557050258257346686661380235142605611619740554343654869800544487929597028759035225840978268359866644658604569424139072909526624993290297344056816068380572662605727708840707347149606006456145407073443278251408747427550672230484535700609221439000299298160821171704791761450519100813267037521493074056785331110605835291278100739174994919784511291591368110739405517520801963053935074024850955377250036705466516233043042508744232426240463211507899733692998540704165626104197670020241509489241185609240963760442961200236459070644977062720791901923596480704892363697986019828308728422856475235316288279132429552481444750552190967204608068954518171220493032185374062724742151974030576904360268636078079200477623242955182947352202724437633902772139208776706571624163975178585925442692342853527432885633685078965196207251941655606187037055021846284543425785038300009537451829295844046491883868579348396115129716058166574509670367749583666669312188176367964494361713041603724305065848513174926405585519401800518090847521186822461697614924323831948643441590855801107307031120150224341607315792952875293683582039700338911211417068521936658978945950315438958901530382714300192958907414994359289408309707707836287591448403704503861896697581120185231923186865996803858381237032915620757883594878094168820553160512819015264759280757495815456422134145937816705699286829989561198235383715788048047870458417539466549769017322031089007030336291176730844845037214566964440146954517385743415781015861878383927855260939913057025557555906094705149809348777332007279757303824598946680968082222134848587382299928179409082566520958165547247524456674369759447468637633242890426977610679193391098330042231029372829879890320939109268283630617361017387812367989864514931170243712828588263048629888449220741564060714705913740552466575697187021735528724543942771480917936443765063786186132434863579741125852086345992780368879249835436329845768765016506511534500869572123950754478568317363155715352704652423525973751340882546160966144074667551422683603195980107215246355106917187133573168548563128085783443562367095965094994696882066118511808603420282133180124941099150260143545001743273079362511307029825049941799428445114647932915459955590958780762163666859179106543596606525352532027365072598912125568684280207724648772201099663182955955290339331228436486447597356085984076094729838954243393262315323991898185226418083129633354635687482886346561850481063228880559673784456200094146560349928087940511531005758712955257196411150685034077371060438037125957559698594936205847751202635494734753474818926225419035267161442928489985753674069216527163008606065437373682355658862648634368915321809557220445677713736831045807558452961283283260631962972852796667436297480082131862792186904428434263073576070399966943078950814726973025381737569492275179535432615691204059483286094999236641228788122641914850485632807206641855705952037503032291689448942757830609091085241060140068327420558396977382315073499610875876370425556496408685507194225634496673243065625925047458176273328181601701969816654242637876360145303594653845032547667499973734083566513818602515652028363738917101654541488267444800910570418616262683797112088614135727961109908829297022969212818097879895139150427093678644498319642013456683390877594300644248562301212461451169792193963440950808322928129427043659914648274998437594211302041829730841717881309037955854560324717081919530277146579455547554475428443440813938890860977601785738930751866190650501807716500184074432585402418436050111824299070232341724367452536534959479906333454075437181269939983371921848541873597984534893459226851506818266249007802933501265882497422624188535252663670282766249934982948874833106176420842901692305289960897860413006510902817980504058710767117904113021748279668235300196022025318557678984331758680637835996879160153892222023657576558158661140919939486159920915991755334178303334764313163501270539069707932656781241590643428472136023521823674121473312449994433415591527431593168747788253315509277033620290122259779480985539220006452716228085539827890658423344755282127651765057266326769114107503484587189699643487577513847914818363510062146681858509634888708145697672202016799119946241777668890791713686594596072646853881077878300216136827669702622345941873747673353799888440342704680304255169412715873932039844437460454781611305662517641275982118193966110185056288055594256606003231211618099462212930100247091334715068226843045868030090424286168202556214094608790006519109949557081581650582898334073946608445756578063669027284346201858732825292479650528668140850353851983752363745192562279549029055790703028395010485483592983454281448730435804705331508151050300152142811717539364913316617262123540552786330800208317705563029496359420165433309409417719632623411938710516157010179805355167937086029136675698609712412036858381295769530779814136570017476135696698614606849143969957383763169582460251334210807262171360194301808720988855141502416381832597525959316553186583311712685794152720661221842266141182515465748487831261034783454674925830872998544742120644509523324505087743149616655525179716802099172002640937492190756993689633028139164720896358177173555584859270652450486251641954055080134351032338981337830249770182275490638149996472333407961304146973947637265086927334710841568560843092131624043462986392084166005590459850649124350526476606760034444161818640367008377411410109432058895559865867007786367189694408962232137403411359719913313594655368544669236765258901210841377743248219181274784789228726489297003237187345615798159983483910041260105074696459943033197881063491392381249050306143340791832800406390709867259619709831126596014747372533052685371774214655400587392462372761736490519871336806772395257078136068668326139501432950947485159472466752720168431658660880751276858475554118438116901162200555211348448896066825922743131900796301158708467011765493539304656335622531124472779666900583119061610197266307397054253143981845737944948678013461821787593907699960202908396567728784690573640156401504769644899394754147460833991869688927115694234549265124664550779255402810503762203596753055860185649205606287909076945333920880884947782889485112215474323019138324556299388102061449026687601020775321091568497783074085964985796715261701003947549453991769879132354655010640735581699940975624814996744327842920276264418979391815839456270817330158216022551965989876937616401986120746675504886111085572676450705262244613022233585207227362048505728923881588493875453522918639971438088406175728622095012250651586310425888413435543197372985621775307202262947555248304444534043488887858117034134534252235431940787797284676018158322709774518092934219318981581248283265895004070485520609989378390034191416304463916388054965878650137504634169565515661829887863070584230696766025405302481147100789978421183048901046405689653970288559553092555863605215895737511408956490584415677493710585964801431587461449125054925319116465382158519737009328019453032057262845265804604633781663142993307664664653076059054896288872418971606022588261757753992205513150937720062486308556282049357575272499556708922163423398360256532873102919400704117691922085001511673567010195897100179701957812089291096941775436990436820256302405482262540190569650771058157424072149633956036527028333440730575007367456226058464988611510168961218111905847171446106871976101745658737379674069713742323875383903031720020020720592848878512391174647167374373792328388196620168762219134623389376259952702567213862211245898021213050140728890430032253550409586681872413936993819306914874471718664618311194260316166407037731648700186479960024304400324224180940227853330901150988087067826883531720076752255313800881878043169019007280483179928741412547612308960683309582837766768828757868868309297600101197453389833195258861963013291709438581661537417179449631917715431250695985348128568461937766989427745917091880252001274990555940728969659479333167224362156789677696670803522903901848573080627567086765862710476940920356559302535274341896592700222704923318682999156093641375700498853730459639615273462939697495174806269645179301871998678853758141597579931480660855723256837430528276417567005028804048942989958094810353483393414492788592526219241554723199714338508663732092663272824351493364070458968385234562474436117525676698776759722343920635750747155291810276261401299248042288399029787992541851749912963028399072963558857989059331779590876907390564602562353356722155225946883829845288292296627513716242217295467867071584092418408414755758253938524096330205134970474069539956789798172786092046228683973577981511186815265988460694975896548131465115039262637774951376155724819511611987725034456471073851343592735553871246237559819381321423844158192907004638977168388720791636174143249707910965816274642971707287172514274589835689709553462682016908535610894489840710058192030217694512077177458879551951047338418473998079630676788584516757572990430697154264238349800987086993367091210839445350624592243231234827854966037465718801489293794514787054060792457590060121962212392872001721558866634573497140953372115165598575794172441988902616701610161155783431502546032878119842402748460851072240667677876085524761777383308950261006438835055020545632434616785945194179566987496851524488384751361818066710831616556420936927052061189851729261714171443465550870630606355101294940030975916779915842604919712095432270267843265429657240327208871432199964531320258710967716512854966996255269860731176371820749882739977060199136209308323073683820645573256376598291257813149222422042797124144162995126594563979275938038380478262316042432539913285112303224703756194232173304785407857624401329171799297924078339071575798142681686465538294684739920588863165593491986789696284044734496802407709283137640810335225524271740410767356542444100448334744010172644105295478729634589864050120360802445119035099497449397361718157527709378020923666813584163626831926340671418279742134254622070541560005095967404561684045177174795279035325493258912048338574659009678173041600052108893461076875400424197780308288518120017336955912713771419501136130440975327919050489158324639914348353164868154857917863293512392555251021118278857369606027693130146966143344964230211438248370563353279385889526767207668897127443581563208810665014956814355879657690985776590276870745365927636497555344961730807816098710324801379513617036776345759497568620801399637455176242514778062872226597145548290676929571364357215267446898788941882075129222575650914355282887461419509786242752788157156640076372103780319404309584427254926998716923433189002214150311399876526068876156674021019720171960239086108297492763956954115303227546017387079562599357978530244347671639959146231793123998998692843797570249236955158729768385400522765149561444710597196288988815710941517170151811474351364385400511624620213117480079198374970010047136343252328157891135545045337190527506822915618500332846956792622620819044247334036250389279207158596003936315336884272437536679969864793474113319832861944146065392278409990314384035456504705678955202482717601187433564369024350308563130955905525039049273161331173492258464460902453507919018441129932169977045183285358648042855682220873721361649058630325636891308410376021567992702000532235543980465311933977545904404507856802139846500969342954731026924994758646605809166998416068464608729394380827430828581747969417287299031101319267557389798409136425347969494348037770336463495847686298259010347072786121862300198660798778268424593383563891957020685352160321163523006498874460020017041305698536515466875202385937518328037285114327481169968369284922044738057063349661871124094783591586962685864358914135985425357768877493274363451475448864086881803036965243175568830020586077325695971608648541583446843248996307701137134467515693024488548207712413355773230694945806726784523594363150787272815790157307003317879685443627952571902362327461426286873273800949774112285623766321490465329407202619753907174042225953924288816455979657003095714138910693684503626823105398674375324005270153474589332567951494185453780882706345729596216908538353537038141811557381637820903256151986974535764641212549807600515614170729804699481359348315056811664279321933527982271471576734018608872151879966935025270075755609971988286306428544812827513928069470275014816328972731434734852852950460488327167397898156367880478044360210900732072736974934463049973144257156043313369038761810094887312071348271081588985748326585420751007795311832686170803707093592761493678253085834048235100363216637895742620255035011686154340737950451648289675569835893552202017367954807578190950269798127114870343119036311224612829530382051287043092947197459469082102563478899543177152437969621128122450342606639926885213307919637027778044885792057304699080092344018663811325209712309647605998994792575985100817303960682221997532730160658262852758257669507854726034938298133582528178670608512656002268871781125359782933734779141273628418865617592083287944741096970387985473698402545806329483502235939354358748022398976091629625011047393116944910066690723063469313016971182063253526924404384009372428442820970936485690946892008737175325255703054353982872781230113980809386701547488580344563187131960267854879389331620500767526411204439023758334272429869965478636853410284885737025472550236566341868091903838867078790720840361940216467012153483797815183282647257862881520710108149955898033811896156944175676134071704653851217090212377788433364965187211990540758187739439752836414395304424591390317881300418879188711455314826746998705558793104024038888408385068734162507165727418513495208496367095554245043948394804597915622828248378793415272036226336956180555637107681488889361927574265993582355943153088793305276755874751236506584396947560429719200231986802435171993786810036110231256836425607959741057415362829718004649774857371837863903703901539737491165468549971645394161121641761071714540176519056505252066227788312904571969320599024137539598386198260320549583950167555250964413711822256149601400302303540789920969867750786720003807426797053030716793229601564862280851840335235017060858951291222324611783025316362894394607365277133651163164644619909902122492241231516899276785586373631552600250348848781323300191018939961670273141699962651194574263676196500243473717272902846220979839487106598227000995491887769618850543265321180221944428222842515255614118743401804194614139451471287252759239125596443735683397289633126767823491035633296129471910151571431157954909339032614119186547523762472153110207936911584874220582274734320173558507712243796985796549158062795027409771688611480761631516185530685669245717176922044366843312739893379411162972245169998546856221570241759471176995291655021168550010898576193463945590882627077531146577522388463435193765397349848024549760760244030808448901068387869726123709783578245166801171485983679405529046198262165669172027426285482393396001825459940925430816969103297841123402288560019054934275022318529471282960969397681373419770427812130014732867760571940596997927551246171843495698564171287248118346542064231871455182415286763056751311626771773506175112454633879942652912701057899567180572143655791835069177793070407573290439749499582241062381051491765023850418273009662017175094059080540895728375540635515221996582075735131570759236153986394592111558640009880975526105383825689927215847850417460651615113378833609760121148487005560165812492470682568442720454728963094203066504452986462235942260085549915891499536064984280345794927570094979594506023787750194706246323949549578230822830668408188025210766390742309737209162853371768062164469354323179178553058331714208479886303408465726426939557002685760575393478885870946005827232305191081175142349126873365859607998917329289158960018150918163374008060354752000515117510290122992487096154592802620607616982721810291673155489294237408519674330791660784990557821019357136624359908836138598085161564174769460547855400819535306708030896976304529468682332105328782374389441156851762717116363094014799096494563545929501307390036268210073263700823561506912696431833517162543903046989893142615442635951136346605737865495124457475262167895470362890483048499680403772251343193737344123661858694458806401858407314763379294038634043591941987235526301565460805186867606804316084512845916042441326987912538560299159967278766195195053176488313469325736689464438255813910848620966374267457983130122234387258312442203309457145754147047929387585823899773851521352372389559664312235643262628601147489086817159281066872708400820337718692153523526926347226809082598988984002620815217828261122931311820866007099686036540981832680755824776706950410997586143624355216194535302920025466736799648504337313349520821075119925892663899564756985870790185612379157886437446903787150950011255021003884531192365296559946190047484662064234794232967006052900370917557818870819352214687142723527763255989808694872111384598001412384216382782441273654244674883338167971620112886191415401936712909478990264666443156098372961501968624228250672306166720943546571425149308642488778598682759588749065077260250951829536765181182368616944724360783764294762469226319498921964644068316928766161506050813846319415116202577907863071801231159458603896562526554223346234454507394788690268159497513116885143694521021688319044616862976332522986385181885004928693572764766823855564636554496400631764828557578586661022855156485990882095868944436254698679523822686115969910056366082926791533753816066112247869531326158531871763885989377929188902998793879810003697307848959270625410484859315854323395683104239029907026344379787569185543408976440760130844481978626507947644083013494243583428188591525929347143631753374958970107287350127078898048163504567666769320755305184043244610074032167647183608370847506512693070766084982529900031785030585368213951273503863824605642510337775580986464339801718620814266307417259222600051109134268107467012901430165410106493321228379082751500100353001565459750832377296543969738204774162657106574082164996062622749618795334790706598897487177956433406484174564574790692517014949981009535341354890875483632757952240720698629102467170357925144176670388660990698572626058124082533622521899200041897574576531512300006444571593170177168863548333305192158205594611735771632113223393196532038619900511617817133400107057665268991970816920221946470432379535641186606392055860903445706415179778214505472227885298721019785884607004742002846887379584422894997433365627187799172113791616449254132971565287952953263975953853592095013863338050756136953089954758488302426196275898594151378051580502576754040178579585244883117210508927708922727343197382388468730716823024878868858551010807352278140537140652075810727084816726397709873145516264691142328610303693298433030032367616271426406758780673188397151500279816337477907877503830798675940459107392103458740421961703492580818990720596129158642020288573400911495523886510791137149533463976398818394880453007507474037228093682053543049495194833283347007516197900868728543996298157560589163762472306916287111113767608648032375245966493041175394613646433780467116505550467067183622128579504806716563042762671142999911348769844705037063790018109688862972175795173243380278061747049630204249291661917188624335559928209324391944571188632155632016165424705537593869662465633412154101403228699093015913288580883124124288287637387274283803859071029274863335150309044532805259779565892055456243429798279413489175638240077161217332473642854016061004433764145722078592171559140103783202013213383309638077890409572381055882939279637438166068683519505927701951536160172215890428785678482068291944169871819286273082704441630396254713053284388337913374768735826122116258360272896162455904189677024745382758396652299371235163048983301242141745578859159425605979242772181990855627984860561745368447892379690797559455515464685316302446232567403489584546225674485820204245739199425309426422450420268903815015268360241255980759752364816280930489127461511962315461140082205639678065853540766868822754265038122599916207601708955674744652423445201766165032594566591296678632462137991922296145867142248249288064768032108647799410041006003390679275237362546027742960073478803835668752200348245769490845686269605771570191917489226063520812973879744383548328613693956245039297680578322340217167655591776684037572348440946176293128849268993687138983882227106027903799001904558336007973927741092665573923314702590923389065438842235132411538801855923495613993022391964505045036935292701156630515335191864186482344249991927202729534595990630487236080415957600296681211168317236603811054280359144572024825645610571405546242082134352094810841715828957244507206354681600230512014084805435874252617101768185388355755871741542477544977222141926131552526910917556333193232224321852542218272914915981058368970250352281300214119248601424806807975369964777193949068046835528083473276103060494097330916903167830979346366118327845318687164626807388336567045660104237685058013950744364796392228411269794513477300492498786496563679490992913271252897765191817542796280608493237552081536111324033971316550439188796019838213858500077324246177884918758145964264233788979333081948816004011312652563569324465939840063689031525472292399141447437706963389357619260391892479363178008310261141954854360515778716004955788656579706658855104288246636305720777890226677704251268157197953322510763890368197628440286102588053923393294746720240885412764923864476021611626208242129916603622991849237822363009834781195229138218473263422857591209798054782852505918379833680178741124264474600225624149806914007409797210232785395756151283458061654111179267104279905793944971349463289504565128688478418717580205045832838748531373691135102550620102775345809439105001021833973245650472889476879298925945019875076712236379187586472012149660611512804870964886305622844083936944387216921208492008515583812510707419551872080937469424597311728117210519289038963703942357768621276682109318276366498404212493814409795986311422543648396549998347908430702176438555435125743682822815303222238083476795111355701480631820045322072379489186357214910624252699399467101536684623410515333814268477062758520352409920797208699145373010955164150331762820019691641154602682072366925527514184299699205398534330730680573723805041671972211273740507892726634063885068673445856077326664838457802771891147580132310551987841336521851907146068138986886710314759826461129379543952667286727599483359025974458786876849646268348443441413591771458776608807784535718393293719373932364083563375766884682111179935055410208556188490102016005056395416874510822060355541081766646052412496622442280454524321603203601946413560979200195902404979292367329892455399010198011214029086869992057589177718807414612220502472858571536753074781438973057178726836636015761361007722863196388526462351255380773194595635679653823624999265518043307963596211067455285214290262949826567553352731004687886573104724664933265679273313451229550591862329373933260860774513507753090157444382948733977960532284935830136183795862648032129736847481751647691366211036036950910666650517171150827820093278835872259839404630683763181180890442362621998812368268078579526219721668720174551747262781803268305854880397097704793483103543985590784355277667603313988460527150313885633246768892710459585193289513916782385773577265810047982563935519352005520408002870596782497393747886052835649359149783803779649600052124458347790017560424658666519980770288394385163809550430492196032443609034008517466042962743097683871519459826447359402342482110447572911177795877313415536095275957089861258677145625239945007593802060935502489200847673322930857422225502064556902391265436635785242724290560532057540308210145123820902174669757976534751725014658374788480805377351504222240429576036137543248619965589193922050469998210629316096756517907513229607778575533102658584257608668676453552092774827556754517716995087894118059363052499449670123759800655349987396663953944170170596981015127193331184076792327185395398097640485278467438723164329100290654953086128333026640075801296184992070220025559721569575883761687843643467927558635739722535648841330601192895746428093578580811323314331152874821797660397125795289003640719892332813161164041693773662801325973822223742681891764895964227033803905929596496964821331144731667650419767811084909664694257170694570078712640144865224284694889761725674653522050616210730010192624831468212035516995015220073163840041320303332423121670826854689317584366304307843507859281044784926639526523987186441733800856816923213474297545832694021612533328379009606486277854941266795136740458774169455961407626566250299006922672678760365871379327960418488393933934692635434154809518362332331752293703521029146413312752037117166754872063473892329378510729029514462927415467619479427471669160304978292889614745870264997970792063872408250230064255449959040119741085351678444090188064629374835443961440035352331030404117845722890295818058103212374382589870274737040106837777159251264535706508300921479258349892475127453622006105854575997369313529707814374284134055195444672148941505745283917160371545308252555834320251254241662445752456296445791076971715214709518505500355054390631688258105785074635656204791466768055698438455202770996971988980723371486956356703177687763789743273492829343905145567060744607970476931646278121417138182743785614621970880870210642110573778514713588373773882407652804519142713748811055974471831009393751976598021002410125112308136826033847449108771613228576602639388492849598982365657272042635720263748256494949126291419171306462805956698254936032613201925280434617043902892602799314043613702658201213128514881585731117821041310335728887181729526271120008147506402683046418988769747879173173703813999188824241699421215277604518595671190941807373479331099709283155468165639527101046113762540664495861838546389822089967783295501114314995936803982223037136329574232173574464734210974149174364199473195884005263872695923183642325491845595504534377846709470450959420120211422086419127904935994521373924871107432314951138042937936554363721726348190757113531270930795272952211247953149896990808946657476955651243605611420086639905609900038030250612423607750329341347289050131677280971316268349596340929224303119508487886710353352002371273020291659297525265703921042149634952385708560572343462157695698513406830454833154590753647114699682420910232143117176922773853477041779407644100130104859609270721132052318538222744487024332710398781147912754608083611568779215131131045008366363100751751102590028086427715020962713662397401075288445468331618211502789264307297635576105511246203324800531059951115054314848295534329598305742724517378865271930007323217362375873273148909109455374027048118555719905168393874535206797085921189640785489504109405699659887159886336207795504521932156336124685303174705443940294182926355240155452316098682553138970188015397045962501691796648125015559323114826730056338357972603286017784741496004569725783495620587328730124514555763452302986481495441009078835298012070126541095251846066620176742045257367994690771908453787482060802904825167017661982073061833123921935356900407052154989390344659388090475077241695436518580750664904594431888629787235716030224813522046010906352145082806397492755128476943549962033991644887919743790209571888632002475020791023790730729637463263366745942755637845356913673455240148971259094803685662823210050039400731066320752572831471151926332892852069672393471750982952602125494764330195357438350925828311133911539063376617373077236302798898699857994501659237690675488379889294006051628261400481504694828140330839164342486509396354589091328059511163345503656348245191505831794980831827281347950507727173359496633718821491928378711646390356692577994245739435547304493555939684803279020861419681508260648109246885433833298663907454780526362916156279880318782827074516303278639075665336219750632242486457694597535966732006038982629300007612514947980089567124525695598275854857690124636865949422422772717715184964175107159841635720724122437196806720392706478942789421712842641334271183184794413346064724314115015509855117124146682433123520628406572269260690474791964472975283227495698196327787281625954012020538073295825004974459308097824095299129654233184987988007716816319860865120883158672565065944140618446837496318929137459934216034848228831582897309421614736892558516992715531155888876007217034102445874402084434282730046730979555566681150130033888958380231464313829002600763228503475830780878895180313981020762788985174353478225120846759497430024437895842895680752663203627696299460180834941994912706559130840005862656399639110406851041282007153246256426371456355757694528492711263557719632506589654553648212545926335525729259528149934158787765156922311915102337344071699165647639820008969846298439977593853981121332181032819896994579261764935829748373387752352859464035138238230626945363458100319367250206982807384333411752831573143426398964163471270530347756991558003118159180911378802688385475769729233988828603230299770430666288695530121027270576339598976894102499684794981684201199256134807564404065594623837087236888125489491487948734808614168105521140018455170084444842948475507327366428272220633658240174549880829130188391401568090500008495465737300032747797209917507461785951579953202237285235920400742515225638616675620318839811761861196022162847431907970250367459282804678178536647393560035403827828184576694782337457113822121932616729501042706940952026502805228985909350023944908745626205345221731194095778301953605185038549614062182530618203651827337062111989390244889753863581809944918157848783365288654365422483020278924170496896511041727594750178122678581439174864942435730090917126487716059592097445811462955422310022008512052258976477811482703942677666427827462593951174380719861872226558650403002846914692786468003183603463817264057027074226203429718755580993868712404656223338914646583055430131550952851097263005080518826527268533537293733856918269371716773031611864749481042421512791591014606569795333133774095936749326441463702427524539335030130992833648540706984034399121245249275580299798824092066446404258596620088874191649877302754037292042158109378147131362262886666945474212449552849091492193371936234029433712557556998865296623645035351920267776379424820828605689362315215231788501452131321491469868548359447068658501098131420589267641611516210940535678073681008973424587293270521085357267638056422884092966588447779527954671073519329547471301507922084032823220442894467821839654711090211734072513972475735700855531274321999675125958256806323588088388436620326226619141493474043649800024739833209241183866742960926946070141838817811071428243965779638843986478231371542498947258304114514952687242361899676305881682084632743744121039055276521871073556452571336011455804558568455865043285991767651961932711434986654077745145004730727117147957122275720181288644644077751746032824231733853376529898104423224046772463204795179809715760258008857689751340594805482687728847762938464549604027037050853941909276993706680455171941604037635118018551365754510952470346022600207417428238494817822549063659920847490375832057446779591067556606407750093471298170058187694080279926904605949872117634151914882251867043955731001793710004665729218037284879797156922788883970419825456570642890898582795862565990137596875007856985342094439959715236676735599115570900614130188539560069330508261157883159790188291287776539696406753920808485822904755619051863754905941764720809084852392996636537774687098568014236137076370467423618029218679592476977765292629290417983927505343294338447653339850122828362798515026374542796671771484197573390657287154305432157523544932053465375423820484485088463459085338667729253852044498441313686375189411768486261360368193736351339325408068522692147430732913446762529322640845330844938647151561813941363435036481779475509763392559882786903696323863303425794452922923775203287448902004053266813935475285501746453171721459950814556136469252665022711533738181759785579504198807548581133628915490090390806077541575736137375598801875730753624873700129122382611343810392343723135368988915337494937863249849417642814170452840829693991724323286772564150483765773114493352155385230017811082761636303709020525950377909253411047057004656525197792567933141088663264059262317889312603152857587164242119033379872577587429012903759362697272343148935725724188379418627686456677586869202760143980501638714352047767388090057892836338177973884573441001499664332358222579253517110594856078918240152199828522694650958763149247127952016446764740270468954543510306982617999140223407285489154680684209574320750662115448762664467579863644388023258636088691875944227152142965066416138496381502797217307126592057826600278471814003420926569307030904457024596467576490185278139314813150920364104984596906022531447482294570702527043630406111445514222766936650125425237207439401827752508941432915215170599745459312594682121435106227633033185043394889512767206372915124936819357031910469357290527628876878250048505480059732307532652277925524199131596179115220694196854791873415669978109670256299399320816450717417349056433986521998663905570935211985243906798615021448623928438739820187602285471230394945966157258750965032007124766575938137212480113415355061675472036957910559746106711254171174536954301471914199373197227971690211613572625243116472289366644142621243854981362369496357128211603685441607108231775107801298304253814190892249208595364610821395648113205316073707772076055993498150342406407751233151215899924629749784547438578559522708926710247919919964504304016600562176296234014928218161152050464381405120101763279790269327122270125927081630457940869593885030885857777676988057712027746185837281858599701772111603710982739324147197937663864843160008415792725306116408501515001652030020014274337639041878862263527470225898484946907769474761327639105259940566038238237163694355547065817482730718247418272636272404623994402844447364245864447510469029976526749734435698570853905781915995859960967506128309101947488656507512613971363292764158349130420830095085110041407455744378492789857607261057697418196336967907551883832201734437643980536829626873285189395308159721384099875365774663549325311393625597895430009119142674075385925496901579734191837104016999179009456783596285732244714790732045696471978631549086284123332517481278482880984876102210097427834751646279055393851966889569651087606287295745908892017023867207401060245389415195473932814246622312689236265027205640264302177690318955552061127114631467170389157733900654528692327208081115787573749910353244466936165351752212468866080593973805468948675560258870687103081189892202421749529345821953530099156135536073159095673469906992487426800195382175246210534986270106132159075726024080430082786835629319838427105219835472751176423302799589268727305311835580568752761240919742444763356809568748444104546702835236514152765627008043630974774537678098208734980384982599248810670297754949535228299516546559850687428317628520857196139379782850577901499623213922046234152416823803889446624267373001896543376476503634125182850951208886485629471439877956655928074916489625621859267154146921767683960545008216421626056106423144435798230691965780470574714846007296818237228797756049608915817868672936323790241579204728364697021031397518009784159855000705536493875321257496167487587258325992595761507433918622843798830134604454088081780968549119454119347026896505991986041099765321119658106296655005116183651706202928808776091498461673164426864197089230648463056754573887202476016525776085293772109335844538710740272925919152462676235381797869306421534013163370113573563511109814182112966221073672626961567267483077524887444841676657370240048508393702558385910122669483580683915454791660164569148630523935977932446725588671741604855038711490317607553732194472830582219155807880752453696932744601747360524205864696869757706121867761972058749104516514271549542385392023252697512349546546309061329460056650728309872803387373515537522356318357025370064940926380803173746348540361146600048468762423108947237916500745179705248628467276633755173036873683856440370498066179092008317107882104981833155261485053735407503510822393924744563010969204227884473716968895091118573692689033665971852253777032962201670810655181267580094085251506847757921913893213809286961195312209050380181076587488368317882781425278626187966760682197703909326006729615127557125278643706989835444409613917379035454851804039733313748052358791095558304048153480453918785403824323690730431027406264177776265730103470338402112966908481804616249648739473458441215530258152221499458222499419419547256410317502114422808652302802213424093193932727678195990608112598623967339458989619071679777780259511631477576264028588262514815821643994413506196081175890461951158539082613354960388032371352224516968118059751218959002859179739086652449528040782713027004537743726785553250485039746375739464609840856589301848223416149865831503466082186223605801948114554903515474266266061295026878409754779814072682395693147248760982803450811893834040961534314863011248676465315478758454946522227531877356089083504383708112088244175993858646630939704811725300402030581340904474505115637705410350141668619124852526949334829785101811147232987404539612754022221909584405087230662326888849704223456700011949751859796494099148971385362279458874076099043285422812773058183040249451087063369869468674008948109753971009084947683041071152955063888765249054565999426077388634739455251144897203610479375725447239660235477481274941606983510131476402364194914610598055637570446515566712365256828270157445284760220781753972337164096986264920557668761564457744644664925477346729725557053882859078923175970676863982496629455560193873152710362720124293120176425224644803181954468333763994613138361445704160888342225371558783580701611560271775414247233315278135669400989800444582389984200640748958923892389275228914732945531240424775520838052379510123938435858775454999001272068286659998579098429303846007329623842629079721823337274766946401526920488143042273943883838698807236503400880952451272600136152570415774978954642745928669621641542751907207896576567620470876291025929888771283405806131718206887950962735523080228036658853093027046194006144644918627856642449420816210203832761116962244213863973115713011899185316991515816502583428128487414927536050735501492751649655689498688144578280724154009011617693658986281137459279032257848909339768816086708570029953457215794209809972205321457514271541122093988698745628011653320792545519698519103842815726835120109236799524290686799545683083885930136672185211353641724422837049206036481544497177998861873906197012650668437064042512445995190900622608217984541513987408615618924659308440274701471016725471601668601739769199766201111998930155354062817781328238679873988318548093651417526904050273992326953229393103604569842520594710877602232101677467927935625307683377220692980995213327549341076406829369625653809798299221502007619065671332333071917531109537696743144582704745219185656561730561853216604259464553856168837599345327673827887812223153728111341735545170735532082760440774525442307854537481125966546355745960432703685421573869622444796092593675008309891400068538363588177874864271068825787874079928341825197714084223048949791551798767827468475408492899386476349839175392445932931291380807387650050522006666627273438445404989680118343255349997625011921767875580980672332416782617825708911630179808819558379107540118050962160109308042257018054929764678411538769143070882475312172313794037236592877104345544696266599992623393329864113710012680408116027696940228713650729810644525201655173386046865040621292457892714722742676386142682367640851641194766265143710139385568064270077829659680486077517949221215629173867163546498898538357515324974315835413991322136505155138410903090275543323644120225300770428211147141918147570961833137822943420725434103155582818669328386683660726916383696779320102142029046813370491534380592465471149708354012272410065039497421641886692274473689950625289450277718989469132963467585879264235211633546474686426054856131577840361143149026954427505648038478887943295655604844339184060202704514682782423151406507022104851959207231200493371767383523709308856526434484194677345382413296885430630247782554350281959571754332687358317282793377410102634717252580005510899808792042744778385364274972065430922479605721400330661597939815697061366098396405520287669991722547240206396060964299454270591546000735367315498807739083001581335160357301111141092801541228066667058785550927033385009831156762851616492425509292830390877098893494607234902865856020542206703715680463500382605276371082398659793184830936764165636079070660523343411137793121612020588095146143773947683538839504721294528349865480864837885019467676945623267019987133184554534837360845127671800567875423588719510589565279780453783448465046814695167753813695184510308323903749657162143307963860154481614495523935111212189443023826954057860116467373664795652065872508159275305713134383569920048999618043254950205219555020617927799305642458366587216753519281750334499239183325623616265020814903557861244051834404038159913582717384337340452974499964059918656664153561242430800162617933750921429658088283221957057843171697946284551330968382460003698996180592987950660376071243272559753650882038636095880904003800176047507866974433258772321543832599839986439501144954150770097282265369583943808509128411041629096637012742498817616344101667423400506836167648232710388942239482025308696722292524340750602651298857635878137500851005688687432827471873232428984773354258150416258955023854489068496767648928297072811584351167607761726048913558510981478950842984983605593659371053202059979044369735340166287645320637188693821897801573219076299810361256838764838726985360129448160731761865806680596837338941198265008732624266960024090883207622611783999157440210584278984506303601419933928362455402768350998972042185962090201621015651922358421194882020912378392755718560554165620545534719697866123505834896282128608208403497311998810772590454586337661085050958238503075128425964285974947159675425924034955860979643401966466721757237237070785184646638370671702995416983298869124728187680273812549629389876072234084657095098943201654876047933946794685134373263039223093317906873031699418007404800068725136597857958599478019949652342728688988717813516171550577839158713864040578956591823213708140058713808836523047167127182200601860881125726033986240354206752127690892108155226032930044410189063723659195711953030288248586847825648830052518126081035421351812247158400462751059244487058370954083531897521523610342040845076413767423473005882203432316047463304350628142321082948724090259476441189103223374049794740857827762204826182195142821798112437267662584689519510699867374022732300260261505970642152746023269994970061582359282822297832868401997290365378168160028841173067332449662838403243536504139753620550910521974909579986059572694138402426755596748637742930858314066480318445315329081532154943458288044293735568005276670180009478873358860913649494583852689279136559434288174186455594102961792995812608097064547465090234261840345010812403353900061073469412097838671627721613708361451511050077201170421405751029551149137025545335020681411652447691784586943540341187913507194728683338966247610118301700497261895611839898160539092008911727724528273299586808380107378131400187606725012692645464509767337470023676782013523567324262478880482343629009996330109765730571074508621321877968280743439896483552427144875730583032180249452109231991204178629832110645618982345049505439716180303956851265380149225169487847955472418638278627582327821299397820742867554710924982182446861479580814083550046687559626157906171759021927186972378454724112985575731793747953518295584299133692814058848042157153807468531130233549462721418440056323974458753772751807146601657065035375000078000547610036786369911132398586213221822462464343501036322398596701728992842523411315434326293039073595342914413933874282187214841861312790716268582668472059546640356511332792729283670421533337815648978787243472316577108118905881159220534134477675212977463550655110980181145470892170124410634923949242422673834943940786546583638685970026019915416838558615578967012722002322003168619541970289247574216667668015248082402211115619098290952882934227840649039533967200864995696544707521184613434097785777736426316586916987627495418868313324751453159002335440951714914081359273191146192006775792158563310761254707093396116441508800727293945636849253271858915516881472096011415405664003892102811864854595041190055800792839471619967600301877000729916613487810389918979927793308260333338334057919338601259926635435064710091260634625238574346352684749297906578001728766596825621946854107798742184455047104825113899365427994459320244389898513442567266932786132950485170204267041681042398878776628283501931254549510108703766963812060312761799621889318777830520450194812047427052045732125487339039302866808539289855145395183070167737253391567927690390733624859034335147611787051779766471010750245076816165572539548200948091105863173298917531184160364021950346357321959475586008320829267512378849551667250649220720609741203129313574353745218554549830258041565179862278016468937481723971338112369536373581105739391053691797392934319775188032524352586080827553740999721015400800469799279434223454476897075803131490654997645727199699628033269209089155838176032139892644880237691008274209066808004373992504541223684971940977467046731673788785204941656447370713254372831395409623181337647384889412182775687605827547211534840641119286609198061422822955249075885258711407213414016352381199891274778913139757468280934247282311021898430070244399964290644450844788027668653946357835978633014357430738552248011805785516300305948035170230529176193766804489745519006229814174022546879385980914228583744941429466840567844786299687303736686339751013910079845588319718939840420585178312625560990751642566660914485766068367937448065297240370993339629283434833266104136871344725962944171536616832569298746075193490043675487124501251738822895942643220617183770595166566490388962341590342836592467623892154316210947396500986925708950750411415781971894579948516829239976768526059094084769255556032094730179889261822947383468868847877421474782112462900504876162420975722951786073395988696418605399569127426110537996486482728821472986544793727051143103641539950430249248903898719047380481217370572566371346514715413122205631956995297107448454232578540931960703748062432887305740374143132382158355626714275687557551361820191763301086283797258551156741723050471906087361627708326296442958048279756363082376436161545554061698004581964467066781024334784598806924847727489529826204516943700371120191295353112919713801759557797453217970689981078697996711614064725835573138528037814479461864582163474520398558975123171364079746838514559204145005217721229144669927864765201003653978899709419567795422900041438454871434885285565176308029925167644424768218649062151219172342568685160060585978089662366883201283965312270307465481821199948225388143004016811445036211672024446204828296777616016563789757634979554872551080910578133942034727744847487698984192182808563041649260299176230362632250441829629652154385628760703742186814004738630945015910913254210303256135110757558287347865626080932564507434633723342240855858163385371530694587826920205239506727247536900139801149643165945829716486863220484179521964244983279488063134646201089139328705313455615037887692114592726850514677135599589063223865076477828269016803601306170856982886336353398216641166133554804037038210034458380815055830340179712082249390950385660958557139537463476283240421751934265668639255917743378325548207038610563301262376287698173472822425094615318907021508205042181039774894076572149908324785285459510024679597393084110627252254156964938923682735814346077275980334626431259827888944181849173802687044960388670718647708315647875891178035430820131865658203435407342292834745576965149868391503976141261336078948099755916482490625516855367948247405098464960856818891720369987375796439800116529527027723722601935755572023263101476869284762636285189304849269092640985472493648181412831689383283125795662135988355445206674089584092314862575591105196220005030802042573700289966012413635564880280339995694656095885763219926030004685397559802876555831710706399750666047614867776356322611612715224267109673618402529108255244615388577666027796080898302837068778139849238125451717898757790676916513246031087551814796001216762016855436138875351111446464459659489862868500384293816775979619127299904591343960428362278214574384910806626737203981596833114583132775573719396476213947036948713448379653367208865076094944310674893862810166860809354876204062953142683679016223243442162500961919886528250184780750093092989616878935144048527844852101949729314912293366428383610958359117926697321050328658637196191306498573320866152431989177517561330725336906062894401403624673579168612419076797307215389609926091477800392182909660567805157424539481270515827865608617662808876754852826435345792975109103743243148049050997201340093871209967992266732745697219975739749835295566344453243455703262602782931368938896296769149005111791641573964151622345962414387998499723972106259104524266556282960145967901286176415352478643304785581496257111395603251503631837450619425879073297479906540337812932343549647709599415970216918103681473383333064151387713221517339840938174656833323752124521204263514948017957370648574825588129624111414646926617747817386015615569677680806354280813392622226805735860439573916273877143508484770186626531697488864738682430941960189287589120213872770961538488095065653207344205898497856821448109934432714379412923407297547932647618296204036144364112746524043691754283585661405959433261009132314486416420497649479552017171086517069812241608482170721710164948247980774918016666318076045716395251838609582718327208657052982558926649231274050673123487720349779982956094106360305165816819038480111470304239018204575837273165208592253994751093890012112219426665445908677926913711549507896665766765460962882777751995705545072979236662085235078168943400320475437404007621799091881351094993966943134279859921580629270421382675621435340592467202350206425854109685955128295988801679474853488276232260898821426027966949488339973538091153102615727526061516646757472311267311304563021016442756282782191487924669897532097832652921682584330479085478336542697584330779557195200010120787240198813494984438436763827041174210036951169011180168326999466120100860532094157901928897613978403516511159934642044414827682054550634184830616197994602704896489524389702584341717731903153309321479805420208961951250759293649016278147407732247725732201913504568055999785692775430546578798428594684085867841341145382412407206567559826482625761903033834174251848538540384703710069087650808535086402176210101567282914356736771103511643978363440428302347807354566914381770474508945872117878391541665309247269795195268639282330037168506787620787754817839108197321829047879932913960788741768330818653181999406597926782213227134596324714095294630761973967499846349363609758067253661551807859814534953582160148026023317625201506366399391351428775115353212411225150570657231152085376502843221015840618982570047043917186490724120891714561202491730043799349994206586637985787346060480619228119464331562925686710879697123496236406193738811218020737915981801097590801132722578430025011137880349579204391899288300516242921760033764107933719681331920675829918260784852475711775242016834934819414005391646393521827371048915003658047925976158343651355349438431915092146293081995018359167094253026540329803249676158439634711435324714370392214861784382826113866885521598461344505803302636914394174355991753787166688140045296893435198765272300845846550156565989521130110485288169394156867063517831922185595530500029864832544477477719955016508265889671396408898805679580669160658060940485139280102227697615613826083190760332454846528661464942948396677330080707320067510426251414296244714536875097068785066005939402651877861032765470280632572990619689759188738667230511012379493292597649574826255195927394471764009255618521185772443088894589313045709752725867071455651423603418198903151954572188621149171034530596578450826186807436497735831757700864758799643227445489500780966711961621513676950853089233612386662834811029398046074355342727244281049032807567670033772711209491284344874508135688221560330504388351754108148303753443420841220816836058132623457677542793161986045430504448510555800411679433767132055814705872720882536047310649679318479637352788447885205873182866006563349325602359088898353777250797020050541440210559461072076492440913633722789739946639751234117883663125090061416232276570285410485067974498127181467643084141030023752565373049527672754845459997871633253310506190240215181468100146512628510397598394128823698621131831524776496795777441913323947985528716530231998698023983984731981788171333103443398908379580000513196534523383390109097044471434794265026285740315181520354650728231183851986580293621352243797543193801983432914312502757766754316869888602865677013500372589696445868683417647387839066544421819235857731078700231917445428714160030268283724049464360347876903573326188114310108132188552798589730345053440330372276915140453182361878321719988989055082908966241976585598057834142873730648098529078214594112649499219651136125677730769946058020640723918086690020175695641759552721135933758979116047598231558725356445682571437465856688982037370549704529071584697376355587060928012017697805329357967838079502279220010520167689887325410838931925090717428881081070862320755101848004176969682629039239983938116236638478713081932018555926786589807098502295373949421754246962535470439547324133924764852103761177731123138500161871304710647783932487585006361991196778775326807139246898440388265936051085465236922261927240349912103831622629724114408355686804998074604837135925207539017014469373916409286486391905375739329455653677543563294895308547919735618116894346944344364303087144425491060982948288158115956356299337794739220978511040672166448032053106709130375948843457873439847370765374740479308090343824433970583053269585629984793830480817797508901932397881964474728134854864856399736790769039302521285919509594533031379751852981866262011761260953213926339182718256327583059118937210691577643838872278422852900912261251408052315081202726247737066716153729796236517171183091817152280526537593373755812823486429693226678471338695988769158095081150499363373569059008428920070548252546176895416471077801175860714328662404483055236425937757985524486960807267305907650248851408147618917999896292907954060691650986275070330910088661199318365347811068950055323212323104099431566975712843211058927290756266529830683461268817435027634457348731308127878539668259480450244508994538506262228156572066562590807106009071947415806434289617313151570605581139989607656842772395481206246549279224664410867393017052678406522475041053604323508688152543821884057815229519878956064995606982745328922732703853758452092709242946673468959337778965806769512859044905739913079487625397989989468534486708427632847644098046534885512094360642889373837105351559587950751036819995860092479405220515488077774998306131379026412827371575710612817362497836474502072277561952126743273581685496119698882583112616695052224021881146693062574953847086995865745998878927868473871986438379048046374622281612687127634511309478316617599707595085332574602849374001043645034556580449442950345318338129078508883338583786977108498206651020627957076698334451779345271803769114102075574774315429329032629532114978826203515987412546422884395277795499289564754347105898585159005508490056969036939946380541274407827207958812061095018266675052829100428644011596909156026024587211745604551094076846979736827481459790404552190484180115456634783353438088153414037239817881907757630647233836848076617187887525444073186583050118647563203017139833900789875424411026277749259455787263151608748702504806203816260628415675429971100845723607943683883177569711607177476019773629986084709225612419033443038680607516077836502789166628360931767596955301493681279793546665239389865492208212613277637898202946799581624398705936239170511750705049392442937122875207210047900369520353054174702688100313142753117445624640735452001303351544161161284530636382206203182714120347105733305706095610419997744129437897233361952936807116294649741746746061619442841957715064212449115406707312213842064141269671545664389159471777969493519583468433678322141303743107342917437343763544159073773807768335545452204160755832450014127128997410149470548864724341589912960922829862407455160589149631021000595871347192097972398368312801101752643186861118351701735867540649265791513740582162972420188375102977202769228078017323536585248661037355246360519741758718234908738197745199604135160468808608272559061048282225757674635819166290343907054759703480904400430433331741346142345412745677987258923240909151087305920242790014967370151347721514257148023878189727890993192321188518040030497628938731198868763397705690319074145176297505582950790551571289772603435467222518751947227775034780629888158027640883058588732114089935625654452632562629304285439933282550329502893699077705490294707962200839029322144411265738208956854344785225355843731269337547937659943069910056990821560314508198864943894886795977365202377638052694955587145427065851747444596468235269410568519337370049144862376065979574374294931376284954237476962984236204040699032232862548282233542016522829128844342157512706020215383178452185648411506693943643644633903294628692150012003317372231594599370244046654644010709546377862736676904564259977586034142337627592585363126437089730757955269968503132069091830679132654203064003148245598623926575975731775912862530894654125166228407163370149790267384325301619010137297886469540342569455726305220387629423264806499623816308550031265168054478855681997310896795755442683922048513091902688240337712017786398604639800256037206069295346015367351300935166490475996904153484422840649464357839627395979697011999599689705500713980267143153912391461161358183406808760534667255305042239792809656622109111184778965033519003128193081404706478740367155552114034070303989072232339159423512652971711214491591287469696454455709228043473384101385887428050725149320183676549865442619068767503039795693902421343747525920284449370703219824095085287439294127815958647543036695336546465043812295538569601870814630360006810223193535677588422170662717787522895393749739449846068819589926057904266324281818832976825708783089016435405464175367797521401491698161349930449104204274172990731837969851312455958606399199659668996108005049400729639709895951757463495011315239540543638424771576730579689978093511231012700060683156013470561688420818621059065843854685352265309940809550686451819643104550056985286403697227264496407220910728050656517590053633194257188261901685209110944462304938727622601300966509801815021611618931499175544866484510193964089242453518586296685358807237025208629039637513544240841676796106254077453543971872022038982925881504881746263214401932459126384677645387821490032187360528840161581467693409724342496695967974551295215247541300383824175967755422515486890349846758461066315941988121179713345250927530701408561426350301527147370879790229663568300178799028808419389392249188448911767008038038758887801697701113453349115348021065850875700255173632568820097595527487122535718255169787531509556908689854648379484303518706149231357340296313652791276152623061043140923956535229749326101802357414494002010757529248895892932458035188934833623226621107047222795178964311353322215133111281302699657056542366660712427360675833767835191012510994437030462907634661496449559967303212585228400681288632060138439153523932091157906047341393629732349275918089423365652609394831336481029064358631183082596587859784715023449078747678799566742485205104010399975710394022063069173474202089699175600528889873675939662936740172098219541833712823393286247731719643862566531455126992223677667770819864349679984152604519464045901639578960492791392910434902756838172684047052298140890671314915262504417545271011235786801298936828493391396383366078142291794554344916809706649131884537812025796215523212883988829483096025415451583015564562313284503174185769799799107895565679960825291655375861223383807006921957963941983742611767676910050735750147104127391778359634794411592416074096491892386416231443150984337999995741238608455687906501796604659040119009310649145976455087441691709361597805467174658993017041375390468254441984930697739630336143300403226370441388424564853196000910240359148604341956718891985856156554657750890144317771286164568621900128459460742160742957104583140046201246390110210193236887462318746390639051846090824746612222586831716989063606402540489350875060019353832358477797777718415669271122400445516770541931073038836943879788904624217590046666091040163622700645067167256329813561916957758561333390398277599652250990403870932789862215954943799706306480709649417708005812270559330916211844004635893785632435864191061540068204787901621404457877173981029526071730009912179711375424333488226661867180593453500359794062610169455894879852873823946192592738300586578623690127192963826592639378195968777634491927813839152734685103171283501167754128969634017633688033476132425006547944835516002423125664608010786702586037609939080045175626009065555413098404273574300500668774331352820617072990338937053222546700420588964046523936142830791854041696667832407095595877094232009415610955534334434913854388408610824862428985961974125657170424006787671236859353722710915670406062194347260204099413954720131755244915834594427491919291350235583440418720697434588605383370185897657206225466863899147406171384409111405424448924181252805873784443759903370271443232078520464193147559475831429194169719062976979044988213080192587590485875701028049890092764667431817419312738798791908667056460174141045183654736392112018312726421352990750753167418604113908507917404417265800928896640035085618299372472136843413149569295704019813001606087541279574664190317973325939240210741676702423535174221182857151618329768142226073090296369487133088077855566632397283342252706565073072518903090395022975145545081413444428165414364410492175062270643628610175717112048366581497058246357800755045626453744628052593284156788579850690105804527975626285722083047835436681313303172332381352647075257795233015289166395286543189995731745780167826728146022264038189956693799484242109824897420088233111400134104409516093083130905465503159555154739774802214624067611052716137579986283543969665783552456700793604975187679585004347859444483487473455259996323925882010445287895767233391108520813799484234715318952612818751089051213546549692460665576734518571774051139809005074932280070940569206554428799289768091385328823923127426496379071979978524909030460958502032813011881918979875386127705098311267968672117810060942886033416074080204485324414144579454721054698929166499819415997508117083997585525312534793070723771948237383367606554185021133373575357116049840886306962649890151155629827792230433349844936783915198562685043252008447985546296912629978831302936306463370450331552637520404739224157072857779988029635328900698400767818969756354021766194294424753725649845722655067873590934057238979378191460803198271139248049794110049229814317594991993103280897957472533768146061745433132644892480370134626426692631734244357427051774756506755634133360059178313763737592038902042651716918654224484136094659863626775433322172909728067721218122945017766423271673310919275233772424505908085892756556434411548443888953213270285605406006435240340117743942638314926942036676773124923344604615279222871174737320682073795040775380702487513973697487220807941836272457926715860587568437382569660152884501593636057997648795546668979631727641445826714098393026058444373118321952735782429923805304609792532175952943764669671785995655479752601048111609001925598770316036928905354642196936179009854720785512572597532576878034184282394193011676471278020013244905417068719406087486425099249004124377799025672362387521344875468680180570026771659045717416750935853746632784661471702229127753816489358140375420413163179662046260168300583584027808425084706102856321467634921644155965653995124411152722520988557631807880862837444395373363866289139439904591869356297993169132074310849123878269670817379838027600732835237130570383538812019217807455705392131250482197669340639428023453350969805452191964164969664205192232229332522498099068094382986082393386867739544526736321944401598659040665286702065100494097867130245089605063631147497897543186327376379320227953001087717684402618218003859090607702934597864093296951123385326149456558596771175442461946208674178988143477802702378918386547507367250701231645954210423036825301549927292304970650068874455290889366465514819384805637455447031971718642272096587063004983436657183030945852528562989254613260790172374623360138208947640472176710210783635306328391852894263097083524184268806663972454351949874594952723688235110647593837053136149452332629900606135442020790080078443591851423810954062463592880074917472326014282915066473564694914907531304041194874612758425172542299337656191322834413615968845123050979229080934778061000120243079336754606956718867847589029167162815104791098481996879505375747610343839289298183475591353372834288849228539396595016459422968490216357698460365666770688497906149378958662389785139503019552520711594791624303805713391044123512797717425894997181320899397240945763050438176542023774937292923666640858263563047018894284713662179628077947581410647203968690057335883783238393851564367692910953212630953023723418877637759513255857198868415635114346544492134618362582001773911196356597362091748028951071191193121616150493566140019891540677191474060450200848900785210448984071558724913181424123745314739095859285491926195512752815404555548948605304395183055163865296351143585542678957884332247030322398462969403700363866705975518962282166849472155167994010237260527619206175045604966371707626384595300533444387899443285436630146414207515026765299871484148385924204684351505285892648354129599964190638362225500616202985179080799955171614289274332216280696351216290296505034545598002429203806611312249987574877781454333495781365580083004587905455655237596430899472829415658468980629431127259755469302188791273103530021686422763366103189051108633596398607097473741955293417850780136533786877679115147383325251300237191023587588679803938552970499832183038998533373533151034580443402573042275868260972398342231501764080332731762263196756598977297183942297165227761967340857344413747591477931793339892435994058139603228134659247855875655057515942861161317673955283481508085185207154795143926672875105744138676971890208777611959245935929086383969620057686509962930381814549128073418097204032036336676649944391993626414522714507350370590760938575244000094748229713623772159263083602213915885590946140740697630129708656969066762442186183635520472790353317530936077977879847080239021859587448789607452374905628374741893102680628048174338130019822127770609218470081525232714598672343785431049783904364930586076453575602598382816254098496399831811297188143863954265440828008619305729179956888798818257244092308607770862635131360946309767740997028472766829666854290845405202291900303432247189820499382480607516387279456689408497366651622813369488285833953135050217053611181750210101696093737288217468389261806871887211712203206733649831281254572692763105648258790106857520808063392875136481758375810969559699338484199106269224113656117112954796777812386239349341400854705378458378285151232798730884093735721100458603654544945301868107329376110867978282803436613333978053861486359437163500877119311955984802182899267779769409139308492689239716108751625981364642795191163033917961291900176095322165573491103747120945790041860289922155117591568303624947160865051863227974295613651983035189714287602237507160599904685227028482420257009629938279147818015406641691792596965023061674967724784194741420092429772971388832511665522273033896444730527049024147727564715409237680664165282261122166055190351049532169538299957017411465102998489825164390569909394598682049855258312876445683898434210936589431051140755583849278936997409501389198821247991620988839243558736555452375362389064107266313657338688715014376676574392829832073461314039495261709530844896580056558463095232963187124518366166304277498527362023490678591557693622053472426111253263891430252893766737392628606460991664258399987464743414163952677732246933313408989228213523671609562825134923485926804073551815360719656739502706913535763434376744311724908455377670326669884144911480888480132493025843817701187856663572935398781140646588369417283873657084337575104479912359736597243445574271838473362051640986039310219592121122572034365100139638906494529671420560890861769828831638828382522970765818961189545729825810733945401727749783454068776411077800440742929663980795802668931294689089150061861841892185304164332216949278213392118277190216752020805967262749463002805388777945962185683074384298925646302408906336676064738970496873626773471431934643782695278376028614658389278933623236160368696588859944071709014385765008562370357074728812300427764747470377946320005543727473658472402618390250818502039941310950397081248122107761283245956399564073037842282949417904179913536533706092953580412844901956771743632655873343028440148149907546510328181387821090721433983745415095721808723321639314118488540488247613315649935403031131319714388566683380217666836082950323604059513677592715516567968029585973380361344069307813757301161300265797024265591786343194362646623018687258796305755636607828996953634981452238866007301477187919864160661439080577725519244870708291097673554991120061231753618478131765439557295038545292365366941334856217878926154740456150452308853118438978335074806994480208158304780292913950274218678869198017655546818144544307419110229272193186444074979031725993977136218099711176146890001377240700923484996330832030429732196758278940008466523507115511183481032014483529474488518863241330396067639585766239272743538647655332592611391601058972069491216041943843627692250204083601834811827158553439255537458236282552372625331435969964636667825593382100917598744440271852251290642515745359479613052718959948884782435317225627541310959985044277475352638887118926497707170550220105682325307115438956475830312255116287639688351432627286298152407558879959620980439465968893295190449010574191419981498587900005334896120691631117546825348582900768395376626414520527393607865138057224150689718558277653895215135856589764073014881111337386924588890982276022973395124413507510038725894820664785445392905511604925465568301792236352637755462684090494780037268471610264950882620693574846316439689789627008337630374301756194573890788481042430928552363102983551745174544660652976708199474320599516529159600871565211546129673541395732775167844348486459833913758485625055460175079220988358771933860139489675148337638021390341529042836264537637458087828153790470854457596761421037723612329619640229202289514696881144705828930980357001504103694841705472869227519704469469729203495196976621766414362032109874971729908144000371573928189152840831974229437336774778258709218717225298406930555693522583678625387769903710832987797950516916962995760702662487725611153210245228717970309373800563354059293108901874004957513305645755468645881925344372271704841107604583450525724291768442356100139456682456604288000474072926191657544095336500054458333133334866581972749276001242323822517184689306940857232461554239281388742202766169379793566344104503711559823675771431249127962314115016452888044094239005173464563780362939532778780163604410427591864202516771182359108124947844895488072585512574549607995691801297131720540384249096087363621351310975286209862242624187424357837677291264179188013767520032056332618924101861651303481024400685680860611048112942740900937527485785858684092297315779366886086199644290736157490533034510746792139213935734146937826784053313199235443303460719515520570066100116930106114645564891680500914500556137849404543693134859106184193301895454863852198600808200402863322268577928659474990069360751057802347420150353177373008364949891672893509093542120975207472725043666188006133495909306114071104645992447597175424019965407306540841697352392504156355003902644002922751551910862941367236000269412071278113087636531615224433516226691901231017136295013316980770097670312259233409954235276489844208910924390275648161700407217392725660242958371557106716854141871300357131023054472593770645420643730283814784191021868821846656713832612826378069753098860829066330396698468396246747688511450649131518615524629479248111598731109079771152980588092092858162277065275677719539311203573194334593434733729518994157214722761903678004308795904799926642428196201630098883714884504398012244624556026604086161331997232849787615931712681400404505618966869098470823941370855181326199637688970212415231378187331300156011219956570354141065353563845243965564267272174345053170897086203476547586741281464071979228057446954068492795994590458209018731765866162517873312969357262487630182748053065600294624181015143131869024087493841124215821508373074128337310032226683957690735069881768275484817730499539131031846532783838656267174760018062788758049254008878403927985646496451552789273450200154100313190509627108096288952376898287265139140819451167195916663181885337312798227947809638418633081232493436827327088471684840823065106804984019899661418486821929237124322629328442483023610179839100426990407744799190837610211112396067250719299793136517706731645047986232255197970299256653151015966045966901508870688829825272864059895142504765564643861397139093020257194585582527139271981132775888695544629206052026876752213679668274687758745287607786344913826995634800825441441318253472049480142126543298296784668605437790613389102060765389746783799090419822642829171356980043472466696993015751149537152043740319181079548689432406229094586232624522096675744952856601646578736884246540265604576973290012958378742017115100574265974925332868258662570245837811521822012775760787227875362544176785168184919799494976491000369349095508194502055638112249647967662496507858023271236894462286697963197153902499010991176720532965920102432741583664628510351940054145719071486388246944690382245688388500782441039250163597153748490569452456053125403791760331016534775001998649580583553661420716997411733105525404205521107731858104589461046273558070947146683528783522244243951951095964801933997282254412372911975335233397882005003209483077806628336460632466710018008706662889771576131803944530851778599796791617562364245799131874799529518736756020672433607862783164465504713334255774562203297058370652084614814618032795565723112891379150610787823672417063157427908602758268048328204825305959448653553053355736089436683787788779088357733165815665640463336311789655775538674513596547437928824432776177665299775378844321226267587896126638330684384900580057761373094604324573314159787616555372263016164233534510023746353682989424782425580648076643361805237741563140378933712699900811546084081424058692844640874238912457751936646699463735915844119317795008584806528052045138617897232991096461177097629716988054741486404035888392795004056809668826825267833258753583516005057945853148483777029676183263606491366056471185080491635911181680573568625676757483627962595423144408426869444178084654590010983008324701273276732518629652810119875667425123718547191741964461099638143692252764876865242964332848802671048804488801559106447698291833644325638379834789224992424734734749255855729315186110345341373356722746257827671875512852296157193501863251721759999422779441251276949166596411764533113076783943587557015112683397880778230893276729219673906565016790988495989997183620183772466979164681588840040150832641339017024402863907008831066490683497676288008809713157726433416470525153647177306613927224056325710013972998990955937477305596363485600615984961253518310745042828059910113561527646137187323074054864438709510376239129317441392679964474732361821363311858580406993658377760655841495332832660287785469689430022926853101934301987370587173582180980066938912507662570847465950628991846834694991196205056288100623524340050240751212565976218356834552257668404916525157075841461441328952097009306872902271637056385906105921696945735131229699292583567531883445210953757017356326181664424591830719173259280537351848183098722945626217254044418986403975038451360611100621071808868929053885565380321231977664500797880892291390719718321553376607146881588861466593708021811848640949124415780158696473723909595858031173549396393423239812188385832226906227304369154796477329036203102315846228211866082858960816909409000618964421346173446825214338630608641076491303096303860615612269477567270566164198322661282955940541852670099389441814526699815121965396719051384313536550321313806824245173488947592503124192484175255740381823511390261635537093686468847101525986682006296660433267158847028467252827367513636915893498572151495769695739379312933338786858701558643847212190881311947133708733823275000562399237447717210347921689995870015046980589562365188542682939856667127230583317473946798938791798447572639669972565150933504944962393298941183809511522027385936199162089315593735213193801270298481882968245692466401589102452240833407352947237676601871908356625734394683547048362244546199371292199455216077052265379834751066676946325551156649491168070523052817308690882682380129412541814673058459343581273433407463471098101697337845113700036146661477797375667676221187825394236037065492372256647519270025082488886040622340981154511333422390177368411359915337237318467663405071568966819381035854807990739961345388882685756724350459189974006910447041116287865267920106161324719998448237152334997836375230143135132826955395290108649420581860439615905305825975400157347529974982723095387057721000539641886970487452897359156879270799441658104944268793902227820026173884246389592211392638749541141959433002708467142370681281377822984874389225801960673229557664622256072650083204373463689206974257310148877783281459700550621125297094395513482069706780920457890090055635993193030074671042570179184746799520164509853815101539696173545527780430626775794877109799136259366223493706483705981684191440900869283841758136960770262652637398421792751865585534001802494738842479507635936962516581598005490110797072695324488613743499388440836614685920902013874629297290938453956893091552470325456494848342558435392750026839808919512438571472788922881800472797910594164937166417567650944337465409728901440632813018914386339280633443494240026022881047169997255339395707641070678950590524163290221291761570720281337963064985988232242671029826426454822793271548045876499212413212681827672309034755957930311584824894830141731719343104663561998293422660845241977274300089475751906443642507040115738131779480959953392691557988400547828953652395636741657296488046346305736737117215809990209894453733255406649244555657047977207914581230450618880669347731611549213528598081110964035642010320650313878329814443085638720657938940705623279586874460852840698062839012831994040317536981729101193027421648744601861963215944684538075570987221296475842610580437101441448910748813376672138354541424787136666538718207128484761707002802307713986200152328528467498051716009417700848306078163074067412915857045857980914354160929061349459709688925710567910055675290074750437994633821119211999009122153965563172633298735935838666500189702103768105655391258112742565036589214291019193567740079666127138230714081882841864932545670050478902357998346296652053903452672297367971122296475763842795337070307941563289311746634899628691051860472272688877875879795365481133097185257748836254995078089623831168239465051168547086261364021782044527622621850946877145846667658899947937102845702785828864945578192102470884098054884049428920275863251351203276836916550933375756877423110361610668383215808025643334645427172202495621806059358605778368398254618223644983354199190818175492396216871052804951422463758912011361597998438034553898743686379416430030513037889583127924845498683990658600640789933528127851940984016719729727069932213390718420955178247520680268463616539771651234574340304432466147817711996108553728243091711263519501191538103322617009607819792294603552601878766923621248636248851290354428397379232513895550640142391307665467538114524402470683765280641424872089134513796385999449351608677107460143274772385102847494666363346194172301607736297628897725128300258084687726530151682029250873001346219923156538719904106055074193036339018444239787442338499826069676057020535368456464272727034894392366484590024597949473948604166711335717028120922680527815688335313264331759029946538574852184710972047718248056721561923131996627637828206706279778643822558087274035538875576372258299905067359154147149474372649839787057663311505334211612174534089654152155497746247888629118303526040368732822025070893530843523458081507195695889241260528757183964963055076628600911167261753007281738884588123735985372692992626426660021729769040932291664578008028615731050138340599605215180202337467493294109576913999967663852175374648850721464227683648609183197332363921592490390006967888121011297463583734052586878544570222146208736858727966414530176263354155887940590732122253946707378265467560810746496041804339579538721133064646799286122948571393385632976161785089115582766119790233799986635770474963796822399350957954508205505111893034779357024430352830442834702410590461224680811375399707428743435120724179827100082933191371419288771409898637054627113614217060316038877158734107562660346260346932057574636326530612059614741096786436632812848924621727699060440035648313727017184326110762869070629628767824833725218167849520870187388883526681880688561553821029179384688125975922387175756873776636521727918293598889124812904849996547644596555459515319230198673421439626905245337463449860371792720542796816889295558794575534131465881283310245574792805020086669571693957780153414390677074688444371997229473142096243098464505318539652190602671100605662171450565239616762915821410039307333892918625670333714472417092407944822081957934969811552492557325408808831648195199484925188597971817916507188649753536943195763660262426172292425480056059572174815355934092538283243334477794234508946594682954801561640088402355037323496549878662171076680106251027447234054777387228233706324422346571309983353563617904512966453592077279387939270095466014610509180273269755513571365490940517098691433383437353862239566253167050813221234673687814427618547883058500581078515556788076939732421220873066182620090830504150607987867207800863874831471046796221804394757556309908624424438280907075163603921360973967193494081982005189308463418418513775869421385970025192357210352359781475656283700649893580619427747837673671656860440124253539425460837473462224960829827247240218753734151044388427140893032903966317059852727435757224919804396406893690833070460690336340376113566927200801720601652587016620924656531832178359034831846684963362317735446303933793489237958382338014835246620707688841775646825727171361914835528944036115796246825347099957785414816484667357356113380319206582213549678296294583794899259090657150858589924036877724709560225206030410594547223573430761992020038703424402223490949671809511947981181231766216132812657418887926717804023857800559852923256156882467651635904834058800448384582302419984176242039750282144203323781364695612918160908807052269274478502357943715614285496103309997013947721460617450078824754170067917818813373073553878679601012421924341739873289763228098676229374534372899811725930082223246243759854001837266087383264712072554491306433644995100194782544525542561198544468963386192334108861190236636252006167177340726844487670870786339928851878574886890695595205756080655359723625548665768065997300269614499791386394913764334395117818656169724575011955527139876663310241993649615936734233367659351899510821050854558659024045243950149586570975168801772980081992225972528916152832643287133019120720262250559930210552005936427206720674365808195919838946862415075380275165662282604255844872369634231527370496473601247293747058235189463777287608586271395235999069223258703599107092753607717873127548150940351270138170794870400279463643368842771692401282640444753830021680605559739911153275674304250791689664936534610664903033926454798262450752752970355117029389549392605026116732805080636191135041503872255354805249503072592208321299167699393857896052191904022659329689320152805385584883267673657568583799428685543148848459878043199371078484089337419779080033863696659632700048007534107331302869582860135928766135088569413072689527062211944465709013500028507817008173296936069944780801165089977469838327533544622311789004142445612565923619067137782218830990126205038713863746110754713824333342606611191124996043119748730035578467538558093194053656414387240871593070280022336202403420926692484103654139246032528151391060258069008679246947846415137742530490811333748592565903252108437870583690180305933853297001096960008742504481418458925985696534556980827237127625540048379270764101702087000675852444325775264579036182680360526238789966875626868457587113494826170278726420774053277917839660593024680537612528783622421631814764204764333456586924291561946174147929303267274533198796275905105825564390641279605996051629410558357700353656324285671397243309359986178485440971818172554477791409320959184050164998438612807378871881675478875650566319631976730470586489462404594926976645328510919874433735121566448881451325097829979985682830183029271812658757997491525942142606638449347618236694361301000778347450454438394059463825316417469621679637540394915211600835534045873007034167447688538635372417591191912573029628757699866983060284505512542557781304919657353708109753889805144982819585172096328879249759668785855762687283638577142823352346656795892694854891954487424195222854027581013272572588484604654951851622252721485896972726328951526610074191959717832883659455976857057726284785595448837240757916288363148490647791455348726575585011194226486996243910900959484219505011828545702189874103457183898179048636464908296777315083677699733551507417008122025805388524519536398345318761778123182923384516149201894678720217480245281591901242255865169874725902155076249749122673765945633076166021194348403239799144070248817343433029272571092865738989424064956181090979765498511842477113390072880929901630186941142126117034372249667476682598881833777489153001358002314602426047205527579931989940964316114429852831611486989731748642308262649341631684527801622286945217652487890699556099151096015879416910388459563596685293612599124572928376935749499600063745405102933125235371942115650133154753736280907142815773185118592763310014484781157730515727741163632176299555971064374278716404082983071046305419155993714815391612554781126437438903974521207357576777750742115050829810085737523835183835753993320297598915782488050412070590464407322768848308743534512264547062694097544451369757257089150573023242573567272108516847067390111372101828058046032247916600738326914541593198292432543374648604963396534224817293832547511140375938437780558100267902359338479895865486078741941416884073043417349690424069174289582911338815739432277101561962477635519024021712746862782472197996762662900919176955643810538569264018591476166954319407769348965559060313034157909445519756029662487545487909111753269937093712638067225675146306074023344598314820578077855253816934364805356807945204538688872214580520228137165269820116250616572973797480750029723392190975012204947494170065939296729602873876719522255063086438503602286418437662400917471903283390839953674746861310109327545085370103248816456357548955860367991893611297876190835673731227482378182701853106426309613472487143605493189037702613329120207411857020396549233685908632729003478722237659894186318952339757526448262322846781474167839417587877928413411223019880554837191966208599311296978303388651675854462279134053844760839423555344903316330001247579965276168526319453293095962731314672619266181983219456648004891272404216573041363833042226648978515562645655321971144729732605821321486152801009727680151040298965202063860686004598161428523749991208520934729230797735030153390597764783428907474877815173481573626882872884730960864188664530329476007533093585380277049260073288432941195208648297113175937525344388958814255548385173295283601137791532901133575981174759080829559047506576584568604989519869930506625060170970783298760630468160095210972977875136018632054589557818005958757171911723235091578713751539912551505260695947605933157635090917977332083361368071945515640749533035718842256369317118343973251605736503774732145350056359563826247493638224705836847521426072791995525107455130424433833640549397003333713488002997859465754494276518303412111970210299873361991177647930047649326491521909917772362558052712725779928418622127220259472957836424156951838904261862931949850239808827901822770867087071935398018357638280472176270261490249184634025236129564512600179754496131220872848373883319385740201890828176785029850526215633527508339394101455547212563763685464079094647653816550805001796737374314099089474841691430065081182103990094191714290554428743486917780828412716328334933373876018980519238236373021976500702991998409539536408192939354484433786725257077729595961387100715792147218370758041500544134986004929074996989379034881020827925069300574236017467712639825044479879477551383688753888207757212063531958650030083910654471490754927971155721846090155394573325186389812858246877695980082741765554992556526378718474988706329149439080307417260367589680254871837399996196829326612241217067713397137880925201702623014717820800639162135982052973855055582095940333264708915619555223566380626412574791346382537492599128801431261443620117181006104722585841850028634156211568844185662028272766006553624341653186172705470460182952332953648960573330745306473007739458174055622180102965869545542962321362680851935845002587357305866651956174463718111344775636102931642292999771284847399247914997524697574616765240133398871189935029199507259403541776788784275863173386202123143312232454999521022646419170590206372156364870441042698336333313821695884831981209369368359190411493162347872763662759215456841070241740529792569429819149817406395271447869051184234337195592612319193757906211785809093205884794836305795612156010565182075216489529364750499783642596878804760925999701865361111313604844810434313726732714932517640677959128270418092840993021480957457866349377922712137553712549498364613219105479011950805481637782317553188054048344745682348295528213063830359546479755313386037131657640783340885935945737671967408625251809781817880369860116613883471299915377112383415452874048995646902826030068854276345189623573554618158222214071967866843102682655738115194937131618234925304365477918872773039577291667603598906829849792753264457930205625004198215817833679758324582016703344016375194361307936066877060596155074581873007405885541857077712937653954611123552017737452675365027712360102626371140850249375451997238811849720048529540760537575504863349851760393434036589529608607344805531229553356882145671180576047588419420583496338454216537702022628873203281426271924191134698071535062364060488010610176139643065506664667145977479275127501313346596764639606994405703118605608781228063289678165765372750629672835762639748283846472950189179856048249198500799160923239976671964767833012638465080880428311109850255466129686185565035001236106852974356644656198492092110126637583119546240112661948930083828438659999992833337948765982135588393330975965394351687477025420380520337338231789387828254304773685927377235747888656685873609256869105637744685113155947867365164849217860389204692057392137396597629342661799387598861055713847390158695380014400337739425963524869263689609087053952625109612729088737679822241074767848829902625921417206513544327199164599833303338205097023670379189129777113900021796464556817013808941825846295987689363592439379803701260437000195453753209475856566862618691377693236555385533736640181142604011712634532053725124468808392504553806425476628093450603910861511948831424647739359453611346263253979030531061551540475704318358069888911685088278357540626047008133489427756419881104615033910819669743603856073267087156087766588589106089607208747158269701690562668719926815848335174102410950604976133322103046832009316294819666417866410892596335403862924130152476404151953276182470723527897727695774543149145720405417995318578837498108505057157671110581585216705522011002403121471715798464585433248907341098761099295649676156544718806244284933719422274740449837985964755841334894107426083336152120775019298015129465672084211550763881645988966176464369760328924324510530298250512242680703731212808393512202255408415132029479998057513768649336556761678499471334926956257739078483718248283155679298197287786862903631056068580099072224021538766147136448019656148112453886271654233428875619797920485573019299975004175881862203550843526937422418347754235756053467255495615418988381785602926769085061313659528803917355560245687971772310603217457604975950232256294196379063093795581044909587621359167786582953930306576530923070439867570625760671427063852605547595952532130478006326107107680832162100145794640977692680069139071937272531922852627428957389504137685477459296033592272625266668352170703189496282724565284582414254606303728040777479798854129463539799924647469133552433723183045353848908080893152518135768485272858917328591746450365612006882947050320471690415376867800192930506366957785508855054236989012229808779126706105235629735806022201829431580735552190937586577473626473699288881279782933393499869773523241375993155463631192982070653727478607258997312069306272104015723943842608756039326387063929022190308589098777220198559385372688147932288292236982590464309339788165229985971114388791916811255637498313161109319061156325528926120586515985149397612705562408767671406059062759367897286320465589407531927159129511701844375575853523697820603460308111408561622204290428905287093487193875366819942119678716034475116563217044041605351341390173136689463887385553138636824336997598597061645706267041304591264372849891483568904556090934810115809231807301845998408799090461574931098614313315919784060635683188419505707596210326850840753951104607136774315063186556811750456842910985936094863468695936722775807730607288379881424681003426858744195332034222592225911315687185512988438399771818481775752765286872747867997560955981443326979802322469251748008480437354026738684446482509456837198696619833088985878352579323281004784980000165924072903146602815056472411034520315765276577171450510804603051297596390336904878227083901331040053851493735374972951613489722639790211988963444866201881902957692950434647230578452652005806799064539004955427487396033311151334342323939281539285755241892542753368993670767360327076953407153977831769329985800290247380912222702470030149732148309934933241880821118256958623294651857563689754163574689598660266517287106373178211544073283084095822937176862803685645159152570329027569036857129883127811874734596074173100978847315628386494861931043501661812266303769593726764588538380943049453023030268014210975502503890721484246009339875439915383842137754597246409868737926602794166204708663284387662736608782721500359892776517074454770653839619602834310285238409133872378563979536825788370583048947266348134821317190888339633672412315363972952037995614054202652355733182260536030151610767270161366775347202108995240601901907310716711572131531313991087346049948558879305557329074866756924991779147777627525721533153059191543757640208556243114944537254595680970256475764244423090474070144938720093148556612673864189942549493136310475961893303490949930728432409009866042964776416063621289476951726567416922104126791976202629175585305961605883598150943813988815546473953900221085978718592405964780276788923924280477323241680115088099429075130067286149727378504160015538097278691011653816376029956001998756771052874341796486349487590228434508102484523242850619456464928288338024674531436007665393932531690693471534111025909155950980999607771081924043400817401909049952241694593670841551263350446837423540829126465380354941695384687191594786448216907197188279045374175897865653963543641749642113833239127266085382956774626422043748613750869656038144115446781746318241578012548976258024056722181651902552564665510417840313993155273497012827464078379677343103957500116764350123239218721736939561572561209629465861258179225997122936015604832529324660590007467538289113588769660502304327546441577272041355353431069230209904095882802842492545660922550473678663353597767011475477937895122163950391748837006069208321431310565114032165914971605450331526087562443039751201627044475665497445082910844914275328651257884320143371916195074243458542671276811026007996977327310910874040713888398593020568547705681283700324106099488089120372337515691677129447677010573628517526922673867332490411057618836343343739931740573619353690777058069918700110387550682586512339634192984733096678757320329048370056903353621683728691586822484931645864130995561280761354315839479796503645798442252939980325213460972862269536267247076289971779632763346141120704154148305304401967545816235986063466572733057403342467568253998855700384203956509771995410026837628297511970715692877805882319026171014758008973737834649921004305707615859532250733610872957027150743122979203137211031512057869458182420174183205651511753381284579981732961300097228591130828209090533147601196781850383675303470470005787483609975909091296303441827655051198429426117421250174531088376152772103209162330883357102085772162595099252986436418206894396569085647751243182903018353395109114675137185342463058517707443432161316913045445620729557791498890485478502949425186992301564204823672996782085432770817139937297136472855162369102809439490498095711147987353263361086154490921362107195786271826589846454595870090692492488205234351128687386269125293356955656244015333447567162409478118265711559547566993684235624999792277233328567847862452694981303829576715883682539034846167149680141385991940555979179178582819757848123724780229627342713273807017121315934540225441686146416206418549556220175802717174193296040307242855759140374875241255836486847826530579021129301504600930097911328939110209284222126288743972398792999872217126802442695704364082691751239472885809766317352190347740207830108250082306867481659929162142043785596907008396343174915704007049111330970230468766158574831350801444759928520207278604062469098 diff --git a/jarviscli/data/websites.csv b/jarviscli/data/websites.csv index 643bf9f13..c6379c7a3 100644 --- a/jarviscli/data/websites.csv +++ b/jarviscli/data/websites.csv @@ -12,3 +12,4 @@ pinterest,https://www.pinterest.com/, apple,https://www.apple.com/, quora,https://www.quora.com/, forbes,https://www.forbes.com/, +discord,https://discord.com/, diff --git a/jarviscli/jarvis.py b/jarviscli/jarvis.py index 0275ddb7b..4958f56b7 100644 --- a/jarviscli/jarvis.py +++ b/jarviscli/jarvis.py @@ -1,13 +1,9 @@ -# -*- coding: utf-8 -*- - -import os import tempfile from cmd import Cmd from typing import Dict, Optional from colorama import Fore -import nltk from api import JarvisAPI from plugin import Plugin from plugin_manager import PluginManager @@ -18,45 +14,27 @@ class Jarvis: - def __init__(self, language_parser, directories=["jarviscli/plugins", "custom"]): - directories = self._rel_path_fix(directories) + def __init__(self, language_parser, plugin_manager, server): self.jarvis_api = JarvisAPI() self.language_parser = language_parser + self.plugin_manager = plugin_manager + self.server = server - self._plugin_manager = PluginManager() - - for directory in directories: - self._plugin_manager.add_directory(directory) - - self.language_parser.train(self._plugin_manager.get_plugins().values()) + plugin_values = self.plugin_manager.get_plugins().values() + self.language_parser.train(plugin_values) + self.server.init_server_endpoints(jarvis_plugins=plugin_values) self.cache = '' self.stdout = self - def _rel_path_fix(self, dirs): - dirs_abs = [] - work_dir = os.path.dirname(__file__) - # remove 'jarviscli/' from path - work_dir = os.path.dirname(work_dir) - - # fix nltk path - nltk.data.path.append(os.path.join(work_dir, "jarviscli/data/nltk")) - - # relative -> absolute paths - for directory in dirs: - if not directory.startswith(work_dir): - directory = os.path.join(work_dir, directory) - dirs_abs.append(directory) - return dirs_abs - def register_io(self, jarvis_io): self.jarvis_api.io = jarvis_io return self.jarvis_api def plugin_info(self): plugin_status_formatter = { - "disabled": len(self._plugin_manager.get_disabled()), - "enabled": self._plugin_manager.get_number_plugins_loaded(), + "disabled": len(self.plugin_manager.get_disabled()), + "enabled": self.plugin_manager.get_number_plugins_loaded(), "red": Fore.RED, "blue": Fore.BLUE, "reset": Fore.RESET @@ -71,7 +49,7 @@ def plugin_info(self): def activate_plugins(self): """Generate do_XXX, help_XXX and (optionally) complete_XXX functions""" - for (plugin_name, plugin) in self._plugin_manager.get_plugins().items(): + for (plugin_name, plugin) in self.plugin_manager.get_plugins().items(): yield plugin plugin.init(self.jarvis_api) @@ -145,7 +123,7 @@ def do_help(self, plugin: Optional[Plugin]): formatString = "Format: command ([aliases for command])" self.jarvis_api.say(headerString) self.jarvis_api.say(formatString, Fore.BLUE) - pluginDict = self._plugin_manager.get_plugins() + pluginDict = self.plugin_manager.get_plugins() uniquePlugins: Dict[str, Plugin] = {} for key in pluginDict.keys(): plugin = pluginDict[key] diff --git a/jarviscli/language/snips.py b/jarviscli/language/snips.py new file mode 100644 index 000000000..5fd994d42 --- /dev/null +++ b/jarviscli/language/snips.py @@ -0,0 +1,47 @@ +import re + +import pluginmanager + +from plugin import PluginStorage +from snips_nlu import SnipsNLUEngine + + +class LanguageParser(pluginmanager.IPlugin, PluginStorage): + """ + interface to parse input text + """ + + def __init__(self): + super(pluginmanager.IPlugin, self).__init__() + self._plugins = {} + self._pre_train_json = dict() + self._pre_train_json['intents'] = {} + self._pre_train_json['entities'] = {} + self._pre_train_json['language'] = 'en' + self.nlu_engine = SnipsNLUEngine() + + def train(self, plugins): + self._generate_pre_train_json(plugins) + self.nlu_engine.fit(self._pre_train_json) + + def _generate_pre_train_json(self, plugins): + for plugin in plugins: + intent = dict() + intent['utterances'] = list() + _data = list() + _data.append(dict({'text': plugin.get_name()})) + intent['utterances'].append(dict({"data": _data})) + intent_name = '_'.join(re.findall(r"[\w']+", plugin.get_name())) + self._pre_train_json['intents'][intent_name] = intent + self._plugins[intent_name] = plugin + + # handle sub commands (recursive) + self._generate_pre_train_json(plugin.get_plugins().values()) + + def identify_action(self, action): + parsed_action = self.nlu_engine.parse(action) + print(parsed_action) + intent_name = parsed_action['intent']['intentName'] + if intent_name not in self._plugins: + return None + return self._plugins[intent_name] diff --git a/jarviscli/main.py b/jarviscli/main.py new file mode 100644 index 000000000..84d49b988 --- /dev/null +++ b/jarviscli/main.py @@ -0,0 +1,86 @@ +import os +import sys + +import colorama + +from jarvis import Jarvis +from ui.server import server +from language import default +from plugin_manager import PluginManager + + +def check_python_version(): + return sys.version_info[0] == 3 + + +def main_cli(): + from ui.cmd_interpreter import CmdInterpreter + from language import snips + + language_parser = snips.LanguageParser() + plugin_manager = build_plugin_manager() + jarvis_server = server.JarvisServer() + jarvis = Jarvis(language_parser, plugin_manager, jarvis_server) + cmd_interpreter = CmdInterpreter(jarvis) + + command = " ".join(sys.argv[1:]).strip() + cmd_interpreter.executor(command) + + +def main_gui(): + from ui.gui.application import JarvisApp + from kivy.utils import platform + + if platform == 'android': + import ui.gui.android_plugins + plugin_manager = ui.gui.android_plugins.build_plugin_manager() + else: + plugin_manager = build_plugin_manager() + + language_parser = default.DefaultLanguageParser() + jarvis_server = server.JarvisServer() + jarvis = Jarvis(language_parser, plugin_manager, jarvis_server) + jarvis_gui = JarvisApp(jarvis) + + jarvis_gui.run() + + +def build_plugin_manager(): + directories = ["jarviscli/plugins", "custom"] + directories = _rel_path_fix(directories) + + plugin_manager = PluginManager() + + for directory in directories: + plugin_manager.add_directory(directory) + return plugin_manager + + +def _rel_path_fix(dirs): + dirs_abs = [] + work_dir = os.path.dirname(__file__) + # remove 'jarviscli/' from path + work_dir = os.path.dirname(work_dir) + + # fix nltk path + import nltk + nltk.data.path.append(os.path.join(work_dir, "jarviscli/data/nltk")) + + # relative -> absolute paths + for directory in dirs: + if not directory.startswith(work_dir): + directory = os.path.join(work_dir, directory) + dirs_abs.append(directory) + return dirs_abs + + +def dump_android_plugins(): + with open('ui/gui/android_plugins.py', 'w') as writer: + writer.write(build_plugin_manager().dump_android()) + + +if __name__ == '__main__': + if check_python_version(): + main_gui() + else: + print("Sorry! Only Python 3 supported.") diff --git a/jarviscli/packages/directions_to.py b/jarviscli/packages/directions_to.py index a2b75b828..5ca51c803 100644 --- a/jarviscli/packages/directions_to.py +++ b/jarviscli/packages/directions_to.py @@ -1,7 +1,11 @@ -from utilities.GeneralUtilities import wordIndex from . import mapps +def wordIndex(data, word): + wordList = data.split() + return wordList.index(word) + + def main(data): word_list = data.split() to_index = wordIndex(data, "to") diff --git a/jarviscli/packages/forecast.py b/jarviscli/packages/forecast.py index 1f75abdfd..d9ce41303 100644 --- a/jarviscli/packages/forecast.py +++ b/jarviscli/packages/forecast.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from utilities.GeneralUtilities import print_say -from colorama import Fore import json -from . import mapps import requests + +from colorama import Fore + from utilities.dateTime import WeekDay +from . import mapps def main(jarvis, s): diff --git a/jarviscli/packages/near_me.py b/jarviscli/packages/near_me.py index b79625a76..ceb957422 100644 --- a/jarviscli/packages/near_me.py +++ b/jarviscli/packages/near_me.py @@ -1,8 +1,11 @@ -from utilities.GeneralUtilities import wordIndex - from . import mapps +def wordIndex(data, word): + wordList = data.split() + return wordList.index(word) + + def main(data): word_list = data.split() try: diff --git a/jarviscli/plugin.py b/jarviscli/plugin.py index df687a24c..1634a9dcf 100644 --- a/jarviscli/plugin.py +++ b/jarviscli/plugin.py @@ -1,15 +1,19 @@ +import enum from inspect import cleandoc, isclass import pluginmanager from requests import ConnectionError -# Constants -# platform -MACOS = "MACOS" -LINUX = "LINUX" -WINDOWS = "WINDOWS" -# Shortcut for MACOS + LINUX -UNIX = "UNIX" + +class Platform(enum.Enum): + MACOS = 0 + LINUX = 1 + WINDOWS = 2 + ANDROID = 3 + # Shortcut for MACOS + LINUX + UNIX = -1 + # Shortcut for MACOS + LINUX + WINDOW + DESKTOP = -2 def plugin(name): @@ -39,6 +43,10 @@ def create_plugin(run): plugin_class._backend = (run,) plugin_class._backend_instance = run + module = run.__module__.replace('_', '.')[:-2] + module = module.replace('pluginmanager.plugin.', 'jarviscli.plugins.') + plugin_class._origin = module + return plugin_class return create_plugin diff --git a/jarviscli/plugin_manager.py b/jarviscli/plugin_manager.py index e4cdc2a76..38ed768df 100644 --- a/jarviscli/plugin_manager.py +++ b/jarviscli/plugin_manager.py @@ -4,7 +4,8 @@ import pluginmanager import plugin -from utilities.GeneralUtilities import warning, error, executable_exists +from plugin import Platform +from utilities.GeneralUtilities import error, executable_exists, warning class PluginManager(object): @@ -15,7 +16,21 @@ class PluginManager(object): """ def __init__(self): + import pluginmanager.module_manager self._backend = pluginmanager.PluginInterface() + + # patch to ignore import exception + _load_source = pluginmanager.module_manager.load_source + + def patched_load_source(*args): + try: + return _load_source(*args) + except ImportError as e: + print(e) + import sys + return sys + pluginmanager.module_manager.load_source = patched_load_source + self._plugin_dependency = PluginDependency() self._cache = None @@ -28,6 +43,7 @@ def __ends_with_py(s): self._backend.set_file_filters(__ends_with_py) self._backend.add_blacklisted_directories("jarviscli/packages/aiml") self._backend.add_blacklisted_directories("jarviscli/packages/memory") + self._backend.add_blacklisted_plugins(plugin.Platform) def add_directory(self, path): """Add directory to search path for plugins""" @@ -45,7 +61,6 @@ def _load(self): return self._cache = plugin.PluginStorage() - self._backend.collect_plugins() (enabled, disabled) = self._validate_plugins(self._backend.get_plugins()) @@ -175,6 +190,51 @@ def get_number_plugins_loaded(self): self._load() return self._plugins_loaded + def add(self, plugin): + self._backend.add_plugins(plugin) + + def dump_android(self): + self._load() + imports = [] + plugins = [] + + for _plugin in self._backend.get_instances(): + require = _plugin.require() + platforms = [] + for key, value in require: + if key == 'platform': + if isinstance(value, plugin.Platform): + platforms.append(value) + else: + platforms.extend(value) + + if plugin.Platform.ANDROID in platforms: + origin = _plugin._origin.replace('jarviscli.', '') + imports += [origin] + plugins += [origin + '.' + _plugin.__class__.__name__] + + imports = sorted(list(set(imports))) + plugins = sorted(plugins) + + return """\ +############################### +# AUTO-GENERATED FILE # +# DO NOT MODIFY # +############################### + +import {} + +from plugin_manager import PluginManager + + +def build_plugin_manager(): + plugin_manager = PluginManager() + plugin_manager.add({}()) + + return plugin_manager +""".format('\nimport '.join(imports), + '())\nplugin_manager.add('.join(plugins)) + class PluginDependency(object): """ @@ -188,11 +248,11 @@ def __init__(self): # plugin shoud match these requirements self._requirement_has_network = True if sys.platform == "darwin": - self._requirement_platform = plugin.MACOS + self._requirement_platform = Platform.MACOS elif sys.platform == "win32": - self._requirement_platform = plugin.WINDOWS + self._requirement_platform = Platform.WINDOWS elif sys.platform.startswith("linux"): - self._requirement_platform = plugin.LINUX + self._requirement_platform = Platform.LINUX else: self._requirement_platform = None warning("Unsupported platform {}".format(sys.platform)) @@ -209,7 +269,7 @@ def _plugin_get_requirements(self, requirements_iter): key = requirement[0] values = requirement[1] - if isinstance(values, str) or isinstance(values, bool): + if isinstance(values, str) or isinstance(values, bool) or isinstance(values, Platform): values = [values] if key in plugin_requirements: @@ -226,7 +286,7 @@ def check(self, plugin): plugin_requirements = self._plugin_get_requirements(plugin.require()) if not self._check_platform(plugin_requirements["platform"]): - required_platform = ", ".join(plugin_requirements["platform"]) + required_platform = ", ".join([x.name for x in plugin_requirements["platform"]]) return "Requires os {}".format(required_platform) if not self._check_network(plugin_requirements["network"], plugin): @@ -242,8 +302,10 @@ def _check_platform(self, values): if not values: return True - if plugin.UNIX in values: - values += [plugin.LINUX, plugin.MACOS] + if Platform.UNIX in values: + values += [Platform.LINUX, Platform.MACOS] + if Platform.DESKTOP in values: + values += [Platform.LINUX, Platform.WINDOWS, Platform.MACOS] return self._requirement_platform in values diff --git a/jarviscli/plugins/MipsConverter.py b/jarviscli/plugins/MipsConverter.py index 23aacd5b7..b46183798 100644 --- a/jarviscli/plugins/MipsConverter.py +++ b/jarviscli/plugins/MipsConverter.py @@ -1,4 +1,5 @@ import os + from plugin import plugin FILE_PATH = os.path.abspath(os.path.dirname(__file__)) diff --git a/jarviscli/plugins/VisitWebsite.py b/jarviscli/plugins/VisitWebsite.py index aba56adc8..642813df0 100644 --- a/jarviscli/plugins/VisitWebsite.py +++ b/jarviscli/plugins/VisitWebsite.py @@ -1,8 +1,9 @@ -import webbrowser import os import socket +import webbrowser from urllib.parse import urlparse -from plugin import plugin, alias, require + +from plugin import alias, plugin, require FILE_PATH = os.path.abspath(os.path.dirname(__file__)) diff --git a/jarviscli/plugins/advice_giver.py b/jarviscli/plugins/advice_giver.py new file mode 100644 index 000000000..0739a5fbb --- /dev/null +++ b/jarviscli/plugins/advice_giver.py @@ -0,0 +1,58 @@ +from plugin import plugin +import random + + +@plugin("give me advice") +def advice(jarvis, s): + answers = [ + "No", + "Yes", + "You Can Do It!", + "I Cant Help You", + "Sorry To hear That, But You Must Forget :(", + "Keep It Up!", + "Nice", + "Dont Do It Ever Again", + "I Like It, Good Job", + "I Am Not Certain", + "Too Bad For You, Try To Find Something Else To Do And Enjoy", + "Time Will Pass And You Will Forget", + "Dont Do It", + "Do It", + "Never Ask Me About That Again", + "I Cant Give Advice Now I Am Sleepy", + "Sorry I Cant Hear This Language", + "Sorry But Your Question Does Not Make Sense"] + + greetings = "#################################################\n" \ + "# HELLO THERE! #\n" \ + "# Ask Me Question And I Will Give You Advice #\n" \ + "# I Am Limited So Pick First Which Fits Context #\n" \ + "#################################################\n" + question = "" + acceptable = 0 + while not acceptable: + question = input("Ask Me A Question : ") + questionTmp = question.strip() + if len(questionTmp) > 0: + if questionTmp[len(questionTmp) - 1] == '?': + acceptable = 1 + + while True: + randPos = random.randint(0, len(answers)) + print(answers[randPos]) + indicator = 0 + while True: + desire = input("Was This In Context? (Y/N) : ") + if desire.strip().lower() == 'n': + print("Its A Pitty :( I'll Try Again!") + break + elif desire.strip().lower() == 'y': + indicator = 1 + print("Good To hear! Happy To Advice You!") + break + else: + continue + if indicator == 1: + print("Good Bye!") + break diff --git a/jarviscli/plugins/akinator.py b/jarviscli/plugins/akinator.py index d4dc88fa7..c1e2a81a5 100644 --- a/jarviscli/plugins/akinator.py +++ b/jarviscli/plugins/akinator.py @@ -1,9 +1,11 @@ -from plugin import plugin, require -from colorama import Fore -import akinator import subprocess import sys +from colorama import Fore + +import akinator +from plugin import plugin, require + """ Simple akinator text based game: think up a character, answer questions and akinator will find it ! @@ -85,7 +87,7 @@ def main_game(jarvis): subprocess.run([imageViewerFromCommandLine, aki.picture]) # display image of answer except Exception: pass - correct = jarvis.input(f"It's {aki.name} ({aki.description})! Was I correct?\n\t") + correct = jarvis.input(f"It's {aki.first_guess['name']} ({aki.first_guess['description']})! Was I correct?\n\t") if correct.lower() == "yes" or correct.lower() == "y": jarvis.say("Yay !!! :D", Fore.GREEN) else: diff --git a/jarviscli/plugins/asteroids_neows.py b/jarviscli/plugins/asteroids_neows.py new file mode 100644 index 000000000..7e945263c --- /dev/null +++ b/jarviscli/plugins/asteroids_neows.py @@ -0,0 +1,99 @@ +import requests +from colorama import Fore +from plugin import plugin, require +import datetime + +API_KEY = '1ebd3b92bf5041249f8c1e7a540ce98c' +headers = {'X-Auth-Token': API_KEY} +# url = 'https://api.nasa.gov/neo/rest/v1/feed?start_date=2020-07-10&end_date=2020-07-10&api_key=DqXuTRFieGmR5EbdTpPA0tIbDybBhuVmWNerhOdN' + + +@require(network=True) +@plugin('neows') +def neows(jarvis, s): + option = get_option(jarvis) + if option == 8: + return + + dt = str(datetime.date.today() + datetime.timedelta(days=option - 1)) + print_objects(jarvis, dt) + + +def print_objects(jarvis, dt): + url = 'https://api.nasa.gov/neo/rest/v1/feed?start_date=' + dt + url += '&end_date=' + dt + url += '&api_key=DqXuTRFieGmR5EbdTpPA0tIbDybBhuVmWNerhOdN' + + r = fetch(url) + day = r["near_earth_objects"] + neos = day[dt] + jarvis.say("Near earth objects: " + str(r["element_count"]), Fore.RED) + print() + + for i in range(0, r["element_count"]): + print("---" + str(i + 1) + "---") + name = "Name: " + neos[i]["name"] + jarvis.say(name, Fore.BLUE) + + jpl = "Nasa jpl url: " + neos[i]["nasa_jpl_url"] + jarvis.say(jpl, Fore.BLUE) + + hazardous = "Is potentially hazardous asteroid: " + if neos[i]["is_potentially_hazardous_asteroid"]: + hazardous += "YES" + else: + hazardous += "NO" + jarvis.say(hazardous, Fore.BLUE) + + sentry = "Is sentry object: " + if neos[i]["is_sentry_object"]: + sentry += "YES" + else: + sentry += "NO" + jarvis.say(sentry, Fore.BLUE) + + print() + + +def get_option(jarvis): + jarvis.say("~> I can detect near earth objects at given date", Fore.RED) + jarvis.say("~> Which date do you want?", Fore.RED) + + day1 = datetime.date.today() + day2 = datetime.date.today() + datetime.timedelta(days=1) + day3 = datetime.date.today() + datetime.timedelta(days=2) + day4 = datetime.date.today() + datetime.timedelta(days=3) + day5 = datetime.date.today() + datetime.timedelta(days=4) + day6 = datetime.date.today() + datetime.timedelta(days=5) + day7 = datetime.date.today() + datetime.timedelta(days=6) + + print("1: " + str(day1)) + print("2: " + str(day2)) + print("3: " + str(day3)) + print("4: " + str(day4)) + print("5: " + str(day5)) + print("6: " + str(day6)) + print("7: " + str(day7)) + print("8: Exit") + print() + + while True: + try: + option = int(jarvis.input("Enter your choice: ", Fore.GREEN)) + if option >= 1 and option <= 8: + return option + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + print() + + +def fetch(url): + r = requests.get(url, headers=headers) + r = r.json() + if "errorCode" in r.keys(): + return None + return r diff --git a/jarviscli/plugins/basketball.py b/jarviscli/plugins/basketball.py new file mode 100644 index 000000000..ba2fef026 --- /dev/null +++ b/jarviscli/plugins/basketball.py @@ -0,0 +1,182 @@ +import requests +import datetime +from plugin import plugin, require +from colorama import Fore +from packages.memory.memory import Memory + +URL = "https://api-basketball.p.rapidapi.com/" + + +@require(network=True) +@plugin("basketball") +class basketball(): + """ + Basketball Plugin for getting information about leagues,games and teams + !!! needs api.basketball.com API_KEY for usage + + """ + + def __call__(self, jarvis, s): + print("Basketball data provided by the api-basketball.com\n") + self.get_api_key(jarvis) + while True: + option = self.get_option(jarvis) + if option is None: + return + self.procces_chosen_option(option, jarvis) + + def get_headers(self): + return {"x-rapidapi-host": "api-basketball.p.rapidapi.com", "x-rapidapi-key": self.key} + + def fetch_data(self, route): + r = requests.get(URL + route, headers=self.get_headers()) + r = r.json() + if "errorCode" in r.keys(): + return None + return r + + def get_api_key(self, jarvis): + m = Memory("basketball.json") + if m.get_data("API_KEY") is None: + user_api_key = jarvis.input("Enter Api-BasketBall.com API_KEY: ", Fore.GREEN) + m.add_data("API_KEY", user_api_key) + m.save() + self.key = user_api_key + else: + self.key = m.get_data("API_KEY") + + def procces_chosen_option(self, option, jarvis): + if option == "search_team": + self.search_team(jarvis) + elif option == "list_leagues": + self.list_leagues(jarvis) + elif option == "todays_games": + self.todays_games(jarvis) + elif option == "search_league": + self.search_league(jarvis) + elif option == "new_key": + self.update_api_key(jarvis) + else: + return + + def update_api_key(self, jarvis,): + user_api_key = jarvis.input("Enter New Api-BasketBall.com API_KEY: ", Fore.GREEN) + m = Memory("basketball.json") + m.update_data("API_KEY", user_api_key) + m.save() + self.key = user_api_key + + def list_leagues(self, jarvis): + jarvis.spinner_start('Fetching...') + response = self.fetch_data("leagues") + if response is None: + jarvis.spinner_stop("Error While Loadin Data - Try Again Later.", Fore.YELLOW) + return + total_count = response["results"] + leagues = response["response"] + jarvis.spinner_stop("Found {} Leagues".format(total_count)) + for i in range(total_count): + print(" {}. {} {}".format(i + 1, leagues[i]["country"]["name"], leagues[i]["name"])) + + def search_request(self, jarvis, query, search_name): + value = jarvis.input("Enter {} Name: ".format(search_name), Fore.GREEN) + while len(value.strip()) < 3: + print() + print("The Search {} must be at least 3 characters in length", search_name.lower()) + value = jarvis.input("Enter {} Name: ".format(search_name), Fore.GREEN) + jarvis.spinner_start('Searching...') + response = self.fetch_data("{}?search={}".format(query, value.strip())) + if response is None: + jarvis.spinner_stop("Error While Searching {} - Try Again Later.".format(search_name), Fore.YELLOW) + return + return response + + def search_team(self, jarvis): + response = self.search_request(jarvis, "teams", "Team") + if response is None: + return + found_count = response["results"] + if found_count == 0: + jarvis.spinner_stop("Nothing was Found With Given Name", Fore.YELLOW) + return + jarvis.spinner_stop("Found {} Teams".format(found_count)) + teams = response["response"] + for i in range(found_count): + team = teams[i] + print(" {}. '{}' Country: {}".format(i + 1, team["name"], team["country"]["name"])) + + def search_league(self, jarvis): + response = self.search_request(jarvis, "leagues", "League") + if response is None: + return + found_count = response["results"] + if found_count == 0: + jarvis.spinner_stop("Nothing was Found With Given Name", Fore.YELLOW) + return + jarvis.spinner_stop("Found {} Leagues".format(found_count)) + leagues = response["response"] + for i in range(found_count): + league = leagues[i] + name = league["name"] + seasons = league["seasons"] + print(" {}. {} {}".format(i + 1, league["country"]["name"], name)) + if len(seasons) > 0: + print(" Last {} {} Seasons".format(len(seasons), name)) + for j in range(len(seasons)): + print(" Season: {} Start: {} End: {}".format(seasons[j]["season"], seasons[j]["start"], seasons[j]["end"])) + + def todays_games(self, jarvis): + jarvis.spinner_start('Fetching...') + date = datetime.datetime.now().strftime('%Y-%m-%d') + response = self.fetch_data("games?date={}".format(date)) + if response is None: + jarvis.spinner_stop("Error While Loading Matches - Try Again Later.", Fore.YELLOW) + return + total_count = response["results"] + matches = response["response"] + if total_count == 0: + jarvis.spinner_stop("There is No Matches Today", Fore.YELLOW) + return + jarvis.spinner_stop("Found {} Matches".format(total_count)) + for i in range(total_count): + match = matches[i] + time = match["time"] + country = match["country"]["name"] + league = match["league"]["name"] + teams = "{} VS {}".format(match["teams"]["home"]["name"], match["teams"]["away"]["name"]) + print(" {}. {} {} {} {}".format(i + 1, country, league, time, teams)) + + def get_option(self, jarvis): + options = {1: "todays_games", 2: "search_team", 3: "list_leagues", 4: "search_league", 5: "new_key"} + + print() + jarvis.say("How Can I Help You?", Fore.BLUE) + print() + print("1: List Todays Games") + print("2: Search Team By Name") + print("3: List All Avaliable Leagues") + print("4: Search League By Name") + print("5: Insert New API_KEY") + print("6: Exit ") + print() + choice = self.get_choice(jarvis) + if choice == -1: + return + else: + return options[choice] + + def get_choice(self, jarvis): + while True: + try: + inserted_value = int(jarvis.input("Enter your choice: ", Fore.GREEN)) + if inserted_value == 6: + return -1 + elif inserted_value == 1 or inserted_value == 2 or inserted_value == 3 or inserted_value == 4 or inserted_value == 5: + return inserted_value + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + print() diff --git a/jarviscli/plugins/battery.py b/jarviscli/plugins/battery.py index c6f11d20d..236f89ebc 100644 --- a/jarviscli/plugins/battery.py +++ b/jarviscli/plugins/battery.py @@ -1,12 +1,13 @@ +import os import subprocess + +from plugin import Platform, plugin, require from utilities.GeneralUtilities import executable_exists -from plugin import plugin, require, LINUX, WINDOWS -import os VALID_OPTIONS = ['status', 'vendor', 'energy', 'technology', 'remaining'] -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('battery') def battery_WIN32(jarvis, s): """ @@ -28,7 +29,7 @@ def secs2hours(secs): (batt.percent, secs2hours(batt.secsleft))) -@require(platform=LINUX, native='upower') +@require(platform=Platform.LINUX, native='upower') @plugin('battery') def battery_LINUX(jarvis, s): """ @@ -97,7 +98,7 @@ def get_specific_info(info_required): return output -@require(platform=LINUX, native='!upower') +@require(platform=Platform.LINUX, native='!upower') @plugin('battery') def battery_LINUX_FALLBACK(jarvis, s): """ diff --git a/jarviscli/plugins/binary.py b/jarviscli/plugins/binary.py index a37c9796e..ce45121fc 100644 --- a/jarviscli/plugins/binary.py +++ b/jarviscli/plugins/binary.py @@ -1,6 +1,7 @@ -from plugin import plugin from colorama import Fore +from plugin import plugin + @plugin("binary") def binary(jarvis, s): diff --git a/jarviscli/plugins/blackjack.py b/jarviscli/plugins/blackjack.py index 247feceb4..89335bb20 100644 --- a/jarviscli/plugins/blackjack.py +++ b/jarviscli/plugins/blackjack.py @@ -1,7 +1,9 @@ import random -from plugin import plugin + from colorama import Fore +from plugin import plugin + def delay(): # method to pause after a series of actions have been completed. n = input("Press enter to continue") diff --git a/jarviscli/plugins/bmi.py b/jarviscli/plugins/bmi.py index aea8b2cbe..9def5e948 100644 --- a/jarviscli/plugins/bmi.py +++ b/jarviscli/plugins/bmi.py @@ -1,5 +1,6 @@ +from colorama import Back, Style + from plugin import plugin -from colorama import Fore, Back, Style @plugin('bmi') diff --git a/jarviscli/plugins/bmr.py b/jarviscli/plugins/bmr.py new file mode 100644 index 000000000..fe83096cd --- /dev/null +++ b/jarviscli/plugins/bmr.py @@ -0,0 +1,30 @@ +from plugin import plugin + + +@plugin("bmr") +def bmr(jarvis, s): + """Prints \"hello world!\"""" + + # gets inputs + jarvis.say("M or F") + sex = jarvis.input() + jarvis.say("What is your height (cm) ?") + height = jarvis.input() + jarvis.say("What is your weight (kg) ?") + weight = jarvis.input() + jarvis.say("What is your age ?") + age = jarvis.input() + + # for catching errors + try: + # formula changes based on sex + if(sex == 'F'): + Bmr = (float(height) * 6.25) + (float(weight) * 9.99) - (float(age) * 4.92) - 116 + jarvis.say(str(Bmr)) + elif(sex == 'M'): + Bmr = (float(height) * 6.25) + (float(weight) * 9.99) - (float(age) * 4.92) - 5 + jarvis.say(str(Bmr)) + else: + jarvis.say("try again! please follow the format") + except BaseException: + jarvis.say("try again! please follow the format") diff --git a/jarviscli/plugins/bulkresize.py b/jarviscli/plugins/bulkresize.py index 06928e2bc..8f522d2a6 100644 --- a/jarviscli/plugins/bulkresize.py +++ b/jarviscli/plugins/bulkresize.py @@ -1,9 +1,10 @@ -import cv2 import os -from plugin import plugin +import cv2 from colorama import Fore +from plugin import plugin + IMAGE_FORMATS = ['.jpg', '.png', '.jpeg', '.svg'] @@ -234,7 +235,7 @@ def spin(jarvis, s): Fore.YELLOW) jarvis.say('Print y for "YES" n for "NO"', Fore.YELLOW) answer = jarvis.input() - if answer is 'y': + if answer == 'y': create_dir(path2) else: while not dir_exist(path2): diff --git a/jarviscli/plugins/buy.py b/jarviscli/plugins/buy.py index 0d1f70ca8..d380d4932 100644 --- a/jarviscli/plugins/buy.py +++ b/jarviscli/plugins/buy.py @@ -1,9 +1,7 @@ -from plugin import plugin -import os -import subprocess -import sys import webbrowser +from plugin import plugin + @plugin("buy") def buy(jarvis, s): diff --git a/jarviscli/plugins/caesar_cipher.py b/jarviscli/plugins/caesar_cipher.py new file mode 100644 index 000000000..cc712fc30 --- /dev/null +++ b/jarviscli/plugins/caesar_cipher.py @@ -0,0 +1,103 @@ +from colorama import Fore + +from plugin import plugin + + +@plugin("caesar cipher") +def caesar_cipher_converter(jarvis, str): + option = get_option(jarvis) + if option == 1: + plain_to_cipher(jarvis) + elif option == 2: + cipher_to_plain(jarvis) + else: + return + + +def get_option(jarvis): + jarvis.say("~> What can I do for you?", Fore.RED) + print("1: Convert plain text to cipher") + print("2: Convert cipher to plain text") + print("3: Exit") + print() + + while True: + try: + option = int(jarvis.input("Enter your choice: ", Fore.GREEN)) + if option == 3: + return + elif option == 1 or option == 2: + return option + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + print() + + +def plain_to_cipher(jarvis): + user_input = get_user_input(jarvis) + converted = "" + + for i in user_input: + if is_ascii(i): + if i.isalpha(): + if i.isupper(): + converted += chr((ord(i) - 68) % 26 + 65) + else: + converted += chr((ord(i) - 100) % 26 + 97) + else: + converted += i + else: + x = ord(i) + if x >= 192 and x <= 255: + converted += chr((ord(i) - 195) % 63 + 192) + else: + converted += i + + jarvis.say(converted, Fore.YELLOW) + + +def is_ascii(s): + return all(ord(c) < 128 for c in s) + + +def cipher_to_plain(jarvis): + user_input = get_user_input(jarvis) + converted = "" + + for i in user_input: + if is_ascii(i): + if i.isalpha(): + if i.isupper(): + converted += chr((ord(i) - 62) % 26 + 65) + else: + converted += chr((ord(i) - 94) % 26 + 97) + else: + converted += i + else: + x = ord(i) + if x >= 192 and x <= 255: + converted += chr((ord(i) - 189) % 63 + 192) + else: + converted += i + + jarvis.say(converted, Fore.YELLOW) + + +def get_user_input(jarvis): + while True: + try: + user_input = jarvis.input("Enter string to convert: ") + if len(user_input) > 0: + return user_input + else: + jarvis.say( + "String length should be minimum 1.", Fore.YELLOW) + except ValueError: + jarvis.say("Sorry, I didn't understand that.", Fore.RED) + continue + + return diff --git a/jarviscli/plugins/calculator.py b/jarviscli/plugins/calculator.py index 7065bde00..b7ce38bc5 100644 --- a/jarviscli/plugins/calculator.py +++ b/jarviscli/plugins/calculator.py @@ -1,4 +1,5 @@ import math + from plugin import plugin diff --git a/jarviscli/plugins/camera.py b/jarviscli/plugins/camera.py index fd6abecdf..ba4b55238 100644 --- a/jarviscli/plugins/camera.py +++ b/jarviscli/plugins/camera.py @@ -1,9 +1,11 @@ import os + from colorama import Fore -from plugin import plugin, require, LINUX, MACOS + +from plugin import Platform, plugin, require -@require(native="cheese", platform=LINUX) +@require(native="cheese", platform=Platform.LINUX) @plugin('open camera') def open_camera__LINUX(jarvis, s): """Jarvis will open the camera for you.""" @@ -11,7 +13,7 @@ def open_camera__LINUX(jarvis, s): os.system("cheese") -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin('open camera') def open_camera__MAC(jarvis, s): """Jarvis will open the camera for you.""" diff --git a/jarviscli/plugins/cat_fact.py b/jarviscli/plugins/cat_fact.py index 861135284..ec04070c7 100644 --- a/jarviscli/plugins/cat_fact.py +++ b/jarviscli/plugins/cat_fact.py @@ -1,5 +1,6 @@ import requests from colorama import Fore + from plugin import plugin, require diff --git a/jarviscli/plugins/cat_history.py b/jarviscli/plugins/cat_history.py index 2750352c1..a9bf4549e 100644 --- a/jarviscli/plugins/cat_history.py +++ b/jarviscli/plugins/cat_history.py @@ -1,5 +1,3 @@ -import tempfile - from colorama import Fore from jarvis import HISTORY_FILENAME diff --git a/jarviscli/plugins/change_mac.py b/jarviscli/plugins/change_mac.py new file mode 100644 index 000000000..5792e8cb2 --- /dev/null +++ b/jarviscli/plugins/change_mac.py @@ -0,0 +1,132 @@ +import re +import subprocess +from platform import system as sys + +from colorama import Fore + +from plugin import Platform, plugin, require + + +@require(platform=Platform.LINUX) +@plugin("mac") +class MacManagerLinux(): + """ + Jarvis plugin for viewing and changing any devices MAC + address connected to your computer + + """ + + def __call__(self, jarvis, s): + devices = self.request_devices(jarvis) + jarvis.say("You have " + str(len(devices)) + " internet device/s") + choice = self.show_options(jarvis, devices) + if choice == "exit": + return + device_choice = list(devices[choice - 1].keys())[0] + mac_choice = self.get_new_mac( + "Please choose a new MAC address: ", jarvis) + jarvis.say('Setting device ' + str(device_choice) + + ' to MAC address: ' + str(mac_choice)) + new_mac = self.change_mac(device_choice, mac_choice, jarvis) + devices = self.request_devices(jarvis) + mac = list(devices[choice - 1].values()) + name = list(devices[choice - 1].keys()) + jarvis.say("Your new MAC address is: " + + str(name[0]) + ' - ' + str(mac[0])) + + def get_choice(self, input_text, max_valid_value, terminator, jarvis): + while True: + try: + inserted_value = int(jarvis.input(input_text, Fore.GREEN)) + if inserted_value == terminator: + return -1 + elif inserted_value <= max_valid_value: + return inserted_value + else: + jarvis.say( + "Invalid input! Enter a number from the" + "choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", + Fore.YELLOW) + jarvis.say("") + + def get_new_mac(self, input_value, jarvis): + while True: + try: + input_value = jarvis.input(input_value, Fore.GREEN) + regex = re.compile(r'([0-9a-f]{2}(?::[0-9a-f]{2}){5})') + if len(re.findall(regex, str(input_value))) == 1: + return input_value + else: + jarvis.say("Invalid input! Enter a number" + "from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say("Invalid input! Enter a number" + "from the choices provided.", Fore.YELLOW) + + def request_devices(self, jarvis): + out = subprocess.Popen(["ip link"], universal_newlines=True, + shell=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + (res, stderr) = out.communicate() + res = str(res) + arr = res.split('\n') + arr.remove('') + span = 2 + arr1 = ["\n".join(arr[i:i + span]) for i in range(0, len(arr), span)] + devices = [] + for x in arr1: + device_name = x.split(':') + if device_name[1].strip() == 'lo': + pass + else: + regex = re.compile(r'([0-9a-f]{2}(?::[0-9a-f]{2}){5})') + mac = re.findall(regex, x) + if len(mac) >= 1: + devices.append({device_name[1].strip(): mac[0]}) + else: + pass + return devices + + def show_options(self, jarvis, arr): + count = 1 + for x in range(len(arr)): + mac = list(arr[x].values()) + name = list(arr[x].keys()) + jarvis.say(str(count) + ": " + str(name[0]) + ' - ' + str(mac[0])) + count = count + 1 + jarvis.say(str(count) + ": Exit") + choice = self.get_choice("Please select a device or Exit: ", + count, count, jarvis) + if choice == -1: + return "exit" + else: + return choice + + def change_mac(self, device, mac, jarvis): + down = subprocess.Popen([f"sudo ip link set {device} down"], + shell=True, universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + down.communicate() + change = subprocess.Popen([f"sudo ip link set {device} address {mac}"], + shell=True, universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + up = subprocess.Popen([f"sudo ip link set {device} up"], + shell=True, universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + restart = subprocess.Popen(["sudo service network-manager restart"], + shell=True, universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + new_out = subprocess.Popen(["ip link"], shell=True, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + (new_res, stderr) = new_out.communicate() + return new_res diff --git a/jarviscli/plugins/chuck.py b/jarviscli/plugins/chuck.py index ff397536f..4857a58fd 100644 --- a/jarviscli/plugins/chuck.py +++ b/jarviscli/plugins/chuck.py @@ -1,5 +1,6 @@ import requests from colorama import Fore + from plugin import alias, plugin, require diff --git a/jarviscli/plugins/clear.py b/jarviscli/plugins/clear.py index 491fb7729..035619464 100644 --- a/jarviscli/plugins/clear.py +++ b/jarviscli/plugins/clear.py @@ -1,4 +1,5 @@ import os + from plugin import plugin diff --git a/jarviscli/plugins/clock.py b/jarviscli/plugins/clock.py index 0d5904ffb..9534fcbfe 100644 --- a/jarviscli/plugins/clock.py +++ b/jarviscli/plugins/clock.py @@ -3,7 +3,7 @@ from colorama import Fore -from plugin import plugin, require +from plugin import plugin @plugin('clock') diff --git a/jarviscli/plugins/coin_flip.py b/jarviscli/plugins/coin_flip.py index 192912fda..b2fbd17a8 100644 --- a/jarviscli/plugins/coin_flip.py +++ b/jarviscli/plugins/coin_flip.py @@ -1,4 +1,5 @@ import random + from plugin import plugin diff --git a/jarviscli/plugins/corona.py b/jarviscli/plugins/corona.py index 41e701299..a4d1a51c9 100644 --- a/jarviscli/plugins/corona.py +++ b/jarviscli/plugins/corona.py @@ -1,7 +1,9 @@ +from inspect import cleandoc + import requests from colorama import Fore + from plugin import plugin, require -from inspect import cleandoc @require(network=True) @@ -15,6 +17,7 @@ class CoronaInfo: ** Data provided by: https://api.covid19api.com/ """ + def __call__(self, jarvis, s): if 'help' in s: jarvis.say(cleandoc(self.__doc__), Fore.GREEN) diff --git a/jarviscli/plugins/countryinfo.py b/jarviscli/plugins/countryinfo.py index 7e722a36e..19d2a7f52 100644 --- a/jarviscli/plugins/countryinfo.py +++ b/jarviscli/plugins/countryinfo.py @@ -1,6 +1,7 @@ -from plugin import plugin, require import requests +from plugin import plugin, require + @require(network=True) @plugin('countryinfo') diff --git a/jarviscli/plugins/create_plugin.py b/jarviscli/plugins/create_plugin.py index a22881905..01ac212b8 100644 --- a/jarviscli/plugins/create_plugin.py +++ b/jarviscli/plugins/create_plugin.py @@ -4,8 +4,10 @@ """ import os + from colorama import Fore -from plugin import plugin, require, MACOS, LINUX + +from plugin import Platform, plugin, require """The os.path method is used to track the path in which this plugin is stored and locate the Jarvis/custom folder through relative pathing. @@ -14,7 +16,7 @@ CUSTOM_PLUGINS_PATH = os.path.join(PLUGINS_PATH, '..', '..', 'custom/') -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin("create plugin") def create_plugin_MAC(jarvis, s): @@ -63,7 +65,7 @@ def create_plugin_MAC(jarvis, s): # The difference in LINUX is the command used to open the created file -@require(platform=LINUX) +@require(platform=Platform.LINUX) @plugin("create plugin") def create_plugin_LINUX(jarvis, s): @@ -121,7 +123,6 @@ def file_exists(filename): def format_filename(name): - """Take a string and return a valid filename constructed from the string. Uses a whitelist approach: any characters not present in valid_chars are removed. Also spaces are replaced with underscores. diff --git a/jarviscli/plugins/cricket.py b/jarviscli/plugins/cricket.py index 3e0990955..fbcf25a20 100644 --- a/jarviscli/plugins/cricket.py +++ b/jarviscli/plugins/cricket.py @@ -1,7 +1,7 @@ from colorama import Fore -from pycricbuzz import Cricbuzz + from plugin import plugin, require -from utilities.animations import SpinnerThread +from pycricbuzz import Cricbuzz @require(network=True) diff --git a/jarviscli/plugins/cryptotracker.py b/jarviscli/plugins/cryptotracker.py index fce655b89..0c52a6938 100644 --- a/jarviscli/plugins/cryptotracker.py +++ b/jarviscli/plugins/cryptotracker.py @@ -1,4 +1,5 @@ import requests + from plugin import plugin # ANSI escape sequences to print in color diff --git a/jarviscli/plugins/curl.py b/jarviscli/plugins/curl.py new file mode 100644 index 000000000..8db66f5f8 --- /dev/null +++ b/jarviscli/plugins/curl.py @@ -0,0 +1,104 @@ +""" +This plugin generates curl request for the user. The user needs to specify the parameters +HTTP Method, Content Type, Data, Endpoint. The output is the curl request. +The plugin also validates the user input before generating the curl output. +""" + + +from plugin import plugin +import json +from colorama import Fore + + +@plugin("generate curl") +class GenerateCurl(object): + + def __call__(self, jarvis, s): + self._input_params = {} + self._collect_parameters(jarvis) + self._validate_parameters(jarvis) + self._generate_curl_request(jarvis) + + def _collect_parameters(self, jarvis): + http_method = jarvis.input(prompt="HTTP Method: ", color=Fore.BLUE) + jarvis.say(text="Select Content Type\n\n1. JSON\n2. No Data\n") + content_type = jarvis.input_number(prompt="Enter you choice: ", color=Fore.BLUE, rtype=int, rmin=1, rmax=2) + data = jarvis.input(prompt="Enter / copy the data: ", color=Fore.BLUE) + endpoint = jarvis.input(prompt="Specify the HTTP endpoint: ", color=Fore.BLUE) + + self._input_params = { + "method": http_method.upper(), + "content_type": content_type, + "data": data, + "endpoint": endpoint + } + + def _validate_parameters(self, jarvis): + + if "method" not in self._input_params: + jarvis.say(text="HTTP Method missing.", color=Fore.RED) + jarvis.exit() + + if "content_type" not in self._input_params: + jarvis.say(text="Content Type missing.", color=Fore.RED) + jarvis.exit() + + if "data" not in self._input_params: + jarvis.say(text="Data missing. Could be empty but should be present.", color=Fore.RED) + jarvis.exit() + + if "endpoint" not in self._input_params: + jarvis.say(text="Endpoint missing.", color=Fore.RED) + jarvis.exit() + + if self._valid_method(jarvis) and self._valid_content_type(jarvis) and \ + self._valid_data(jarvis) and self._valid_endpoint(jarvis): + pass + else: + jarvis.exit() + + def _valid_method(self, jarvis): + + valid_methods = ["GET", "POST", "PUT", "PATCH", "DELETE"] + + if self._input_params["method"] not in valid_methods: + jarvis.say(text="Invalid HTTP method provided.", color=Fore.RED) + return False + return True + + def _valid_content_type(self, jarvis): + return True + + def _valid_data(self, jarvis): + + data = self._input_params["data"] + content_type = self._input_params["content_type"] + + if content_type == 2: + return True + + if content_type == 1: + try: + json.loads(data) + return True + except ValueError as e: + jarvis.say(text="Invalid data format based on content type.", color=Fore.RED) + return False + + def _valid_endpoint(self, jarvis): + return True + + def _generate_curl_request(self, jarvis): + + curl_string = "curl " + + curl_string += "-X{} ".format(self._input_params["method"]) + + if self._input_params["content_type"] == 1: + curl_string += '-H "Content-type: application/json" ' + + if self._input_params["content_type"] != 2: + curl_string += " -d '{}'".format(self._input_params["data"]) + + curl_string += " '{}'".format(self._input_params["endpoint"]) + jarvis.say(text=curl_string, color=Fore.GREEN) diff --git a/jarviscli/plugins/currencyconv.py b/jarviscli/plugins/currencyconv.py index 313d9b7ff..358f0f91c 100644 --- a/jarviscli/plugins/currencyconv.py +++ b/jarviscli/plugins/currencyconv.py @@ -1,6 +1,7 @@ -import os import csv +import os from decimal import Decimal + from forex_python.bitcoin import BtcConverter from forex_python.converter import CurrencyRates from plugin import plugin, require diff --git a/jarviscli/plugins/detect_language.py b/jarviscli/plugins/detect_language.py new file mode 100644 index 000000000..332fcddbf --- /dev/null +++ b/jarviscli/plugins/detect_language.py @@ -0,0 +1,62 @@ +import contextlib +import json +import os + +from colorama import Fore + +import fasttext +from plugin import plugin + +FILE_PATH = os.path.abspath(os.path.dirname(__file__)) + + +@plugin("detect lang") +def detect_language(jarvis, s): + """ + Detects the language of an input string + """ + + while s == "": + s = jarvis.input("Enter text \n") + + model = open_model() + output = model.predict(s) + generate_response(jarvis, output) + + +def generate_response(jarvis, output): + """ + Generates response based on the probability of a predicted language + """ + score = output[1][0] + label = output[0][0] + code_to_lang = open_languages() + lang_code = label.split('_')[-1] + language = code_to_lang[lang_code] + if score > 0.5: + jarvis.say('The language of the text is ' + language, Fore.GREEN) + elif score > 0.25: + jarvis.say("I'm not sure, but the language might be " + language, Fore.YELLOW) + else: + jarvis.say("I couldn't identify the language", Fore.BLUE) + + +def open_model(): + """ + Opens a language detector model and disables warnings + """ + model_path = os.path.join(FILE_PATH, "../data/lid.176.ftz") + fasttext.FastText.eprint = print + with open(os.devnull, "w") as f, contextlib.redirect_stdout(f): + model = fasttext.load_model(model_path) + return model + + +def open_languages(): + """ + Opens a dictionary from code to its corresponding language + """ + language_path = os.path.join(FILE_PATH, "../data/code_to_lang.json") + with open(language_path, 'r') as f: + languages = json.load(f) + return languages diff --git a/jarviscli/plugins/dial_code.py b/jarviscli/plugins/dial_code.py index 23f77a88f..059dfd43b 100644 --- a/jarviscli/plugins/dial_code.py +++ b/jarviscli/plugins/dial_code.py @@ -1,8 +1,10 @@ -from plugin import plugin, alias -import os import json +import os + from colorama import Fore +from plugin import alias, plugin + FILE_PATH = os.path.abspath(os.path.dirname(__file__)) diff --git a/jarviscli/plugins/dice.py b/jarviscli/plugins/dice.py index b60435d3b..401d98b39 100644 --- a/jarviscli/plugins/dice.py +++ b/jarviscli/plugins/dice.py @@ -1,10 +1,13 @@ -from colorama import Fore -import re import random +import re + +from colorama import Fore + +from plugin import Platform, plugin, require from utilities.textParser import parse_number -from plugin import plugin +@require(platform=[Platform.ANDROID, Platform.DESKTOP]) @plugin('roll') class Roll(): """ diff --git a/jarviscli/plugins/dns_lookup.py b/jarviscli/plugins/dns_lookup.py index 41abb3514..1da5eecb7 100644 --- a/jarviscli/plugins/dns_lookup.py +++ b/jarviscli/plugins/dns_lookup.py @@ -1,7 +1,9 @@ -from plugin import plugin, alias import socket + from colorama import Fore +from plugin import plugin + def ip_lookup(hostname): return str(socket.gethostbyname(hostname)) diff --git a/jarviscli/plugins/evaluator.py b/jarviscli/plugins/evaluator.py index 905dfc146..35fec796e 100644 --- a/jarviscli/plugins/evaluator.py +++ b/jarviscli/plugins/evaluator.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- import re -import sympy from colorama import Fore +import sympy from plugin import alias, plugin diff --git a/jarviscli/plugins/expression_checker.py b/jarviscli/plugins/expression_checker.py new file mode 100644 index 000000000..0e1e96469 --- /dev/null +++ b/jarviscli/plugins/expression_checker.py @@ -0,0 +1,87 @@ +from plugin import plugin + + +@plugin("check simple expression") +def expression_checker(jarvis, s): + validCharacters = "()+-*/0123456789" + digits = "0123456789" + operators = "+-*/" + brackets = "()" + + while True: + expression = input("Enter Expression To Analyze : ") + while len(expression.strip()) == 0: + expression = input("Expression Length is 0 , Enter Again : ") + if expression.lower() == "stop": + print("You Terminated Expression Checker! Bye!") + break + terminateCurrentAnalyzation = 0 + for character in expression: + if character not in validCharacters: + print("Not Valid Expression! Invalid Character : " + character) + terminateCurrentAnalyzation = 1 + break + + previousIsOperator = 0 + previousIsDigit = 0 + previousIsOpenBracket = 0 + previousIsCloseBracket = 0 + for character in expression: + if character in operators: + if previousIsOperator or previousIsOpenBracket: + print("Not Valid Expression! Invalid Expression Character Progression") + terminateCurrentAnalyzation = 1 + break + previousIsOperator = 1 + previousIsDigit = 0 + previousIsOpenBracket = 0 + previousIsCloseBracket = 0 + elif character in digits: + if previousIsCloseBracket: + print("Not Valid Expression! Invalid Expression Character Progression") + terminateCurrentAnalyzation = 1 + break + previousIsOperator = 0 + previousIsDigit = 1 + previousIsOpenBracket = 0 + previousIsCloseBracket = 0 + elif character == '(': + if previousIsDigit or previousIsCloseBracket: + print("Not Valid Expression! Invalid Expression Character Progression") + terminateCurrentAnalyzation = 1 + break + previousIsOperator = 0 + previousIsDigit = 0 + previousIsOpenBracket = 1 + previousIsCloseBracket = 0 + elif character == ')': + if previousIsOperator or previousIsOpenBracket: + print("Not Valid Expression! Invalid Expression Character Progression") + terminateCurrentAnalyzation = 1 + break + previousIsOperator = 0 + previousIsDigit = 0 + previousIsOpenBracket = 0 + previousIsCloseBracket = 1 + stack = [] + for character in expression: + if character == ')': + if len(stack) == 0: + print("Not Valid Expression! Invalid Bracket Progression") + terminateCurrentAnalyzation = 1 + break + else: + picked = stack.pop() + if picked == '(': + continue + else: + stack.append(picked) + stack.append(character) + elif character == '(': + stack.append(character) + if len(stack) != 0: + print("Not Valid Expression! Invalid Bracket Progression") + terminateCurrentAnalyzation = 1 + if terminateCurrentAnalyzation: + continue + print("Expression Is Valid!") diff --git a/jarviscli/plugins/file_organise.py b/jarviscli/plugins/file_organise.py index dcbef6518..35b31884a 100644 --- a/jarviscli/plugins/file_organise.py +++ b/jarviscli/plugins/file_organise.py @@ -1,11 +1,12 @@ -from __future__ import print_function -from colorama import Fore import os import sys -from plugin import plugin, require, UNIX + +from colorama import Fore + +from plugin import Platform, plugin, require -@require(platform=UNIX) +@require(platform=Platform.UNIX) @plugin('file organise') class File_Organise(): """ diff --git a/jarviscli/plugins/football.py b/jarviscli/plugins/football.py index 293935d7d..ba49534d0 100644 --- a/jarviscli/plugins/football.py +++ b/jarviscli/plugins/football.py @@ -1,8 +1,8 @@ import requests -from tabulate import tabulate from colorama import Fore + from plugin import plugin, require -from utilities.animations import SpinnerThread +from tabulate import tabulate API_KEY = '1ebd3b92bf5041249f8c1e7a540ce98c' url = 'https://api.football-data.org/v2' @@ -209,10 +209,8 @@ def formatMatchInfo(self, match): if status != "SCHEDULED": # Get the score after 90 mins ordinary time scores = match["score"] - homeScore = scores["halfTime"]["homeTeam"] + \ - scores["fullTime"]["homeTeam"] - awayScore = scores["halfTime"]["awayTeam"] + \ - scores["fullTime"]["awayTeam"] + homeScore = scores["fullTime"]["homeTeam"] + awayScore = scores["fullTime"]["awayTeam"] lines.append("SCORE: {} - {}".format(homeScore, awayScore)) if scores["extraTime"]["homeTeam"] is not None: # Match went on to extra time diff --git a/jarviscli/plugins/game.py b/jarviscli/plugins/game.py index ba8e244e7..80deb3f5f 100644 --- a/jarviscli/plugins/game.py +++ b/jarviscli/plugins/game.py @@ -1,6 +1,8 @@ -from plugin import plugin, alias from random import randint -from colorama import Fore, Style + +from colorama import Fore + +from plugin import alias, plugin # function for generating 4-digit number diff --git a/jarviscli/plugins/geocode.py b/jarviscli/plugins/geocode.py index 261a113d7..8eb7f112a 100644 --- a/jarviscli/plugins/geocode.py +++ b/jarviscli/plugins/geocode.py @@ -1,6 +1,8 @@ import re + import requests from colorama import Fore + from plugin import plugin, require diff --git a/jarviscli/plugins/get_host_info.py b/jarviscli/plugins/get_host_info.py new file mode 100644 index 000000000..b60970274 --- /dev/null +++ b/jarviscli/plugins/get_host_info.py @@ -0,0 +1,86 @@ +from whois import query, exceptions +from pprint import pprint +from os import popen +from re import findall +from plugin import plugin, require, Platform + + +# https://pypi.org/project/nslookup/ +def ns_lookup(host): + ping_str = 'nslookup ' + host + stream = popen(ping_str) + output = stream.read() + return output + + +def whois_lookup(host): + try: + domain = query(host) + return domain + except exceptions.UnknownTld: + return None + + +def ping(host): + ping_str = 'ping ' + host + ' -c1' + stream = popen(ping_str) + output = stream.read() + return output + + +# cutom regex to extract (sub)domain from string and prevents cmd injection +def sanitize_host(host, jarvis, s): + try: + domain_regex = (r'([a-z,A-Z,0-9,.]+[.][a-z,A-Z]{1,9})') + host = findall(domain_regex, host)[0] + except IndexError: + try: + ip_regex = (r'\b(?:' + r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' + r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)') + host = findall(ip_regex, host)[0] + except IndexError: + jarvis.say('\nInvalid Hostname or IP') + get_host_info(jarvis, s) + return None + return host + + +def get_host_info(jarvis, s): + green = "\x1b[1;32m" + white = "\x1b[1;37m" + + host = input(white + 'Enter Domain Name or IP Address: ') + if host is not None: + if host.lower() == "q" or host.lower() == "quit": + return None + + host = sanitize_host(host, jarvis, s) + if host is None: + return None + + # nslookup + jarvis.say("\n" + green + "nslookup on " + host + ":" + white) + response = ns_lookup(host) + jarvis.say(response) + + # whois lookup + jarvis.say(green + "whois Lookup on " + host + ":" + white) + response = whois_lookup(host) + if response: + pprint(response.__dict__) + else: + jarvis.say("Could not run whois lookup on host " + host) + + # ping host + jarvis.say("\n" + green + "ping " + host + ":" + white) + response = ping(host) + jarvis.say(response) + + return None + + +@require(platform=Platform.UNIX, network=True, native=['whois', 'nslookup', 'ping']) +@plugin("hostinfo") +def main(jarvis, s): + get_host_info(jarvis, s) diff --git a/jarviscli/plugins/get_joke.py b/jarviscli/plugins/get_joke.py new file mode 100644 index 000000000..93ab0151d --- /dev/null +++ b/jarviscli/plugins/get_joke.py @@ -0,0 +1,12 @@ +import pyjokes +from colorama import Fore +from plugin import plugin, require + + +@require(network=True) +@plugin('joke') +def joke(jarvis, s): + """Tells a random joke""" + joke = pyjokes.get_joke() + + jarvis.say(joke, Fore.BLUE) diff --git a/jarviscli/plugins/gmail.py b/jarviscli/plugins/gmail.py index 8004bbdd9..e825c8dda 100644 --- a/jarviscli/plugins/gmail.py +++ b/jarviscli/plugins/gmail.py @@ -1,5 +1,6 @@ -import smtplib # import stmplib -from plugin import plugin # import plugin +import smtplib # import stmplib + +from plugin import plugin # import plugin @plugin('gmail') # decorator diff --git a/jarviscli/plugins/google.py b/jarviscli/plugins/google.py index bc2de9f8d..38f173fb6 100644 --- a/jarviscli/plugins/google.py +++ b/jarviscli/plugins/google.py @@ -1,6 +1,7 @@ -import requests import bs4 +import requests from colorama import Fore + from plugin import plugin, require diff --git a/jarviscli/plugins/hackathon.py b/jarviscli/plugins/hackathon.py index aea203e11..bf37a1956 100644 --- a/jarviscli/plugins/hackathon.py +++ b/jarviscli/plugins/hackathon.py @@ -1,6 +1,7 @@ +import json + import bs4 import requests -import json from plugin import plugin, require diff --git a/jarviscli/plugins/hangman.py b/jarviscli/plugins/hangman.py new file mode 100644 index 000000000..97a0d8d7c --- /dev/null +++ b/jarviscli/plugins/hangman.py @@ -0,0 +1,132 @@ +from plugin import plugin +from random_word import RandomWords + + +@plugin('hangman') +def hangman(jarvis, s): + initialText = "#########################################\n" \ + "# Hello Hangman Game Is About To Begin! #\n" \ + "# Guesses Should Be Characters! #\n" \ + "# Type 'stop' To End Game! #\n" \ + "#########################################\n" + print(initialText) + randomWords = RandomWords() + terminateFlag = 0 + + while not terminateFlag: + lives = 8 + usedLetters = "" + actualWordToGuess = "" + while len(actualWordToGuess) < 4: + try: + actualWordToGuess = randomWords.get_random_word() + except BaseException: + continue + actualWordToGuess = actualWordToGuess.lower() + wordToGuess = "" + for x in range(len(actualWordToGuess)): + wordToGuess = wordToGuess + "_" + while True: + if lives == 0: + print("You Lost!\n") + break + if actualWordToGuess == wordToGuess: + print("You Won!\n") + break + print("Word To Guess Looks Like This : " + wordToGuess + "\n") + guess = input("Enter Your Guess : ") + print("\n") + guess = guess.strip() + guess = guess.lower() + if guess == "stop": + print("You Stopped Playing Hangman!\n") + break + if len(guess) == 0: + print("Woops! You Have Not Entered Anything\n") + lives = lives - 1 + print("Penalty! Lives Decrease By 1, Remains : " + str(lives) + "\n") + drawStickMan(8 - lives) + continue + if len(guess) > 1: + print("Woops! You Have Entered Input Longer Than Character Size\n") + lives = lives - 1 + print("Penalty! Lives Decrease By 1, Remains : " + str(lives) + "\n") + drawStickMan(8 - lives) + continue + if guess in usedLetters: + print("Woops! You Have Entered Letter That Is Already Used\n") + lives = lives - 1 + print("Penalty! Lives Decrease By 1, Remains : " + str(lives) + "\n") + drawStickMan(8 - lives) + continue + if guess not in actualWordToGuess: + print("Woops! You Have Entered Wrong Guess\n") + lives = lives - 1 + print("Penalty! Lives Decrease By 1, Remains : " + str(lives) + "\n") + drawStickMan(8 - lives) + continue + if guess.lower() in actualWordToGuess: + print("YES! You Have Entered Correct Guess\n") + drawStickMan(8 - lives) + + usedLetters = usedLetters + guess + newWordToGuess = "" + for position in range(len(actualWordToGuess)): + if actualWordToGuess[position] == guess: + newWordToGuess = newWordToGuess + guess + else: + newWordToGuess = newWordToGuess + wordToGuess[position] + wordToGuess = newWordToGuess + + print("Word To Guess Was : " + actualWordToGuess.upper()) + terminateFlag = continueOrNot() + + goodByeText = "#########################################\n" \ + "# Farewell! #\n" \ + "# May The Force Be With You! #\n" \ + "#########################################\n" + print(goodByeText) + + +def continueOrNot(): + terminationFlag = 0 + desire = input("Do You Want To Play Again? (Y/N) ") + print("\n") + desire = desire.strip() + desire = desire.lower() + while desire != "n" and desire != "y": + desire = input("Enter proper answer! (Y/N) ") + print("\n") + desire = desire.strip() + desire = desire.lower() + if desire == "n": + terminationFlag = 1 + elif desire == "y": + print("Hangman Game Resets!\n") + return terminationFlag + + +def drawStickMan(phaseMain): + stickman = "" + phase = 0 + while phase != phaseMain: + if phase == 0: + stickman = stickman + " |---\n" + stickman = stickman + " | '\n" + stickman = stickman + " | O\n" + elif phase == 1: + stickman = stickman + " | --|--\n" + elif phase == 2: + stickman = stickman + " |' | '\n" + elif phase == 3: + stickman = stickman + " | | \n" + elif phase == 4: + stickman = stickman + " | ---\n" + elif phase == 5: + stickman = stickman + " | | |\n" + elif phase == 6: + stickman = stickman + " | | |\n" + elif phase == 7: + stickman = stickman + " |__ __\n" + phase = phase + 1 + print(stickman) diff --git a/jarviscli/plugins/history.py b/jarviscli/plugins/history.py index c3e4eb67d..7e21273e6 100644 --- a/jarviscli/plugins/history.py +++ b/jarviscli/plugins/history.py @@ -1,10 +1,11 @@ -from plugin import plugin, require -import requests import datetime import random -import json + +import requests from colorama import Fore +from plugin import plugin, require + @require(network=True) @plugin('history') @@ -223,7 +224,7 @@ def _get_data(self, jarvis, query, api_cfg): 'links': fact['links'] } jarvis.spinner_stop() - except: + except BaseException: jarvis.spinner_stop( message="\nTask execution Failed!", color=Fore.RED) jarvis.say( diff --git a/jarviscli/plugins/hotspot.py b/jarviscli/plugins/hotspot.py index f12575446..7a7dec8b8 100644 --- a/jarviscli/plugins/hotspot.py +++ b/jarviscli/plugins/hotspot.py @@ -1,8 +1,9 @@ from os import system -from plugin import plugin, require, LINUX +from plugin import Platform, plugin, require -@require(network=True, platform=LINUX, native=["ap-hotspot", "sudo"]) + +@require(network=True, platform=Platform.LINUX, native=["ap-hotspot", "sudo"]) @plugin('hotspot start') def hotspot_start(jarvis, string): """ @@ -11,7 +12,7 @@ def hotspot_start(jarvis, string): system("sudo ap-hotspot start") -@require(network=True, platform=LINUX, native=["ap-hotspot", "sudo"]) +@require(network=True, platform=Platform.LINUX, native=["ap-hotspot", "sudo"]) @plugin('hotspot stop') def hotspot_stop(jarvis, string): """ diff --git a/jarviscli/plugins/htmltopdf.py b/jarviscli/plugins/htmltopdf.py index a38d9ad8b..b63a239e9 100644 --- a/jarviscli/plugins/htmltopdf.py +++ b/jarviscli/plugins/htmltopdf.py @@ -1,16 +1,17 @@ import pdfkit -from plugin import plugin, require, LINUX +from plugin import Platform, plugin, require -@require(platform=LINUX, native=["wkhtmltopdf"]) +@require(platform=Platform.LINUX, native=["wkhtmltopdf"]) @plugin("htmltopdf") class htmltopdf: """Convert your html file or web page into pdf file""" + def __call__(self, jarvis, s): jarvis.say("Welcome to the htmltopdf convertor! \nType 'help htmltopdf' to learn how to use it") -@require(platform=LINUX, native=["wkhtmltopdf"]) +@require(platform=Platform.LINUX, native=["wkhtmltopdf"]) @plugin("htmltopdf file") class htmltopdf_file: """ @@ -21,6 +22,7 @@ class htmltopdf_file: 'example.pdf' Your html file must be in the jarvis source directory """ + def __call__(self, jarvis, s): if not s: jarvis.say("please enter a file name after calling the plugin") @@ -33,7 +35,7 @@ def __call__(self, jarvis, s): jarvis.say("OS error: {0}".format(err) + "\nMake sur your file is in the source directory of Jarvis and is an html file") -@require(platform=LINUX, native=["wkhtmltopdf"], network=True) +@require(platform=Platform.LINUX, native=["wkhtmltopdf"], network=True) @plugin("htmltopdf url") class htmltopdf_url: """ @@ -42,6 +44,7 @@ class htmltopdf_url: The output file will be the following: 'google.com.pdf' """ + def __call__(self, jarvis, s): if not s: jarvis.say("please enter an url after calling the plugin") diff --git a/jarviscli/plugins/imgtopdf.py b/jarviscli/plugins/imgtopdf.py new file mode 100644 index 000000000..33979b1e6 --- /dev/null +++ b/jarviscli/plugins/imgtopdf.py @@ -0,0 +1,114 @@ +import img2pdf +from PIL import Image +import os +from plugin import plugin +from colorama import Fore + + +@plugin('image to pdf') +class ImageToPDF: + """ + A tool to converrt images to pdf file + """ + + def __init__(self): + # Path of the folder or image to be converted + self.path = None + self.image = None + + def __call__(self, jarvis, s): + self.imgtopdf(jarvis) + + def imgtopdf(self, jarvis): + jarvis.say('') + jarvis.say('This tool will help you convert image to pdf') + while True: + + self.available_options(jarvis) + user_input = jarvis.input('Your choice: ') + user_input = user_input.lower() + + if user_input == 'q' or user_input == 'quit' or user_input == '3': + jarvis.say("See you next time :D", Fore.CYAN) + break + + # For single image to be converted to pdf + elif user_input == '1': + while True: + image_path = jarvis.input( + 'Enter the full path of the image: ') + if os.path.exists(image_path) and (image_path.endswith('.jpg') or image_path.endswith('.png')): + break + else: + jarvis.say( + 'Opps! Looks like you entered an invalid path. Kindly Re-enter', Fore.RED) + pdf_bytes = self.single_image_to_pdf(jarvis, image_path) + + # For multiple images in a folder to be converted to pdf + elif user_input == '2': + while True: + folder_path = jarvis.input( + 'Enter the full path of the folder: ') + if os.path.exists(folder_path): + break + else: + jarvis.say( + 'Opps! Looks like you entered an invalid path. Kindly Re-enter', Fore.RED) + pdf_bytes = self.folder_to_pdf(jarvis, folder_path) + + # For an incorrectly entered option + else: + jarvis.incorrect_option() + continue + + destination = jarvis.get_saving_directory(self.path) + # Naming and saving the pdf file + file_name = jarvis.input('What would you like to name your pdf? ') + pdf_destination = destination + '/' + file_name + '.pdf' + print('Final Destination ' + pdf_destination) + self.save_pdf(jarvis, pdf_bytes, pdf_destination) + + def available_options(self, jarvis): + """ + Message displayed to prompt the user about converting + images to pdf + """ + jarvis.say('Select one of the following options:') + jarvis.say('1: Convert a single image') + jarvis.say('2: Convert all images of the folder') + jarvis.say('3: Quit') + + def single_image_to_pdf(self, jarvis, image_path): + """ + This function is used to convert a single image + with a given path to a pdf file. + """ + self.path = image_path + self.image = Image.open(image_path) + pdf_bytes = img2pdf.convert(self.image.filename) + self.image.close() + return pdf_bytes + + def folder_to_pdf(self, jarvis, folder_path): + """ + This function is used to convert all the images + in a given folder path to a single PDF file + """ + self.path = folder_path + source_images = [] + os.chdir(self.path) + for image in os.listdir(os.getcwd()): + if image.endswith('.jpg') or image.endswith('.png'): + source_images.append(image) + pdf_bytes = img2pdf.convert(source_images) + return pdf_bytes + + def save_pdf(self, jarvis, pdf_bytes, destination): + """ + Save the pdf to the thus supplied location + or prompt the user to choose a new location + """ + pdf_file = open(destination, 'wb') + pdf_file.write(pdf_bytes) + pdf_file.close() + jarvis.say('Your pdf is created successfully', Fore.GREEN) diff --git a/jarviscli/plugins/imgur.py b/jarviscli/plugins/imgur.py index d3b36d5f4..3a1878653 100644 --- a/jarviscli/plugins/imgur.py +++ b/jarviscli/plugins/imgur.py @@ -1,11 +1,13 @@ -from plugin import plugin, require -from utilities.GeneralUtilities import IS_WIN -import os -import sys -import requests -import json import base64 import glob +import json +import os + +import requests + +from plugin import plugin, require +from utilities.GeneralUtilities import IS_WIN + if IS_WIN: from pyreadline import Readline readline = Readline() diff --git a/jarviscli/plugins/internet_control.py b/jarviscli/plugins/internet_control.py new file mode 100644 index 000000000..5cee7d20f --- /dev/null +++ b/jarviscli/plugins/internet_control.py @@ -0,0 +1,42 @@ +from plugin import plugin +import sys +import os +import socket + + +def has_internet(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + try: + socket.setdefaulttimeout(3) + sock.connect(('8.8.8.8', 8000)) + + return True + + except socket.timeout: + return False + + +@plugin("internet") +def internet(jarvis, s): + connected = has_internet() + + if connected: + if sys.platform == "win32": + os.system("netsh interface set interface \"Wi-Fi\" DISABLED") + + elif sys.platform == "linux": + os.system("nmcli radio wifi off") + + elif sys.platform == "darwin": + os.system("networksetup -setairportpower en0 off") + + else: + if sys.platform == "win32": + os.system("netsh interface set interface \"Wi-Fi\" ENABLED") + + elif sys.platform == "linux": + os.system("nmcli radio wifi on") + + elif sys.platform == "darwin": + os.system("networksetup -setairportpower en0 on") \ No newline at end of file diff --git a/jarviscli/plugins/ip.py b/jarviscli/plugins/ip.py index 2fc3078f1..397e3cef1 100644 --- a/jarviscli/plugins/ip.py +++ b/jarviscli/plugins/ip.py @@ -1,11 +1,12 @@ from os import system + from colorama import Fore +from plugin import Platform, plugin, require from utilities.GeneralUtilities import executable_exists -from plugin import plugin, require, UNIX, WINDOWS -@require(platform=UNIX) +@require(platform=Platform.UNIX) @plugin('ip') class IP(): """ @@ -38,7 +39,7 @@ def _get_public_ip(self, jarvis): system(self._public_ip_v6) -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('ip') def ip_WIN32(jarvis, s): """ diff --git a/jarviscli/plugins/jarvis_server.py b/jarviscli/plugins/jarvis_server.py new file mode 100644 index 000000000..20e08d07a --- /dev/null +++ b/jarviscli/plugins/jarvis_server.py @@ -0,0 +1,184 @@ +from multiprocessing import Process +from plugin import Platform, require, plugin + + +@require(network=True, platform=Platform.MACOS) +@plugin("server start") +def server_start(jarvis, s): + jarvis_server = jarvis.get_server() + + if s != "": + if ":" in s: + jarvis_server.server_host, jarvis_server.port = s.split(":")[0], int(s.split(":")[1]) + else: + jarvis_server.server_host, jarvis_server.port = s.split(" ")[0], int(s.split(" ")[1]) + + jarvis.server_thread = Process(target=jarvis_server.start_server) + jarvis.server_thread.start() + + +@require(network=True, platform=Platform.MACOS) +@plugin("server stop") +def server_stop(jarvis, s): + if not hasattr(jarvis, "server_thread") or jarvis.server_thread is not None: + jarvis.say("No server is running.") + return + # server.stop_server(jarvis) + # print(jarvis.server_thread.isAlive()) + jarvis.server_thread.terminate() + jarvis.server_thread.join() + jarvis.server_thread = None + + +@require(network=True, platform=Platform.MACOS) +@plugin("server restart") +def server_restart(jarvis, s): + server_stop(jarvis, s) + server_start(jarvis, s) + +# +# +# @require(network=True) +# @plugin("home server") +# class HomeServer(BaseHTTPRequestHandler): +# +# def __init__(self, request, client_address, server): +# super().__init__(request, client_address, server) +# self.devices = dict() +# self.app_logins = dict() +# +# def do_POST(self): +# if self.request_handler.headers["Auth"] != auth: +# self._return_unauthorized() +# else: +# content_len = int(self.headers.get('Content-Length')) +# device_type = self.headers.get('User-Agent') +# post_body = self.rfile.read(content_len) +# print(self.headers.get('Device-Agent') + " found!") +# if device_type.contains("ESP8266"): +# self._register_device(self.headers, post_body) +# self._respond(200, '') +# else: +# response_type = self._respond_to_agent(self.headers, post_body) +# if response_type == "Success": +# self._respond(200, response_type) +# elif response_type is not None: +# self._respond(404, response_type) +# +# def _respond(self, code: int, message: str): +# self.send_response(code) +# self.send_header("Auth", auth) +# self.end_headers() +# self.wfile.write(message.encode('utf-8')) +# +# def _register_device(self, headers, body): +# agent = headers["Device-Agent"] +# self.devices[agent] = dict(json.loads(body.decode('utf-8'))) +# self.devices[agent]["counter"] = 0 +# self.devices[agent]["connected"] = True +# self.devices[agent]["thread"] = Thread(target=self._handle_heartbeat, +# args=(agent,)) +# self.devices[agent]["thread"].start() +# +# def _respond_to_agent(self, headers, body): +# agent = headers["Device-Agent"] +# self.app_logins[agent] = dict(json.loads(body.decode('utf-8'))) +# self.app_logins[agent]["connected"] = True +# return self._handle_app_request(agent) +# +# def _handle_app_request(self, agent: str): +# app_request_type = self.app_logins[agent]["request_type"] +# +# if app_request_type == 'intro': +# return self._send_intro_to_app() +# +# return self._respond_to_app_request(agent) +# +# def _send_intro_to_app(self): +# app_intro = dict() +# app_intro["devices"] = self.get_connected_devices() +# app_intro["users"] = self.get_connected_users() +# return json.dumps(app_intro) +# +# def _respond_to_app_request(self, agent: str): +# app_request = self.app_logins[agent]["request"] +# device = app_request["device"] +# device_switch = app_request["switch"] +# device_switch_request = device_switch["request"] +# device_switch_value = device_switch["value"] +# if not self.devices[device]["connected"]: +# return "Device disconnected" +# device_url = self.devices[device]["localIP"] +# device_headers = dict({"Auth": auth}) +# +# try: +# request = requests.get(device_url + "/" + device_switch_request + +# "/" + device_switch_value, headers=device_headers) +# except requests.exceptions.ConnectionError as ce: +# print("Device Connection Error: " + agent) +# self.devices[agent]["connected"] = False +# return "Device disconnected" +# return "Success" +# +# def _handle_heartbeat(self, agent: str): +# device_url = self.devices[agent]["localIP"] +# while self.devices[agent]["connected"]: +# device_headers = dict({"Auth": auth}) +# try: +# request = requests.get(device_url + "/heartbeat", +# headers=device_headers) +# self.devices[agent]["counter"] = 0 +# except requests.exceptions.ConnectionError as ce: +# print(ce) +# print("Device Connection Error: " + agent) +# self.devices[agent]["counter"] += 1 +# if self.devices[agent]["counter"] >= 3: +# self.devices[agent]["connected"] = False +# time.sleep(5) +# +# def get_connected_devices(self): +# connected_devices = [device for device in self.devices.keys() +# if self.devices[device]["connected"]] +# print(connected_devices) +# return connected_devices +# +# def get_connected_users(self): +# connected_users = [user for user in self.app_logins.keys() +# if self.app_logins[user]["connected"]] +# return connected_users +# +# def _return_unauthorized(self): +# self.send_response(403) +# self.send_header("Status Code", 403) +# self.end_headers() +# self.wfile.write(b'Unauthorized') +# +# +# @require(network=True, platform=MACOS) +# @plugin('home server start') +# def home_server(jarvis, s): +# web_server = HTTPServer((hostName, serverPort), HomeServer) +# web_server.serve_forever() +# jarvis.say("Server started http://%s:%s" % (hostName, serverPort)) +# +# +# @require(network=True, platform=MACOS) +# @plugin('home server connections') +# def home_server_get_connections(jarvis, s): +# if web_server is None: +# jarvis.say("Unable to find a running server. Did you start a Server ?") +# return +# connections = dict() +# connections["devices"] = web_server.get_connected_devices() +# connections["users"] = web_server.get_connected_users() +# return json.dumps(connections) +# +# +# @require(network=True, platform=MACOS) +# @plugin('home server stop') +# def home_server_stop(jarvis, s): +# if web_server is None: +# jarvis.say("Unable to stop server. Did you start a Server ?") +# return +# web_server.server_close() +# jarvis.say("Server killed!") diff --git a/jarviscli/plugins/joke_of_day.py b/jarviscli/plugins/joke_of_day.py index 5edb81ecb..d02d0c595 100644 --- a/jarviscli/plugins/joke_of_day.py +++ b/jarviscli/plugins/joke_of_day.py @@ -1,7 +1,8 @@ -from plugin import plugin, require import requests from colorama import Fore +from plugin import plugin, require + @require(network=True) @plugin('joke daily') @@ -28,7 +29,7 @@ def get_joke(self, jarvis): jarvis.spinner_start('Fetching') r = requests.get(url) if r is None: - spinner.stop() + jarvis.spinner_stop() jarvis.say( "Error in fetching joke - try again! later", Fore.RED) jarvis.spinner_stop() diff --git a/jarviscli/plugins/lengthconv.py b/jarviscli/plugins/lengthconv.py index e96bd8041..3a8b13c94 100644 --- a/jarviscli/plugins/lengthconv.py +++ b/jarviscli/plugins/lengthconv.py @@ -1,4 +1,5 @@ from __future__ import division + from plugin import plugin diff --git a/jarviscli/plugins/location.py b/jarviscli/plugins/location.py index a14363dda..6f09485e1 100644 --- a/jarviscli/plugins/location.py +++ b/jarviscli/plugins/location.py @@ -1,6 +1,7 @@ -import requests import json -from colorama import Fore + +import requests + from plugin import plugin, require diff --git a/jarviscli/plugins/lyrics.py b/jarviscli/plugins/lyrics.py index af5173332..debfbc883 100644 --- a/jarviscli/plugins/lyrics.py +++ b/jarviscli/plugins/lyrics.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -import requests import bs4 +import requests + from plugin import plugin, require # TODO: handle errors and instructions better diff --git a/jarviscli/plugins/mars_weather.py b/jarviscli/plugins/mars_weather.py new file mode 100644 index 000000000..e969fbcbc --- /dev/null +++ b/jarviscli/plugins/mars_weather.py @@ -0,0 +1,93 @@ +import requests +from colorama import Fore +from plugin import plugin, require +import datetime + +API_KEY = 'OsZ9DfFtdgR6zPlVjxFTch1Np5zAcqt9g9i34ga2' +headers = {'X-Auth-Token': API_KEY} +url = 'https://api.nasa.gov/insight_weather/?api_key=OsZ9DfFtdgR6zPlVjxFTch1Np5zAcqt9g9i34ga2&feedtype=json&ver=1.0' + + +@require(network=True) +@plugin('mars weather') +def mars_weather(jarvis, s): + print("") + jarvis.say("~> I'll show you MARS weather forecast", Fore.RED) + print("") + + r = fetch() + x = r['sol_keys'] + + count = 0 + + for i in x: + dt = str(datetime.date.today() + datetime.timedelta(days=count)) + jarvis.say(dt, Fore.BLUE) + + season = "SEASON: " + r[i]['Season'] + jarvis.say(season, Fore.BLUE) + print("") + + get_temperature(jarvis, r, i) + print("") + + get_wind(jarvis, r, i) + print("") + + get_pressure(jarvis, r, i) + print("") + + count += 1 + + +def fetch(): + r = requests.get(url, headers=headers) + r = r.json() + if "errorCode" in r.keys(): + return None + return r + + +def get_temperature(jarvis, fulldata, sol): + jarvis.say("TEMPERATURE:", Fore.RED) + temperature = fulldata[sol]['AT'] + + avg = "AVG temp: " + str(round(temperature['av'], 1)) + " ºF" + ct = "Recorded samples: " + str(temperature['ct']) + min_temp = "MIN temp: " + str(round(temperature['mn'], 1)) + " ºF" + max_temp = "MAX temp: " + str(round(temperature['mx'], 1)) + " ºF" + + jarvis.say(avg, Fore.RED) + jarvis.say(min_temp, Fore.RED) + jarvis.say(max_temp, Fore.RED) + jarvis.say(ct, Fore.RED) + + +def get_wind(jarvis, fulldata, sol): + jarvis.say("WIND:", Fore.GREEN) + wind = fulldata[sol]['HWS'] + + avg = "AVG speed: " + str(round(wind['av'], 1)) + " mph" + ct = "Recorded samples: " + str(wind['ct']) + min_speed = "MIN speed: " + str(round(wind['mn'], 1)) + " mph" + max_speed = "MAX speed: " + str(round(wind['mx'], 1)) + " mph" + + jarvis.say(avg, Fore.GREEN) + jarvis.say(min_speed, Fore.GREEN) + jarvis.say(max_speed, Fore.GREEN) + jarvis.say(ct, Fore.GREEN) + + +def get_pressure(jarvis, fulldata, sol): + jarvis.say("PRESSURE:", Fore.YELLOW) + pressure = fulldata[sol]['PRE'] + + avg = "AVG pressure: " + str(round(pressure['av'], 1)) + " Pa" + ct = "Recorded samples: " + str(pressure['ct']) + min_pressure = "MIN pressure: " + str(round(pressure['mn'], 1)) + " Pa" + max_pressure = "MAX pressure: " + str(round(pressure['mx'], 1)) + " Pa" + + jarvis.say(avg, Fore.YELLOW) + jarvis.say(min_pressure, Fore.YELLOW) + jarvis.say(max_pressure, Fore.YELLOW) + jarvis.say(ct, Fore.YELLOW) diff --git a/jarviscli/plugins/massconv.py b/jarviscli/plugins/massconv.py index ca6b67f17..cae7cb9fa 100644 --- a/jarviscli/plugins/massconv.py +++ b/jarviscli/plugins/massconv.py @@ -1,4 +1,3 @@ -from __future__ import division from plugin import plugin diff --git a/jarviscli/plugins/match.py b/jarviscli/plugins/match.py index 00d572030..8895ac465 100644 --- a/jarviscli/plugins/match.py +++ b/jarviscli/plugins/match.py @@ -1,5 +1,7 @@ from os import system + from colorama import Fore + from plugin import plugin, require diff --git a/jarviscli/plugins/matrix_add.py b/jarviscli/plugins/matrix_add.py new file mode 100644 index 000000000..4d25da179 --- /dev/null +++ b/jarviscli/plugins/matrix_add.py @@ -0,0 +1,45 @@ +from plugin import plugin +from colorama import Fore + + +def get_matrix(jarvis, r, c): + matrix = [] + for i in range(r): + row_str = jarvis.input("enter row #{:d}: ".format(i)) + cur_row = [int(n) for n in row_str.split()] + while len(cur_row) != c: + jarvis.say("Row length should be {:d}".format(c)) + row_str = jarvis.input("enter row #{:d}: ".format(i)) + cur_row = [int(n) for n in row_str.split()] + matrix.append(cur_row) + return matrix + + +def print_matrix(matrix, r, c): + for i in range(r): + print(matrix[i]) + + +@plugin("matrix add") +def matrix_add(jarvis, s): + jarvis.say("Sum of matrices with dimensions M x N", Fore.GREEN) + + row = int(jarvis.input_number('Enter M (rows): ')) + col = int(jarvis.input_number('Enter N (cols): ')) + + initial = get_matrix(jarvis, row, col) + + while True: + next_decision = jarvis.input("Continue with next matrix? ") + if next_decision.lower() == 'no': + break + next_matrix = get_matrix(jarvis, row, col) + for i in range(row): + for j in range(col): + initial[i][j] += next_matrix[i][j] + current_decision = jarvis.input("Print current sum matrix? ") + if current_decision.lower() != 'no': + print_matrix(initial, row, col) + + jarvis.say('Final sum matrix:') + print_matrix(initial, row, col) diff --git a/jarviscli/plugins/motivate.py b/jarviscli/plugins/motivate.py new file mode 100644 index 000000000..9555d4049 --- /dev/null +++ b/jarviscli/plugins/motivate.py @@ -0,0 +1,28 @@ +from plugin import plugin +from colorama import Fore + + +@plugin("motivate me") +def motivate(jarvis, s): + quotes = ["You're gonna wake up and work hard at it", "Don't let you're dreams be dreams", + "Make your dreams come true", "Nothing is impossible"] + quotes += ['Yes you can', 'JUST DO IT!', 'Just do it', 'Stop giving up', 'Yesterday you said tommorow', + 'You should get to the point where anyone else quits and you\'re not gonna stop there'] + quotes += ['Make your dreams come true', 'Nothing is impossible', + 'Go what are you waiting for', 'JUST DO IT', 'Make your dreams come true', 'Just do it'] + + ind = 0 + + jarvis.say("Enter \'n\' to print nextquote : ", Fore.GREEN) + + while True: + motivational_quote = quotes[ind] + jarvis.say(motivational_quote, Fore.GREEN) + user_input = jarvis.input() + if (user_input != 'n'): + break + ind += 1 + if (ind == len(quotes)): + break + + jarvis.say('That\'s it, go and work now!') diff --git a/jarviscli/plugins/movie.py b/jarviscli/plugins/movie.py index 3c1281e65..ba400659f 100644 --- a/jarviscli/plugins/movie.py +++ b/jarviscli/plugins/movie.py @@ -1,7 +1,9 @@ -import imdb +from functools import lru_cache + from colorama import Fore, Style + +import imdb from plugin import plugin, require -from functools import lru_cache app = imdb.IMDb() diff --git a/jarviscli/plugins/movies.py b/jarviscli/plugins/movies.py index c8d9afebb..c0cd137fb 100644 --- a/jarviscli/plugins/movies.py +++ b/jarviscli/plugins/movies.py @@ -1,9 +1,11 @@ import os + from colorama import Fore -from plugin import plugin, require, LINUX + +from plugin import Platform, plugin, require -@require(platform=LINUX, native='ims') +@require(platform=Platform.LINUX, native='ims') @plugin('movies') def movies(jarvis, s): """Jarvis will find a good movie for you""" diff --git a/jarviscli/plugins/music.py b/jarviscli/plugins/music.py index dbf8d78be..fe6533b4f 100644 --- a/jarviscli/plugins/music.py +++ b/jarviscli/plugins/music.py @@ -1,7 +1,8 @@ import os + from colorama import Fore -from plugin import plugin, alias, require, LINUX +from plugin import Platform, alias, plugin, require def find_cached_music(music): @@ -13,7 +14,7 @@ def find_cached_music(music): @alias("music") -@require(platform=LINUX) +@require(platform=Platform.LINUX) @plugin('play') def play(jarvis, data): """ diff --git a/jarviscli/plugins/myinfo.py b/jarviscli/plugins/myinfo.py index f59c536d5..4dd6ad6a3 100644 --- a/jarviscli/plugins/myinfo.py +++ b/jarviscli/plugins/myinfo.py @@ -1,6 +1,7 @@ -from plugin import plugin import csv +from plugin import plugin + @plugin("myinfo") def myinfo(jarvis, s): diff --git a/jarviscli/plugins/new_year.py b/jarviscli/plugins/new_year.py new file mode 100644 index 000000000..f5ebdaf67 --- /dev/null +++ b/jarviscli/plugins/new_year.py @@ -0,0 +1,16 @@ +from plugin import plugin + + +@plugin("christmas-tree") +def new_year(jarvis, data): + tree = " *\n"\ + " ###\n"\ + " #####\n"\ + " ###\n"\ + " #####\n"\ + " #######\n"\ + " #####\n"\ + " #######\n"\ + " ###\n"\ + " ###" + jarvis.say(tree) diff --git a/jarviscli/plugins/news.py b/jarviscli/plugins/news.py index bd98f4042..a245d661b 100644 --- a/jarviscli/plugins/news.py +++ b/jarviscli/plugins/news.py @@ -1,9 +1,11 @@ # !!! This uses the https://newsapi.org/ api. TO comply with the TOU # !!! we must link back to this site whenever we display results. import json -import requests import webbrowser + +import requests from colorama import Fore + from plugin import plugin, require @@ -75,7 +77,7 @@ def __call__(self, jarvis, s): jarvis.say("Missing API key", Fore.RED) jarvis.say("Visit https://newsapi.org/ to get the key", Fore.RED) jarvis.say( - "Use \'news updatekey\' command to add a key\n", + "Use 'news updatekey' command to add a key\n", Fore.RED) elif s == "" or s == " ": self.parse_articles(self.get_headlines(jarvis), jarvis) diff --git a/jarviscli/plugins/numbersapi.py b/jarviscli/plugins/numbersapi.py index fa3c97b9a..8aaf3acee 100644 --- a/jarviscli/plugins/numbersapi.py +++ b/jarviscli/plugins/numbersapi.py @@ -1,5 +1,6 @@ import requests from colorama import Fore + from plugin import plugin, require diff --git a/jarviscli/plugins/open.py b/jarviscli/plugins/open.py index f9583b8d1..0ba70749d 100644 --- a/jarviscli/plugins/open.py +++ b/jarviscli/plugins/open.py @@ -1,9 +1,9 @@ import os -from plugin import LINUX, plugin, require +from plugin import Platform, plugin, require -@require(platform=LINUX) +@require(platform=Platform.LINUX) @plugin('open') def open(jarvis, s): diff --git a/jarviscli/plugins/pdftoimg.py b/jarviscli/plugins/pdftoimg.py new file mode 100644 index 000000000..3bbe96dfa --- /dev/null +++ b/jarviscli/plugins/pdftoimg.py @@ -0,0 +1,80 @@ +import os +from plugin import plugin +from colorama import Fore +from pdf2image import convert_from_path + + +@plugin('pdf to images') +class PdfToImage: + """ + A tool for converting and storing all the + pages of a pdf in form of a images in a folder + """ + + def __init__(self): + self.path = None + + def __call__(self, jarvis, s): + self.pdf_to_img(jarvis) + + def pdf_to_img(self, jarvis): + jarvis.say('') + jarvis.say('This tool will help you convert pdf to images') + while True: + self.available_options(jarvis) + user_input = jarvis.input('Your choice: ') + user_input = user_input.lower() + + # For quiting the program + if user_input == 'q' or user_input == 'quit' or user_input == '2': + jarvis.say("See you next time :D", Fore.CYAN) + break + + # Converting a pdf with a given path to image + elif user_input == '1': + while True: + pdf_path = jarvis.input('Enter the full path of the pdf: ') + if os.path.exists(pdf_path) and (pdf_path.endswith('.pdf')): + break + else: + jarvis.say( + 'Opps! Looks like you entered an invalid path. Kindly Re-enter', Fore.RED) + pages = self.convert_to_images(pdf_path, jarvis) + + # For an incorrectly entered option + else: + jarvis.incorrect_option() + continue + + destination = jarvis.get_saving_directory(self.path) + self.save_images(pages, destination, jarvis) + + def convert_to_images(self, pdf_path, jarvis): + """ + Convert all the pages in the pdf to individual + pages option and return it + """ + self.path = pdf_path + pages = convert_from_path(pdf_path) + return pages + + def available_options(self, jarvis): + """ + Message displayed to prompt the user about converting + pdf to image + """ + jarvis.say('Select one of the following options:') + jarvis.say('1: Convert pdf to images') + jarvis.say('2: Quit') + + def save_images(self, pages, destination, jarvis): + """ + Save the thus generated images to the destination + specified + """ + page_count = 1 + for page in pages: + page.save('page_' + str(page_count) + '.jpg', 'JPEG') + page_count += 1 + + jarvis.say('Your images are saved successfully', Fore.GREEN) diff --git a/jarviscli/plugins/personality.py b/jarviscli/plugins/personality.py new file mode 100644 index 000000000..cc027b1a0 --- /dev/null +++ b/jarviscli/plugins/personality.py @@ -0,0 +1,99 @@ +import os +import random +from colorama import Fore, Back, Style +import webbrowser +import time +from plugin import plugin + + +FILE_PATH = os.path.abspath(os.path.dirname(__file__)) + + +def read_questions(): + Q = [] + with open(os.path.join(FILE_PATH, + "../data/personality_questions.tsv")) as f: + for i, line in enumerate(f): + Q.append([i + 1] + line.strip().split('\t')) + return Q + + +@plugin("personality") +class personality_test: + """ + Runs Personality test + Taken from: https://openpsychometrics.org/tests/OJTS/development/#liscmark + Test is licensed under Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 + International License. + """ + + def __init__(self): + self.Q = read_questions() + self.answers = {} + self.instruction = Back.YELLOW + "There are a total of " +\ + "32 pairs of descriptions. For each pair, choose on a scale of " +\ + "1-5. Choose 1 if you are all the way to the left, and choose " +\ + "3 if you are in the middle, etc." + Style.RESET_ALL + + self.types = ['IE', 'SN', 'FT', 'JP'] + self.scoring_scheme = ((30, (15, 23, 27), (3, 7, 11, 19, 31)), + (12, (4, 8, 12, 16, 20, 32), (24, 28)), + (30, (6, 10, 22), (2, 14, 18, 26, 30)), + (18, (1, 5, 13, 21, 29), (9, 17, 25))) + + self.scores = [] + self.type = [] + + def get_scores(self): + for i, personality_type in enumerate(self.types): + score = self.scoring_scheme[i][0] + for Q_id in self.scoring_scheme[i][1]: + score += self.answers[Q_id] + for Q_id in self.scoring_scheme[i][2]: + score -= self.answers[Q_id] + + self.scores.append(score) + if score <= 24: + self.type.append(personality_type[0]) + else: + self.type.append(personality_type[1]) + self.type = ''.join(self.type) + + def open_analysis(self): + url = "https://www.16personalities.com/{}-personality" + webbrowser.open_new(url.format(self.type.lower())) + + def __call__(self, jarvis, s): + prompt = "{black}Q{Q_id} {cyan}{left} {black}--- {green}{right}" + prompt_formatter = { + 'cyan': Fore.CYAN, + 'black': Fore.BLACK, + 'green': Fore.GREEN + } + jarvis.say("Start personality test..", color=Fore.BLACK) + jarvis.say(self.instruction) + for i, (Q_id, left, right) in enumerate(self.Q): + prompt_formatter['Q_id'] = i + prompt_formatter['left'] = left + prompt_formatter['right'] = right + + jarvis.say(prompt.format(**prompt_formatter)) + user_input = jarvis.input_number( + prompt="Enter your choice on the scale of 1-5:\n", rmin=1, + rmax=5, color=Fore.BLUE, rtype=int) + self.answers[Q_id] = user_input + self.get_scores() + + jarvis.say( + "{}Your personality is: {}{}{}{}".format( + Fore.BLUE, + Fore.BLACK, + Back.MAGENTA, + self.type, + Style.RESET_ALL)) + jarvis.say( + "Redirecting to your personality analysis\ + in 3s...", color=Fore.BLUE) + time.sleep(3) + self.open_analysis() diff --git a/jarviscli/plugins/pi.py b/jarviscli/plugins/pi.py new file mode 100644 index 000000000..1ca0f78f1 --- /dev/null +++ b/jarviscli/plugins/pi.py @@ -0,0 +1,25 @@ +from plugin import plugin +import os +from colorama import Fore + + +FILE_PATH = os.path.abspath(os.path.dirname(__file__)) +NUM_NEXT = 100 + + +@plugin("pi") +def next_pi(jarvis, s): + jarvis.say("Pi equals 3.14...", Fore.GREEN) + # today = datetime.date.today() + + # print(today) + + pi_file = open(os.path.join(FILE_PATH, '../data/pi.txt'), 'r') + pi_number = pi_file.read() + index = 4 + while True: + user_input = jarvis.input("Enter \'n\' to print next {} digits : ".format(str(NUM_NEXT))) + if (user_input != 'n'): + break + jarvis.say(pi_number[index: index + NUM_NEXT], Fore.GREEN) + index += NUM_NEXT diff --git a/jarviscli/plugins/picshow.py b/jarviscli/plugins/picshow.py index 5b1134bfe..8944d20b7 100644 --- a/jarviscli/plugins/picshow.py +++ b/jarviscli/plugins/picshow.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import webbrowser -from plugin import plugin, require, alias + +from plugin import alias, plugin, require @alias('show pics') diff --git a/jarviscli/plugins/project_euler.py b/jarviscli/plugins/project_euler.py index 2a2016591..bca18012a 100644 --- a/jarviscli/plugins/project_euler.py +++ b/jarviscli/plugins/project_euler.py @@ -1,8 +1,10 @@ -from plugin import plugin, alias, require -from colorama import Fore, Style -import requests import random + import bs4 +import requests +from colorama import Fore + +from plugin import alias, plugin, require @alias('project euler') @@ -56,7 +58,7 @@ def get_problem_by_number(self, number): try: page = requests.get(url) except ConnectionError: - jarvis.say("Can't get info from site, exit", Fore.RED) + self.jarvis.say("Can't get info from site, exit", Fore.RED) return # Use bs4 to parse the page @@ -112,7 +114,8 @@ def show_info(self): info_text += "problems intended to be solved with computer programs. " info_text += "The project attracts adults and students interested in mathematics and computer programming. " info_text += "Since its creation in 2001 by Colin Hughes, Project Euler has gained notability and popularity worldwide. " - info_text += "It now includes " + str(self.last_problem_id) + " problems. A new one is added once every one or two weeks. " + info_text += "It now includes " + str(self.last_problem_id) + \ + " problems. A new one is added once every one or two weeks. " info_text += "Problems are of varying difficulty, but each is solvable in less than a minute of CPU time using an efficient " info_text += "algorithm on a modestly powered computer. " diff --git a/jarviscli/plugins/quote.py b/jarviscli/plugins/quote.py index 258fb5aed..3c36e8a9a 100644 --- a/jarviscli/plugins/quote.py +++ b/jarviscli/plugins/quote.py @@ -1,7 +1,8 @@ -import requests +import json + import bs4 +import requests -import json from plugin import plugin, require diff --git a/jarviscli/plugins/random_list.py b/jarviscli/plugins/random_list.py new file mode 100644 index 000000000..e1a18e9cf --- /dev/null +++ b/jarviscli/plugins/random_list.py @@ -0,0 +1,30 @@ +from plugin import plugin +import random +from colorama import Fore + + +@plugin("random list") +def generate_random_list(jarvis, str): + ls = get_user_input(jarvis) + + if len(ls) <= 1: + jarvis.say("Enter at least 2 strings", Fore.RED) + else: + random.shuffle(ls) + for i in ls: + jarvis.say(i, Fore.GREEN) + + +def get_user_input(jarvis): + ls = list() + while True: + try: + user_input = jarvis.input("Enter string (enter \"JarvisStop\" to end): ") + if user_input == "JarvisStop": + break + ls.append(user_input) + except ValueError: + jarvis.say("Sorry, I didn't understand that.", Fore.RED) + continue + + return ls diff --git a/jarviscli/plugins/random_number.py b/jarviscli/plugins/random_number.py index 377b5526f..cd0ca6b40 100644 --- a/jarviscli/plugins/random_number.py +++ b/jarviscli/plugins/random_number.py @@ -1,6 +1,7 @@ -from plugin import plugin import random +from plugin import plugin + @plugin("random number") def generate_random_number(jarvis, numbers): diff --git a/jarviscli/plugins/random_password.py b/jarviscli/plugins/random_password.py index e0d5c6338..75378df18 100644 --- a/jarviscli/plugins/random_password.py +++ b/jarviscli/plugins/random_password.py @@ -1,8 +1,10 @@ -from plugin import plugin import random import string + from colorama import Fore +from plugin import plugin + @plugin("random password") def random_password(jarvis, s): diff --git a/jarviscli/plugins/reminder.py b/jarviscli/plugins/reminder.py index 95ca6b87b..306acba26 100644 --- a/jarviscli/plugins/reminder.py +++ b/jarviscli/plugins/reminder.py @@ -1,12 +1,12 @@ +import datetime import json import time -import datetime -from pick import pick from colorama import Fore -from pytimeparse.timeparse import timeparse +from pick import pick from plugin import plugin +from pytimeparse.timeparse import timeparse from utilities.textParser import parse_date diff --git a/jarviscli/plugins/rockpaperscissors.py b/jarviscli/plugins/rockpaperscissors.py index 6cba60868..e248b7ab0 100644 --- a/jarviscli/plugins/rockpaperscissors.py +++ b/jarviscli/plugins/rockpaperscissors.py @@ -1,7 +1,9 @@ -from plugin import plugin -from colorama import Back, Fore, Style import random +from colorama import Back + +from plugin import plugin + @plugin('rockpaperscissors') class rockpaperscissors(): diff --git a/jarviscli/plugins/roulette.py b/jarviscli/plugins/roulette.py new file mode 100644 index 000000000..d4469a0f0 --- /dev/null +++ b/jarviscli/plugins/roulette.py @@ -0,0 +1,309 @@ +import random + +from colorama import Fore + +from plugin import plugin + +reds = [1, 3, 5, 7, 9, 12, 14, 16, 18, + 19, 21, 23, 25, 27, 30, 32, 34, 36] + + +@plugin("roulette") +def roulette(jarvis, s): + print("") + cash = 100 + jarvis.say("~> Hello, it's roulette game", Fore.RED) + n = input("Press enter to play") + start_game(jarvis, cash) + + +def start_game(jarvis, cash): + print("") + jarvis.say("~> Your start balance is $100", Fore.YELLOW) + print("") + + while True: + jarvis.say("1. Number (win: 36x)", Fore.YELLOW) + jarvis.say("2. RED/BLACK (win: 2x)", Fore.YELLOW) + jarvis.say("3. ODD/EVEN (win: 2x)", Fore.YELLOW) + jarvis.say("4. 1-12 / 13-24 / 25-36 (win: 3x)", Fore.YELLOW) + jarvis.say("5. 1-18 / 19-36 (win: 2x)", Fore.YELLOW) + jarvis.say("6. Exit game", Fore.YELLOW) + print("") + + if cash == 0: + jarvis.say("You haven't enough money to play", Fore.RED) + jarvis.say("Thanks for playing", Fore.RED) + print("") + return + + choice = get_user_choice(jarvis, cash) + if choice == 6: + return + elif choice == 1: + cash = first_choice(jarvis, cash) + jarvis.say("Current Balance: " + str(cash), Fore.RED) + elif choice == 2: + cash = second_choice(jarvis, cash) + jarvis.say("Current Balance: " + str(cash), Fore.RED) + elif choice == 3: + cash = third_choice(jarvis, cash) + jarvis.say("Current Balance: " + str(cash), Fore.RED) + elif choice == 4: + cash = fourth_choice(jarvis, cash) + jarvis.say("Current Balance: " + str(cash), Fore.RED) + elif choice == 5: + cash = fifth_choice(jarvis, cash) + jarvis.say("Current Balance: " + str(cash), Fore.RED) + + +def get_user_choice(jarvis, cash): + while True: + try: + option = int(jarvis.input("Enter your choice: ", Fore.GREEN)) + if option >= 1 and option <= 6: + return option + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + + +def first_choice(jarvis, cash): + print("") + choice = bet_number(jarvis) + jarvis.say("Your choice: " + str(choice), Fore.RED) + bet = bet_amount(jarvis, cash) + jarvis.say("Your bet: $" + str(bet), Fore.RED) + n = input("Press enter to spin roulette") + result = random.randint(0, 36) + cash -= bet + + print("") + jarvis.say("Result: " + str(result), Fore.YELLOW) + + if result == choice: + cash += bet * 36 + jarvis.say("WIN: " + str(bet * 36), Fore.GREEN) + else: + jarvis.say("YOU LOST") + + print("") + + return cash + + +def second_choice(jarvis, cash): + print("") + jarvis.say("1. RED", Fore.GREEN) + jarvis.say("2. BLACK", Fore.GREEN) + choice = bet_two_choice(jarvis) + + if choice == 1: + jarvis.say("Your choice: RED", Fore.RED) + else: + jarvis.say("Your choice: BLACK", Fore.RED) + + print("") + bet = bet_amount(jarvis, cash) + jarvis.say("Your bet: $" + str(bet), Fore.RED) + print("") + + n = input("Press enter to spin roulette") + result = random.randint(0, 36) + cash -= bet + + print("") + jarvis.say("Result: " + str(result), Fore.YELLOW) + + is_red = False + if result in reds: + is_red = True + + if (choice == 1 and is_red and result != 0) or (choice == 2 and is_red is False and result != 0): + cash += bet * 2 + jarvis.say("WIN: " + str(bet * 2), Fore.GREEN) + else: + jarvis.say("YOU LOST") + + print("") + + return cash + + +def third_choice(jarvis, cash): + print("") + jarvis.say("1. ODD", Fore.GREEN) + jarvis.say("2. EVEN", Fore.GREEN) + choice = bet_two_choice(jarvis) + + if choice == 1: + jarvis.say("Your choice: ODD", Fore.RED) + else: + jarvis.say("Your choice: EVEN", Fore.RED) + + print("") + bet = bet_amount(jarvis, cash) + jarvis.say("Your bet: $" + str(bet), Fore.RED) + print("") + + n = input("Press enter to spin roulette") + result = random.randint(0, 36) + cash -= bet + + print("") + jarvis.say("Result: " + str(result), Fore.YELLOW) + + if (choice == 1 and result % 2 == 1 and result != 0) or (choice == 2 and result % 2 == 0 and result != 0): + cash += bet * 2 + jarvis.say("WIN: " + str(bet * 2), Fore.GREEN) + else: + jarvis.say("YOU LOST") + + print("") + + return cash + + +def fourth_choice(jarvis, cash): + print("") + jarvis.say("1. 1-12", Fore.GREEN) + jarvis.say("2. 13-24", Fore.GREEN) + jarvis.say("2. 25-36", Fore.GREEN) + choice = bet_three_choice(jarvis) + + if choice == 1: + jarvis.say("Your choice: 1-12", Fore.RED) + elif choice == 2: + jarvis.say("Your choice: 13-24", Fore.RED) + else: + jarvis.say("Your choice: 25-36", Fore.RED) + + print("") + bet = bet_amount(jarvis, cash) + jarvis.say("Your bet: $" + str(bet), Fore.RED) + print("") + + n = input("Press enter to spin roulette") + result = random.randint(0, 36) + cash -= bet + + print("") + jarvis.say("Result: " + str(result), Fore.YELLOW) + + if choice == 1: + if result >= 1 and result <= 12: + cash += bet * 3 + jarvis.say("WIN: " + str(bet * 3), Fore.GREEN) + else: + jarvis.say("YOU LOST") + elif choice == 2: + if result >= 13 and result <= 24: + cash += bet * 3 + jarvis.say("WIN: " + str(bet * 3), Fore.GREEN) + else: + jarvis.say("YOU LOST") + else: + if result >= 25 and result <= 36: + cash += bet * 3 + jarvis.say("WIN: " + str(bet * 3), Fore.GREEN) + else: + jarvis.say("YOU LOST") + + print("") + + return cash + + +def fifth_choice(jarvis, cash): + print("") + jarvis.say("1. 1-18", Fore.GREEN) + jarvis.say("2. 19-36", Fore.GREEN) + choice = bet_two_choice(jarvis) + + if choice == 1: + jarvis.say("Your choice: 1-18", Fore.RED) + else: + jarvis.say("Your choice: 19-36", Fore.RED) + + print("") + bet = bet_amount(jarvis, cash) + jarvis.say("Your bet: $" + str(bet), Fore.RED) + print("") + + n = input("Press enter to spin roulette") + result = random.randint(0, 36) + cash -= bet + + print("") + jarvis.say("Result: " + str(result), Fore.YELLOW) + + if (choice == 1 and result >= 1 and result <= 18) or (choice == 2 and result >= 19 and result <= 36): + cash += bet * 2 + jarvis.say("WIN: " + str(bet * 2), Fore.GREEN) + else: + jarvis.say("YOU LOST") + + print("") + + return cash + + +def bet_two_choice(jarvis): + while True: + try: + print("") + option = int(jarvis.input("Enter your choice: ", Fore.GREEN)) + if option == 1 or option == 2: + return option + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + + +def bet_three_choice(jarvis): + while True: + try: + print("") + option = int(jarvis.input("Enter your choice: ", Fore.GREEN)) + if option == 1 or option == 2 or option == 3: + return option + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + + +def bet_number(jarvis): + while True: + try: + option = int(jarvis.input("Enter your choice (0-36): ", Fore.GREEN)) + if option >= 0 and option <= 36: + return option + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + + +def bet_amount(jarvis, cash): + while True: + try: + bet = int(jarvis.input("Enter your bet: ", Fore.GREEN)) + if bet >= 1 and bet <= cash: + return bet + else: + jarvis.say( + "Invalid input! Enter a bet amount from your cash", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) diff --git a/jarviscli/plugins/routine.py b/jarviscli/plugins/routine.py new file mode 100644 index 000000000..af668ae59 --- /dev/null +++ b/jarviscli/plugins/routine.py @@ -0,0 +1,72 @@ +from plugin import plugin + + +ROUTINES = "routines" + + +def create_routine(jarvis, commands, name): + """ + Saves a new routine to memory with a name and a list of instructions. + :return: Nothing to return. + """ + routines = jarvis.get_data(ROUTINES) + if routines is None: + jarvis.add_data(ROUTINES, {name: commands}) + else: + routines[name] = commands + jarvis.update_data(ROUTINES, routines) + + +def execute_routine(jarvis, name): + """ + Executes a routine saved in memory based on its name. If the routine doesn't exist, returns False. + + :return: Whether or not the requested routine exists. + """ + routines = jarvis.get_data(ROUTINES) + try: + routine = routines[name] + except (TypeError, KeyError) as e: + return False + for command in routine: + jarvis.eval(command) + return True + + +@plugin("create routine") +def new_routine(jarvis, s): + if s == "": + jarvis.say("You forgot to give a name.") + return + commands = [] + while True: + jarvis.say("Type a command, or press enter to finish the routine.") + command = jarvis.input() + if command == "": + break + commands.append(command) + create_routine(jarvis, commands, s) + + +@plugin("call routine") +def call_routine(jarvis, s): + success = execute_routine(jarvis, s) + if not success: + jarvis.say("The routine you requested doesn't seem to exist.") + + +@plugin("list routines") +def list_routines(jarvis, s): + routines = jarvis.get_data(ROUTINES) + for i in routines.keys(): + jarvis.say(str(i)) + + +@plugin("delete routine") +def delete_routines(jarvis, s): + routines = jarvis.get_data(ROUTINES) + if s in routines: + routines.pop(s) + jarvis.update_data(ROUTINES, routines) + else: + jarvis.say("Invalid routine name") diff --git a/jarviscli/plugins/search.py b/jarviscli/plugins/search.py index 1ee1efc44..c691112fb 100644 --- a/jarviscli/plugins/search.py +++ b/jarviscli/plugins/search.py @@ -1,5 +1,6 @@ import webbrowser -from plugin import require, plugin + +from plugin import plugin, require @require(network=True) diff --git a/jarviscli/plugins/shutdown.py b/jarviscli/plugins/shutdown.py index 755779657..ae2a2174b 100644 --- a/jarviscli/plugins/shutdown.py +++ b/jarviscli/plugins/shutdown.py @@ -1,8 +1,9 @@ import os -from plugin import plugin, require, LINUX, MACOS, WINDOWS +from plugin import Platform, plugin, require -@require(platform=LINUX) + +@require(platform=Platform.LINUX) @plugin('shutdown') def shutdown_LINUX(jarvis, s): """ @@ -21,7 +22,7 @@ def shutdown_LINUX(jarvis, s): os.system(string) -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin('shutdown') def shutdown_MACOS(jarvis, s): """ @@ -40,7 +41,7 @@ def shutdown_MACOS(jarvis, s): os.system(string) -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('shutdown') def shutdown_WIN32(jarvis, s): """ @@ -59,7 +60,7 @@ def shutdown_WIN32(jarvis, s): os.system(string) -@require(platform=LINUX) +@require(platform=Platform.LINUX) @plugin('reboot') def reboot_LINUX(jarvis, s): """Reboot the system""" @@ -69,7 +70,7 @@ def reboot_LINUX(jarvis, s): os.system(string) -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin('reboot') def reboot_MACOS(jarvis, s): """Reboot the system""" @@ -77,7 +78,7 @@ def reboot_MACOS(jarvis, s): os.system(string) -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('reboot') def reboot_WIN32(jarvis, s): """Reboot the system""" @@ -87,7 +88,7 @@ def reboot_WIN32(jarvis, s): os.system(string) -@require(native="systemctl", platform=LINUX) +@require(native="systemctl", platform=Platform.LINUX) @plugin('hibernate') def hibernate_LINUX(jarvis, s): """ @@ -100,7 +101,7 @@ def hibernate_LINUX(jarvis, s): os.system('sudo systemctl hibernate') -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('hibernate') def hibernate_WIN32(jarvis, s): """Hibernates the system""" @@ -108,7 +109,7 @@ def hibernate_WIN32(jarvis, s): os.system(string) -@require(native="systemctl", platform=LINUX) +@require(native="systemctl", platform=Platform.LINUX) @plugin('hybridsleep') def hybridsleep_LINUX(jarvis, s): """ @@ -120,7 +121,7 @@ def hybridsleep_LINUX(jarvis, s): os.system("sudo systemctl hybrid-sleep") -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('hybridsleep') def hybridsleep_WIN32(jarvis, s): """Performs shutdown and prepares forfast startup""" @@ -128,7 +129,7 @@ def hybridsleep_WIN32(jarvis, s): os.system(string) -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('log off') def log_off_WIN32(jarvis, s): """Log off the system""" @@ -136,7 +137,7 @@ def log_off_WIN32(jarvis, s): os.system(string) -@require(native="systemctl", platform=LINUX) +@require(native="systemctl", platform=Platform.LINUX) @plugin('suspend') def suspend_LINUX(jarvis, s): """ diff --git a/jarviscli/plugins/speedtest.py b/jarviscli/plugins/speedtest.py index 395b3fe66..0dfc4a2ac 100644 --- a/jarviscli/plugins/speedtest.py +++ b/jarviscli/plugins/speedtest.py @@ -1,13 +1,14 @@ -import speedtest as st from colorama import Fore + +import speedtest as st from plugin import plugin, require -from utilities.animations import SpinnerThread @require(network=True) @plugin('speedtest') def speedtest(jarvis, s): """Runs a speedtest on your internet connection""" + import speedtest as st try: res = st.Speedtest() except st.ConfigRetrievalError: diff --git a/jarviscli/plugins/spinthewheel.py b/jarviscli/plugins/spinthewheel.py index a7533aa46..17f1d1112 100644 --- a/jarviscli/plugins/spinthewheel.py +++ b/jarviscli/plugins/spinthewheel.py @@ -1,6 +1,7 @@ -from plugin import plugin import random +from plugin import plugin + def spinit(list): return(random.choice(list)) diff --git a/jarviscli/plugins/stateinfo.py b/jarviscli/plugins/stateinfo.py index 78b9b8488..97ace9893 100644 --- a/jarviscli/plugins/stateinfo.py +++ b/jarviscli/plugins/stateinfo.py @@ -1,4 +1,4 @@ -from plugin import plugin, alias +from plugin import alias, plugin @alias("state capital", "state abbreviation") @@ -11,6 +11,7 @@ class stateinfo: state capital state abbreviation """ + def __call__(self, jarvis, s): capital_dict = { 'alabama': 'montgomery', diff --git a/jarviscli/plugins/stock.py b/jarviscli/plugins/stock.py index a3310d2e2..2c722b99a 100644 --- a/jarviscli/plugins/stock.py +++ b/jarviscli/plugins/stock.py @@ -1,8 +1,10 @@ +import re +from inspect import cleandoc + import requests -from plugin import plugin from colorama import Fore -from inspect import cleandoc -import re + +from plugin import plugin @plugin('stock') diff --git a/jarviscli/plugins/switchingwin.py b/jarviscli/plugins/switchingwin.py index 387a2f382..9fc1136bf 100644 --- a/jarviscli/plugins/switchingwin.py +++ b/jarviscli/plugins/switchingwin.py @@ -1,4 +1,5 @@ import os + from plugin import plugin, require diff --git a/jarviscli/plugins/systemOptions.py b/jarviscli/plugins/systemOptions.py index db91f4fdd..e3188dc40 100644 --- a/jarviscli/plugins/systemOptions.py +++ b/jarviscli/plugins/systemOptions.py @@ -1,26 +1,28 @@ import os -from platform import architecture, release, mac_ver +from platform import architecture, mac_ver, release from platform import system as sys -import distro + from colorama import Fore, Style -from plugin import LINUX, UNIX, MACOS, WINDOWS, plugin, require + +import distro +from plugin import Platform, plugin, require -@require(platform=MACOS, native="pmset") +@require(platform=Platform.MACOS, native="pmset") @plugin('screen off') def screen_off__MAC(jarvis, s): """Turn of screen instantly""" os.system('pmset displaysleepnow') -@require(platform=LINUX, native="xset") +@require(platform=Platform.LINUX, native="xset") @plugin('screen off') def screen_off__LINUX(jarvis, s): """Turn of screen instantly""" os.system('xset dpms force off') -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin('os') def Os__MAC(jarvis, s): """Displays information about your operating system""" @@ -37,7 +39,7 @@ def Os__MAC(jarvis, s): jarvis.say('[*] ' + _, Fore.GREEN) -@require(platform=[LINUX, WINDOWS]) +@require(platform=[Platform.LINUX, Platform.WINDOWS]) @plugin('os') def Os__LINUX(jarvis, s): """Displays information about your operating system""" @@ -49,7 +51,7 @@ def Os__LINUX(jarvis, s): jarvis.say('[*] ' + _, Fore.GREEN) -@require(platform=LINUX) +@require(platform=Platform.LINUX) @plugin('systeminfo') def systeminfo__LINUX(jarvis, s): """Display system information with distribution logo""" @@ -57,21 +59,21 @@ def systeminfo__LINUX(jarvis, s): archey.main() -@require(platform=MACOS, native="screenfetch") +@require(platform=Platform.MACOS, native="screenfetch") @plugin('systeminfo') def systeminfo__MAC(jarvis, s): """Display system information with distribution logo""" os.system("screenfetch") -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin('systeminfo') def systeminfo_win(jarvis, s): """Display system infomation""" os.system("systeminfo") -@require(native="free", platform=UNIX) +@require(native="free", platform=Platform.UNIX) @plugin("check ram") def check_ram__UNIX(jarvis, s): """ @@ -82,7 +84,7 @@ def check_ram__UNIX(jarvis, s): os.system("free -lm") -@require(platform=WINDOWS) +@require(platform=Platform.WINDOWS) @plugin("check ram") def check_ram__WINDOWS(jarvis, s): """ diff --git a/jarviscli/plugins/system_update.py b/jarviscli/plugins/system_update.py index 30e6da97b..62f2400a6 100644 --- a/jarviscli/plugins/system_update.py +++ b/jarviscli/plugins/system_update.py @@ -2,16 +2,16 @@ import os import subprocess -from plugin import plugin, require, LINUX, MACOS +from plugin import Platform, plugin, require -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin("update system") def update_system__macos(jarvis, s): os.system('brew upgrade && brew update') -@require(platform=LINUX) +@require(platform=Platform.LINUX) @require(native='lsb_release') @plugin("update system") def update_system(jarvis, s): diff --git a/jarviscli/plugins/tasks.py b/jarviscli/plugins/tasks.py new file mode 100644 index 000000000..cd5a6f218 --- /dev/null +++ b/jarviscli/plugins/tasks.py @@ -0,0 +1,218 @@ +from colorama import Fore + +from packages.memory.memory import Memory +from plugin import plugin + + +@plugin("tasks") +class TaskManager(): + """ + Jarvis plugin For Managing User Tasks + + """ + + def __call__(self, jarvis, s): + self.load_tasks() + jarvis.say("Welcome To Your Tasks Manager\n") + while True: + option = self.get_option(jarvis) + if option is None: + return + self.procces_chosen_option(option, jarvis) + + def list_all(self, jarvis): + jarvis.say("") + task_count = len(self.tasks) + if task_count == 0: + jarvis.say("Your Task List Is Empty, Good Job") + return + jarvis.say("You Have {} {}".format(task_count, "Task" if task_count == 1 else "Tasks")) + for i in range(task_count): + try: + priority = self.tasks[i]["priority"] + jarvis.say("{}. {} PR: {}".format( + i + 1, self.tasks[i]["name"], priority), self.get_color_with_priority(priority)) + except BaseException: + jarvis.say("{}. {}".format(i + 1, self.tasks[i]["name"])) + + def get_color_with_priority(self, priority): + if priority == "High": + return Fore.RED + elif priority == "Medium": + return Fore.YELLOW + else: + return Fore.GREEN + + def add_task(self, jarvis): + new_task = jarvis.input("Enter New Task: ", Fore.GREEN) + self.tasks.append({"name": new_task}) + self.update_tasks(self.tasks) + jarvis.say("Task Was Successfully Added") + self.list_all(jarvis) + + def choose_task(self, exit_text, input_text, jarvis): + tasks_count = len(self.tasks) + self.list_all(jarvis) + jarvis.say("{}. {}".format(tasks_count + 1, exit_text)) + return self.get_choice(input_text, tasks_count, tasks_count + 1, jarvis) + + def update_task(self, jarvis): + task_index = self.choose_task("EXIT Editing", "Exit Editing or Choose Which Task To Edit: ", jarvis) + if task_index == -1: + return + chosen_task_name = self.tasks[task_index - 1]["name"] + jarvis.say("Chosen Task: {}".format(chosen_task_name)) + updated_task = jarvis.input("Enter Updated Task: ", Fore.GREEN) + new_tasks = map(lambda task: {"name": updated_task} if task["name"] == chosen_task_name else task, self.tasks) + self.update_tasks(list(new_tasks)) + jarvis.say("Task Was Successfully Updated") + + def get_priority(self, jarvis): + jarvis.say("") + jarvis.say("Choose Priority For Task", Fore.BLUE) + jarvis.say("") + jarvis.say("1: High") + jarvis.say("2: Medium") + jarvis.say("3: Low") + jarvis.say("4: Exit Priority Mode ") + jarvis.say("") + return self.get_choice("Enter your choice: ", 3, 4, jarvis) + + def add_priority_to_task(self, jarvis): + options = {1: "High", 2: "Medium", 3: "Low"} + task_index = self.choose_task("EXIT Add Priority Mode", + "Exit Priority Mode or Choose Taks To Add Priority: ", jarvis) + if task_index == -1: + return + priority = self.get_priority(jarvis) + if priority == -1: + return + task_name = self.tasks[task_index - 1]["name"] + new_tasks = map(lambda t: {"name": task_name, + "priority": options[priority]} if t["name"] == task_name else t, self.tasks) + self.update_tasks(list(new_tasks)) + + def delete_task(self, jarvis): + task_index = self.choose_task("EXIT Delete Mode", "Exit Delte Mode or Choose Taks To Delete: ", jarvis) + if task_index == -1: + return + chosen_task_name = self.tasks[task_index - 1]["name"] + new_tasks = filter(lambda x: x["name"] != chosen_task_name, self.tasks) + self.update_tasks(list(new_tasks)) + jarvis.say("Task Was Successfully Deleted") + + def get_sorting_startegy(self, jarvis): + jarvis.say("") + jarvis.say("Choose Sorting Strategy", Fore.BLUE) + jarvis.say("") + jarvis.say("1: By Name") + jarvis.say("2: By Priority") + jarvis.say("3: Exit Sorting Mode ") + jarvis.say("") + return self.get_choice("Enter your choice: ", 2, 3, jarvis) + + def display_sorted(self, jarvis): + options = {1: "name", 2: "pr"} + tasks_count = len(self.tasks) + if tasks_count == 0 | tasks_count == 1: + jarvis.say("There is Not Enough Tasks To sort") + return + strategy = self.get_sorting_startegy(jarvis) + if strategy == -1: + return + + def sort_by_priority(task): + try: + priority = task["priority"] + if priority == "High": + return 3 + elif priority == "Medium": + return 2 + else: + return 1 + except BaseException: + return 0 + + def sort_by_name(task): + return task["name"] + + sorted_tasks = self.tasks.copy() + if options[strategy] == "name": + sorted_tasks.sort(key=sort_by_name) + else: + sorted_tasks.sort(key=sort_by_priority, reverse=True) + jarvis.say("") + for i in range(len(sorted_tasks)): + try: + priority = sorted_tasks[i]["priority"] + color = self.get_color_with_priority(priority) + jarvis.say("{}. {} PR: {}".format(i + 1, sorted_tasks[i]["name"], priority), color) + except BaseException: + jarvis.say("{}. {}".format(i + 1, sorted_tasks[i]["name"])) + + def procces_chosen_option(self, option, jarvis): + if option == "ann_new": + self.add_task(jarvis) + elif option == "edit_cur": + self.update_task(jarvis) + elif option == "list_all": + self.list_all(jarvis) + elif option == "add_priority": + self.add_priority_to_task(jarvis) + elif option == "delete_task": + self.delete_task(jarvis) + elif option == "sort": + self.display_sorted(jarvis) + else: + return + + def get_option(self, jarvis): + options = {1: "list_all", 2: "ann_new", 3: "edit_cur", 4: "delete_task", 5: "add_priority", 6: "sort"} + jarvis.say("") + jarvis.say("How Can I Help You?", Fore.BLUE) + jarvis.say("") + jarvis.say("1: List All My Tasks") + jarvis.say("2: Add New Task") + jarvis.say("3: Edit Existing Task") + jarvis.say("4: Delete Task") + jarvis.say("5: Add Priority To Task") + jarvis.say("6: Sort") + jarvis.say("7: Exit ") + jarvis.say("") + choice = self.get_choice("Enter your choice: ", 6, 7, jarvis) + if choice == -1: + return + else: + return options[choice] + + def get_choice(self, input_text, max_valid_value, terminator, jarvis): + while True: + try: + inserted_value = int(jarvis.input(input_text, Fore.GREEN)) + if inserted_value == terminator: + return -1 + elif inserted_value <= max_valid_value: + return inserted_value + else: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", Fore.YELLOW) + jarvis.say("") + + def load_tasks(self): + m = Memory("tasks.json") + if m.get_data("tasks_list") is None: + self.tasks = [] + else: + self.tasks = m.get_data("tasks_list") + + def update_tasks(self, new_tasks): + m = Memory("tasks.json") + if m.get_data("tasks_list") is None: + m.add_data("tasks_list", new_tasks) + else: + m.update_data("tasks_list", new_tasks) + m.save() + self.tasks = new_tasks diff --git a/jarviscli/plugins/tempconv.py b/jarviscli/plugins/tempconv.py index 094a0df95..a74ab222c 100644 --- a/jarviscli/plugins/tempconv.py +++ b/jarviscli/plugins/tempconv.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import re + from colorama import Fore + from plugin import plugin diff --git a/jarviscli/plugins/tic_tac_toe.py b/jarviscli/plugins/tic_tac_toe.py index 71ac7ab34..959b771b1 100644 --- a/jarviscli/plugins/tic_tac_toe.py +++ b/jarviscli/plugins/tic_tac_toe.py @@ -1,6 +1,7 @@ -from plugin import plugin from colorama import Fore +from plugin import plugin + board = {'7': ' ', '8': ' ', '9': ' ', '4': ' ', '5': ' ', '6': ' ', '1': ' ', '2': ' ', '3': ' '} diff --git a/jarviscli/plugins/timeconv.py b/jarviscli/plugins/timeconv.py index 1e05b9293..19cb5d7f8 100644 --- a/jarviscli/plugins/timeconv.py +++ b/jarviscli/plugins/timeconv.py @@ -1,4 +1,3 @@ -from __future__ import division from plugin import plugin diff --git a/jarviscli/plugins/translate.py b/jarviscli/plugins/translate.py index 4949824c3..2c9657f8e 100644 --- a/jarviscli/plugins/translate.py +++ b/jarviscli/plugins/translate.py @@ -1,7 +1,7 @@ -from plugin import plugin, require, alias +import nltk from googletrans import Translator from googletrans.constants import LANGCODES, LANGUAGES, SPECIAL_CASES -import nltk +from plugin import alias, plugin, require @require(network=True) diff --git a/jarviscli/plugins/trivia.py b/jarviscli/plugins/trivia.py new file mode 100644 index 000000000..072584c4f --- /dev/null +++ b/jarviscli/plugins/trivia.py @@ -0,0 +1,92 @@ +import requests + +from plugin import plugin, require + + +@require(network=True) +@plugin('trivia') +class trivia: + errCode = "An error occurred. Please try again later." + """ + Usage: Type trivia and follow the instructions. + This plugin gives you trivia questions (mcq or true/false) + for you to test your trivia knowledge + """ + + def __call__(self, jarvis, s): + trivia_fetch = self.get_trivia(jarvis) + question_type = trivia_fetch["results"][0]["type"] + options = trivia_fetch["results"][0]["incorrect_answers"] + if trivia_fetch is not None: + if(question_type == "multiple"): + self.mcq_question(jarvis, trivia_fetch) + else: + self.true_false_question(jarvis, trivia_fetch) + + def get_trivia(self, jarvis): + """ + function creates request to api and fetches the corresponding data + """ + url = "https://opentdb.com/api.php?amount=1" + r = requests.get(url) + return r.json() + + def true_false_question(self, jarvis, trivia_fetch): + response_code = trivia_fetch["response_code"] + if (response_code != 0): + jarvis.say(response_code) + return + else: + question = trivia_fetch["results"][0]["question"] + question = question.replace(""", "\"") + jarvis.say("True/False: " + question) + options = ["true", "false"] + correct = trivia_fetch["results"][0]["correct_answer"] + correct = correct.lower() + self.true_false_answer(jarvis, options, correct) + + def true_false_answer(self, jarvis, options, correctAnswer): + answerPrompt = "Please enter either \'true\' or \'false\'" + answer = (jarvis.input(answerPrompt + "\n")).lower() + while answer not in options: + jarvis.say("Invalid option") + answer = (jarvis.input(answerPrompt + "\n")).lower() + if (answer == correctAnswer): + jarvis.say("Correct!!") + else: + jarvis.say("Sorry, that's incorrect") + + def mcq_question(self, jarvis, trivia_fetch): + response_code = trivia_fetch["response_code"] + if (response_code != 0): + jarvis.say(response_code) + return + else: + question = trivia_fetch["results"][0]["question"] + question = question.replace(""", "\"") + question = question.replace(''', "'") + jarvis.say("Multiple Choice: " + question) + options = trivia_fetch["results"][0]["incorrect_answers"] + correct_answer = trivia_fetch["results"][0]["correct_answer"] + options.append(correct_answer) + options.sort() + option_count = 0 + answersDict = {} + for option in options: + option_count = option_count + 1 + answersDict[str(option_count)] = option + jarvis.say(str(option_count) + ". " + option) + self.mcq_answer(jarvis, answersDict, correct_answer, option_count) + return + + def mcq_answer(self, jarvis, answersDict, correctAnswer, maxCount): + answerPrompt = "Please enter an integer 1-" + str(maxCount) + answer = jarvis.input(answerPrompt + "\n") + while answer not in answersDict.keys(): + jarvis.say("Invalid option") + answer = jarvis.input(answerPrompt + "\n") + userAnswer = answersDict[answer] + if (userAnswer == correctAnswer): + jarvis.say("Correct!!") + else: + jarvis.say("Sorry, the correct answer was " + correctAnswer) diff --git a/jarviscli/plugins/typingtest.py b/jarviscli/plugins/typingtest.py index 558f89630..0b0cd07a7 100644 --- a/jarviscli/plugins/typingtest.py +++ b/jarviscli/plugins/typingtest.py @@ -1,14 +1,16 @@ -import time -import sys -import requests -import json -import re +import csv import curses -from plugin import plugin, require, UNIX +import json import os -import csv +import re +import sys +import time + +import requests from colorama import Fore +from plugin import Platform, plugin, require + FILE_PATH = os.path.abspath(os.path.dirname(__file__)) @@ -194,7 +196,7 @@ def print_screen(): sys.stdout.write(string) -@require(network=True, platform=UNIX) +@require(network=True, platform=Platform.UNIX) @plugin("typingtest") def typingtest(jarvis, s): game_start() diff --git a/jarviscli/plugins/upside-down.py b/jarviscli/plugins/upside-down.py new file mode 100644 index 000000000..0cb96bb4e --- /dev/null +++ b/jarviscli/plugins/upside-down.py @@ -0,0 +1,21 @@ +from plugin import plugin +from colorama import Fore + + +@plugin('upside down') +def generate_random_list(jarvis, str): + user_input = jarvis.input("Enter string to be converted to upside-down (only english letters will be converted): ") + result = convert_input(jarvis, user_input) + jarvis.say(result, Fore.GREEN) + + +def convert_input(jarvis, u_input): + upside_str = 'zʎxʍʌnʇsɹbdouɯןʞſıɥbɟǝpɔqɐ' + normal_str = 'abcdefghijklmnopqrstuvwxyz' + upside_str = upside_str[::-1] + converter_dict = {a: b for a, b in zip(normal_str, upside_str)} + result = '' + for letter in u_input: + if letter in converter_dict: + result += converter_dict[letter] + return result[::-1] diff --git a/jarviscli/plugins/voice.py b/jarviscli/plugins/voice.py index ee424c368..86fa71828 100644 --- a/jarviscli/plugins/voice.py +++ b/jarviscli/plugins/voice.py @@ -1,5 +1,6 @@ from colorama import Fore -from plugin import LINUX, UNIX, MACOS, WINDOWS, plugin, require + +from plugin import Platform, plugin, require @plugin('enable sound') @@ -7,9 +8,9 @@ def enable_sound(jarvis, s): """Let Jarvis use his voice.""" jarvis.speech = jarvis.enable_voice() jarvis.say(Fore.BLUE + "Jarvis uses Googles speech engine.\nDo you consent with data " - + "collection when Jarvis talks out loud? If yes, type:" + Fore.RED + " gtts") + + "collection when Jarvis talks out loud? If yes, type:" + Fore.RED + " gtts") jarvis.say(Fore.BLUE + "If not, Jarvis will talk using the built-in speech engine. " - + " If you wish to disable GTTS, type: " + Fore.RED + "disable gtts") + + " If you wish to disable GTTS, type: " + Fore.RED + "disable gtts") @plugin('disable sound') @@ -52,7 +53,7 @@ def gtts(jarvis, s): jarvis.disable_voice() -@require(platform=[LINUX, WINDOWS]) +@require(platform=[Platform.LINUX, Platform.WINDOWS]) @plugin('talk faster') def talk_faster(jarvis, s): """Make Jarvis' speech engine talk faster. @@ -61,10 +62,10 @@ def talk_faster(jarvis, s): jarvis.change_speech_rate(40) else: jarvis.say("Type 'enable sound' to allow Jarvis to talk out loud.", - Fore.BLUE) + Fore.BLUE) -@require(platform=[LINUX, WINDOWS]) +@require(platform=[Platform.LINUX, Platform.WINDOWS]) @plugin('talk slower') def talk_slower(jarvis, s): """Make Jarvis' speech engine talk slower. @@ -73,4 +74,4 @@ def talk_slower(jarvis, s): jarvis.change_speech_rate(-40) else: jarvis.say("Type 'enable sound' to allow Jarvis to talk out loud.", - Fore.BLUE) + Fore.BLUE) diff --git a/jarviscli/plugins/voice_control.py b/jarviscli/plugins/voice_control.py index af7bcb82a..088611b04 100644 --- a/jarviscli/plugins/voice_control.py +++ b/jarviscli/plugins/voice_control.py @@ -1,6 +1,9 @@ import os + +import socket from plugin import plugin, require + voice_control_installed = True try: import speech_recognition as sr @@ -15,13 +18,30 @@ 'voice_control_requirements (install portaudio + re-run setup.sh)'] +def has_internet(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + try: + socket.setdefaulttimeout(3) + sock.connect(('8.8.8.8', 8000)) + + return True + + except socket.timeout: + return False + + @require(native=requirements) @plugin("hear") def hear(jarvis, s): r = sr.Recognizer() # intializing the speech_recognition listen = False + _jarvis = jarvis._jarvis # calling jarvis object. _jarvis.speech.text_to_speech("Say listen to start voice mode") + + connected = has_internet() + while listen is False: try: with sr.Microphone() as source: @@ -29,13 +49,19 @@ def hear(jarvis, s): print("Say listen to start listening") r.adjust_for_ambient_noise(source) # Eleminating the noise. audio = r.listen(source) # Storing audio. - pinger = r.recognize_google(audio) # Converting speech to text + + if connected: + pinger = r.recognize_google(audio) # Converting speech to text using google recognition. + + else: + pinger = r.recognize_sphinx(audio) # Converting speech to text using Sphinx CMU in case user is not connected to internet + try: if (pinger.lower() == "listen"): listen = True _jarvis.speech.text_to_speech("Voice mode activated") print("Voice mode activated. Say something!") - break + else: continue except LookupError: @@ -44,18 +70,23 @@ def hear(jarvis, s): continue # For ignoring the unreconized words error while listen is True: - print("Say somthing") + print("Say something") try: with sr.Microphone() as source: r.adjust_for_ambient_noise(source) audio = r.listen(source) - pinger = r.recognize_google(audio).lower() + + if connected: + pinger = r.recognize_google(audio).lower() + + else: + pinger = r.recognize_sphinx(audio).lower() if (pinger == "stop"): listen = False print("Listening stopped.") _jarvis.speech.text_to_speech("Listening stopped.") - break + else: print(pinger) if listen: diff --git a/jarviscli/plugins/volume.py b/jarviscli/plugins/volume.py index 9e31420d2..4c06de087 100644 --- a/jarviscli/plugins/volume.py +++ b/jarviscli/plugins/volume.py @@ -1,16 +1,16 @@ from os import system -from plugin import LINUX, MACOS, plugin, require +from plugin import Platform, plugin, require -@require(platform=LINUX, native="pactl") +@require(platform=Platform.LINUX, native="pactl") @plugin('increase volume') def increase_volume__LINUX(jarvis, s): """Increases your speaker's sound.""" system("pactl -- set-sink-volume 0 +3%") -@require(platform=MACOS, native="osascript") +@require(platform=Platform.MACOS, native="osascript") @plugin('increase volume') def increase_volume__MAC(jarvis, s): """Increases your speaker's sound.""" @@ -20,7 +20,7 @@ def increase_volume__MAC(jarvis, s): ) -@require(platform=MACOS, native="osascript") +@require(platform=Platform.MACOS, native="osascript") @plugin('max volume') def max_volume__MAC(jarvis, s): """Maximizes your speaker's sound.""" @@ -29,7 +29,7 @@ def max_volume__MAC(jarvis, s): ) -@require(platform=MACOS, native="osascript") +@require(platform=Platform.MACOS, native="osascript") @plugin('mute') def mute__MAC(jarvis, s): """Mute: Silence your speaker's sound.""" @@ -38,14 +38,14 @@ def mute__MAC(jarvis, s): ) -@require(platform=LINUX, native="pactl") +@require(platform=Platform.LINUX, native="pactl") @plugin('decrease volume') def decrease_volume__LINUX(jarvis, s): """Decreases your speaker's sound.""" system("pactl -- set-sink-volume 0 -10%") -@require(platform=MACOS, native="osascript") +@require(platform=Platform.MACOS, native="osascript") @plugin('decrease volume') def decrease_volume__MAC(jarvis, s): """Decreases your speaker's sound.""" @@ -55,7 +55,7 @@ def decrease_volume__MAC(jarvis, s): ) -@require(platform=LINUX, native="pactl") +@require(platform=Platform.LINUX, native="pactl") @plugin('mute') def mute__LINUX(jarvis, s): """Mute: Silence your speaker's sound.""" diff --git a/jarviscli/plugins/website_status.py b/jarviscli/plugins/website_status.py index a768a89c4..118727b25 100644 --- a/jarviscli/plugins/website_status.py +++ b/jarviscli/plugins/website_status.py @@ -1,7 +1,9 @@ -from plugin import plugin import urllib.request + from colorama import Fore +from plugin import plugin + @plugin("website status") def check_website_status(jarvis, s): diff --git a/jarviscli/plugins/whoami.py b/jarviscli/plugins/whoami.py index 07941baea..4d7eb1498 100644 --- a/jarviscli/plugins/whoami.py +++ b/jarviscli/plugins/whoami.py @@ -1,10 +1,11 @@ import os import re -from plugin import plugin, require, LINUX, MACOS, feature + +from plugin import Platform, feature, plugin, require @require(native="id") -@require(platform=LINUX) +@require(platform=Platform.LINUX) @plugin("whoami") def whoami(jarvis, s): """ @@ -59,7 +60,7 @@ def check(s) -> bool: @feature(case_sensitive=True) @require(native="id") -@require(platform=MACOS) +@require(platform=Platform.MACOS) @plugin("whoami") def whoami(jarvis, s): """ diff --git a/jarviscli/plugins/wifi_password_getter.py b/jarviscli/plugins/wifi_password_getter.py new file mode 100644 index 000000000..b85c66746 --- /dev/null +++ b/jarviscli/plugins/wifi_password_getter.py @@ -0,0 +1,75 @@ +from plugin import Platform, plugin, require +from platform import system as sys +from colorama import Fore +import subprocess + + +@require(platform=Platform.LINUX) +@plugin("wifi") +class wifiPasswordGetter(): + """ + A Jarvis plugin that will find and display all the profiles of the + wifis that you have connected to and then display the password if selected + + """ + def __call__(self, jarvis, s): + profiles = self.get_wifi_profiles() + choice = self.show_options(jarvis, profiles) + if choice == "exit": + return + password = self.display_password(profiles[choice - 1]) + strip_password = password.split("=", 1)[1] + jarvis.say("Wifi Name: " + profiles[choice - 1] + + '\nPassword: ' + strip_password) + + def get_wifi_profiles(self): + out = subprocess.Popen( + ["ls", + "/etc/NetworkManager/system-connections/"], + universal_newlines=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + (res, stderr) = out.communicate() + data = res.split('\n') + return data + + def show_options(self, jarvis, arr): + count = 1 + for x in range(len(arr)-1): + option = arr[x] + jarvis.say(str(count) + ": " + option) + count = count + 1 + jarvis.say(str(count) + ": Exit") + choice = self.get_choice("Please select a number or Exit: ", + count, count, jarvis) + if choice == -1: + return "exit" + else: + return choice + + def get_choice(self, input_text, max_valid_value, terminator, jarvis): + while True: + try: + inserted_value = int(jarvis.input(input_text, Fore.GREEN)) + if inserted_value == terminator: + return -1 + elif inserted_value <= max_valid_value: + return inserted_value + else: + jarvis.say( + "Invalid input! Enter a number from the" + "choices provided.", Fore.YELLOW) + except ValueError: + jarvis.say( + "Invalid input! Enter a number from the choices provided.", + Fore.YELLOW) + jarvis.say("") + + def display_password(self, ssid): + path = "/etc/NetworkManager/system-connections/" + display = subprocess.Popen([f"sudo grep -r '^psk=' {path}{ssid}"], + shell=True, universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + (new_res, stderr) = display.communicate() + return new_res diff --git a/jarviscli/plugins/wiki.py b/jarviscli/plugins/wiki.py index 75ccb8fb9..3920267cd 100644 --- a/jarviscli/plugins/wiki.py +++ b/jarviscli/plugins/wiki.py @@ -1,5 +1,6 @@ import wikipedia -from plugin import plugin, complete, require + +from plugin import complete, plugin, require @require(network=True) diff --git a/jarviscli/plugins/workout.py b/jarviscli/plugins/workout.py index 4dcbab2fe..b1d01a5ca 100644 --- a/jarviscli/plugins/workout.py +++ b/jarviscli/plugins/workout.py @@ -1,7 +1,9 @@ -from plugin import plugin -from colorama import Fore import time -from utilities.notification import* + +from colorama import Fore + +from plugin import plugin +from utilities.notification import * def push_compute_rest(maximum): @@ -53,7 +55,7 @@ def timer(rest): def pushups(jarvis, s): try: maximum = int(s) - except: + except BaseException: jarvis.say("Please enter an integer only!", Fore.BLUE) quit(jarvis) return @@ -92,7 +94,7 @@ def pushups(jarvis, s): def pullups(jarvis, s): try: maximum = int(s) - except: + except BaseException: jarvis.say("Please enter an integer only!", Fore.BLUE) quit(jarvis) return diff --git a/jarviscli/plugins/write_agenda.py b/jarviscli/plugins/write_agenda.py index b1d79ce92..0f67f55a0 100644 --- a/jarviscli/plugins/write_agenda.py +++ b/jarviscli/plugins/write_agenda.py @@ -1,6 +1,7 @@ -from plugin import plugin import csv +from plugin import plugin + @plugin("write agenda") def write_agenda(jarvis, s): diff --git a/jarviscli/tests/test_basketball.py b/jarviscli/tests/test_basketball.py new file mode 100644 index 000000000..e921d02be --- /dev/null +++ b/jarviscli/tests/test_basketball.py @@ -0,0 +1,66 @@ +import unittest +from tests import PluginTest +from plugins.basketball import basketball +from mock import patch, call +import requests +import datetime +from packages.memory.memory import Memory + + +class BasketballTest(PluginTest): + """ + Tests For Basketball Plugin + !!! test will be executed only if user has added his own api.basketball.com API_KEY + """ + + def setUp(self): + self.test = self.load_plugin(basketball) + m = Memory("basketball.json") + self.unable_to_test_plugin = False + if m.get_data("API_KEY") is None: + self.unable_to_test_plugin = True + else: + self.headers = {"x-rapidapi-host": "api-basketball.p.rapidapi.com", "x-rapidapi-key": m.get_data("API_KEY")} + + def test_todays_games(self): + if self.unable_to_test_plugin: + return + with patch.object(requests, 'get', headers=self.headers) as get_mock: + self.test.get_api_key(self.jarvis_api) + self.test.todays_games(self.jarvis_api) + date = datetime.datetime.now().strftime('%Y-%m-%d') + get_mock.assert_called_with( + "https://api-basketball.p.rapidapi.com/games?date={}".format(date), headers=self.headers) + + def test_search_team(self): + if self.unable_to_test_plugin: + return + with patch.object(requests, 'get', headers=self.headers) as get_mock: + self.test.get_api_key(self.jarvis_api) + self.queue_input("boston") + self.test.search_team(self.jarvis_api) + get_mock.assert_called_with( + "https://api-basketball.p.rapidapi.com/teams?search=boston", headers=self.headers) + + def test_search_league(self): + if self.unable_to_test_plugin: + return + with patch.object(requests, 'get', headers=self.headers) as get_mock: + self.test.get_api_key(self.jarvis_api) + self.queue_input("nba") + self.test.search_league(self.jarvis_api) + get_mock.assert_called_with( + "https://api-basketball.p.rapidapi.com/leagues?search=nba", headers=self.headers) + + def test_list_leagues(self): + if self.unable_to_test_plugin: + return + with patch.object(requests, 'get', headers=self.headers) as get_mock: + self.test.get_api_key(self.jarvis_api) + self.test.list_leagues(self.jarvis_api) + get_mock.assert_called_with( + "https://api-basketball.p.rapidapi.com/leagues", headers=self.headers) + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_binary.py b/jarviscli/tests/test_binary.py new file mode 100644 index 000000000..85839873c --- /dev/null +++ b/jarviscli/tests/test_binary.py @@ -0,0 +1,36 @@ +import unittest +from tests import PluginTest +from plugins import binary + + +class BinaryTest(PluginTest): + def setUp(self): + self.test = self.load_plugin(binary.binary) + + def test_simple_1(self): + self.test.run("0") + self.assertEqual(self.history_say().last_text(), "0") + + def test_simple_2(self): + self.test.run("2") + self.assertEqual(self.history_say().last_text(), "10") + + def test_simple_3(self): + self.test.run("73") + self.assertEqual(self.history_say().last_text(), "1001001") + + def test_large_1(self): + self.test.run("1978273") + self.assertEqual(self.history_say().last_text(), "111100010111110100001") + + def test_negative_1(self): + self.test.run("-1") + self.assertEqual(self.history_say().last_text(), "-1") + + def test_negative_2(self): + self.test.run("-7289") + self.assertEqual(self.history_say().last_text(), "-1110001111001") + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_cat_fact.py b/jarviscli/tests/test_cat_fact.py new file mode 100644 index 000000000..a597dd011 --- /dev/null +++ b/jarviscli/tests/test_cat_fact.py @@ -0,0 +1,24 @@ +import unittest +from tests import PluginTest +from plugins.cat_fact import cat_fact +from mock import patch, call +import requests + + +class CatFactTest(PluginTest): + """ + Tests For Cat Fact Plugin + """ + + def setUp(self): + self.test = self.load_plugin(cat_fact) + + def test_main(self): + with patch.object(requests, 'get') as get_mock: + self.test(self.jarvis_api, "") + get_mock.assert_called_with( + "https://catfact.ninja/fact") + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_chuck.py b/jarviscli/tests/test_chuck.py new file mode 100644 index 000000000..f35db08f0 --- /dev/null +++ b/jarviscli/tests/test_chuck.py @@ -0,0 +1,24 @@ +import unittest +from tests import PluginTest +from plugins.chuck import chuck +from mock import patch, call +import requests + + +class ChuckTest(PluginTest): + """ + Tests For Chuck Plugin + """ + + def setUp(self): + self.test = self.load_plugin(chuck) + + def test_main(self): + with patch.object(requests, 'get') as get_mock: + self.test(self.jarvis_api, "") + get_mock.assert_called_with( + "https://api.chucknorris.io/jokes/random") + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_corona.py b/jarviscli/tests/test_corona.py new file mode 100644 index 000000000..aa41d29c8 --- /dev/null +++ b/jarviscli/tests/test_corona.py @@ -0,0 +1,24 @@ +import unittest +from tests import PluginTest +from plugins.corona import CoronaInfo +from mock import patch, call +import requests + + +class CoronaInfoTest(PluginTest): + """ + Tests For CoronaInfo Plugin + """ + + def setUp(self): + self.test = self.load_plugin(CoronaInfo) + + def test_get_corona_info(self): + with patch.object(requests, 'get') as get_mock: + self.test.get_corona_info("usa") + get_mock.assert_called_with( + "https://api.covid19api.com/summary") + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_football.py b/jarviscli/tests/test_football.py new file mode 100644 index 000000000..6a87cae13 --- /dev/null +++ b/jarviscli/tests/test_football.py @@ -0,0 +1,47 @@ +import unittest +from tests import PluginTest +from plugins.football import Football +from mock import patch, call +import requests +import datetime + +""" + Provided By Plugin Creator +""" +API_KEY = '1ebd3b92bf5041249f8c1e7a540ce98c' +headers = {'X-Auth-Token': API_KEY} + + +class FootballTest(PluginTest): + """ + Tests For FootBall Plugin + + """ + + def setUp(self): + self.test = self.load_plugin(Football) + + def test_get_competition(self): + with patch.object(requests, 'get', headers=headers) as get_mock: + self.queue_input("0") + self.test.get_competition(self.jarvis_api) + get_mock.assert_called_with( + "https://api.football-data.org/v2/competitions?plan=TIER_ONE", headers=headers) + + def test_competition(self): + with patch.object(requests, 'get', headers=headers) as get_mock: + compId = 1 + self.test.competition(self.jarvis_api, compId) + get_mock.assert_called_with( + "https://api.football-data.org/v2/competitions/{}/standings".format(compId), headers=headers) + + def test_matches(self): + with patch.object(requests, 'get', headers=headers) as get_mock: + compId = 1 + self.test.matches(self.jarvis_api, compId) + get_mock.assert_called_with( + "https://api.football-data.org/v2/matches?competitions={}".format(compId), headers=headers) + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_geocode.py b/jarviscli/tests/test_geocode.py index 1cfcfc5ec..6454b86a2 100644 --- a/jarviscli/tests/test_geocode.py +++ b/jarviscli/tests/test_geocode.py @@ -8,6 +8,7 @@ class MockResponse: """ This class is used to create a mock Response from requests.get """ + def __init__(self, text): self.text = text @@ -19,6 +20,7 @@ class GeocoderTest(PluginTest): """ This class is testing the geocode plugin. """ + def setUp(self): self.test_geocoder = self.load_plugin(Geocoder) diff --git a/jarviscli/tests/test_joke_of_day.py b/jarviscli/tests/test_joke_of_day.py new file mode 100644 index 000000000..952f6231a --- /dev/null +++ b/jarviscli/tests/test_joke_of_day.py @@ -0,0 +1,24 @@ +import unittest +from tests import PluginTest +from plugins.joke_of_day import joke_of_day +from mock import patch, call +import requests + + +class joke_of_day_test(PluginTest): + """ + Tests For joke_of_day Plugin + """ + + def setUp(self): + self.test = self.load_plugin(joke_of_day) + + def test_get_joke(self): + with patch.object(requests, 'get') as get_mock: + self.test.get_joke(self.jarvis_api) + get_mock.assert_called_with( + "https://api.jokes.one/jod") + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/tests/test_tasks.py b/jarviscli/tests/test_tasks.py new file mode 100644 index 000000000..7e6329ff5 --- /dev/null +++ b/jarviscli/tests/test_tasks.py @@ -0,0 +1,92 @@ +import unittest +from tests import PluginTest +from plugins.tasks import TaskManager +from packages.memory.memory import Memory + + +class TaskManagerTest(PluginTest): + """ + Tests For TaskManger Plugin + + """ + + def setUp(self): + self.test = self.load_plugin(TaskManager) + self.test.load_tasks() + + def test_add_task(self): + self.queue_input("test_task") + self.test.add_task(self.jarvis_api) + self.assertTrue(self.lookup_taks_in_memory("test_task")) + + def test_edit_task(self): + self.queue_input("test_task") + self.test.add_task(self.jarvis_api) + self.queue_input(self.get_task_count()) + self.queue_input("test_task_updated") + self.test.update_task(self.jarvis_api) + self.assertTrue(self.lookup_taks_in_memory("test_task_updated")) + self.assertFalse(self.lookup_taks_in_memory("test_task")) + + def test_add_priority(self): + self.queue_input("test_task") + self.test.add_task(self.jarvis_api) + self.queue_input(self.get_task_count()) + self.queue_input(1) + self.test.add_priority_to_task(self.jarvis_api) + self.assertTrue(self.lookup_taks_priority_in_memory("test_task", "High")) + + def test_delete_task(self): + self.queue_input("test_task") + self.test.add_task(self.jarvis_api) + self.queue_input(self.get_task_count()) + self.test.delete_task(self.jarvis_api) + self.assertFalse(self.lookup_taks_in_memory("test_task")) + + def get_task_count(self): + m = Memory("tasks.json") + task_list = m.get_data("tasks_list") + if task_list is None: + return 0 + return len(task_list) + + def lookup_taks_priority_in_memory(self, task_name, desired_priority): + m = Memory("tasks.json") + task_list = m.get_data("tasks_list") + if task_list is None: + return False + result = False + for i in range(len(task_list)): + if task_list[i]["name"] == task_name: + try: + result = task_list[i]["priority"] == desired_priority + except BaseException: + pass + self.remove_task_from_memory(task_name) + return result + + def lookup_taks_in_memory(self, task_name): + m = Memory("tasks.json") + task_list = m.get_data("tasks_list") + if task_list is None: + return False + result = False + for i in range(len(task_list)): + if task_list[i]["name"] == task_name: + result = True + self.remove_task_from_memory(task_name) + return result + + # delete task which was added while testing + def remove_task_from_memory(self, task_name): + m = Memory("tasks.json") + task_list = m.get_data("tasks_list") + if task_list is None: + return + old_task = filter(lambda x: x["name"] != task_name, task_list) + m.update_data("tasks_list", list(old_task)) + m.save() + + +if __name__ == '__main__': + unittest.main() diff --git a/jarviscli/ui/CmdInterpreter.py b/jarviscli/ui/cmd_interpreter.py similarity index 99% rename from jarviscli/ui/CmdInterpreter.py rename to jarviscli/ui/cmd_interpreter.py index a4b84c0c1..906fd7f45 100644 --- a/jarviscli/ui/CmdInterpreter.py +++ b/jarviscli/ui/cmd_interpreter.py @@ -167,7 +167,7 @@ def try_do(self, s): else: do(s) except Exception: - if self._api.is_spinner_running(): + if self.is_spinner_running(): self.spinner_stop("It seems some error has occured") print( Fore.RED diff --git a/jarviscli/ui/gui/__init__.py b/jarviscli/ui/gui/__init__.py new file mode 100644 index 000000000..571b86f55 --- /dev/null +++ b/jarviscli/ui/gui/__init__.py @@ -0,0 +1,34 @@ +import colorama +import kivy +from kivy.utils import platform + +_JARVIS_APP = None + + +def app(): + return _JARVIS_APP + + +def main(): + from jarvisgui.application import JarvisApp, JarvisGuiAPI + from jarviscli.Jarvis import Jarvis + + # enable color on windows + colorama.init() + # start Jarvis + if platform == 'android': + from jarvisgui.android_plugins import build_plugin_manager + else: + from jarviscli import build_plugin_manager + + plugin_manager = build_plugin_manager() + jarviscli = Jarvis(plugin_manager, jarvis_api_class=JarvisGuiAPI) + jarvisgui = JarvisApp(jarviscli) + global _JARVIS_APP + _JARVIS_APP = jarvisgui + + jarvisgui.run() + + +if __name__ == '__main__': + main() diff --git a/jarviscli/ui/gui/android_plugins.py b/jarviscli/ui/gui/android_plugins.py new file mode 100644 index 000000000..c4ee46486 --- /dev/null +++ b/jarviscli/ui/gui/android_plugins.py @@ -0,0 +1,15 @@ +############################### +# AUTO-GENERATED FILE # +# DO NOT MODIFY # +############################### + +import plugins.dice + +from plugin_manager import PluginManager + + +def build_plugin_manager(): + plugin_manager = PluginManager() + plugin_manager.add(plugins.dice.Roll()) + + return plugin_manager diff --git a/jarviscli/ui/gui/application.py b/jarviscli/ui/gui/application.py new file mode 100644 index 000000000..9196547f1 --- /dev/null +++ b/jarviscli/ui/gui/application.py @@ -0,0 +1,41 @@ +from kivy.app import App +from kivy.lang import Builder +from kivy.uix.screenmanager import ScreenManager + +from ui.gui.screen_prompt import PromptScreen + +Builder.load_file('ui/gui/screen_prompt.kv') + + +class GuiIO(): + def __init__(self, app): + self.app = app + + def say(self, text, color=""): + self.app.say(text) + + def input(self, prompt="", color=""): + return '' + + def exit(self): + pass + + +class JarvisApp(App): + def __init__(self, jarvis): + super().__init__() + self.jarvis = jarvis + self.api_io = GuiIO(self) + self.jarvis.register_io(self.api_io) + + def build(self): + self.screen_manager = ScreenManager() + + self.prompt_screen = PromptScreen(self.jarvis, name='Prompt') + self.screen_manager.add_widget(self.prompt_screen) + + self.screen_manager.current = 'Prompt' + return self.screen_manager + + def say(self, text): + self.prompt_screen.impl.say(text) diff --git a/jarviscli/ui/gui/gen_android_plugin.py b/jarviscli/ui/gui/gen_android_plugin.py new file mode 100644 index 000000000..ecdf94c4b --- /dev/null +++ b/jarviscli/ui/gui/gen_android_plugin.py @@ -0,0 +1,3 @@ +from main import dump_android_plugins + +dump_android_plugins() diff --git a/jarviscli/ui/gui/screen_prompt.kv b/jarviscli/ui/gui/screen_prompt.kv new file mode 100644 index 000000000..8cd83edc4 --- /dev/null +++ b/jarviscli/ui/gui/screen_prompt.kv @@ -0,0 +1,29 @@ +: + impl: _prompt + jarvis: None + + Prompt: + id: _prompt + jarvis: root.jarvis + +: + output: _output + prompt_text: _prompt_text + + orientation: 'vertical' + TextInput: + id: _output + multiline: True + text: "Test" + disabled: True + + BoxLayout: + size_hint: 1.0, 0.2 + orientation: 'horizontal' + TextInput: + id: _prompt_text + multiline: False + text: 'TEST' + Button: + text: "OK" + on_press: root.press_ok() diff --git a/jarviscli/ui/gui/screen_prompt.py b/jarviscli/ui/gui/screen_prompt.py new file mode 100644 index 000000000..c8f22715d --- /dev/null +++ b/jarviscli/ui/gui/screen_prompt.py @@ -0,0 +1,17 @@ +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.screenmanager import Screen + + +class PromptScreen(Screen): + def __init__(self, jarvis, **args): + super().__init__(**args) + self.jarvis = jarvis + + +class Prompt(BoxLayout): + def press_ok(self): + self.jarvis.execute_once(self.prompt_text.text) + self.prompt_text.text = '' + + def say(self, text): + self.output.text += text + '\n' diff --git a/jarviscli/ui/server/__init__.py b/jarviscli/ui/server/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/jarviscli/ui/server/server.py b/jarviscli/ui/server/server.py new file mode 100644 index 000000000..5827e304f --- /dev/null +++ b/jarviscli/ui/server/server.py @@ -0,0 +1,97 @@ +import re + +from flask import Flask + +from jarvis import Jarvis +from language import snips + + +class ServerIO: + def __init__(self): + self.recorded_texts = [] + + def say(self, text, color=''): + self.recorded_texts.append(text) + + def input(self, prompt="", color=""): + # TO IMPLEMENT PROBABLY LATER + # because 'input' might be tricky to implement + pass + + def exit(self): + # STOP SERVER + pass + + def fetch_recorded_texts(self): + tmp = self.recorded_texts + self.recorded_texts = [] + return tmp + + +class JarvisServer: + + def __init__(self): + + self.server_app = Flask(__name__) + self.host_name = "0.0.0.0" + self.port = 8008 + self.auth_string = "new String that I created just so I could access this just for myself" + self.routes = [ + dict(route="/health", endpoint="/health", func=self._health) + ] + + def start_server(self): + print("Starting a thread for home server!") + + from main import build_plugin_manager + plugin_manager = build_plugin_manager() + language_parser = snips.LanguageParser() + jarvis_server = JarvisServer() + self.io = ServerIO() + + self.jarvis = Jarvis(language_parser, plugin_manager, jarvis_server) + self.jarvis.register_io(self.io) + + self.server_app.run(host=self.host_name, port=self.port, threaded=True, debug=True, use_reloader=False) + + def check_running(self) -> bool: + pass + + def init_server_endpoints(self, jarvis_plugins): + + self._get_all_routes(jarvis_plugins) + + for route in self.routes: + try: + self.server_app.add_url_rule( + route["route"], + route["endpoint"], + route["func"] + ) + except Exception as e: + print(e) + + def _wrap_plugin(self, plugin): + def _run(): + plugin.run(self.jarvis.jarvis_api, '') + _text = self.io.fetch_recorded_texts() + print(_text) + return '\n'.join(_text) + return _run + + def _get_all_routes(self, jarvis_plugins): + for plugin in jarvis_plugins: + route = dict() + endpoint_string = "/" + '_'.join(re.findall(r"[\w']+", plugin.get_name())) + + if len(plugin.get_plugins().values()) != 0: + self._get_all_routes(plugin.get_plugins().values()) + else: + route["route"] = endpoint_string + route["endpoint"] = endpoint_string + _plug = plugin + route["func"] = self._wrap_plugin(plugin) + self.routes.append(route) + + def _health(self): + return "Healthy" diff --git a/jarviscli/utilities/GeneralUtilities.py b/jarviscli/utilities/GeneralUtilities.py index 7ce0bc937..f3d69733e 100644 --- a/jarviscli/utilities/GeneralUtilities.py +++ b/jarviscli/utilities/GeneralUtilities.py @@ -1,29 +1,37 @@ # -*- coding: utf-8 -*- -import sys -import os -from colorama import Fore import distutils.spawn +import os +from platform import win32_ver +import sys +import warnings +from colorama import Fore MACOS = 'darwin' WIN = 'win32' IS_MACOS = sys.platform == MACOS IS_WIN = sys.platform == WIN - - -def wordIndex(data, word): - wordList = data.split() - return wordList.index(word) +WIN_VER = None +if IS_WIN: + WIN_VER = win32_ver()[0] def print_say(text, self, color=""): """ - This method give the jarvis the ability to print a text - and talk when sound is enable. + Gives Jarvis the ability to print text + and talk when sound is enabled. :param text: the text to print (or talk) color: Fore.COLOR (ex Fore.BLUE), color for text :return: Nothing to return. - """ + + .. deprecated:: + Use ``JarvisAPI.say(text, color="", speak=True))`` instead. + """ + warnings.warn( + "GeneralUtilities.print_say is deprecated now and will be \ + removed in the future. Please use \ + ``JarvisAPI.say(text, color=\"\", speak=True))`` instead.", + DeprecationWarning) print(color + text + Fore.RESET) if self.enable_voice: self.speech.text_to_speech(text) @@ -56,14 +64,28 @@ def noop_wrapper(func): def wrapped(*args, **kwargs): if sys.platform == platform: if not silent: - print('{}Command is unsupported for platform `{}`{}'. - format(Fore.RED, sys.platform, Fore.RESET)) + print( + '{}Command is unsupported for platform `{}`{}'.format( + Fore.RED, sys.platform, Fore.RESET)) else: func(*args, **kwargs) + return wrapped + return noop_wrapper def executable_exists(name): binary_path = distutils.spawn.find_executable(name) return binary_path is not None and os.access(binary_path, os.X_OK) + + +def get_parent_directory(path): + """ + Removes the file name from the folder and returns + the remaining path + """ + path = path.split('/') + path.pop() + destination = '/'.join(path) + return destination diff --git a/jarviscli/utilities/notification.py b/jarviscli/utilities/notification.py index 3172dec82..0f6c16983 100644 --- a/jarviscli/utilities/notification.py +++ b/jarviscli/utilities/notification.py @@ -1,4 +1,4 @@ -from utilities.GeneralUtilities import IS_MACOS, executable_exists +from utilities.GeneralUtilities import IS_MACOS, IS_WIN, WIN_VER, executable_exists NOTIFY_LOW = 0 @@ -21,6 +21,13 @@ def notify__LINUX(name, body, urgency=NOTIFY_NORMAL): system("notify-send -u {} '{}' '{}'".format(urgency, str(name), str(body))) +WIN_URGENCY_CONVERTER = {0: None, 1: 'icons\\default.ico', 2: "icons\\warn.ico"} + + +def notify__WIN10(name, body, urgency=NOTIFY_NORMAL): + win10toast.ToastNotifier().show_toast(name, body, duration=5, icon_path=WIN_URGENCY_CONVERTER[urgency]) + + GUI_FALLBACK_DISPLAY_TIME = 3000 @@ -51,6 +58,9 @@ def notify__CLI_FALLBACK(name, body, urgency=NOTIFY_NORMAL): if IS_MACOS: import pync notify = notify__MAC +elif IS_WIN and WIN_VER == '10': + import win10toast + notify = notify__WIN10 else: if executable_exists("notify-send"): from os import system diff --git a/jarviscli/utilities/voice.py b/jarviscli/utilities/voice.py index 079f63817..f43b683f2 100644 --- a/jarviscli/utilities/voice.py +++ b/jarviscli/utilities/voice.py @@ -2,20 +2,28 @@ from utilities.GeneralUtilities import IS_MACOS, IS_WIN import os import subprocess -from gtts import gTTS -from pydub import AudioSegment, playback +try: + from gtts import gTTS + from pydub import AudioSegment, playback + # patch pydup - hide std output + FNULL = open(os.devnull, 'w') + _subprocess_call = playback.subprocess.call + playback.subprocess.call = lambda cmd: _subprocess_call(cmd, stdout=FNULL, stderr=subprocess.STDOUT) -# patch pydup - hide std output -FNULL = open(os.devnull, 'w') -_subprocess_call = playback.subprocess.call -playback.subprocess.call = lambda cmd: _subprocess_call(cmd, stdout=FNULL, stderr=subprocess.STDOUT) + HAS_GTTS = True +except ImportError: + HAS_GTTS = False if IS_MACOS: from os import system else: - import pyttsx3 + try: + import pyttsx3 + HAS_PYTTSX3 = True + except ImportError: + HAS_PYTTSX3 = False def create_voice(self, gtts_status, rate=180): @@ -24,7 +32,7 @@ def create_voice(self, gtts_status, rate=180): :param rate: Speech rate for the engine (if supported by the OS) """ - if gtts_status is True: + if HAS_GTTS and gtts_status is True: return VoiceGTTS() else: if IS_MACOS: @@ -77,35 +85,31 @@ def text_to_speech(self, speech): system('say $\'{}\''.format(speech)) -class VoiceLinux(): +class Voice_general(): def __init__(self, rate): - """ - This constructor creates a pyttsx3 object. - """ self.rate = rate self.min_rate = 50 self.max_rate = 500 self.create() def create(self): - """ - This method creates a pyttsx3 object. - :return: Nothing to return. - """ self.engine = pyttsx3.init() self.engine.setProperty('rate', self.rate) def destroy(self): """ - This method destroys a pyttsx3 object in order + Destroys a pyttsx3 object in order to create a new one in the next interaction. - :return: Nothing to return. """ del self.engine + +class VoiceLinux(Voice_general): + def __init__(self, rate): + super().__init__(rate) + def text_to_speech(self, speech): """ - This method converts a text to speech. :param speech: The text we want Jarvis to generate as audio :return: Nothing to return. A bug in pyttsx3 causes segfault if speech is '', so used 'if' to avoid that. @@ -121,7 +125,7 @@ def text_to_speech(self, speech): def change_rate(self, delta): """ - This method changes the speech rate which is used to set the speech + Changes the speech rate which is used to set the speech engine rate. Restrict the rate to a usable range. :param delta: The amount to modify the rate from the current rate. Note: The actual engine rate is set by create(). @@ -135,20 +139,14 @@ def change_rate(self, delta): class VoiceWin(): + def __init__(self, rate): - """ - This constructor creates a pyttsx3 object. - """ self.rate = rate self.min_rate = 50 self.max_rate = 500 self.create() def create(self): - """ - This method creates a pyttsx3 object. - :return: Nothing to return. - """ self.engine = pyttsx3.init() self.engine.setProperty('rate', self.rate) diff --git a/setup.sh b/setup.sh index 84645b8cd..adb600ed7 100755 --- a/setup.sh +++ b/setup.sh @@ -1,11 +1,11 @@ #!/bin/bash cd $(dirname $0) -if python --version; then +if python --version &> /dev/null; then python installer -elif python3 --version; then +elif python3 --version &> /dev/null; then python3 installer -elif python2 --version; then +elif python2 --version &> /dev/null; then python2 installer else echo "Could not find Python installation"