#!/usr/bin/env python3 """ Apple Shortcuts Generator Generates .shortcut files compatible with iOS/macOS Shortcuts app """ import json import sys import argparse import base64 import plistlib import uuid from pathlib import Path from datetime import datetime TEMPLATES = { "voice-to-notion": { "name": "Voice to Notion", "description": "Record voice, transcribe, and add to Notion inbox", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.recordaudio", "WFWorkflowActionParameters": {} }, { "WFWorkflowActionIdentifier": "is.workflow.actions.transcribeaudio", "WFWorkflowActionParameters": {} }, { "WFWorkflowActionIdentifier": "is.workflow.actions.gettext", "WFWorkflowActionParameters": { "WFTextActionText": "Add to Notion Inbox:" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.openapp", "WFWorkflowActionParameters": { "WFAppIdentifier": "com.philipyoungg.notione" } } ] }, "expense-logger": { "name": "Quick Expense", "description": "Log expense to Notion database", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.ask", "WFWorkflowActionParameters": { "WFAskActionPrompt": "Amount?", "WFAskActionAnswerType": "Number" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.choosefromlist", "WFWorkflowActionParameters": { "WFChooseFromListActionPrompt": "Category?", "WFChooseFromListActionItems": ["Food", "Transport", "Entertainment", "Shopping", "Bills"] } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.gettext", "WFWorkflowActionParameters": { "WFTextActionText": { "Value": { "string": "Logged: $amount$ for $category$ on $date$" }, "WFSerializationType": "WFTextTokenString" } } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.openurl", "WFWorkflowActionParameters": { "WFURLActionURL": "https://t.me/clawdbot?start=expense_" } } ] }, "ha-scene": { "name": "Home Assistant Scene", "description": "Trigger Home Assistant scene", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.openurl", "WFWorkflowActionParameters": { "WFURLActionURL": "https://t.me/clawdbot?start=ha_scene_" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.showresult", "WFWorkflowActionParameters": { "Text": "Scene activated!" } } ] }, "morning-briefing": { "name": "Trigger Morning Briefing", "description": "Manually trigger your Morning Intelligence Briefing", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.openurl", "WFWorkflowActionParameters": { "WFURLActionURL": "https://t.me/clawdbot?start=morning_briefing_now" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.showresult", "WFWorkflowActionParameters": { "Text": "Morning briefing requested! Check Telegram in a moment." } } ] }, "send-to-openclaw": { "name": "Send to OpenClaw", "description": "Send text, clipboard, or input to OpenClaw", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.gettext", "WFWorkflowActionParameters": { "WFTextActionText": { "Value": { "attachmentsByRange": { "{0, 1}": { "Type": "Clipboard", "Aggrandizements": [] } }, "string": "$0" }, "WFSerializationType": "WFTextTokenString" } } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.urlencode", "WFWorkflowActionParameters": {} }, { "WFWorkflowActionIdentifier": "is.workflow.actions.openurl", "WFWorkflowActionParameters": { "WFURLActionURL": { "Value": { "string": "https://t.me/clawdbot?start=shortcut_", "attachmentsByRange": {} }, "WFSerializationType": "WFTextTokenString" } } } ] }, "quick-task": { "name": "Quick Task to Notion", "description": "Add a quick task to your Work To-do list", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.ask", "WFWorkflowActionParameters": { "WFAskActionPrompt": "Task name?" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.choosefromlist", "WFWorkflowActionParameters": { "WFChooseFromListActionPrompt": "Priority?", "WFChooseFromListActionItems": ["High", "Medium", "Low"] } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.openurl", "WFWorkflowActionParameters": { "WFURLActionURL": "https://t.me/clawdbot?start=task_" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.showresult", "WFWorkflowActionParameters": { "Text": "Task sent to OpenClaw!" } } ] }, "log-to-notion": { "name": "Log to Notion", "description": "Quick log entry to Notion journal/daily notes", "actions": [ { "WFWorkflowActionIdentifier": "is.workflow.actions.ask", "WFWorkflowActionParameters": { "WFAskActionPrompt": "What happened?" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.getcurrentdatetime", "WFWorkflowActionParameters": { "WFCurrentDateFormat": "Short" } }, { "WFWorkflowActionIdentifier": "is.workflow.actions.openurl", "WFWorkflowActionParameters": { "WFURLActionURL": "https://t.me/clawdbot?start=log_" } } ] } } def create_shortcut_json(name, actions, description=""): """Create the JSON structure for a .shortcut file""" shortcut = { "WFWorkflowClientVersion": "1092.0.2", "WFWorkflowClientRelease": "4.0", "WFWorkflowMinimumClientVersion": 900, "WFWorkflowMinimumClientVersionString": "900", "WFWorkflowIcon": { "WFWorkflowIconStartColor": 4292093695, "WFWorkflowIconGlyphNumber": 61456 }, "WFWorkflowImportQuestions": [], "WFWorkflowTypes": ["NCWidget", "WatchKit"], "WFWorkflowInputContentItemClasses": [ "WFAppStoreAppContentItem", "WFArticleContentItem", "WFContactContentItem", "WFDateContentItem", "WFEmailAddressContentItem", "WFGenericFileContentItem", "WFImageContentItem", "WFiTunesProductContentItem", "WFLocationContentItem", "WFDCMapsLinkContentItem", "WFAVAssetContentItem", "WFPDFContentItem", "WFPhoneNumberContentItem", "WFRichTextContentItem", "WFSafariWebPageContentItem", "WFStringContentItem", "WFURLContentItem" ], "WFWorkflowActions": actions, "WFWorkflowName": name } return shortcut def generate_shortcut_file(template_name, output_dir="~/Downloads", custom_name=None): """Generate a .shortcut file from template""" if template_name not in TEMPLATES: print(f"āŒ Template '{template_name}' not found!") print(f"Available: {', '.join(TEMPLATES.keys())}") return None template = TEMPLATES[template_name] name = custom_name or template["name"] shortcut_data = create_shortcut_json(name, template["actions"], template["description"]) # Create output path output_path = Path(output_dir).expanduser() / f"{name.replace(' ', '_')}.shortcut" # Write as plist (binary format that Shortcuts app expects) with open(output_path, 'wb') as f: plistlib.dump(shortcut_data, f) print(f"āœ… Generated: {output_path}") print(f" Description: {template['description']}") print(f" Actions: {len(template['actions'])}") print(f"\nšŸ“± To install:") print(f" 1. AirDrop to your iPhone/Mac, or") print(f" 2. Open in Files app") print(f" 3. Tap 'Add Shortcut'") return output_path def list_templates(): """List all available templates""" print("šŸ“‹ Available Shortcut Templates:") print("=" * 50) for key, template in TEMPLATES.items(): print(f"\nšŸ”¹ {key}") print(f" Name: {template['name']}") print(f" Description: {template['description']}") print(f" Actions: {len(template['actions'])}") def generate_custom_shortcut(description, output_dir="~/Downloads"): """Generate a custom shortcut based on description""" print(f"šŸŽÆ Generating custom shortcut...") print(f" Description: {description}") print() print("āš ļø Custom shortcut generation requires AI processing.") print(" In a full implementation, this would:") print(" 1. Parse your description") print(" 2. Generate appropriate Shortcuts actions") print(" 3. Output a working .shortcut file") print() print(" For now, use --template with one of the pre-built options!") print() list_templates() def main(): parser = argparse.ArgumentParser(description="Generate Apple Shortcuts (.shortcut files)") parser.add_argument("--template", "-t", help="Template name to use") parser.add_argument("--list", "-l", action="store_true", help="List available templates") parser.add_argument("--output", "-o", default="~/Downloads", help="Output directory") parser.add_argument("--name", "-n", help="Custom name for the shortcut") parser.add_argument("--custom", "-c", help="Custom description for AI-generated shortcut") parser.add_argument("--scene", "-s", help="Scene name (for ha-scene template)") args = parser.parse_args() if args.list: list_templates() return if args.custom: generate_custom_shortcut(args.custom, args.output) return if args.template: # Handle scene parameter for ha-scene template custom_name = args.name if args.template == "ha-scene" and args.scene: custom_name = f"Scene: {args.scene}" generate_shortcut_file(args.template, args.output, custom_name) return # No arguments - show help parser.print_help() print("\n") list_templates() if __name__ == "__main__": main()