Setting and Getting Contacts Info

I have a database that is using the script below to update one person’s info in the Contacts.app, or add it if there’s no such person. It loads names and addresses from the database into the script, then runs it. I’m sure it’s not elegantly constructed but it does work with two minor exceptions.

If Contacts is not already open, it fails without the Activate. No big deal, but it would be nice if it could run without even opening Contacts, as scripts can do for Calendar. Why is Contacts different?

Even when everything runs properly and the script is done, it returns “missing value”. I can’t figure out what value is missing. When debugging, I’ve cut it back to almost nothing and it still gives that result.

My biggest problem though, is in reversing the process. I want to retrieve the same data in order to update the database. Sometimes there may be a couple of phones, sometimes not. I’m getting the name and organization, but so far can’t find the format to get the phone numbers and email. If anyone can just give me a lead on how to put that together and combine it all in any kind of string, I’d be grateful for the help.


tell application "Contacts"
	activate
	try
		set thePerson to first person whose (first name is "Joe" and last name is "Shmoe")
	on error
		set thePerson to make new person with properties {first name:"Joe", last name:"Shmoe"}
	end try
	try
		set organization of thePerson to "Acme Company"
	on error
		tell thePerson
			make new organization at end of organizations with properties {organization:"Big Co"}
		end tell
	end try
	try
		set (street of first address) of thePerson to "123 New Street"
		set (city of first address) of thePerson to "San Francisco"
		set (state of first address) of thePerson to "CA"
		set (zip of first address) of thePerson to "94111"
	on error
		tell thePerson
			make new address at end of addresses with properties {label:"Address", street:"321 California St.", city:"San Francisco", state:"CA", zip:"94111"}
		end tell
	end try
	try
		set (label of first email) of thePerson to "direct"
		set (value of first email) of thePerson to "Joe@shmoe.com"
	on error
		tell thePerson
			make new email at end of emails with properties {label:"persona", value:"joe@xyz.com"}
		end tell
	end try
	try
		set (label of second email) of thePerson to "gmail"
		set (value of second email) of thePerson to "Joe@gmail.com"
	on error
		tell thePerson
			make new email at end of emails with properties {label:"gmail", value:"gmail.com"}
		end tell
	end try
	try
		set (label of first url) of thePerson to "website"
		set (value of first url) of thePerson to "big.com"
	on error
		tell thePerson
			make new url at end of urls with properties {label:"website", value:"big.com"}
		end tell
	end try
	
	
	try
		set (label of first phone) of thePerson to "Work"
		set (value of first phone) of thePerson to "(415) 123-2121"
	on error
		tell thePerson
			make new phone at end of phones with properties {label:"Work", value:"(415) 123-3174"}
		end tell
	end try
	
	try
		set (label of first phone) of thePerson to "Work"
		set (value of first phone) of thePerson to "(415) 123-4356"
	on error
		tell thePerson
			make new phone at end of phones with properties {label:"Work", value:"(415) 123-3174"}
		end tell
	end try
	try
		set (label of second phone) of thePerson to "home"
		set (value of second phone) of thePerson to "(666) 123-3221"
	on error
		tell thePerson
			make new phone at end of phones with properties {label:"home", value:"(415) 123-3221"}
		end tell
	end try
	try
		set (label of third phone) of thePerson to "cell"
		set (value of third phone) of thePerson to "(212) 123-3221"
	on error
		tell thePerson
			make new phone at end of phones with properties {label:"cell", value:"(212) 123-3221"}
		end tell
	end try
	save
end tell

Retreiving info:


tell application "Contacts"
	activate
	set theContact to first person whose (first name is "Joe" and last name is "Shmoe")
	
	set selection to theContact
	set theName to name of theContact
	set theOrg to organization of theContact
	
	set theLabel1 to (label of first phone) of theContact
	set thePhone1 to (value of first phone) of theContact
	set theEmail to (value of first email) of theContact
	-- these all get nothing
	
end tell

return theName & "|" & theOrg


Model: MacPro 2009
AppleScript: 2.8.1
Browser: Safari 601.3.9
Operating System: Mac OS X (10.10)

Here is an old script in which you will find the wanted infos.

--[SCRIPT Mail2Contacts]
(*
 
Enregistrer le script en tant que Script : Mail2Contacts.scpt
déplacer le fichier créé dans le dossier
<VolumeDeDémarrage>:Users:<votreCompte>:Library:Scripts:Applications:Mail:
Il vous faudra peut-être créer le dossier Mail et peut-être même le dossier Applications.
 
On peut également l'enregistrer comme Application exécutable par double-clic.

Copier des adresses dans le contenu brut d'un mail.
Sélectionner un groupe dans Contacts
Exécuter le script pour créer de nouvelles fiches dans Contacts.

#=====
 
Yvan KOENIG (VALLAURIS, France)
2013/11/28
*)

#=====

on run
	
	local leNomDuGroupe, lesFiches, uneFiche, i, item_i, hex, fin, prenomNom, leNom, lePrenom, lAdresseMail
	
	# Récupère le nom du groupe sélectionné dans Contacts
	tell application "Contacts"
		set leNomDuGroupe to name of first group whose selected is true
	end tell
	(*
	# Récupère les adresses Mail disponibles dans un fichier texte
	set p2a to (path to desktop as text) & "desAdresses.txt"
	set enTexte to read file p2a
	*)
	# Récupère les adresses Mail contenues dans le presse-papiers
	the clipboard as text
	my recolle(paragraphs of result, "")
	my supprime(result, tab)
	set lesFiches to my decoupe(result, ",")
	(*
Loop scanning the list of datas
Boucle balayant la liste de données *)
	repeat with uneFiche in lesFiches
		set uneFiche to uneFiche as text
		if uneFiche contains "=?" then (*
Nettoyage à la hache les adresses contenant des caractères non ASCII ou ASCII étendu *)
			set uneFiche to my supprime(uneFiche, {"?= =?ISO-8859-1?Q?", "=?ISO-8859-1?Q?", "?="})
			set uneFiche to my decoupe(uneFiche, "=")
			repeat with i from 2 to count uneFiche
				set item_i to item i of uneFiche
				set {hex, fin} to {text 1 thru 2 of item_i, text 3 thru -1 of item_i}
				set item i of uneFiche to character id (my hex2num(hex)) & fin
			end repeat
			set uneFiche to my recolle(uneFiche, "")
			set uneFiche to my remplace(uneFiche, "_", space)
		end if # uneFiche contains "=?"
		(*
Sépare prenomNom de l'adresse *)
		if uneFiche contains " <" then
			set {prenomNom, lAdresseMail} to my decoupe(uneFiche, " <")
			set lAdresseMail to text 1 thru -2 of lAdresseMail
		else
			set prenomNom to item 1 of my decoupe(uneFiche, "@")
			set lAdresseMail to uneFiche
		end if # uneFiche contains " <"
		# Supprime possibles espaces en début de chaîne
		repeat while prenomNom starts with space
			set prenomNom to text 2 thru -1 of prenomNom
		end repeat
		(*
Tente de séparer le prénom du nom. Attention, ce n'est pas une science exacte ! *)
		if prenomNom contains space then
			set prenomNom to my decoupe(prenomNom, space)
			if (count prenomNom) > 2 then
				set lePrenom to my recolle(items 1 thru 2 of prenomNom, space)
				set leNom to my recolle(items 3 thru -1 of prenomNom, space)
			else
				set lePrenom to item 1 of prenomNom
				set leNom to my recolle(items 2 thru -1 of prenomNom, space)
			end if
		else if prenomNom contains "." then
			set prenomNom to my decoupe(prenomNom, ".")
			set lePrenom to item 1 of prenomNom
			set leNom to my recolle(items 2 thru -1 of prenomNom, ".")
		else
			set {lePrenom, leNom} to {"", prenomNom}
		end if # prenomNom .
		(*
Calls Contacts passing some parameters
Insère la fiche dans Contacts *)
		my add_update_record(leNom, lAdresseMail, lePrenom, leNomDuGroupe)
	end repeat
	
end run

#=====

(*
- properties which may be defined directly :
birth date (date) : The birth date of this person.
company (boolean) : Is the current record a company or a person.
department (Unicode text) : Department that this person works for.
first name (Unicode text) : The First name of this person.
home page (Unicode text) : The home page of this person.
image (TIFF picture) : Image for person.
job title (Unicode text) : The job title of this person.
last name (Unicode text) : The Last name of this person.
maiden name (Unicode text) : The Maiden name of this person.
middle name (Unicode text) : The Middle name of this person.
nickname (Unicode text) : The Nickname of this person.
note (Unicode text) : Notes for this person.
organization (Unicode text) : Organization that employs this person.
phonetic first name (Unicode text) : The phonetic version of the First name of this person.
phonetic last name (Unicode text) : The phonetic version of the Last name of this person.
phonetic middle name (Unicode text) : The Phonetic version of the Middle name of this person.
suffix (Unicode text) : The Suffix of this person.
title (Unicode text) : The title of this person..
*)

(*
- properties which must be defined indirectly :
components of address
email
URL
Group
*)

#=====
# Toutes les fiches sont considérées comme professionnelles

on add_update_record(the_LastName, the_MailAddress, the_FirstName, the_GroupName)
	local the_Person
	
	tell application "Contacts"
		(* try to grab an existing record *)
		try (* I moved it to comply with Leopard Behaviour *)
			set the_Person to get 1st person whose (first name is the_FirstName) and (last name is the_LastName) and (group is the_GroupName)
			
			the_Person (* Generates an error if there is no such a person *)
			(*
We are here if there is already a record for such first and last name. Edit it.
On est là s'il existe déjà une fiche pour ce couple nom + prénom dans le groupe. On édite l'adresse mail *)
			tell the_Person
				if the_MailAddress is not missing value then
					try (* 1 *)
						set value of (get email 1) to the_MailAddress
					on error (* 
We are here if there was no email data in the record, create it 
On est là si la fiche ne contenait pas d'adresse mail, on en insère une *)
						try (* 2 *)
							make new email at the end of emails with properties {value:the_MailAddress}
						end try (* 2 *)
					end try (* 1 *)
				end if -- the_MailAddress
			end tell -- the_Person
			
		on error
			(* 
We are here if there was no such a record. Create a new one.
On est là si ce couple nom + prénom n'existait pas dans ce groupe, on crée une fiche *)
			try (* 1 *)
				# Cette formule semble préférable à la création d'un prénom contenant une chaîne vide
				# On laisse ainsi le même état que lors d'une création manuelle.
				if the_FirstName is "" then
					set the_Person to (make new person with properties {last name:the_LastName, company:true})
				else
					set the_Person to (make new person with properties {first name:the_FirstName, last name:the_LastName, company:true})
				end if
			end try (* 1 *)
			tell the_Person
				try (* 1 *)
					make new email at the end of emails with properties {value:the_MailAddress, label:"work"}
				end try (* 1 *)
			end tell -- the_Person
		end try
		
		add the_Person to group the_GroupName (*  inserts the person in the group. *)
		
		save
		
	end tell -- Contacts
end add_update_record

#=====

on decoupe(t, d)
	local oTIDs, l
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe

#=====

on recolle(l, d)
	local oTIDs, t
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
	set t to "" & l
	set AppleScript's text item delimiters to oTIDs
	return t
end recolle

#=====
(*
replaces every occurences of d1 by d2 in the text t
*)
on remplace(t, d1, d2)
	local oTIDs, l
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d1}
	set l to text items of t
	set AppleScript's text item delimiters to d2
	set t to "" & l
	set AppleScript's text item delimiters to oTIDs
	return t
end remplace

#=====
(*
removes every occurrences of d in text t
*)
on supprime(t, d)
	local oTIDs, l
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
	set l to text items of t
	set AppleScript's text item delimiters to ""
	set t to l as text
	set AppleScript's text item delimiters to oTIDs
	return t
end supprime

#=====

on hex2num(t)
	return ((offset of (text item 1 of t) in "0123456789ABCDEF") - 1) * 16 + (offset of (text item 2 of t) in "0123456789ABCDEF") - 1
end hex2num

#===== 
--[/SCRIPT]

As always, Contacts must be open to be able to compile such script.

Yvan KOENIG running El Capitan 10.11.2 in French (VALLAURIS, France) vendredi 11 décembre 2015 11:27:46

Been a little while since I had written this, so I don’t remember all of the issues I had with the various pieces.
One thing I remember was that I had a lot of problems working with any information in a contact record until I did everything by the person id. So the first thing is to get the person id.
Note that I was putting everything into variables that I could work with later. This was a part of a larger script working to sync some of the contact info with another application.
Hope it helps you.


tell application "Contacts"
	activate
	-- collect the main information from this record
	-- will search for a person by name i.e. people where (last name contains "Body" and first name is "Some")
	-- use the first and last of the person you want to find
	-- get the id of the person if found
	set abID to id of every person where (last name contains "Toth" and first name is "Stephen")
	set abFirstName to first name of person id abID
	set abLastName to last name of person id abID
	set abCompany to organization of person id abID
	set abIsCo to company of person id abID
	set abNotes to note of person id abID
	
	set AddressCnt to count of every address of person id abID
	-- only interested in the first address
	if AddressCnt ≥ 1 then
		set abAddress to street of address 1 of person id abID
		set abCity to city of address 1 of person id abID
		set abState to state of address 1 of person id abID
		set abZip to zip of address 1 of person id abID
	end if
	
	-- get the phones
	set abPhone to {}
	set phoneCnt to count of every phone of person id abID
	if phoneCnt ≥ 1 then
		repeat with i from 1 to phoneCnt
			set thisPhone to {}
			set thisPhone to thisPhone & {label of phone i of person id abID}
			set thisPhone to thisPhone & {value of phone i of person id abID}
			set abPhone to abPhone & {thisPhone}
		end repeat
	end if
	
	-- get the emails
	set abEmail to {}
	set emailCnt to count of every email of person id abID
	if emailCnt ≥ 1 then
		repeat with i from 1 to emailCnt
			set thisEmail to {}
			set thisEmail to thisEmail & {label of email i of person id abID}
			set thisEmail to thisEmail & {value of email i of person id abID}
			set abEmail to abEmail & {thisEmail}
		end repeat
	end if
	
end tell