|
| 1 | +# W.I.L.L |
| 2 | + |
| 3 | +W.I.L.L is a Python based, event and plugin driven personal assistant. |
| 4 | + |
| 5 | + - Smart, efficient, spacy driven multithreaded nlp |
| 6 | + - A full, event driven plugin framework supporting multiple types of plugins with room for expansion |
| 7 | + - Can be used with a variety of different clients |
| 8 | + |
| 9 | +Plugins: |
| 10 | + - Can be written in Python or Json (more coming!) |
| 11 | + - Are activiated on W.I.L.L initiation or shutdown |
| 12 | + - Are activated based on entity recognition, POS tags, keywords, or even the presence of questions |
| 13 | + - Can interactively communicate with the user without having to re-run parsing every time |
| 14 | + |
| 15 | + |
| 16 | +W.I.L.L started as my personal project but now has an active community of users and contributers. If you want to contribute by adding plugins, or even editing the framework, just submit a pull reuqest! Working plugins that are legal, useful, and original will always be accepted. . |
| 17 | + |
| 18 | +### Version |
| 19 | +3.0.1 |
| 20 | + |
| 21 | +### Tech |
| 22 | + |
| 23 | +W.I.L.L is open source with a [public repository][will] |
| 24 | + on GitHub. |
| 25 | + |
| 26 | +### Installation |
| 27 | + |
| 28 | +W.I.L.L is available via pip, but first some things must be obtained |
| 29 | + |
| 30 | +To run the search module, you need a wolframalpha API key. Get one from http://products.wolframalpha.com/api/ |
| 31 | + |
| 32 | +### Plugins |
| 33 | + |
| 34 | +The following plugins are currently available for W.I.L.L, with many more on the way. |
| 35 | + |
| 36 | +Finished: |
| 37 | +* Open (Use xdg-open, start, or open to open any program or file on the operating system) |
| 38 | +* Search (Use Google, Wolframalpha, and Wikipedia to find an answer to most questions) |
| 39 | +* Execute (Execute a terminal command) |
| 40 | + |
| 41 | +In development: |
| 42 | +* Chromecast (Uses pychromecast and splinter to cast several popular streaming services) |
| 43 | +* Spotify (Access Spotify premium API) |
| 44 | + |
| 45 | +### Plugin Creation |
| 46 | + |
| 47 | +Writing a plugin for W.I.L.L is easier than you might think. Currently, W.I.L.L supports two types of plugins. Json and Python. All plugins should go in W.I.L.L/plugins. |
| 48 | + |
| 49 | +Special thanks to https://github.com/brenttaylor for his contributions to the plugin framework |
| 50 | + |
| 51 | +#### Python |
| 52 | +The way W.I.L.L is designed, you can plug in pretty much any python file that you've made, just by adding a decorator. |
| 53 | +Let's write a plugin. First, you need to import the API that W.I.L.L uses. If you're in the plugin directory, you can do that with this line: |
| 54 | +```python |
| 55 | +import will.plugins.API as API |
| 56 | +``` |
| 57 | +Now we can write a function that we want W.I.L.L to execute. All plugin functions should accept 4 arguments. The first will be the first word of the command. The second will be the raw, full text of the command. The third should be *args, and that will contain all of the nlp data that your plugin might want to access. The fourth should be **kwargs, and that will contain metadata about the command. You shouldn't needed this unless you're debugging the plugin framework |
| 58 | +```python |
| 59 | +def test_func(leader, sentence, *args, **kwargs): |
| 60 | + print leader #The first word of the command |
| 61 | + print sentence #The entire command |
| 62 | + print args['ents'] #A dict of recognized entities |
| 63 | + print args['struct'] #A dict of recognized pos tags |
| 64 | + print kwargs #Dispatcher debug info |
| 65 | +``` |
| 66 | +Now that we have that function, let's hook it up to the W.I.L.L API with a decorator. There are currently 4 functions inside the api that you can use. The first two go together. `@init`, which will run on initialization of W.I.L.L, and `@shutdown`, which will run as W.I.L.L exits. These do not need the standard arugments in the accompanying functions. Next, we have `@API.subscribe_to_any`. That will, as the name implies, be activated when any command is entered into W.I.L.L. Finally, the most important one. `@API.subscribe_to`. Unlike the others, this requires input data, so W.I.L.L can know under what circumstances to run it. You'll pass it a dictionary containing a neat package of information about the plugin. Included in this dictionary are the name of the plugin, what entities the plugin needs (list of supported entities can be found at https://spacy.io/docs#annotation-ner), what parts of speech the plugin needs, if the plugin needs questions, and any key words that it needs. Here's a sample of the dictionary. |
| 67 | +```python |
| 68 | +{ |
| 69 | +"name" : "test", |
| 70 | +"ents_needed" : ["PERSON"], #Could also be left empty by writing "ents_needed" : False |
| 71 | +"structure" : {"needed":["VERB"]}, #Could also be left empty with "structure" : {"needed":False} |
| 72 | +"questions_needed" : False, #Always a bool, True or False if it needs questions or not |
| 73 | +"key_words" : ["test"] #Can be left empty with"key_words" : False |
| 74 | +} |
| 75 | +``` |
| 76 | +Finally, you can load values in the config by importing will.config. Import it like this: |
| 77 | +```python |
| 78 | +import will.config as config |
| 79 | +``` |
| 80 | +There are threwe methods available to plugins in config, `load_config`,`remove_config`, and `add_config`. `load_config`takes a header of something already in the config and returns the value. `add_config` updates the config with a dictionary passed to it, and `remove_config` removes takes a header and removes that item from the config. Like so: |
| 81 | +```python |
| 82 | +import will.config as config |
| 83 | + |
| 84 | +#Add an item to the config |
| 85 | +config.add_config({"docs_read" : True}) |
| 86 | + |
| 87 | +#Load an item from the config |
| 88 | +docs_read = config.load_config("docs_read") #The value you just added |
| 89 | + |
| 90 | +#Remove the item from the config |
| 91 | +config.remove_config("docs_read") |
| 92 | +``` |
| 93 | + |
| 94 | +Now that we've written our function defined the plugin, and familiarized ourself with the methods, let's tie it all together in a file. |
| 95 | + |
| 96 | +`W.I.L.L/plugins/test.py` |
| 97 | +```python |
| 98 | +import will.plugins.API as API |
| 99 | +import will.config as config |
| 100 | + |
| 101 | +#A function that will run on initialization |
| 102 | +@init |
| 103 | +def on_init(): |
| 104 | + print "W.I.L.L Started!" |
| 105 | + #Add an item into the config |
| 106 | + config.add_config({"will_started" : True}) |
| 107 | + |
| 108 | +#The dictionary we made earlier |
| 109 | +plugin_data = |
| 110 | +{ |
| 111 | +"name" : "test", |
| 112 | +"ents_needed" : ["PERSON"], #Could also be left empty by writing "ents_needed" : False |
| 113 | +"structure" : {"needed":["VERB"]}, #Could also be left empty with "structure" : {"needed":False} |
| 114 | +"questions_needed" : False, #Always a bool, True or False if it needs questions or not |
| 115 | +"key_words" : ["test"] #Can be left empty with"key_words" : False |
| 116 | +} |
| 117 | + |
| 118 | +#Subscribe the plugin_data dictionary to the function we wrote earlier |
| 119 | +@API.subscribe_to(plugin_data) |
| 120 | +def test_func(leader, sentence, *args, **kwargs): |
| 121 | + print leader #The first word of the command |
| 122 | + print sentence #The entire command |
| 123 | + print args['ents'] #A dict of recognized entities |
| 124 | + print args['structure']['tags'] #A dict of recognized pos tags |
| 125 | + print kwargs #Dispatcher debug info |
| 126 | + #While we're in the function, why not load the config item we added earlier |
| 127 | + will_started = config.load_config("will_started") |
| 128 | + print will_started |
| 129 | + |
| 130 | +#A function that will run as will exits |
| 131 | +@shutdown |
| 132 | +def on_shutdown(): |
| 133 | + print "W.I.L.L is shutting down" |
| 134 | + #Remove the config item |
| 135 | + config.remove_config("will_started") |
| 136 | +``` |
| 137 | + |
| 138 | +Now let's run our plugin! So we can see what's going on, let's use W.I.L.L in the terminal |
| 139 | +```python |
| 140 | +>>>import will #This might take some time as the nlp models take time to load |
| 141 | +>>>will.run() |
| 142 | +W.I.L.L Started! |
| 143 | +>>>will.main("test this sentence includes Will, a person") |
| 144 | +test |
| 145 | +test this sentence includes Will, a person |
| 146 | +{"Will", "PERSON"} |
| 147 | +{'a': 'DT', 'sentence': 'NN', 'this': 'DT', 'is': 'VBZ', 'who': 'WP', ',': ',', 'includes': 'VBZ', 'Will': 'NNP', u'person': 'NN', 'test': 'NN'} |
| 148 | +{'signal': 'test', 'sender': _Any} |
| 149 | +True |
| 150 | +>>>will.exit_func() #This will be called however W.I.L.L exits, you don't need to call it explicitly |
| 151 | +W.I.L.L is shutting down |
| 152 | +``` |
| 153 | +And there you have it! A working W.I.L.L plugin! |
| 154 | +### Todos |
| 155 | + |
| 156 | + - Add JSON plugin docs |
| 157 | + - Add client creation docs |
| 158 | + - Chromecast plugin |
| 159 | + - Spotify plugin |
| 160 | + - Add more structural NLP |
| 161 | + - Add more clients |
| 162 | + |
| 163 | +License |
| 164 | +---- |
| 165 | + |
| 166 | +MIT |
| 167 | + |
| 168 | +These docs made on dillinger.io, off the default template. |
| 169 | + |
| 170 | + |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | + [will]: <https://github.com/ironman5366/W.I.L.L> |
0 commit comments