Were you to attempt something like this in AppleScript

· 2108 words · 10 minute read

I started down the path of building some sort of PRM in org-mode because I couldn’t find anything that worked the way I wanted. I did briefly look at Apple’s Contacts app, and also at Cardhop, which builds on top of your Contacts database but still makes some assumptions about how good you are at all at remembering to reach out to people.

I also looked at Monica, an open source PRM. The promising part of Monica is its API. The web UI itself shows comprehensive data for each contact, but does not do anything in the way of bulk editing and has no automation at all. It’s laborious to bootstrap.

In the process of trying to figure out how I could write some automation to move Contacts information beyond the basic vCard fields into Monica I did end up having to learn about how macOS Contacts work and realized you can create custom labels for date fields then add them to your card editing template in Contacts’ preferences.

The data entry widget for these fields expects a date and is tolerant of not entering a year (which helps it support, er, “polite” birthdays). You can, in turn, use it somewhat opaquely in a Contacts smart list: There’s a generic “Date” field you can filter on that looks at date fields in the card. Paired with a “within/not within,” or “in the next” parameter, you can make a smart list of “people not contacted in the past 30 days,” etc.

If Contacts smart lists could also use groups, you could do a lot by just setting a “last contacted” date field and making a set of smart lists based on group membership. Contacts smart lists can’t use groups, though, which is a strange oversight.

As I was trying to figure out, though, how to get my contacts uploaded to Monica in a way that would let me use its API to add tags to them once they were imported, I worked out some AppleScript that let me prepend a contact’s group into its note as a hashtag. Contacts smart lists can filter on the contents of notes.

As a solution goes, it’s in the category of “cheap and cheerful.” If you wanted to use macOS Contacts to keep track of your most recent touchpoint with someone, and drive a little automation to surface contacts you haven’t reached out to in a while, you could do it with one custom field and adopting a simple convention for notes. You’re also well into the territory of things AppleScript can do to help out, too: It is trivial to write scripts that automate logging, etc. or even write reminders or make events in a contacts calendar. In fact, here’s a script that operates on the selected contact and lets you log activity in its note:

set theDate to current date
set noteDate to do shell script "date '+%Y-%m-%d'"
tell application "Contacts"
	set selectedPeople to selection
	repeat with thePerson in selectedPeople
		set customDates to custom dates of thePerson
		set lastContactedExists to false
		repeat with aCustomDate in customDates
			if label of aCustomDate is "last contacted" then
				set value of aCustomDate to theDate
				set lastContactedExists to true
			end if
		end repeat
		if not lastContactedExists then
			if length of customDates > 0 then
				set firstCustomDate to first item of customDates
				set newCustomDate to make new custom date at after firstCustomDate
			else
				make new custom date at end of custom dates of thePerson with properties {label:"last contacted", value:theDate}
			end if
		end if
		set theNote to note of thePerson as string
		if theNote is "missing value" then set theNote to ""
		set prependText to text returned of (display dialog "Enter text to prepend to the note of " & name of thePerson & ":" default answer "" buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel")
		set noteUpdated to "[" & noteDate & "] " & prependText
		if theNote is not "" then set noteUpdated to noteUpdated & return & theNote & return & " " & return
		set note of thePerson to noteUpdated
		save thePerson
	end repeat
end tell

So, looking at that, would you want to glue all this together with Applescript?

I don’t think so. I don’t, anyhow. It just took me a morning to figure that out.

In the process of roughing out automation for creating reminders, for instance, I managed to get Reminders.app to beachball on every run with a simple five-liner. Why? I don’t know. Stack Overflow didn’t know. But after a good 15 years of using AppleScript for jobs large and small, I know that sometimes you find its weird little corner cases and that’s all there is to it. I’d rather tell Jamie Zawinski that I don’t know who the author of XScreensaver is than ask Apple to fix it.

When I ask myself “would I want to build something on top of this ecosystem that I mean to use forever?” I can’t even figure out three scripts I would need to write then get to the end of debugging the second one before I know the answer is “no.”

I mean, what does forever mean? Because my personal belief is that we have to assess the foreverness of the competing candidates, and probably think about how amenable data kept in the least forever of those formats is to being migrated to a more forever format/system:

  • macOS Contacts
  • org-mode
  • vCard
  • plain text with moderately elaborate markup amenable to some automated processing

(I put that list in ascending order of longevity/permanence, feeling very appreciative that org-mode allowed me to reorder it with opt arrow).

I trust macOS Contacts a bit. I don’t how much money I would be willing to risk on a series of bets about it working as it does today in 2, 5, or 10 years. While trying to understand my AppleScript options with it I was brought face-to-face with changes to the underlying scripting model several times. In all fairness, those changes played out over decades and it’s only because I’m at a point in life where I can still think “OS X is new” that they even seem mentionable. It’s also completely possible to get Contacts info out into some other format. There’s also just the whole “ramming your head against AppleScript” aspect of the problem.

Moving up the Pyramid of Forever, I trust org-mode to be around for a very long time, but can also see how the API is still subject to change. Functions come and go so automation can break and make it hard to keep a contacts list maintained. On the other hand, I’m not doing much now that couldn’t be done by hand until I figured it out, and the things I’ve bumped into are pretty small so far: changes in the namespace, functional replacements, etc.

Moving on:

I trust the vCard standard to stay how it is a bit more. It’s on … version 3 or 4? … of the spec? There’s a spec. There are a lot of stakeholders interested in that spec. Even Apple quietly crams a whole vCard property into each contact, even if it has its own version of each property you’d find in a vCard anyhow.

So, moving on to the top of the pyramid:

I trust structured plain text to be useful for the rest of my life.

So something built on org-mode seems like the smart play for data longevity? Even if all my automation broke, core org-mode makes it easy to do the things I do: change todo states, add values to the PROPERTIES drawer, add tags, log changes, etc.

The one thing that I’ve mostly decided not to worry about is the disconnect between my contacts list and my org-contacts file.

One useful feature of contacts via Fastmail is the dynamic “Autosaved” group. If I write someone, they go into that group. Periodically moving contacts that surface there into one of my permanent groups then removing them from “Autosaved” provides a simple, organic workflow for keeping up with new people. There is also a bunch of useful automation present in macOS/iOS for surfacing things about contacts from other apps, so you’re constantly being offered the opportunity to pull in new information. This morning, for instance, going through my Contacts list, I noticed that something somewhere in the bowels of macOS or iOS was beginning to notice connections between my contacts and Ivory, the Mastodon client. That’s pretty cool.

On the other hand, this is well within AppleScript’s wheelhouse:

tell application "Contacts"
	set selectedContacts to selection
	if length of selectedContacts is not 1 then
		display alert "Please select exactly one contact."
		return
	end if

	set theContact to first item of selectedContacts

	set theName to name of theContact as text
	set theEmails to value of emails of theContact as text
	set thePhones to value of phones of theContact as text
	set theNote to note of theContact as text

	-- Format the contact data as an org contacts string
	set theContactString to "** " & theName & "
:PROPERTIES:
:EMAIL: " & theEmails & "
:PHONE: " & thePhones & "
:NOTE: " & theNote & "
:NAME: " & theName & "
:END:"

	-- Copy the contact string to the clipboard
	set the clipboard to theContactString
end tell

“Look through new contacts, run that script from a keyboard shortcut, paste the new contact into my contacts.org file.”

I could go one step further and append the text to my contacts.org file, but I don’t like operating on busy files like that.

AppleScript was plainly built to do little things like that. You have to learn its sort of crabbed, verbose way of doing things, but it’s not too hard (and Shortcuts is getting pretty good if you can deal with the sudden drop in functionality that can appear out of nowhere when you hit the limits of some Apple engineer’s imagination or time).

Anyhow, it all just comes down to aesthetics and preferences, right?

I’ve got a draft heading sitting in my blog.org file with a title of “Plain text is calming.” I’m not sure where that little essay is going, but I know where it started: When I’m staring at a text editor I feel much better than when I’m staring at a web or app UI. I might have some challenges with discoverability, or a lack of forgiveness for little slips of the finger, or whatever. But I still feel better because I’ve been a happy citizen of Plaintext Land for over 30 years, and there is a governing mentality there that does not exist in other parts of the technology world. I’m not saying the occasional wheel doesn’t get reinvented, but I am saying that with most plaintext stuff you get to choose your tools, or make them for yourself if there are no good choices. So it’s calming because I don’t have that feeling of always looking for the exit when I encounter a plaintext system. I know it’s there. That’s the preference, anyhow. I made money for a few years being really, really good at turning CMS databases into plaintext and massaging them into other CMSes, so my patience for finding the structure and working with it is high. I don’t worry about where the exit is because in my 40-year history with computers, it has never eluded me in the plaintext world.

The aesthetics are another kettle of fish, and plaintext people run a weird gamut from “text editors are like samurai swords” to “mastery of a plaintext interface is a kind of performance art.” Right now I’m sort of luxuriating in Evil mode, because I finally get the emphasis on ruthless elimination of motion vi engenders. I’ve made terrible fun of people over this in the distant past and feel a little bad about that, but less than two months into this particular experiment, I don’t want to go back. Evil mode is like the end of the first Star Trek movie as far as I’m concerned.

Will Decker and Ilia merging into some sort of computer overmind in Star Trek: TMP

Anyhow, I do want to get back to the whole “plaintext is calming” idea and do some more writing about it. Today’s jaunt into “what if I could make what I wanted in the macOS ecosystem?” was one of those processes I go through when I’ve gone so far with an idea and wonder if I really want to commit – if I’m not making life a little harder on myself than I need to, or if there’s not some simpler way to do it (even if still in the DIY mode,) and the answer came back “don’t think there is.” I’m at home in what I made.