├── Example Shortcuts └── krypted.shortcut ├── LICENSE ├── README.md └── shortcutter.py /Example Shortcuts/krypted.shortcut: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krypted/shortcutter/d1ab8f34607346701a053ab4c559625207200ec7/Example Shortcuts/krypted.shortcut -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Charles Edge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shortcutter 2 | Shortcuts are Javascript scripts wrapped in a binary property list. This script generates iOS Shortcuts by providing a .js file as an input, wrapping it in the necessary XML and then converting it to a binary property list. The output file should be named with a .shortcut extension if you want to run the file on a device and can be converted back to xml1 with plutil if you want to validate the contents prior to distribution. 3 | 4 | Note: Use python3, python2 seems to fail with the plists dump() function. 5 | 6 | # Usage pattern 7 | python3 shortcutter.py [-h] [-o OUTPUT] -i INPUT 8 | 9 | # Example 10 | python3 shortcutter.py -i somefile.js -o outputName 11 | or 12 | python3 shortcutter.py -i somefile.js 13 | 14 | # Help 15 | You can check the script options with -h, the result will be the next: 16 | 17 | optional arguments: 18 | -h, --help show this help message and exit 19 | -o OUTPUT, --output OUTPUT 20 | The name of your plist file, default = 21 | "java_script_file.shortcut" 22 | 23 | required named arguments: 24 | -i INPUT, --input INPUT 25 | The java script file to insert into the plist file 26 | -------------------------------------------------------------------------------- /shortcutter.py: -------------------------------------------------------------------------------- 1 | import plistlib 2 | import os 3 | import argparse 4 | 5 | parser = argparse.ArgumentParser(description='Shortcutter tool') 6 | 7 | parser.add_argument('-o', '--output', action='store', dest='output', 8 | help='The name of your plist file, default = "java_script_file.shortcut"') 9 | requiredNamed = parser.add_argument_group('required named arguments') 10 | requiredNamed.add_argument('-i', '--input', action='store', dest='input', 11 | help='The java script file to insert into the plist file', required=True) 12 | 13 | results = parser.parse_args() 14 | 15 | bfileName = "" 16 | sfileName = "" 17 | if not results.output: 18 | output_file = results.input 19 | if output_file[-3:] == ".js": 20 | output_file = output_file[:-3] 21 | bfileName = output_file + ".shortcut" 22 | sfileName = output_file + ".shortcutXML" 23 | else: 24 | output_file = results.output 25 | bfileName = output_file 26 | sfileName = output_file + "XML" 27 | 28 | if os.path.exists(results.input): 29 | with open(results.input, 'r') as f: 30 | try: 31 | d = { 'WFWorkflowActions':[ 32 | {'WFWorkflowActionIdentifier':'is.workflow.actions.runjavascriptonwebpage', 33 | 'WFWorkflowActionParameters':{'WFJavaScript':f.read()} 34 | } 35 | ], 36 | 'WFWorkflowClientRelease':'2.0', 37 | 'WFWorkflowClientVersion':'700', 38 | 'WFWorkflowIcon':{'WFWorkflowIconGlyphNumber':59801, 39 | 'WFWorkflowIconImageData':b'', 40 | 'WFWorkflowIconStartColor':4292093695}, 41 | 'WFWorkflowImportQuestions':[], 42 | 'WFWorkflowInputContentItemClasses':['WFWorkflowInputContentItemClasses'], 43 | 'WFWorkflowTypes':['NCWidget','WatchKit','ActionExtension'], 44 | 45 | 46 | } 47 | with open(sfileName, 'wb') as fp: 48 | plistlib.dump(d, fp, fmt=plistlib.FMT_XML) 49 | with open(bfileName, 'wb') as fp: 50 | plistlib.dump(d, fp, fmt=plistlib.FMT_BINARY) 51 | 52 | except: 53 | print ("An error happened when trying to open the file") 54 | else: 55 | print ("Could not open the js file") --------------------------------------------------------------------------------