204 lines
5.3 KiB
Python
Executable File
204 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Send message to Claude Desktop using AppleScript
|
|
|
|
Features:
|
|
- CMD+R refresh before sending (with configurable delay)
|
|
- Reliable AppleScript-based automation
|
|
- Better error handling and state management
|
|
|
|
Usage:
|
|
python3 send_to_claude.py "Your message here"
|
|
python3 send_to_claude.py "Message" --no-refresh
|
|
python3 send_to_claude.py # Uses default message
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
import logging
|
|
import subprocess
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Configuration
|
|
REFRESH_DELAY_SECONDS = 15 # Wait time after CMD+R to ensure refresh completes
|
|
|
|
|
|
def wake_screen():
|
|
"""Wake screen if screen saver is active"""
|
|
try:
|
|
subprocess.run(['caffeinate', '-u', '-t', '1'], check=True)
|
|
time.sleep(0.5)
|
|
logger.debug("Screen wake command sent")
|
|
except Exception as e:
|
|
logger.debug(f"Screen wake failed (non-critical): {e}")
|
|
|
|
|
|
def activate_claude():
|
|
"""
|
|
Activate Claude Desktop, launching if needed
|
|
|
|
Returns:
|
|
bool: True if successful
|
|
"""
|
|
try:
|
|
# Try to activate if already running
|
|
result = subprocess.run([
|
|
'osascript', '-e',
|
|
'tell application "Claude" to activate'
|
|
], capture_output=True, text=True, timeout=5)
|
|
|
|
if result.returncode == 0:
|
|
logger.info("Activated Claude Desktop")
|
|
time.sleep(0.5)
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.debug(f"Activation attempt failed: {e}")
|
|
|
|
# Launch if not running
|
|
try:
|
|
logger.info("Claude not running, launching...")
|
|
subprocess.run(['open', '-a', 'Claude'], check=True, timeout=5)
|
|
time.sleep(2)
|
|
logger.info("Launched Claude Desktop")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to launch Claude: {e}")
|
|
return False
|
|
|
|
|
|
def send_refresh(delay_seconds=REFRESH_DELAY_SECONDS):
|
|
"""
|
|
Send CMD+R to refresh and wait for completion
|
|
|
|
Args:
|
|
delay_seconds: Time to wait after CMD+R (default: 15)
|
|
|
|
Returns:
|
|
bool: True if successful
|
|
"""
|
|
try:
|
|
logger.info("Sending CMD+R to refresh...")
|
|
|
|
subprocess.run([
|
|
'osascript', '-e',
|
|
'tell application "System Events" to keystroke "r" using command down'
|
|
], check=True, timeout=2)
|
|
|
|
logger.info(f"Waiting {delay_seconds}s for refresh to complete...")
|
|
time.sleep(delay_seconds)
|
|
logger.info("✓ Refresh complete")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to send CMD+R: {e}")
|
|
return False
|
|
|
|
|
|
def send_message_applescript(message):
|
|
"""
|
|
Send message to Claude Desktop using AppleScript
|
|
|
|
Args:
|
|
message: Message text to send
|
|
|
|
Returns:
|
|
bool: True if successful
|
|
"""
|
|
try:
|
|
logger.info(f"Sending message: {message[:50]}...")
|
|
|
|
# Escape backslashes and quotes for AppleScript
|
|
escaped_message = message.replace('\\', '\\\\').replace('"', '\\"')
|
|
|
|
subprocess.run([
|
|
'osascript', '-e',
|
|
f'tell application "System Events" to keystroke "{escaped_message}"'
|
|
], check=True, timeout=10)
|
|
|
|
# Wait for Claude app input box to become ready
|
|
# (app blocks input briefly after receiving text)
|
|
logger.info("Waiting 15s for input box to become ready...")
|
|
time.sleep(15)
|
|
|
|
subprocess.run([
|
|
'osascript', '-e',
|
|
'tell application "System Events" to keystroke return'
|
|
], check=True, timeout=2)
|
|
|
|
logger.info("✓ Message sent successfully")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to send message: {e}")
|
|
return False
|
|
|
|
|
|
def send_to_claude(message, do_refresh=True):
|
|
"""
|
|
Send message to Claude Desktop
|
|
|
|
Args:
|
|
message: Message text to send
|
|
do_refresh: Whether to send CMD+R before sending (default: True)
|
|
|
|
Returns:
|
|
bool: True if successful
|
|
"""
|
|
try:
|
|
# Wake screen
|
|
wake_screen()
|
|
|
|
# Activate Claude
|
|
if not activate_claude():
|
|
logger.error("Could not activate Claude Desktop")
|
|
return False
|
|
|
|
# Send refresh if requested
|
|
if do_refresh:
|
|
if not send_refresh():
|
|
logger.warning("Refresh failed, continuing anyway...")
|
|
|
|
# Send message
|
|
return send_message_applescript(message)
|
|
|
|
except Exception as e:
|
|
logger.error(f"ERROR: {e}")
|
|
import traceback
|
|
logger.debug(traceback.format_exc())
|
|
return False
|
|
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
# Parse arguments
|
|
if len(sys.argv) < 2:
|
|
message = f"System check at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
|
else:
|
|
message = sys.argv[1]
|
|
|
|
# Check for --no-refresh flag
|
|
do_refresh = '--no-refresh' not in sys.argv
|
|
|
|
logger.info("=" * 60)
|
|
logger.info("Claude Desktop Automation - Send Message")
|
|
logger.info(f"Refresh: {'Yes' if do_refresh else 'No'}")
|
|
logger.info(f"Refresh delay: {REFRESH_DELAY_SECONDS}s")
|
|
logger.info("=" * 60)
|
|
|
|
success = send_to_claude(message, do_refresh=do_refresh)
|
|
sys.exit(0 if success else 1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|