343 lines
12 KiB
Python
343 lines
12 KiB
Python
#!/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()
|