├── weatherdata ├── manual.md ├── weatherdata.py └── weatherdata_action.md ├── githubstatus ├── manual.md ├── githubstatus_action.md └── githubstatus.py ├── differ ├── differ_action.md ├── manual.md └── differ.py ├── bitbucketstatus ├── manual.md ├── bitbucketstatus_action.md └── bitbucketstatus.py ├── string_hashes ├── strings_hashes_action.md ├── manual.md └── strings_hashes.py ├── postal_codes ├── city_by_postalcode_action.md ├── postalcode_by_city_action.md ├── city_by_postalcode_manual.md ├── postalcode_by_city_manual.md ├── city_by_postalcode.py └── postalcode_by_city.py ├── search_and_replace ├── search_and_replace_action.md ├── manual.md └── search_and_replace.py ├── link_shortener_isgd ├── link_shortener_isgd_action.md ├── manual.md └── link_shortener_isgd.py ├── link_shortener_drngdk ├── link_shortener_drngdk_action.md ├── manual.md └── link_shortener_drngdk.py ├── LICENSE.md ├── README.md └── draftsaction.py /weatherdata/manual.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kultprok/pythonista-drafts-recipes/HEAD/weatherdata/manual.md -------------------------------------------------------------------------------- /weatherdata/weatherdata.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kultprok/pythonista-drafts-recipes/HEAD/weatherdata/weatherdata.py -------------------------------------------------------------------------------- /githubstatus/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | Sometimes, Github servers are down. Fortunately, Github provides an API to check the status of its servers. 4 | 5 | Using the script via Drafts is easy. Just make a new note in Drafts, enter whatever you want - some whitespace for instance - and trigger the action. The script will then call the status API and then create a new note in Drafts with the latest status info. 6 | 7 | # Remarks 8 | 9 | Drafts won't trigger, when a note is empty, so at least one character has to be provided. 10 | -------------------------------------------------------------------------------- /differ/differ_action.md: -------------------------------------------------------------------------------- 1 | differ 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *differ*. 7 | 8 | pythonista://differ?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Differ&url=pythonista%3A%2F%2Fdiffer%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) 15 | -------------------------------------------------------------------------------- /bitbucketstatus/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | Sometimes, Bitbucket servers are down. Bitbucket provides a feed to check the status of its servers. 4 | 5 | Using the script via Drafts is easy. Just make a new note in Drafts, enter whatever you want - some whitespace for instance - and trigger the action. The script will then call the status API and then create a new note in Drafts with the latest status info. 6 | 7 | # Remarks 8 | 9 | - Drafts won't trigger, when a note is empty, so at least one character has to be provided. 10 | - The feed only contains warnings and alerts. If there is no such warning for the current day, it means, the servers are probably fine. 11 | -------------------------------------------------------------------------------- /weatherdata/weatherdata_action.md: -------------------------------------------------------------------------------- 1 | weatherdata 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *weatherdata*. 7 | 8 | pythonista://weatherdata?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Weather%20Data&url=pythonista%3A%2F%2Fweatherdata%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) 15 | -------------------------------------------------------------------------------- /string_hashes/strings_hashes_action.md: -------------------------------------------------------------------------------- 1 | string-hashes 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *string-hashes*. 7 | 8 | pythonista://string-hashes?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=String%20Hashes&url=pythonista%3A%2F%2Fstring-hashes%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) 15 | -------------------------------------------------------------------------------- /githubstatus/githubstatus_action.md: -------------------------------------------------------------------------------- 1 | githubstatus 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *githubstatus*. 7 | 8 | pythonista://githubstatus?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Github%20Server%20Status&url=pythonista%3A%2F%2Fgithubstatus%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) 15 | -------------------------------------------------------------------------------- /bitbucketstatus/bitbucketstatus_action.md: -------------------------------------------------------------------------------- 1 | bitbucketstatus 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *bitbucketstatus*. 7 | 8 | pythonista://bitbucketstatus?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Bitbucket%20Server%20Status&url=pythonista%3A%2F%2Fbitbucketstatus%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) 15 | -------------------------------------------------------------------------------- /postal_codes/city_by_postalcode_action.md: -------------------------------------------------------------------------------- 1 | city-by-postalcode 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *city-by-postalcode*. 7 | 8 | pythonista://city-by-postalcode?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Postal%20Code%20for%20City&url=pythonista%3A%2F%2Fcity-by-postalcode%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D 15 | ) 16 | -------------------------------------------------------------------------------- /postal_codes/postalcode_by_city_action.md: -------------------------------------------------------------------------------- 1 | postalcode-by-city 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *postalcode-by-city*. 7 | 8 | pythonista://postalcode-by-city?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Postal%20Code%20for%20City&url=pythonista%3A%2F%2Fpostalcode-by-city%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D 15 | ) 16 | -------------------------------------------------------------------------------- /search_and_replace/search_and_replace_action.md: -------------------------------------------------------------------------------- 1 | search-and-replace 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *search-and-replace*. 7 | 8 | pythonista://search-and-replace?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare yourself the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Search%20and%20Replace&url=pythonista%3A%2F%2Fsearch-and-replace%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) -------------------------------------------------------------------------------- /link_shortener_isgd/link_shortener_isgd_action.md: -------------------------------------------------------------------------------- 1 | link_shortener_isgd 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *link_shortener_isgd*. 7 | 8 | pythonista://link_shortener_isgd?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Shorten%20Link%20%28is.gd%29&url=pythonista%3A%2F%2Flink_shortener_isgd%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) -------------------------------------------------------------------------------- /link_shortener_drngdk/link_shortener_drngdk_action.md: -------------------------------------------------------------------------------- 1 | link_shortener_drngdk 2 | ========================= 3 | 4 | # The Drafts Action 5 | 6 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *link_shortener_drngdk*. 7 | 8 | pythonista://link_shortener_drngdk?action=run&argv=[[draft]] 9 | 10 | # Import Link 11 | 12 | If you're on an iOS device and have Drafts installed, you can spare the effort of manually creating the action in Drafts. Just click on the following link: 13 | 14 | [Import action to Drafts](drafts://x-callback-url/import_action?type=URL&name=Shorten%20Link%20%28drng.dk%29&url=pythonista%3A%2F%2Flink_shortener_drngdk%3Faction%3Drun%26argv%3D%5B%5Bdraft%5D%5D) 15 | -------------------------------------------------------------------------------- /postal_codes/city_by_postalcode_manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script is pretty straightforward. Use this syntax in Drafts, then trigger the action: 4 | 5 | [POSTALCODE], [COUNTRY ABBREVIATION] 6 | 7 | This is not case-sensitive. 8 | 9 | For example, if you want to know the postal codes of Beverky Hills: 10 | 11 | 90210, US 12 | 13 | For now, the API doesn't support empty country fields. This can be a hassle. You can find the country codes [here](http://www.zippopotam.us/#where), but the state codes can be a little hard to find. 14 | 15 | # Result 16 | 17 | A markdown list of all city with that postal code in the given country. 18 | 19 | # Remarks 20 | 21 | This script utilizes the awesome API of [Zippopotamus](http://www.zippopotam.us/). 22 | -------------------------------------------------------------------------------- /postal_codes/postalcode_by_city_manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script is pretty straightforward. Use this syntax in Drafts, then trigger the action: 4 | 5 | [CITY], [STATE ABBREVIATION], [COUNTRY ABBREVIATION] 6 | 7 | This is not case-sensitive. 8 | 9 | For example, if you want to know the postal codes of Beverky Hills: 10 | 11 | Beverly Hills, CA, US 12 | 13 | For now, the API doesn't support empty state and country fields. This can be a hassle. You can find the country codes [here](http://www.zippopotam.us/#where), but the state codes can be a little hard to find. 14 | 15 | # Result 16 | 17 | A markdown list of all postal codes for the city in the state of the given country. 18 | 19 | # Remarks 20 | 21 | This script utilizes the awesome API of [Zippopotamus](http://www.zippopotam.us/). 22 | -------------------------------------------------------------------------------- /link_shortener_drngdk/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script is pretty straightforward. Provide a URL to shorten, send it to [drng.dk](http://is.gd/) and copy the short-link to the clipboard. 4 | 5 | - Open Drafts and create a new note. 6 | - Type or paste a full URL, that you wish to shorten. Not less, not more. 7 | - Choose the corresponding action and trigger it. 8 | - Pythonista will open and run the script. 9 | - In case of an error, Pythonista will show a dialog informing you about the type of error. Then the script will return to Drafts. 10 | - In the more likely case of success. The script will open Drafts again, the shortened link was copied to the clipboard. 11 | 12 | # Remarks 13 | 14 | *drng.dk* provides a hassle-free API. There's no registration or login required. So there's no need to handle account credentials. 15 | -------------------------------------------------------------------------------- /link_shortener_isgd/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script is pretty straight-forward. Provide a URL to shorten, send it to [is.gd](http://is.gd/) and copy the short-link to the clipboard. 4 | 5 | - Open Drafts and create a new note. 6 | - Type or paste a full URL, that you wish to shorten. Not less, not more. 7 | - Choose the corresponding action and trigger it. 8 | - Pythonista will open and run the script. 9 | - In case of an error, Pythonista will show a dialog informing you about the type of error. Then the script will return to Drafts. 10 | - In the more likely case of success. The script will open Drafts again, the shortened link was copied to the clipboard. 11 | 12 | # Remarks 13 | 14 | I chose *is.gd* because of its hassle-free API. There's no registration or login required. So there's no need to handle account credentials. -------------------------------------------------------------------------------- /differ/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script will work like a command-line tool to compare the content of two given texts. The output of the help-command looks like this: 4 | 5 | usage: differ.py [-h] [-f STRING [STRING ...]] [-t STRING [STRING ...]] 6 | [-wc INT] 7 | 8 | diff two texts. 9 | 10 | optional arguments: 11 | -h, --help show this help message and exit 12 | -f STRING [STRING ...], --f STRING [STRING ...], -from STRING [STRING ...], --from STRING [STRING ...] 13 | the first text to compare 14 | -t STRING [STRING ...], --t STRING [STRING ...], -to STRING [STRING ...], --to STRING [STRING ...] 15 | the second text to compare 16 | -wc INT, --wc INT, -wrapcolumn INT, --wrapcolumn INT 17 | number of characters each column in diff should wrap 18 | to 19 | 20 | The diff table will be shown in the default browser. 21 | 22 | # Remarks 23 | 24 | It will create a file called *diff_table.html* in the working directory. 25 | -------------------------------------------------------------------------------- /link_shortener_drngdk/link_shortener_drngdk.py: -------------------------------------------------------------------------------- 1 | import clipboard 2 | from console import alert 3 | from json import loads 4 | from sys import argv, exit 5 | from urllib import urlopen 6 | import webbrowser 7 | 8 | def error_dialog(title, message): 9 | '''A diaolog box for error messages.''' 10 | try: 11 | alert(title, message) 12 | except KeyboardInterrupt: 13 | pass 14 | webbrowser.open('drafts://') 15 | exit(message) 16 | 17 | def shorten_with_drngdk(long_url): 18 | '''basic link-shortening via drng.dk.''' 19 | api_url_base = 'http://api.drng.dk/create-link.php?url=' 20 | try: 21 | response = urlopen(api_url_base + long_url) 22 | except IOError: 23 | error_dialog('Connection Error', 'Unable to perform request.') 24 | if response.getcode() == 200: 25 | jsonresponse = loads(response.read()) 26 | short_link = jsonresponse['link'] 27 | clipboard.set(short_link) 28 | else: 29 | error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read())) 30 | webbrowser.open('drafts://') 31 | 32 | if __name__ == '__main__': 33 | shorten_with_drngdk(argv[1]) 34 | -------------------------------------------------------------------------------- /link_shortener_isgd/link_shortener_isgd.py: -------------------------------------------------------------------------------- 1 | import clipboard 2 | from console import alert 3 | from sys import argv, exit 4 | from urllib import urlopen 5 | import webbrowser 6 | 7 | def error_dialog(title, message): 8 | '''A diaolog box for error messages.''' 9 | try: 10 | alert(title, message) 11 | except KeyboardInterrupt: 12 | pass 13 | # Open Drafts again. 14 | webbrowser.open('drafts://') 15 | exit(1) 16 | 17 | def shorten_with_isgd(long_url): 18 | '''basic link-shortening via is.gd.''' 19 | api_base_url = 'http://is.gd/create.php?format=simple&url={url}' 20 | try: 21 | response = urlopen(api_url_base.formtat(url=long_url)) 22 | except IOError: 23 | error_dialog('Connection Error', 'Unable to perform request.') 24 | if response.getcode() == 200: 25 | short_link = response.read() 26 | clipboard.set(short_link) 27 | else: 28 | error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read())) 29 | # Opening Drafts, the short-link is in the clipboard. 30 | webbrowser.open('drafts://') 31 | 32 | if __name__ == '__main__': 33 | shorten_with_isgd(argv[1]) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013, kultprok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /search_and_replace/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script will work like a command-line tool to replace the string in a given text. The output of the help-command looks like this: 4 | 5 | usage: search-and-replace.py [-h] [-old STRING|REGEXP] [-new STRING] [-c INT] 6 | [-f INT|re.FLAG] 7 | [STRING [STRING ...]] 8 | 9 | Search and Replace for Drafts. 10 | 11 | positional arguments: 12 | STRING the string to search in 13 | 14 | optional arguments: 15 | -h, --help show this help message and exit 16 | -old STRING|REGEXP, --old STRING|REGEXP 17 | the old string. regular expression allowed. 18 | -new STRING, --new STRING 19 | the new string to replace with. 20 | -c INT, --c INT, -count INT, --count INT 21 | the number of times to replace. 22 | -f INT|re.FLAG, --f INT|re.FLAG, -flags INT|re.FLAG, --flags INT|re.FLAG 23 | the flags for regeular expression substitution. 24 | 25 | # Remarks 26 | 27 | Possible inputs for flags: 28 | 29 | 2 = IGNORECASE 30 | 4 = LOCALE 31 | 8 = MULTILINE 32 | 16 = DOTALL 33 | 64 = VERBOSE -------------------------------------------------------------------------------- /bitbucketstatus/bitbucketstatus.py: -------------------------------------------------------------------------------- 1 | from console import alert 2 | import feedparser 3 | from sys import exit 4 | from urllib import urlopen, quote 5 | import webbrowser 6 | 7 | def error_dialog(title, message): 8 | '''A diaolog box for error messages.''' 9 | try: 10 | alert(title, message) 11 | except KeyboardInterrupt: 12 | pass 13 | webbrowser.open('drafts://') 14 | exit(message) 15 | 16 | def request(): 17 | '''a request to bitbuckets status feed.''' 18 | api_url_base = 'http://feeds.feedburner.com/BitBucketServerStatus' 19 | try: 20 | response = urlopen(api_url_base) 21 | except IOError: 22 | error_dialog('Connection Error', 'Unable to perform request.') 23 | if response.getcode() != 200: 24 | error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read())) 25 | return response.read() 26 | 27 | def bitbucket_status(): 28 | '''get information and admin messages for bitbucket servers from bitbucket feed.''' 29 | bitbucket_feed = request() 30 | feed = feedparser.parse(bitbucket_feed) 31 | output = ''' 32 | {title} 33 | ================ 34 | Last entries: 35 | '''.format(title=feed['feed']['title']) 36 | for entry in feed['entries']: 37 | output += '\n{date}: {comment}\n'.format(date=entry['title'], 38 | comment=entry['summary_detail']['value']) 39 | output += '\nFor further information: http://status.bitbucket.org/' 40 | webbrowser.open('drafts://x-callback-url/create?text={0}'.format(quote(output))) 41 | 42 | if __name__ == '__main__': 43 | bitbucket_status() -------------------------------------------------------------------------------- /postal_codes/city_by_postalcode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from console import alert 3 | from json import loads 4 | from sys import argv, exit 5 | from urllib import urlopen, quote 6 | import webbrowser 7 | 8 | def error_dialog(title, message): 9 | ''' 10 | a diaolog box for error messages. 11 | ''' 12 | try: 13 | alert(title, message) 14 | except KeyboardInterrupt: 15 | pass 16 | webbrowser.open('drafts://') 17 | exit(message) 18 | 19 | def handle_data(data): 20 | ''' 21 | process json response from zippopotamus. 22 | will return a markdown list of items. 23 | ''' 24 | city_json = loads(data) 25 | output = '' 26 | for item in city_json['places']: 27 | output += '- Ort: {place}, Region: {state}\n'.format(place=item['place name'], state=item['state']) 28 | return output 29 | 30 | def get_by_postalcode(data): 31 | ''' 32 | get all possible cities for a postal code in 33 | the given country. 34 | ''' 35 | api_url_base = 'http://api.zippopotam.us/{country}/{postcode}' 36 | try: 37 | postcode_, country_= [item.strip() for item in data.split(',')] 38 | except Exception as err: 39 | error_dialog(str(err.__class__), err.message) 40 | 41 | try: 42 | response = urlopen(api_url_base.format(country=country_, postcode=postcode_)) 43 | except IOError: 44 | error_dialog('Connection Error', 'Unable to perform request.') 45 | if response.getcode() == 200: 46 | postcode_data = handle_data(response.read()) 47 | webbrowser.open('drafts://x-callback-url/create?text={0}'.format(quote(postcode_data))) 48 | else: 49 | error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read())) 50 | 51 | if __name__ == '__main__': 52 | get_by_postalcode(argv[1]) -------------------------------------------------------------------------------- /postal_codes/postalcode_by_city.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from console import alert 3 | from json import loads 4 | from sys import argv, exit 5 | from urllib import urlopen, quote 6 | import webbrowser 7 | 8 | def error_dialog(title, message): 9 | ''' 10 | a diaolog box for error messages. 11 | ''' 12 | try: 13 | alert(title, message) 14 | except KeyboardInterrupt: 15 | pass 16 | webbrowser.open('drafts://') 17 | exit(message) 18 | 19 | def handle_data(data): 20 | ''' 21 | process json response from zippopotamus. 22 | will return a markdown list of items. 23 | ''' 24 | city_json = loads(data) 25 | output = '' 26 | for item in city_json['places']: 27 | output += '- City: {place}, Post code: {postcode}\n'.format(place=item['place name'], postcode=item['post code']) 28 | return output 29 | 30 | def get_by_city(data): 31 | ''' 32 | get all possible post codes for a city in 33 | the given country. 34 | ''' 35 | api_url_base = 'http://api.zippopotam.us/{country}/{state}/{city}' 36 | try: 37 | city_, state_, country_= [item.strip() for item in data.split(',')] 38 | except Exception as err: 39 | error_dialog(str(err.__class__), err.message) 40 | 41 | try: 42 | response = urlopen(api_url_base.format(country=country_, state=state_, city=city_)) 43 | except IOError: 44 | error_dialog('Connection Error', 'Unable to perform request.') 45 | if response.getcode() == 200: 46 | city_data = handle_data(response.read()) 47 | webbrowser.open('drafts://x-callback-url/create?text={0}'.format(quote(city_data))) 48 | else: 49 | error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read())) 50 | 51 | if __name__ == '__main__': 52 | get_by_city(argv[1]) -------------------------------------------------------------------------------- /string_hashes/manual.md: -------------------------------------------------------------------------------- 1 | # Manual 2 | 3 | This script is designed to work like a command-line interface. The help option will produce the following output: 4 | 5 | usage: hasher.py [-h] [-hs HASH-NAME] [-u URL] [STRING [STRING ...]] 6 | input a string to hash. 7 | 8 | positional arguments: 9 | STRING the string to hash 10 | 11 | optional arguments: 12 | -h, --help show this help message and exit 13 | -hs HASH-NAME, --hs HASH-NAME, -hash HASH-NAME, --hash HASH-NAME the hash function of hashlib to use. defaults to sha1 14 | -u URL, --u URL, -url URL, --url URL url scheme to call after hashing. use to call an app. 15 | 16 | Note that the default hash algorithm is SHA1. The result will be send to the clipboard. Then Drafts is opened, or another app that supports URL schemes. 17 | 18 | # Examples 19 | 20 | The simplest input would be without any optional argument. It will send *505b32a013ac5e803f24579b99acb19b679e0a82* to clipboard. It's SHA1: 21 | 22 | this_is_a_test 23 | 24 | Get the MD5 of the same string. It will send *05e4ad8f026394d064eb94b88ce4a0d4* to clipboard: 25 | 26 | this_is_a_test -hs md5 27 | 28 | Get the SHA512 of that string, and open Chrome browser after calculating the hash. Will send *0d6ef2f011a4f8f02afb68fa0d39a98d4901c3c329c066fff3288c99181cd9bf47c92a3d2ceb38fbf07237f108fce3effcbe3fc2b84e4dac2394c2a8dd3bd5c9* to clipboard, and open Chrome: 29 | 30 | this_is_a_test -hs sha512 -u chrome:// 31 | 32 | # Remarks 33 | 34 | You may enter all hash algorithms that are supported by Python's hashlib module. At the moment these are: 35 | 36 | - MD5 37 | - SHA1 38 | - SHA224 39 | - SHA256 40 | - SHA384 41 | - SHA512 42 | 43 | For further information see: [hashlib Python Documentation](http://docs.python.org/2/library/hashlib.html) -------------------------------------------------------------------------------- /githubstatus/githubstatus.py: -------------------------------------------------------------------------------- 1 | from console import alert 2 | from json import loads 3 | from sys import exit 4 | from urllib import urlopen, quote 5 | import webbrowser 6 | 7 | def error_dialog(title, message): 8 | '''A diaolog box for error messages.''' 9 | try: 10 | alert(title, message) 11 | except KeyboardInterrupt: 12 | pass 13 | webbrowser.open('drafts://') 14 | exit(message) 15 | 16 | def formatdate(date): 17 | '''bring the date into shape''' 18 | return date[:-1].replace('T', ' ') 19 | 20 | def request(method): 21 | '''a request to github's status API.''' 22 | api_url_base = 'https://status.github.com/api/{method}' 23 | try: 24 | response = urlopen(api_url_base.format(method=method)) 25 | except IOError: 26 | error_dialog('Connection Error', 'Unable to perform request.') 27 | if response.getcode() != 200: 28 | error_dialog('Error', 'Status code: {0} - Message: {1}'.format(response.getcode(), response.read())) 29 | return loads(response.read()) 30 | 31 | def github_status(): 32 | '''get information and admin messages for github servers.''' 33 | status = request('status.json') 34 | messages = request('messages.json') 35 | output = ''' 36 | GITHUB STATUS 37 | ================ 38 | {date}: {status} 39 | 40 | Last messages: 41 | '''.format(date=formatdate(status['last_updated']), 42 | status=status['status']) 43 | for item in messages: 44 | output += '\n{date}: {status}\n{comment}\n'.format(date=formatdate(item['created_on']), 45 | status=item['status'], 46 | comment=item['body']) 47 | output += '\nFor further information: https://status.github.com/' 48 | webbrowser.open('drafts://x-callback-url/create?text={0}'.format(quote(output))) 49 | 50 | if __name__ == '__main__': 51 | github_status() -------------------------------------------------------------------------------- /differ/differ.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import difflib 3 | import hashlib 4 | from sys import argv 5 | import webbrowser 6 | 7 | def diff(from_, to_, wrap_col): 8 | ''' 9 | Create a diff and write it to a file, so that it can be opened in the browser. 10 | ''' 11 | # Since from_ and to_ come as lists of words, we need 12 | # to put them back together into sentences and then 13 | # split at line-breaks to get a list of lines for the diff. 14 | from_ = ' '.join(from_).splitlines() 15 | to_ = ' '.join(to_).splitlines() 16 | 17 | delta_html = difflib.HtmlDiff(wrapcolumn=wrap_col).make_file(from_, to_, fromdesc='from text', todesc='to text') 18 | 19 | with open('diff_table.html', 'w') as file: 20 | file.write(delta_html) 21 | 22 | webbrowser.open('diff_table.html') 23 | 24 | def parse_input(data): 25 | ''' 26 | Parse input from Drafts command-line-like. 27 | ''' 28 | parser = argparse.ArgumentParser(description='diff two texts.') 29 | 30 | parser.add_argument('-f', '--f', '-from', '--from', 31 | dest='from_', 32 | metavar='STRING', 33 | nargs='+', 34 | help='the first text to compare') 35 | 36 | parser.add_argument('-t', '--t', '-to', '--to', 37 | dest='to_', 38 | metavar='STRING', 39 | nargs='+', 40 | help='the second text to compare') 41 | 42 | parser.add_argument('-wc', '--wc', '-wrapcolumn', '--wrapcolumn', 43 | dest='wrap_col', 44 | default=50, 45 | metavar='INT', 46 | type=int, 47 | help='number of characters each column in diff should wrap to') 48 | 49 | args = parser.parse_args(data) 50 | diff(args.from_, args.to_, args.wrap_col) 51 | 52 | if __name__ == '__main__': 53 | parse_input(argv[1].split(' ')) -------------------------------------------------------------------------------- /search_and_replace/search_and_replace.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | from sys import argv 4 | from urllib import quote 5 | import webbrowser 6 | 7 | def search_and_replace(data_string, old, new, count=0, flags=0): 8 | substitute = re.sub(old, new, data_string, count, flags) 9 | drafts_call = 'drafts://x-callback-url/create?text={0}'.format(quote(substitute)) 10 | webbrowser.open(drafts_call) 11 | 12 | def parse_input(data): 13 | '''Parse input from Drafts command-line-like.''' 14 | parser = argparse.ArgumentParser(description='Search and Replace for Drafts.') 15 | 16 | parser.add_argument('datastring', 17 | metavar='STRING', 18 | nargs='*', 19 | help='the string to search in') 20 | 21 | parser.add_argument('-old', '--old', 22 | metavar='STRING|REGEXP', 23 | dest='old', 24 | help='the old string. regular expression allowed.') 25 | 26 | parser.add_argument('-new', '--new', 27 | metavar='STRING', 28 | dest='new', 29 | help='the new string to replace with.') 30 | 31 | parser.add_argument('-c', '--c', '-count', '--count', 32 | metavar='INT', 33 | default=0, 34 | dest='count', 35 | help='the number of times to replace.') 36 | 37 | # Possible inputs for flags: 38 | # 2 = IGNORECASE 39 | # 4 = LOCALE 40 | # 8 = MULTILINE 41 | # 16 = DOTALL 42 | # 64 = VERBOSE 43 | # May be summed. 44 | parser.add_argument('-f', '--f', '-flags', '--flags', 45 | metavar='INT|re.FLAG', 46 | default=0, 47 | dest='flags', 48 | help='the flags for regeular expression substitution.') 49 | 50 | args = parser.parse_args(data) 51 | search_and_replace(' '.join(args.datastring), args.old, args.new, int(args.count), int(args.flags)) 52 | 53 | if __name__ == '__main__': 54 | parse_input(argv[1].split(' ')) 55 | -------------------------------------------------------------------------------- /string_hashes/strings_hashes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import clipboard 4 | import hashlib 5 | from sys import argv 6 | import webbrowser 7 | 8 | def hash_data(hashstring, hashfunction, url): 9 | '''Get the hash of given string.''' 10 | # Determine if a method for hashfunction 11 | # exists in hashlib. If no attribute/method is 12 | # found, default to sha1. 13 | if hasattr(hashlib, hashfunction): 14 | hash_method = getattr(hashlib, hashfunction) 15 | else: 16 | hash_method = hashlib.sha1 17 | 18 | # Put hash to clipboard, if hashstring exists. 19 | if hashstring: 20 | clipboard.set(hash_method(hashstring).hexdigest()) 21 | else: 22 | raise ValueError 23 | 24 | # Pythonista doesn't support x-callback. 25 | # So this is a pragmatic approach to calling 26 | # another app after hashing the string. 27 | webbrowser.open(url) 28 | 29 | def parse_input(data): 30 | '''Parse input from Drafts command-line-like.''' 31 | parser = argparse.ArgumentParser(description='input a string to hash.') 32 | 33 | # Expects strings to hash. 34 | parser.add_argument('inputstring', 35 | metavar='STRING', 36 | nargs='*', 37 | help='the string to hash') 38 | 39 | # Set the hash function. 40 | parser.add_argument('-hs', '--hs', '-hash', '--hash', 41 | metavar='HASH-NAME', 42 | default='sha1', 43 | dest='hash', 44 | help='the hash function of hashlib to use. defaults to sha1') 45 | 46 | # Intended to set a callback-like action. 47 | # Use to open a specific app via url scheme, if necessary. Otherwise will open Drafts. 48 | parser.add_argument('-u', '--u', '-url', '--url', 49 | metavar='URL', 50 | default='drafts://', 51 | dest='url', 52 | help='url scheme to call after hashing. use to call an app.') 53 | 54 | args = parser.parse_args(data) 55 | hash_data(' '.join(args.inputstring), args.hash, args.url) 56 | 57 | if __name__ == '__main__': 58 | parse_input(argv[1].split(' ')) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pythonista-drafts-recipes 2 | ========================= 3 | 4 | # The Short Description 5 | 6 | A collection of short Python scripts for use in Pythonista on iOS. Triggered by using custom actions for Drafts. 7 | 8 | ========================= 9 | 10 | # The Longer Description 11 | 12 | ## What's Pythonista? 13 | 14 | Pythonista is a great IDE for Python on iOS devices. It actually is one of the finest IDEs for Python I know. It's well thought out and easy on the eyes, too. Pythonista vastly expands your options and possibilities to automate workflows under iOS. 15 | 16 | [Give Pythonista a try](http://omz-software.com/pythonista/) 17 | 18 | ## And this Drafts thingy? 19 | 20 | Don't judge Drafts only by its shining surface. It's not yet another, although beautiful, text editor or note-taking app. It supports URL schemes and allows to create your own actions. These actions are URL scheme calls to other apps, which makes Draft some sort of shell for all apps on your device, that support URL schemes. 21 | 22 | [Drafts is here](http://agiletortoise.com/drafts/) 23 | 24 | ## So, now what? 25 | 26 | You see, Pythonista gives you the power of Python on your iOS device, and it supports calling your own scripts via URL schemes. Get it? Pythonista + Drafts = Automation liberation. As easy as that. 27 | 28 | ## And those recipes? 29 | 30 | The recipes are a collection of Drafts actions and Python scripts for Pythonista. They are intended to be called from within Drafts. 31 | 32 | ========================= 33 | 34 | # The Recipes 35 | 36 | All recipes are stored in their own subfolders. Each subfolder should at least contain: 37 | 38 | - The python script file, e.g. *link_shortener_isgd.py*. 39 | - A markdown file with an example Drafts action to trigger, e.g. *link_shortener_isgd_action.md*. 40 | - A manual on how to call and use the script, e.g. *manual.md*. 41 | 42 | ========================= 43 | 44 | # Automation helpers 45 | 46 | ## draftsaction 47 | 48 | There's *draftsaction.py* which is intended to provide a class to easily create draft action links and import links for Drafts for any given script. Also, it should provide templates for manual and action markdown files. This is in an early stage right now. 49 | 50 | ### Attributes of DraftsAction class: 51 | 52 | - name: The script's name to be referenced in documentation. 53 | - action: The pythonista action that should be called from within Drafts. 54 | - actiontitle: How the action should be called in Drafts. 55 | - absolutepath: The absolute path to the Python script. 56 | - importlink: A link to automatically import the action into Drafts. 57 | - data: A dictionary containing all above data. 58 | 59 | ### Methods of DraftsAction class: 60 | 61 | - data_update(): Update all data in data dictionary. 62 | - drafts_action(*args): Create the Pythonista link to be called by Drafts action. args will be arguments of the Pythonista script. 63 | - drafts\_import\_link(): Create the import link. 64 | -------------------------------------------------------------------------------- /draftsaction.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from console import write_link 3 | from os import path 4 | import sys 5 | import types 6 | from urllib import quote, unquote 7 | 8 | action_markdown = ''' 9 | # {name} 10 | ========================= 11 | 12 | # The Drafts Action 13 | 14 | Here's the code for the action. It assumes that the Python scripts is in Pythonista's Documents folder and the script's title is *{name}*. 15 | 16 | {action} 17 | 18 | # Import Link 19 | 20 | If you're on an iOS device and have Drafts installed, you can spare yourself the effort of manually creating the action in Drafts. Just click on the following link: 21 | 22 | [Import action to Drafts]({importlink}) 23 | ''' 24 | 25 | manual_markdown = ''' 26 | # Manual 27 | 28 | [[Instructions here]] 29 | 30 | # Remarks 31 | 32 | [[Remarks here]] 33 | ''' 34 | 35 | class DraftsAction(): 36 | def __init__(self, title='', scriptpath=''): 37 | if scriptpath == '': 38 | self._scriptpath = sys.argv[0] 39 | else: 40 | self._scriptpath = scriptpath 41 | 42 | self.name = self._name_of_script() 43 | 44 | if title == '': 45 | self.actiontitle = self.name 46 | else: 47 | self.actiontitle = title 48 | 49 | self.absolutepath = self._path_of_script() 50 | self.action = '' 51 | self.importlink = '' 52 | # self.data should be read-only. 53 | self.data = {} 54 | self.data_update() 55 | 56 | 57 | def __str__(self): 58 | self.data_update() 59 | return str(self.data) 60 | 61 | 62 | def _name_of_script(self): 63 | ''' 64 | Get the name of current file. 65 | Return name, if it is a .py-script, 66 | or else raise an error. 67 | ''' 68 | basename = path.basename(self._scriptpath) 69 | scriptname = path.splitext(basename) 70 | return scriptname[0] 71 | 72 | 73 | def _path_of_script(self): 74 | ''' 75 | Return the path to the script file. 76 | ''' 77 | absolutepath = path.abspath(self._scriptpath) 78 | return path.split(absolutepath)[0] 79 | 80 | 81 | def drafts_action(self, *args): 82 | ''' 83 | A url scheme to a Pythonista script. 84 | This is to be used as an action content 85 | in Drafts. 86 | ''' 87 | argstring = '' 88 | for arg in args: 89 | argstring += '&argv=' + arg 90 | scheme = 'pythonista://{script}?action=run{args}' 91 | self.action = scheme.format(script=self.name, 92 | args=argstring) 93 | self.data_update() 94 | 95 | 96 | def drafts_import_link(self): 97 | ''' 98 | A link for Drafts to import an action. 99 | ''' 100 | if self.action == '': return 101 | scheme = 'drafts://x-callback-url/import_action?type=URL&name={name}&url={url}' 102 | self.importlink = scheme.format(name=quote(self.actiontitle), 103 | url=quote(self.action)) 104 | self.data_update() 105 | 106 | 107 | def data_update(self): 108 | ''' 109 | Update the data dictionary. 110 | ''' 111 | self.data = {'action': self.action, 112 | 'actiontitle': self.actiontitle, 113 | 'importlink': self.importlink, 114 | 'name': self.name, 115 | 'path': self.absolutepath} 116 | 117 | 118 | def create_action_file(self): 119 | ''' 120 | Create a markdown file with Drafts action 121 | and import link in the scripts directory. 122 | ''' 123 | if self.action == '' or self.importlink == '': 124 | raise ValueError("Make sure you've defined an action and an import link.") 125 | return action_markdown.format(name=self.name, 126 | action=self.action, 127 | importlink=self.importlink) 128 | 129 | 130 | def create_manual(self): 131 | ''' 132 | Create a markdown manual template in 133 | the scripts directory. 134 | ''' 135 | if self.action == '' or self.importlink == '': 136 | raise ValueError("Make sure you've defined an action and an import link.") 137 | return manual_markdown 138 | --------------------------------------------------------------------------------