Reference selected Calendar events in AppleScript

Unlike most Apple applications, Calendar does not include a selection property in its AppleScript dictionary. This makes it difficult to script tasks that require references to multiple events, such as adding alerts to several meetings from different calendars.

reference_selected_eventsSome users have resorted to GUI scripting or parsing event properties from the clipboard as a workaround. Here is a better solution:

In Yosemite, a “SelectedEvents” property has been included in Calendar’s plist. Using sqlite3 to query the Calendar Cache file, we can quickly get the id of the selected event and that of its calendar.

------------------------------
-- ABOUT
------------------------------
-- Written and tested in Yosemite, Calendar Version 8.0
-- The plist may take several seconds to update after a new event has been selected.
-- Only the first instance of a recurring event will be referenced. 

-------------------------------
-- MAIN CODE
------------------------------
set defaultsReply to (do shell script "defaults read com.apple.ical SelectedEvents")
set selectedEvents to parseDefaults(defaultsReply)

if selectedEvents = {} then
	display notification "Please try again" with title "No Calendar Event Selected"
	return
end if

set eventReferenceList to {}
repeat with sEvent in selectedEvents
	set {eventID, calendarID} to sqlQuery(sEvent)
	tell application "Calendar"
		set eventReference to event id eventID of calendar id calendarID
		-- INSERT YOUR CODE TO PROCESS EACH EVENT
		
		-- Example of "Alert 15 minutes before start"
		my addDisplayAlarm(eventReference, -15)
		
		-- OR BUILD A LIST OF EVENTS
		set end of eventReferenceList to eventReference
	end tell
end repeat
return eventReferenceList

------------------------------
-- HANDLERS
------------------------------
on parseDefaults(resultText)
	set localUIDs to {}
	set {TID, text item delimiters} to {text item delimiters, quote}
	set resultItems to text items of resultText
	set text item delimiters to TID
	repeat with i from 1 to (count resultItems)
		if i mod 2 = 0 then set end of localUIDs to resultItems's item i
	end repeat
	return localUIDs
end parseDefaults


on sqlQuery(localUID)
	local dateString, localUID
	if localUID contains "/" then
		set {TID, text item delimiters} to {text item delimiters, "/"}
		set {dateString, localUID} to text items of localUID
		set text item delimiters to TID
	end if
	
	set sqlText to "
		SELECT DISTINCT zcalendaritem.zshareduid AS eventID
			 , znode.zuid as calID
		  FROM zcalendaritem
		  JOIN znode
			ON znode.z_pk = zcalendaritem.zcalendar
		   AND zcalendaritem.zlocaluid = '" & localUID & "'
		;"
	
	set sqlPath to POSIX path of (path to library folder from user domain) & "Calendars/Calendar Cache"
	set {TID, text item delimiters} to {text item delimiters, "|"}
	set {eID, cID} to text items of (do shell script "echo " & quoted form of sqlText & " | sqlite3 " & quoted form of sqlPath)
	set text item delimiters to TID
	return {eID, cID}
end sqlQuery


on addDisplayAlarm(myEvent, triggerInterval)
	tell application "Calendar"
		tell myEvent
			if not (exists (display alarms whose trigger interval = triggerInterval)) then
				set myAlarm to make new display alarm at end of display alarms with properties {trigger interval:triggerInterval}
			end if
		end tell
	end tell
end addDisplayAlarm

,

6 Responses to Reference selected Calendar events in AppleScript

  1. Takaaki Naganoya February 2, 2015 at 4:37 am #

    Great! Adding these codes to the beginning of your script, I can get selected events almost at all times.

    tell application “Calendar” to activate
    set defaultsReply to (do shell script “sync”)

  2. Alan Kim March 29, 2015 at 5:24 pm #

    When I run your script, the command to make a new display alarm fails.

    set myAlarm to make new display alarm at end of display alarms with properties {trigger interval:triggerInterval}

    Script Debugger lists errors as:

    getting display alarm error code -1719

    Thanks for the work you put into writing the code, as the remainder is working. If you have ideas on the display alarm issue, I would be interested. I tried writing that code independent of your script, but Applescript errors regardless.

    OS X 10.10.2

  3. Patrick July 9, 2015 at 2:08 pm #

    How did you find out about this? Do you read and compare every app’s plist every time there is an update?
    In any case, THANK YOU SO FRICKIN MUCH! I have been using such a convoluted workaround up until this point. I am being forced to upgrade my lab to Yosemite and my past workaround broke. But the selection property is what I wanted in the first place. And you even gave the code to parse out the result from the initial search… thank you for serving the scripting community so well!

  4. jfw September 19, 2015 at 7:13 am #

    Nice! I had to add “my” before each of the subroutine calls in the first repeat statement, but otherwise it worked beautifully. (I’m on 10.11 El Capitan.) Thanks!

  5. Vicky Stamatopoulou November 11, 2016 at 5:20 am #

    Great, many thanks for providing this info!

    I was writing a script to combine Calendar.app with the application I most often use (Merlin).
    My script already can create events (based on the selection on my app) to a calendar of the Calendar.app, but I also wanted to get events of the Calendar.app to be transferred to my app. Now I have the scripts for both ways.

    Thank you so much, Vicky

    PS: I’ll put this url into the comments of my script for giving you credit to this part of the script.

  6. Per-Daniel Liljegren March 30, 2017 at 3:22 am #

    One of the smartest and most peculiar workarounds I’ve ever seen! Now, my road to Alfred is straightforward, syncing with Due and lots of other valuable stuff.

    Thank you so much!

Leave a Reply