Copying selected mail.app messages header info

Hi,

I did a search for mail.app in the forums here and didn’t quite find what I was looking for so my apologies if it exists somewhere and I didn’t search as thoroughly as I needed to.

I want to be able to copy specific headers in multiple selected mail.app messages to the clipboard.

ie: if I have 5 messages selected, I want to copy all the “from” email addresses in them to the clipboard OR copy all the “reply to” email addresses to the clipboard so I can process them as needed.

I’m wondering if such a script exists or, if not, can I pay someone to make one?

Thanks!

Here is a script doing the job for reply to.

tell application "Mail"
	set theSel to selection
	set theHeaders to {}
	repeat with aMail in theSel
		set end of theHeaders to (reply to of aMail)
		
	end repeat
end tell
set the clipboard to my recolle(theHeaders, linefeed)

#=====

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

#=====

Replace reply to by sender to get the From address.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 5 octobre 2019 10:48:44

Yvan this is fantastic thank you so much!

What if I wanted to just copy the email address part of reply to or sender without the name. Or what if I wanted to just copy the name itself? Can I change the “reply to” to something different to just get that specific info?

THANK YOU!

It’s easy to grab only the address.
As there is not always a ‘name’ available, the other case would return a mixed set of datas.

property wantAddresses : true #  Define it to fit your needs
# true grab the addresses
# false try to grab the names

tell application "Mail"
	set theSel to selection
	set theHeaders to {}
	repeat with aMail in theSel
		set aHeader to (reply to of aMail)
		if wantAddresses then
			set theValue to my getAddress(aHeader)
		else
			set theValue to my getName(aHeader)
		end if
		if theValue is not in theHeaders then
			set end of theHeaders to theValue
		end if
	end repeat
end tell
set the clipboard to my recolle(theHeaders, linefeed)

#=====

on getAddress(aHeader)
	if aHeader contains "<" then
		set anAddress to item 2 of my decoupe(aHeader, {"<", ">"})
	else
		set anAddress to aHeader
	end if
	return anAddress
end getAddress

#=====

on getName(aHeader)
	if aHeader contains "<" then
		set aValue to item 1 of my decoupe(aHeader, {" <"})
	else
		set aValue to aHeader
	end if
	return aValue
end getName

#=====

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

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 5 octobre 2019 21:32:46

Above and beyond. (Let me know where I can tip you for your time on this.)

Couple questions on this:

  1. Is it possible to copy just the name (even a blank variable if the “name” isn’t in place)?

  2. When I run the script on 4 messages it doesn’t copy duplicates into the clip-board (ie if 2 messages are from or “reply to” the same person it only copies 3 of them.) Is it possible to copy all of them even if there are duplicates…the idea is I want to paste these into a spreadsheet and each different email might be a different submission so I don’t want to lose the duplicates?

No need for reward, I just did my brain jogging.

Here is a version designed to fit your wishes.

property wantAddresses : true #  Define it to fit your needs
# true grab the addresses
# false try to grab the names

tell application "Mail"
	set theSel to selection
	set theHeaders to {}
	repeat with aMail in theSel
		set aHeader to (reply to of aMail)
		if wantAddresses then
			set theValue to my getAddress(aHeader)
		else
			set theValue to my getName(aHeader)
		end if
		-- if theValue is not in theHeaders then # EDITED
		set end of theHeaders to theValue
		-- end if # EDITED
	end repeat
end tell
set the clipboard to my recolle(theHeaders, linefeed)

#=====

on getAddress(aHeader)
	if aHeader contains "<" then
		set anAddress to item 2 of my decoupe(aHeader, {"<", ">"})
	else
		set anAddress to aHeader
	end if
	return anAddress
end getAddress

#=====

on getName(aHeader)
	if aHeader contains "<" then
		set aValue to item 1 of my decoupe(aHeader, {" <"})
	else
		set aValue to " " -- aHeader # EDITED, replaced an empty string by a space character
	end if
	return aValue
end getName

#=====

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

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 6 octobre 2019 11:02:25

Edited for clarity, function separation:

set {sendr, replyTo} to {{}, {}}
global sendr, replyTo
tell application "Mail" to repeat with aMessage in (get selection)
	set {sendr's end, replyTo's end} to aMessage's {sender, reply to}
	set {sendr's end, replyTo's end} to {return, return}
end repeat
set text item delimiters to linefeed
tell (choose from list {"sender address", "reply address", "organization"}) to if not it is false then if item 1 is "sender address" then
	my stripSenderAddress()
else
	if item 1 is "reply address" then
		my stripReplyAddress()
	else
		my stripOrgName()
	end if
end if

on stripSenderAddress()
	(do shell script "echo " & (sendr as text)'s quoted form & " | egrep -o '<.*>|.*([.]\\w{3}$)'| tr -d '<>' ")
end stripSenderAddress

on stripReplyAddress()
	(do shell script "echo " & (replyTo as text)'s quoted form & " | egrep -o '<.*>|.*([.]\\w{3}$)'| tr -d '<>' ")
end stripReplyAddress

on stripOrgName()
	(do shell script "echo " & (replyTo as text)'s quoted form & " | egrep -o '.+<' | tr -d '<>" & quote & "'")
end stripOrgName


Hi Mark, in this version I selected three messages and it copied 6 total items…or maybe it copied them all twice?

What I’m hoping for is one script that can copy just names (including a blank if there’s no name so it cuts and pastes nicely into a spreadsheet)

And one script that copies just the email address from either “from” or “reply to” depending on which variable is in the script.

Thank you both!

It seems that you missed an exchange.
My original code was deliberately dropping entries already stored in the build list.

It was done by:

if theValue is not in theHeaders then
	set end of theHeaders to theValue
end if

As rickpoet wrote that it was not what he wanted, I edited the code as:

-- if theValue is not in theHeaders then # EDITED
set end of theHeaders to theValue
-- end if # EDITED

So, now all the entries are inserted in the list.
I guess that this late version (message #6) matches rickpoet’s needs.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 6 octobre 2019 22:03:58

Yvan, it does appear that I overlooked a comment from your post and was erroneously mixing the requested functions. I’ll edit my method. The reply and sender requests are easy to accommodate, however, I think the blank line request for no-name organization entries may be less feasible under my pattern matching scheme.