Get Distribution List email addresses from an Address Book Group

Lately I was working on an AppleScript that sends personalized bulk email along with an individual PDF attachment to simplify and automate the coming christmas mailing.

But while creating the small application I soon ran into an annoying problem: How can I access the eMail addresses chosen in the Distribution List editor of the Mac OS X Address Book with AppleScript? Yes, I can get all persons of an Address Book Group and I can also get all their eMail addresses. But no, unfortunately I cannot easily get the Distribution List eMail addresses…

At first I was very frustrated. Moreover I was very jelous of Christian Fries, because his excellent (and free) Serial Mail software is obviously able to access the Distribution List eMail addresses :wink: But his software is not written entirely in AppleScript, he also uses Objective-C. And this fact pointed me to Apple’s Address Book Programming Guide where I finally found the solution to my problem!

Now I was able to write a small Python helper script utilizing PyObjC’s AddressBook framework that simply returns the Distribution List eMail addresses of a certain Address Book Group to the calling AppleScript. Happiness came back!

And it really works like a charm here on Mac OS X 10.5. And it even works without opening the Address Book at all. If you are interested in the script code or want to give it a try on your Mac, then I invite you to download the following sample script, which can also be studied in the Script Editor:

ABG Distribution List Emails - Get Distribution List email addresses from an Address Book Group (ca. 37 KB)

Please note that the AppleScript requires Mac OS X 10.5 Leopard. Or in case you are using an earlier incarnation of our beloved operating system, you need to manually install PyObjC on your Mac.

The sample script WILL open the Address Book at the end, but just to retrieve certain values for the found persons (name, company, etc.). This can be avoided by further customizing the Python script.

Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because the AppleScript internally relies on a Python helper script, which is located inside its Script bundle. Therefor please download the complete script here.


-- author: Martin Michel
-- created: 02.08.2008
-- required:
-- ¢ Mac OS X 10.5 Leopard
-- tested on:
-- ¢ Intel- and PowerPC-based Macs

-- This AppleScript let's you choose an Address Book Group and then displays the email address for every person,
-- which is used when the person is contacted through the chosen group (distribution list email address).
-- As AppleScript itself cannot access this information, a Python script utilizing the PyObjC AddressBook
-- framework is used as a helper script.
-- The script can come in handy when you want to create a custom serial mailing :)

property mytitle : "ABG Distribution List Emails"

-- I am called when the user opens the script with a doubleclick
on run
	try
		-- asking the user to choose an address book group
		set chosenabgrouprecord to my chooseabgroup()
		-- no address book groups availabe or user canceled
		if chosenabgrouprecord is missing value then
			return
		end if
		-- unique id of the chosen address book
		set abgroupid to item 1 of chosenabgrouprecord
		-- name of the chosen address book
		set abgroupname to item 2 of chosenabgrouprecord
		-- getting the distribution list emails of the chosen adress book group
		set dlistrecord to my getdlistemails(abgroupid)
		-- no members available in this address book group
		if dlistrecord is missing value then
			return
		end if
		-- unique id of the member/person
		set personids to item 1 of dlistrecord
		-- email address used when contacting the person/member through the chosen group
		set dlistemails to item 2 of dlistrecord
		set countpersonids to length of personids
		-- this is just a demo that displays the found distribution list email address
		-- for all contacts in the chosen group
		repeat with i from 1 to countpersonids
			set personid to item i of personids
			set dlistemail to item i of dlistemails
			set {personfname, personlname, personorganization} to my getpersondata(personid)
			tell me
				activate
				display dialog "AB Group: " & abgroupname & return & "Member: " & personfname & space & personlname & return & "Company: " & personorganization & return & "Email: " & dlistemail with title mytitle
			end tell
		end repeat
		-- catching unexpected errors
	on error errmsg number errnum
		-- ignoring 'User canceled'-error
		if errnum is not equal to -128 then
			my dsperrmsg(errmsg, errnum)
		end if
	end try
end run

-- I am returning a list containing the person's first name, last name and organization for the given person id
on getpersondata(personid)
	tell application "Address Book"
		set personobj to item 1 of (every person whose id is personid)
		set personfname to (first name of personobj) as Unicode text
		if personfname is "missing value" then
			set personfname to ""
		end if
		set personlname to (last name of personobj) as Unicode text
		if personlname is "missing value" then
			set personlname to ""
		end if
		set personorganization to (organization of personobj) as Unicode text
		if personorganization is "missing value" then
			set personorganization to ""
		end if
	end tell
	return {personfname, personlname, personorganization}
end getpersondata

-- I am asking the user to choose an Address Book Group
-- I return a list containing the id and name of the chosen Address Book Group
-- or missing value in case no groups are available or the user cancels the dialog
on chooseabgroup()
	set pyscriptpath to POSIX path of (((path to me) as Unicode text) & "Contents:Resources:Scripts:abx.py")
	set command to "/usr/bin/python " & quoted form of pyscriptpath & " getgroups"
	
	set shelllines to paragraphs of (do shell script command)
	if shelllines is {} then
		set errmsg to "Your Adress Book does not contain any groups yet."
		my dsperrmsg(errmsg, "--")
		return missing value
	end if
	-- the Python script returns tab delimited lines containing the
	-- ids and names of the address book groups, e.g.:
	-- 181C5BBB-FE2A-419F-AD6E-D2A92A678B66:ABGroup	Hotels
	-- FF2EEE74-9F40-11D8-BC68-000A95971BFE:ABGroup	Restaurants
	-- .
	set abgroupnames to {}
	set abgroupids to {}
	repeat with shelline in shelllines
		set olddelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to {tab}
		set {abgroupid, abgroupname} to text items of shelline
		set AppleScript's text item delimiters to olddelims
		set abgroupids to abgroupids & abgroupid
		set abgroupnames to abgroupnames & abgroupname
	end repeat
	-- we have to number the address book group names, so that we can later choose/return the
	-- corresponding address book group id
	set menuitems to {}
	set counter to 0
	repeat with abgroupname in abgroupnames
		set counter to counter + 1
		if counter < 10 then
			set strcounter to ("0" & counter) as Unicode text
		else
			set strcounter to (counter as Unicode text)
		end if
		set menuitems to menuitems & (strcounter & " " & abgroupname)
	end repeat
	-- finally asking the user to choose an address book group
	choose from list menuitems with prompt "Please choose an Address Book Group:" OK button name "Select" cancel button name "Cancel" with title mytitle without empty selection allowed and multiple selections allowed
	set usrchoice to result
	if usrchoice is not false then
		set chosenmenuitem to (item 1 of usrchoice)
		set spaceoffset to offset of " " in chosenmenuitem
		set idx to ((characters 1 through 2 of chosenmenuitem) as Unicode text) as integer
		return {item idx of abgroupids, item idx of abgroupnames}
	else
		return missing value
	end if
end chooseabgroup

-- I am returning a list containing the person ids and distribution list emails for a given address book id
on getdlistemails(abgroupid)
	set pyscriptpath to POSIX path of (((path to me) as Unicode text) & "Contents:Resources:Scripts:abx.py")
	set command to "/usr/bin/python " & quoted form of pyscriptpath & " getdlistemails " & quoted form of abgroupid
	
	set shelllines to paragraphs of (do shell script command)
	if shelllines is {} then
		set errmsg to "The chosen Address Book Group does not contain any members."
		my dsperrmsg(errmsg, "--")
		return missing value
	end if
	
	set personids to {}
	set dlistemails to {}
	
	repeat with shelline in shelllines
		set olddelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to {tab}
		set {personid, dlistemail} to text items of shelline
		set AppleScript's text item delimiters to olddelims
		set personids to personids & personid
		set dlistemails to dlistemails & dlistemail
	end repeat
	
	return {personids, dlistemails}
end getdlistemails

-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
	tell me
		activate
		display dialog "Sorry, an error occured:" & return & return & errmsg & return & "(" & errnum & ")" buttons {"OK"} default button 1 with icon stop giving up after 20 with title mytitle
	end tell
end dsperrmsg