Create a New Contact with Keyboard Maestro and AppleScript

Hi @Jim and @ccstone - I was just wondering... if you have ever tried automating the creation of a new Contact using Keyboard Maestro and AppleScript similar to your approach for creating new events in Calendar?Jim's Add an Event to Apple Calendar Macro:

With Contacts, the normal process to create a new contact involves (for me at least) a lot of fiddly clicking in fields and copy and paste. Even adding a new address can't be done in one go - each line of the address, post code etc, has to be entered bit by bit. I am surprised that Contacts won't allow a whole address to be pasted in one go (even if some editing had to be done afterwards).

It feels like a perfect thing to get help from Keyboard Maestro with but I haven't had much success with anything I've tried.

AppleScript seems like the way to go but I haven't found many examples. There is a MacSparky Keyboard Maestro Macro that looked very interesting but it is limited in what fields it works and I couldn't adapt it to do anything else - in fact it just fails with an error when I try it.

Creating a new Contact with User Prompts (like your Calendar Event Macro) feels like it might make the process a bit smoother and I was wondering if either of you have tried that?

Hi @Zabobon — I haven't, as I don't add new contacts very often.

That said, here is a AppleScript that I just put together:

display dialog "Enter the first name:" default answer "Tim"
set varFirst to text returned of the result
display dialog "Enter the last name:" default answer "Cook"
set varLast to text returned of the result
display dialog "Enter the street:" default answer "1 Infinite Loop"
set varStreet to text returned of the result
display dialog "Enter the city:" default answer "Cupertino"
set varCity to text returned of the result
display dialog "Enter the state:" default answer "CA"
set varState to text returned of the result
display dialog "Enter the zip code:" default answer "95014"
set varZip to text returned of the result
display dialog "Enter the country:" default answer "USA"
set varCountry to text returned of the result
set varPhoneLabel to (choose from list {"Home", "Work", "Mobile"} with title "List of phone labels" with prompt "Please select a phone type" default items "Work" OK button name {"Choose"}) as string
display dialog "Enter the phone number:" default answer "(408) 606-5775"
set varPhone to text returned of the result
set varEmailLabel to (choose from list {"Home", "Work", "Personal"} with title "List of e-mail labels" with prompt "Please select a e-mail type" default items "Work" OK button name {"Choose"}) as string
display dialog "Enter the e-mail address:" default answer "tim@apple.com"
set varEmail to text returned of the result
tell application "Contacts"
	set myCard to make new person with properties {first name:varFirst, last name:varLast}
	tell myCard
		make new address at end of addresses with properties {street:varStreet, city:varCity, state:varState, zip:varZip, country:varCountry}
		make new phone at end of phones with properties {label:varPhoneLabel, value:varPhone}
		make new email at end of emails with properties {label:varEmailLabel, value:varEmail}
	end tell
	save
end tell
tell application "Contacts"
	activate
	set selection to myCard
end tell

I hope that helps!

You may want to check out Cardhop, which sounds pretty useful.

2 Likes

Wow @Jim ! I had just expected a few pointers but you've done a complete script! This is so good and like your first Calendar Script it can easily be adapted to individual needs.

I will play with it and (if I can make any additions that I think are useful) I will post back my Macro.

I've downloaded Cardhop from the App Store - straight away I see it copes with my main gripe with the existing Contacts App (being able to just copy and paste a complete address and the App intelligently works out what is a street, a town, a zip code etc).

I really am surprised the default App can't do this. In fact in this regard the Calendar App is quite smart, being able to intelligently make events from typed sentences.

Thank you so much again!

Hi @Jim - so, that gave me a huge head start and I've adapted your AppleScript very easily to my own needs. I'm uploading it in case there is anything in my modification that might be useful to you or others.

I made use of Script Debugger Library to find the AppleScript for the other items I wanted to include and test the AppleScript out.

The adaptions I made to your original:

  1. I kept a lot of the default answers to "" (blank answers) so, that if that field isn't needed for the new Contact I can just press :leftwards_arrow_with_hook: to move on and leave that field blank.
  2. I used the Current Clipboard for the text in the Note field (often I create a new Contact while looking at some other info like an email) so, copying some text becomes the starting point for the Contact's Note.
  3. I like to choose my own labels for Phone numbers and emails (like "John mobile", "Betty email" that kind of thing)
  4. I added in a Prompt for the Company or Organisation Name
  5. I added in a Prompt for Address Label (home or work)
  6. And put the defaults for my own region in the UK.

Thanks again for your complete AppleScript! I am going to use this Macro a lot.

01)Contacts ⇢ Create New Contact.kmmacros (6.6 KB)

set theDate to short date string of (current date)
set storage to get the clipboard

display dialog "Enter a Note:" default answer (storage as text)
set varNote to text returned of the result

display dialog "Enter the First Name:" default answer ""
set varFirst to text returned of the result

display dialog "Enter the Last Name:" default answer ""
set varLast to text returned of the result

display dialog "Enter the Company Name:" default answer ""
set varCompany to text returned of the result

set varAddressLabel to (choose from list {"home", "work"} with title "Choose the Address Label:" with prompt "Please select the Address Label" default items "home" OK button name {"Choose"}) as string

display dialog "Enter the Street:" default answer ""
set varStreet to text returned of the result

display dialog "Enter the city:" default answer "London"
set varCity to text returned of the result

display dialog "Enter the Region:" default answer ""
set varState to text returned of the result

display dialog "Enter the Post Code:" default answer ""
set varZip to text returned of the result

display dialog "Enter the Country:" default answer "United Kingdom"
set varCountry to text returned of the result

display dialog "Enter the Phone Label:" default answer "mobile"
set varPhoneLabel to text returned of the result

display dialog "Enter the Phone Number:" default answer ""
set varPhone to text returned of the result

display dialog "Enter Email Label:" default answer "email"
set varEmailLabel to text returned of the result

display dialog "Enter the e-mail Address:" default answer ""
set varEmail to text returned of the result

tell application "Contacts"
  set myCard to make new person with properties {first name:varFirst, last name:varLast, organization:varCompany, note:varNote}
  tell myCard
    make new address at end of addresses with properties {label:varAddressLabel, street:varStreet, city:varCity, state:varState, zip:varZip, country:varCountry}
    make new phone at end of phones with properties {label:varPhoneLabel, value:varPhone}
    make new email at end of emails with properties {label:varEmailLabel, value:varEmail}
  end tell
  save
end tell

tell application "Contacts"
  activate
  set selection to myCard
end tell

Hey Guys,

A couple of things.

A) Don't forget you're using Keyboard Maestro.

image

User Prompt for Making a New Contact v1.00.kmmacros (7.6 KB)

B) In AppleScript it's not especially good practice to use the result variable. It's too mutable and can make debugging a real drag.

set varCity to text returned of ¬
   (display dialog "Enter the city:" default answer "London")

C) If I just had to to use AppleScript for this task regularly I'd create a form with AppleScriptObjC and run it with FastScripts (KM won't work in that contact) – OR I'd do something like this and parse the result:

set contactDataDefaults to text 2 thru -2 of "
First:
Last:
Street:
City:
State:
Zip:
Country:
PhoneLabel:
Phone:
EmailLabel:
Email:
"

set varContactData to text returned of ¬
   (display dialog "Please Enter Contact Data:" default answer contactDataDefaults)

-Chris

1 Like

Thanks @ccstone! This looks really good. I will have a play with it tomorrow - just winding down for the evening here in the UK. Much appreciated. I can see how using Keyboard Maestro to do the single prompt and passing the Variables to the AppleScript will simplify the process. Like you say, we are using Keyboard Maestro so, good to actually make use of its strengths.

1 Like

@Zabobon — you're welcome!

@ccstone — thank you for your input. I am removing the result variable from my scripts. Vey helpful, and the code reads better to boot.

I don't like the Keyboard Maestro prompt dialog. It is too large and I don't like the icon or the space that it takes up.

There is something about a small and focused dialog that appeals to me.

I've added in the company. I also added in a link, which is pulled from the front tab in Safari. I added in notes drawn from the Mac clipboard. Instead of directly entering the contents into notes, I placed it in a notes dialog, where they can be edited or deleted, as desired.

Here is my updated script:

tell application "Safari"
	set varURL to URL of current tab of window 1
end tell
set varFirst to text returned of (display dialog "Enter the first name:" default answer "Tim")
set varLast to text returned of (display dialog "Enter the last name:" default answer "Cook")
set varCompany to text returned of (display dialog "Enter the company:" default answer "Apple Computer Inc.")
set varStreet to text returned of (display dialog "Enter the street address:" default answer "1 Infinite Loop")
set varCity to text returned of (display dialog "Enter the city:" default answer "Cupertino")
set varState to text returned of (display dialog "Enter the state:" default answer "CA")
set varZip to text returned of (display dialog "Enter the zip code:" default answer "95014")
set varCountry to text returned of (display dialog "Enter the country:" default answer "USA")
set varPhoneLabel to (choose from list {"Home", "Work", "Mobile"} with title "List of phone labels" with prompt "Please select a phone type" default items "Work" OK button name {"Choose"})
set varPhone to text returned of (display dialog "Enter the phone number:" default answer "(408) 606-5775")
set varEmailLabel to (choose from list {"Home", "Work", "Personal"} with title "List of e-mail labels" with prompt "Please select a e-mail type" default items "Work" OK button name {"Choose"})
set varEmail to text returned of (display dialog "Enter the e-mail address:" default answer "tim@apple.com")
set varLink to text returned of (display dialog "Enter the website:" default answer varURL)
set varNote to text returned of (display dialog "Enter notes:" default answer (the clipboard as text))
tell application "Contacts"
	set varContact to make new person with properties {first name:varFirst, last name:varLast, organization:varCompany}
	tell varContact
		make new address at end of addresses with properties {street:varStreet, city:varCity, state:varState, zip:varZip, country:varCountry}
		make new phone at end of phones with properties {label:varPhoneLabel, value:varPhone}
		make new email at end of emails with properties {label:varEmailLabel, value:varEmail}
		make new url at end of urls with properties {value:varLink}
		set note of varContact to varNote
	end tell
	save
end tell
tell application "Contacts"
	activate
	set selection to varContact
end tell
2 Likes

The default contacts app is awful, and I can’t imagine anyone at Apple actually uses it.

Cardhop is superb. Not perfect, but 10,000 times better than the crap app Apple made.

2 Likes

I'm way back on 10.11.6 El Capitan, but has the ability to copy/paste an address into Contacts and have it be properly parsed gone away? It's long worked here and I just successfully tested again in case I was mistaken—copied an entire postal address from Messages, pasted that into the Home > Street field in a new card in Contacts, and all of it was parsed.

1 Like

Thank you (again) @Jim and @ccstone - plenty of material here to allow me (and others to make their own customised Contacts Creation Macro (or to use yours as they are). I have just edited my original question now that this question has been moved to Questions & Suggestions - to make it easier for others to follow in the future.

Hi @NaOH

Yes, that seems to be what has happened. I am sure I was able to do this in the past which is why I suppose I was trying to paste in whole addresses. I am on Big Sur. I have more than one Mac and I have the same on all of them so, it's not something specific to me I think. More likely Apple have removed this feature.

Yes, @tjluoma - I have installed Cardhop and it feels good. I have only been using for a day but I can already see how much simpler and cleaner to use than the Default App. The good thing is it works with the existing Default Contacts database.

2 Likes

It works for me in Big Sur. Copied an address from Google Maps, inserted cursor into the street field, and pasted. The street, city, state, and zip parsed.

I've found that this occasionally doesn't work and have to parse it myself.

I've had functions come and go over the years. Haven't a clue why. I mess with my system a great deal and usually figure it's that.

The biggest instance was dragging emails to the desktop. For many years I could only drag one at a time. Then, one day, I could drag as many as I'd like after hearing that others could always drag as many as they like.

1 Like

Interesting @BernSh. I just tried and it sometimes works (which explains why I thought it was possible). Yes, if I copy and paste an address direct from the Google Maps Page into the Street Field, the Contacts App gets that it is an address and populates the correct fields. But if I type out the same address myself and copy and paste it, or copy and paste from an email address that someone has sent me it doesn't work. So, Contacts seems very, very fussy about the exact format. And sometimes working and sometimes not is just annoying rather than useful :smiling_face:

However, Cardhop gets it right every time - even with addresses in different Formats and from different Countries.

Yes, inconsistency reduces utility.

I bought then forgot to reinstall Cardhop to facilitate Contact entry. Thanks for the reminder.

Patches, hacks, and workarounds remain state of the art. Keep that Duck Tape handy! :roll_eyes:

It'd be interesting to take the variously formatted addresses apart to see what Contacts needs then have a script that strips, reformats, and pastes into Contacts whatever address is copied.

1 Like

@BernSh @ccstone @Jim
I have found an interesting AppleScript Example on the Web that basically converts each line of a Mulit-Line User Entry as separate AppleScript Variables... and the User Input can be either typed in line by line or just pasted in a single go (and even edited prior to pressing the "Okay" button.

So, what I am thinking is... I can adapt this to expect a standard address - where each line is what I would expect - Street, Town, Region, Zip Code. This would allow me to paste in the address in one go and then edit if need be before going on to the next entry fields in the complete Macro. Still better than entering each item line by line. Here is the original AppleScript.

Running it unmodified brings up a box like this:

Typing or pasting into it works:

Generating 2 Variables:

Seems like it should be simple to adapt this and incorporate it into one of the existing solutions above. I will report back. Here is the unmodified AppleScript:

-- multiple input dialog

on run -- example
    set {firstName, lastName} to (inputItems for {"• First Name", "• Last Name"} with title given prompt:"Enter the following items separated by a carriage return:")
    display dialog "First Name:  \"" & firstName & "\"" & return & "Last Name:  \"" & lastName & "\""
end run

to inputItems for someItems given title:theTitle, prompt:thePrompt
    (*
    displays a dialog for multiple item entry - a carriage return is used between each input item
    for each item in someItems, a line of text is displayed in the dialog and a line is reserved for the input
        the number of items returned are padded or truncated to match the number of items in someItems
    to fit the size of the dialog, items should be limited in length (~30) and number (~15)  
        parameters -        someItems [list/integer]: a list or count of items to get from the dialog
                        theTitle [boolean/text]: use a default or the given dialog title
                        thePrompt [boolean/text]: use a default or the given prompt text
        returns [list]:     a list of the input items
    *)
    if thePrompt is in {true, false} then -- "with" or "without" prompt
        if thePrompt then
            set thePrompt to "Input the following items:" & return & return -- default
        else
            set thePrompt to ""
        end if
    else -- fix up the prompt a bit
        set thePrompt to thePrompt & return & return
    end if

    if theTitle is in {true, false} then if theTitle then -- "with" or "without" title
        set theTitle to "Multiple Input Dialog" -- default
    else
        set theTitle to ""
    end if

    if class of someItems is integer then -- no item list
        set {theCount, someItems} to {someItems, ""}
        if thePrompt is not "" then set thePrompt to text 1 thru -2 of thePrompt
    else
        set theCount to (count someItems)
    end if
    if theCount is less than 1 then error "inputItems handler:  empty input list"
    set {theItems, theInput} to {{}, {}}

    repeat theCount times -- set the number of lines in the input
        set the end of theInput to ""
    end repeat
    set {tempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
    set {someItems, theInput} to {someItems as text, theInput as text}
    set AppleScript's text item delimiters to tempTID

    set theInput to paragraphs of text returned of (display dialog thePrompt & someItems with title theTitle default answer theInput)

    repeat with anItem from 1 to theCount -- pad/truncate entered items
        try
            set the end of theItems to (item anItem of theInput)
        on error
            set the end of theItems to ""
        end try
    end repeat
    return theItems
end inputItems

It seems like it is worth experimenting with.Since I don't add many new contacts on a regular basis, I am going to stick with my script—it serves my needs.

Try out item C. in my post #5 of this thread...  :sunglasses:

-Chris

Hi @ccstone so, are you saying it is possible to split the result Variable of that Dialog (varContactData) into the multiple tokens/variables that Contacts needs to make a Contact? In other words can varContactData be split into varStreet, varCity, varState, varZip, varCountry ?

Yes.

I did basically what you did in post #15, but I put field labels in to cue the user and for parsing.

Run this as is in the Script Editor and scope out the result:

set contactDataDefaults to text 2 thru -2 of "
First:Christopher
Last:Stone
Street:Somewhere in Dallas
City:Dallas
State:TX
Zip:75200
Country:USA
PhoneLabel:MyPhoneLabel
Phone:000-000-0000
EmailLabel:MyEmailLabel
Email:ccs@scriptmeister.com
"

set varContactData to text returned of ¬
   (display dialog "Please Enter Contact Data:" default answer contactDataDefaults)

set AppleScript's text item delimiters to {"First:"}
set fName to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"Last:"}
set lName to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"Street:"}
set theStreet to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"City:"}
set theCity to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"State:"}
set theState to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"Zip:"}
set theZip to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"Country:"}
set theCountry to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"PhoneLabel:"}
set phoneLabel to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"Phone:"}
set phoneNum to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"EmailLabel:"}
set emailLabel to paragraph 1 of text item 2 of varContactData
set AppleScript's text item delimiters to {"Email:"}
set emailAddress to paragraph 1 of text item 2 of varContactData

items 1 thru -2 of {¬
   fName, ¬
   lName, ¬
   theStreet, ¬
   theCity, ¬
   theState, ¬
   theZip, ¬
   theCountry, ¬
   phoneLabel, ¬
   phoneNum, ¬
   emailLabel, ¬
   emailAddress, ¬
   ""}

If I was doing this for real, I'd put in tests to allow the user flexibility in what fields they fill out. I'd also use regular expressions to parse the text instead of TIDs.

But this will do as a basic demonstration.

-Chris

1 Like

@ccstone This looks brilliant and nice, clean and simple (the AppleScript I had found on the Web was very difficult to edit and had many lines I didn't understand). I will adapt it to my needs (incorporating the early simplified versions you and @Jim worked out).

I like Jim's multiple dialog boxes for Name, email etc as it is actually helpful to have separate prompts for those - but the Address (Street, Town, Zip etc) is better to have as one User Prompt Dialog as very often I will have copied that already into the Clipboard).

I can see a genuinely useful Macro (for me at least) coming out of all this. Possibly nicer than using Cardhop to create new Contacts. I will post back my result...

Thank you so much for your help.

Yes... it's probably a result of me moving house a few times in the last few years, meeting new neighbours, new parents at my daughter's school etc - but for whatever reason I have to create new Contacts enough times to find the Default Apple Contacts method of clicking in little fields, multiple copying and pasting etc a bit fiddly and frustrating :smiling_face:

Years ago I bought a fabulous app called Formal Address that parses freeform text into contacts for this very reason. Unfortunately it appears to no longer be available for sale. Confound it!

This Python library is worth a look if you're at all command line savvy:

usaddress 0.5.4

BusyContacts can parse text via its New Quick Contact command. ($49.95 USD)

-Chris

1 Like