Detect if Mail message has been sent or received

Hi All,

this may seem like a simple question but is there any concrete way of telling if an Mail message has been sent or received?

I have a situation whereby the message I have selected could be in a Mailbox folder that’s not Inbox or Sent items and I need to know if I received the message or sent it.

The most obvious way I can see of doing this is by getting the Sender address and checking whether or not it is one of my accounts but thought ask if there’s a simpler way.

This is what I currently have:

tell application "Mail"
	set emailAddressList to {}
	repeat with i from 1 to count of accounts
		set thisAccount to account i
		copy (email addresses of account i as string) to end of emailAddressList
	end repeat
	
	set theMessages to selection
	repeat with a from 1 to count of theMessages
		set thisMessage to item a of theMessages
		set thisMessageID to id of thisMessage
		set thisSender to sender of thisMessage
		set cleanedSenderAddress to do shell script "echo " & quoted form of thisSender & " | cut -d '<' -f2- | cut -d '>' -f1"
		if cleanedSenderAddress is not in emailAddressList then
			set senderReceiverName to sender of thisMessage
			set senderReceiverName to do shell script "echo " & quoted form of senderReceiverName & " | cut -d '<' -f1  | sed 's/ *$//'"
			display dialog senderReceiverName
		else if cleanedSenderAddress is in emailAddressList then
			set senderReceiverName to name of item 1 of (every to recipient of thisMessage)
			set senderReceiverName to do shell script "echo " & quoted form of senderReceiverName & " | cut -d '<' -f1  | sed 's/ *$//'"
			display dialog cleanedSenderAddress & " sent this message to " & senderReceiverName
		end if
	end repeat
end tell

Thanks,
Nik

As you may see below, I made some changes.

tell application "Mail"
	set emailAddressList to {}
	repeat with thisAccount in accounts
		set emailAddresses to email addresses of thisAccount
		repeat with anAddress in emailAddresses
			copy (anAddress as string) to end of emailAddressList
		end repeat
	end repeat
	
	set theMessages to selection
	repeat with a from 1 to count of theMessages
		set thisMessage to item a of theMessages
		-- set thisMessageID to id of thisMessage # UNUSED
		set thisSender to sender of thisMessage
		tell me to set cleanedSenderAddress to do shell script "echo " & quoted form of thisSender & " | cut -d '<' -f2- | cut -d '>' -f1"
		if cleanedSenderAddress is not in emailAddressList then
			set senderReceiverName to sender of thisMessage
			tell me to set senderReceiverName to do shell script "echo " & quoted form of senderReceiverName & " | cut -d '<' -f1  | sed 's/ *$//'"
			display dialog "sent by : " & senderReceiverName
		else -- if cleanedSenderAddress is in emailAddressList then
			set senderReceiverName to name of item 1 of (every to recipient of thisMessage)
if senderReceiverName is missing value then set senderReceiverName to address of item 1 of (every to recipient of thisMessage)
			display dialog cleanedSenderAddress & " sent this message to " & senderReceiverName
		end if
	end repeat
end tell

(1) your instruction : copy (email addresses of account i as string) to end of emailAddressList
didn’t added what was needed when an account has several addresses.
It inserted “@me.com@icloud.com**********@mac.com” when we wanted it to insert “@me.com", "@icloud.com”, “**********@mac.com

(2) In your main if then else instruction, there was no need for:
[format]else if cleanedSenderAddress is in emailAddressList then[/format]
a simple
[format]else[/format]
is sufficient

(3) when executed, your instruction : set cleanedSenderAddress to do shell script “echo " & quoted form of thisSender & " | cut -d ‘<’ -f2- | cut -d ‘>’ -f1”

generated :
[format] do shell script “echo ‘**** ****** **********@mac.com’ | cut -d ‘<’ -f2- | cut -d ‘>’ -f1”

tell current application
do shell script “echo ‘**** ****** **********@mac.com’ | cut -d ‘<’ -f2- | cut -d ‘>’ -f1”
end tell[/format]

Inserting tell me to at beginning drops the first failing call.
[format]tell current application
do shell script “echo ‘**** ****** **********@mac.com’ | cut -d ‘<’ -f2- | cut -d ‘>’ -f1”
end tell[/format]

(4) In the else part of the code, when it’s availale, name of… is a string
When name of… is missing value I extract the address of the target.
In both case we get a string so there is no need to trigger do shell script.

An alternate structure may be simpler and easier to read:

tell application "Mail"
	set emailAddressList to {}
	repeat with thisAccount in accounts
		set emailAddresses to email addresses of thisAccount
		repeat with anAddress in emailAddresses
			copy (anAddress as string) to end of emailAddressList
		end repeat
	end repeat
	
	set theMessages to selection
	repeat with a from 1 to count of theMessages
		set thisMessage to item a of theMessages
		--set thisMessageID to id of thisMessage # UNUSED
		set cleanedSenderAddress to my getAddress(sender of thisMessage)
		if cleanedSenderAddress is not in emailAddressList then
			set senderReceiverName to my getAddress(sender of thisMessage)
			display dialog "sent by : " & senderReceiverName
		else if cleanedSenderAddress is in emailAddressList then
			set senderReceiverName to name of item 1 of (every to recipient of thisMessage)
			if senderReceiverName is missing value then set senderReceiverName to address of item 1 of (every to recipient of thisMessage)
			display dialog cleanedSenderAddress & " sent this message to " & senderReceiverName
		end if
	end repeat
end tell

on getAddress(someOne)
	return do shell script "echo " & quoted form of someOne & " | cut -d '<' -f2- | cut -d '>' -f1"
end getAddress

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 27 aout 2019 12:40:13

If like me you aren’t fond of shell scripts you may also use:

use framework "Foundation"
use framework "AppKit"
use scripting additions

tell application "Mail"
	set emailAddressList to {}
	repeat with thisAccount in accounts
		set emailAddresses to email addresses of thisAccount
		repeat with anAddress in emailAddresses
			copy (anAddress as string) to end of emailAddressList
		end repeat
	end repeat
	
	set theMessages to selection
	repeat with thisMessage in theMessages
		set cleanedSenderAddress to my getAddress(sender of thisMessage)
		if cleanedSenderAddress is not in emailAddressList then
			set senderReceiverName to my getAddress(sender of thisMessage)
			display dialog "sent by : " & senderReceiverName
		else --if cleanedSenderAddress is in emailAddressList then # Unneeded
			set senderReceiver to item 1 of (every to recipient of thisMessage)
			set senderReceiverName to name of senderReceiver
			if senderReceiverName is missing value then set senderReceiverName to address of senderReceiver
			display dialog cleanedSenderAddress & "  sent this message to  " & senderReceiverName
		end if
	end repeat
end tell

on getAddress(theString)
	set anNSString to current application's NSString's stringWithString:theString
	set theNSDataDetector to current application's NSDataDetector's dataDetectorWithTypes:(current application's NSTextCheckingTypeLink) |error|:(missing value)
	set theURLsNSArray to theNSDataDetector's matchesInString:theString options:0 range:{0, anNSString's |length|()}
	set thePred to (current application's NSPredicate's predicateWithFormat:("URL.scheme == 'mailto'"))
	return item 1 of (((theURLsNSArray's filteredArrayUsingPredicate:thePred)'s valueForKeyPath:"URL.resourceSpecifier") as list)
end getAddress

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 27 aout 2019 14:38:05

Perhaps this:

if all headers of thisMessage contains linefeed & "Received: " then
   -- it's a received message

Hi Guys,

many thanks for taking the time to look into my request.

Shane,

first impressions looked good using the headers but on further testing I seem to be getting inconsistent results using these. I thought it might be an issue when selecting a message that I had replied to but messages that I haven’t replied to have headers that contain the Received: text.

Yvan,

I appreciate you streamlining my code a making it a little more efficient. So far this approach seems to be giving me consistent results whether the message is part of a conversation and has been replied to.

Thanks again,
Nik

As they should. But I’m not sure what that has to do with your question, which was sorting sent from received.

Here is a bare draft of what bay be done with Shane’s proposal:

tell application "Mail"
	set theMessages to selection
	repeat with thisMessage in theMessages
		set itsSender to sender of (get properties of thisMessage)
		# You already know how to extract the address of itsSender
		tell (item 1 of recipients of thisMessage)
			set itsName to its name
			if itsName is missing value then set itsName to its address
		end tell
		if all headers of thisMessage contains linefeed & "Received: " then
			display dialog itsName & "  received this message from  " & itsSender
		else
			display dialog itsSender & "  sent this message to  " & itsName
		end if
	end repeat
end tell

In my own scripts you have all informations required to treat the results as you want.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 27 aout 2019 16:23:07

Hi Shane,

I was testing using the below code:

tell application "Mail"
	set theMessages to selection
	repeat with a from 1 to count of theMessages
		set thisMessage to item a of theMessages
		set thisMessageID to id of thisMessage
		if all headers of thisMessage contains linefeed & "Received: " then
			display dialog "Received message from " & sender of thisMessage
		else
			set senderReceiver to item 1 of (every to recipient of thisMessage)
			set senderReceiverName to name of senderReceiver
			display dialog "Sent message to " & senderReceiverName
		end if
	end repeat
end tell

and on the odd occasion, messages that where in my Inbox, that had been sent directly to me where giving me a response saying I had sent them. Therefore is it possible that I could receive a message that contains the “Received” tag in the header?

Apologies if I was unclear.

Update**

Shane it appears as the the linefeed is the issue. If I change this line

if all headers of thisMessage contains linefeed & "Received: " then

to this

if all headers of thisMessage contains "Received: from " then

then I seem to get more accurate results.

Thanks,
Nik

This might be safer:

set allHeaders to all headers of thisMessage
if allHeaders contains linefeed & "Received: " or allHeaders contains return & "Received: "or alleaders begins with "Received: " then

Yes, hopefully that should catch all of the variants Shane.

Thanks

Yvan,

thanks for your updated examples also,

Nik