Mail.app save attachment - stopped working after Sierra 10.12.2 update

I made several attempts but I never got a rule able to run an applescript.

Bingo. Today I’m more lucky. I was able to get a script running.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) vendredi 13 janvier 2017 11:09:10

Here is where I am.
I edited the script a bit because the compiler refused to compile the instruction

set attachmentsFolder to (path to documents folder as text) & "4attachments:"

I replaced tell application “System Events” by tell application “Finder” because with the original instruction the existing folder was not seen.
As you may see, I added several say instructions to trace the execution.
At this time execution reach point 7 but fail to execute the instruction

set theAttachments to theMessage's mail attachments

Here is the script called by the rule :


using terms from application "Mail"
	on perform mail action with messages theMessages for rule SAVE_ATTACHMENT_SIERRA
		say "point 1"
		tell application "Finder"
			tell me to say "point 2"
			tell me to set attachmentsFolder to (path to documents folder as text) & "4attachments:"
			tell me to say "point 3"
			if not (exists folder attachmentsFolder) then
				tell me to say "the folder " & attachmentsFolder & " doesn't exist!"
				error "the folder " & attachmentsFolder & " doesn't exist!"
			end if
			tell me to say "point 4"
		end tell
		say "point 5"
		tell application "Mail"
			set selectedMessages to theMessages
			tell me to say "point 6"
			repeat with theMessage in selectedMessages
				tell me to say "point 7"
				set theAttachments to theMessage's mail attachments
				tell me to say "point 8"
				tell me to say (get count of theAttachments)
				repeat with theAttachment in theAttachments
					tell me to say "point 9"
					# CAUTION: if the Hfs name of an attachment contain a slash, Mail replace it by a colon
					try
						set originalName to name of theAttachment
						tell me to say "point 10"
						tell me to say originalName
						if originalName contains ":" then
							set savePath to my remplace(originalName, ":", "/")
						end if
						set savePath to attachmentsFolder & originalName
						tell me to say "point 11"
						tell me to say savePath
						tell me to close access (open for access savePath) # THE TIP
						tell me to say "point 12"
						
						# As this script is a workaround for Sierra 10.12.2/3, «class furl» is available
						(*
						set POSIXPath to POSIX path of savePath
						tell me to set savePath to POSIX file POSIXPath
						*)
						tell me to set savePath to savePath as «class furl»
						tell me to say "point 13"
						save theAttachment in savePath
						tell me to say "point 14"
					end try
				end repeat # theAttachment
			end repeat # theMessage
		end tell # Mail
		
	end perform mail action with messages
end using terms from

#=====
(*
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 as text
	set AppleScript's text item delimiters to oTIDs
	return t
end remplace

#=====

and here is the script used to mimic the one above.


tell application "Mail"
	activate
	set theMessages to (get the selection)
end tell

say "point 1"
tell application "Finder"
	tell me to say "point 2"
	tell me to set attachmentsFolder to (path to documents folder as text) & "4attachments:"
	tell me to say "point 3"
	if not (exists folder attachmentsFolder) then
		tell me to say "the folder " & attachmentsFolder & " doesn't exist!"
		error "the folder " & attachmentsFolder & " doesn't exist!"
	end if
	tell me to say "point 4"
end tell
say "point 5"
tell application "Mail"
	set selectedMessages to theMessages
	tell me to say "point 6"
	repeat with theMessage in selectedMessages
		tell me to say "point 7"
		set theAttachments to theMessage's mail attachments
		tell me to say "point 8"
		tell me to say (get count of theAttachments) --> say 1
		repeat with theAttachment in theAttachments
			tell me to say "point 9"
			# CAUTION: if the Hfs name of an attachment contain a slash, Mail replace it by a colon
			try
				set originalName to name of theAttachment
				tell me to say "point 10"
				tell me to say originalName --> say "bouquins.rtf"
				if originalName contains ":" then
					set savePath to my remplace(originalName, ":", "/")
				end if
				set savePath to attachmentsFolder & originalName
				tell me to say "point 11"
				tell me to say savePath --> say "SSD 500:Users:myHome:Documents:4attachments:bouquins.rtf"
				tell me to close access (open for access savePath) # THE TIP
				tell me to say "point 12"
				
				# As this script is a workaround for Sierra 10.12.2/3, «class furl» is available
				(*
						set POSIXPath to POSIX path of savePath
						tell me to set savePath to POSIX file POSIXPath
						*)
				tell me to set savePath to savePath as «class furl»
				tell me to say "point 13"
				save theAttachment in savePath
				tell me to say "point 14"
			end try
		end repeat # theAttachment
	end repeat # theMessage
end tell # Mail


#=====
(*
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 as text
	set AppleScript's text item delimiters to oTIDs
	return t
end remplace

#=====

This late version behaves flawlessly so I’m hitting a wall : why is the instruction

set theAttachments to theMessage's mail attachments

failing when the script is triggered by the rule ?

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) vendredi 13 janvier 2017 12:47:45

I found this info about having Applescript execute properly under Mail rules :

http://www.macandiostips.com/Mac_and_iOS_Tips/Mac_Tips_%26_Tricks/Entries/2013/8/2_How_to_make_AppleScripts_Execute_Correctly_under_Mail_Rules.html

The article is from 2013, so its way back under earlier OS X versions I guess. But the writer has a point, in the middle part… Quoting:

“But I have found that the most problems appear when running scripts are combined in a rule that also moves the message to another mailbox. It is almost like the moving will appear asynchronous such that the script fails to find it and therefore cannot parse the necessary info it needs to run right.”

I removed all my other rule actions (mark email as read and move email to a different email-folder). Then it actually seems to work. Leaving just the “Run applescript” action.

I can live with marking the emails as read manually myself, or if it would be possible to include that in the applescript it self… at some point :

set read status of selectedMessages to true

Niklas

Thanks but the script which I am testing is the only item triggered by the rule. This one moves nothing, changes no attribute. It only triggers the script.
I studied an other track to do the job but it failed.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) vendredi 13 janvier 2017 18:26:30

@NickeZ28

With which system were you able - if you ever did - to save the attachments with a script triggered by a rule ?
As it fails with 10.12.2, I tried with 10.12.1 : fails too.
I tried with 10.11.5 : fails too.

It fails always upon the instruction : set theAttachments to theMessage’s mail attachments.

As I was tired to hit a wall I decided to try an other scheme.
It rely upon three files.

A script attached to a rule which apply no change to the messages

(*
tell application "Mail"
	set test_list to selection
	tell me to perform mail action with messages (test_list)
end tell
*)
# Entry point used by the Mail rule
using terms from application "Mail"
	on perform mail action with messages theMessages --for rule SAVE_ATTACHMENT_SIERRA
		
		tell application "Mail"
			set theList to {}
			repeat with theMessage in theMessages
				set messageID to (get message id of theMessage)
				set end of theList to messageID
			end repeat
		end tell
		
		set theString to my recolle(theList, linefeed)
		
		tell application "Finder"
			tell me to set thePath to ((path to desktop) as text) & "messagesIDs_sDIsegassem.txt" # Adjust to fit your needs
		end tell
		
		my writeto(thePath, theString, «class utf8», false)
		
		tell application "Finder"
			set theApp to open ((path to desktop as text) & "driven.app") as alias # Adjust to fit your needs
		end tell
		
	end perform mail action with messages
end using terms from

#=====

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 as text
	set AppleScript's text item delimiters to oTIDs
	return t
end recolle

#=====
(*
Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
*)
on writeto(targetFile, theData, dataType, apendData)
	-- targetFile is the path to the file you want to write
	-- theData is the data you want in the file.
	-- dataType is the data type of theData and it can be text, list, record etc.
	-- apendData is true to append theData to the end of the current contents of the file or false to overwrite it
	try
		set targetFile to targetFile as «class furl»
		set openFile to open for access targetFile with write permission
		if not apendData then set eof of openFile to 0
		write theData to openFile starting at eof as dataType
		close access openFile
		return true
	on error
		try
			close access targetFile
		end try
		return false
	end try
end writeto

#=====

The script creates the second file which contain the message id of every passed message.

When the 2nd file is written, the script call the 3rd file which is a script saved as an application.

on run
	set finalMailBox to "aaa/aac" # Edit to fit your needs
	
	# Builds the path to the text file containing the message IDs.
	# It must match the path defined by the script triggered by the rule
	set thePath to ((path to desktop) as text) & "messagesIDs_sDIsegassem.txt" #
	# Read the message IDs
	set theIDs to paragraphs of (read file thePath)
	# Build the list of messages
	set theMessages to {}
	repeat with anID in theIDs
		set end of theMessages to my searchMail(anID)
	end repeat
	
	tell me to say "point 2"
	# Build the path to the folder were attachments must be saved
	set attachmentsFolder to (path to documents folder as text) & "4attachments:" # Edit to fit your needs
	tell application "Finder" # was System Events
		tell me to say "point 3"
		if not (exists folder attachmentsFolder) then
			tell me
				say "point 4"
				error "the folder " & attachmentsFolder & " doesn't exist!"
			end tell # current application
		end if
	end tell
	
	tell application "Mail"
		tell me to say "point 6"
		tell me to say "il y a " & (count theMessages) & " messages"
		set pass to 0
		repeat with theMessage in theMessages
			set pass to pass + 1
			tell me to say "point 7 message " & pass
			set theAttachments to every mail attachment of theMessage # FAILS when called by a rule
			tell me
				say "point 8 message " & pass
				say (get count of theAttachments) --> say 1
			end tell # current application
			set attch to 0
			repeat with theAttachment in theAttachments
				set attch to attch + 1
				tell me to say "point 9 message " & pass & " attachment " & attch
				# CAUTION: if the Hfs name of an attachment contain a slash, Mail replace it by a colon
				try
					set originalName to name of theAttachment
					tell me
						say "point 10 message " & pass & " attachment " & attch
						say originalName --> say "bouquins.rtf"
						if originalName contains ":" then
							set savePath to my remplace(originalName, ":", "/")
						end if
						set savePath to attachmentsFolder & originalName
						say "point 11 message " & pass & " attachment " & attch
						--say savePath --> say "SSD 500:Users:myHome:Documents:4attachments:bouquins.rtf"
						close access (open for access savePath)
						say "point 12 message " & pass & " attachment " & attch
						
						# As this script is a workaround for Sierra 10.12.2/3, «class furl» is available
						(*
						set POSIXPath to POSIX path of savePath
						tell me to set savePath to POSIX file POSIXPath
						*)
						set savePath to savePath as «class furl»
						say "point 13 message " & pass & " attachment " & attch
					end tell # current application
					save theAttachment in savePath
					tell me to say "point 14 message " & pass & " attachment " & attch & " is saved"
				end try
			end repeat # theAttachment
			set read status of theMessage to true
			tell me to say "point 15 message " & pass
			move theMessage to mailbox "aaa/aac"
			tell me to say "point 16 message " & pass
		end repeat # theMessage
	end tell # Mail
end run

on searchMail(messageID)
	tell application "Mail"
		repeat with a in accounts
			repeat with mb in mailboxes of a
				set x to (messages whose message id is messageID) of mb
				--	if length of x is greater than 0 then return (item 1 of x)
				if (count x) > 0 then return (item 1 of x)
			end repeat
		end repeat
		repeat with mb in mailboxes
			set x to (messages whose message id is messageID) of mb
			--	if length of x is greater than 0 then return (item 1 of x)
			if (count x) > 0 then return (item 1 of x)
		end repeat
	end tell
end searchMail

This late item will read the text file containing every message id .
It use them to build a list of references to the messages
If a message has attachments they are saved in the dedicated folder
The read status of the message is set to true
The message is moved to the dedicated mailbox.

I know that it’s awful but at least, it does the job.

Of course, if you decide to use the scripts, it would be a good idea to remove the instructions say something which are just useful to trace the script behavior during tests.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) samedi 14 janvier 2017 18:27:02

Im on Sierra 10.12.2 now. And Apple Mail.app version 10.2 (3259).

Im currently able to save the attachments in a folder (dropbox folder if that matters) using a rule that triggers this script :

using terms from application "Mail"
	on perform mail action with messages theMessages for rule theRule
		set attachmentsFolder to "Users:niklas:Dropbox:folder1:folder2:"
		--set attachmentsFolder to (path to documents folder as text) & "4attachments:"
		--set attachmentsFolder to (path to desktop as text) & "4attachments:"
		--return attachmentsFolder
		tell application "System Events"
			if not (exists folder attachmentsFolder) then error "the folder " & attachmentsFolder & " doesn't exist!"
		end tell
		tell application "Mail"
			set theMessages to the selection
			set selectedMessages to theMessages
			repeat with theMessage in selectedMessages
				repeat with theAttachment in theMessage's mail attachments
					# CAUTION: if the Hfs name of an attachment contain a slash, Mail replace it by a colon
					try
						set originalName to name of theAttachment
						if originalName contains ":" then
							set savePath to my remplace(originalName, ":", "/")
						end if
						set savePath to attachmentsFolder & originalName
						
						close access (open for access savePath) # THE TIP
						# As this script is a workaround for Sierra 10.12.2/3, «class furl» is available
						(*
				set POSIXPath to POSIX path of savePath
				tell me to set savePath to POSIX file POSIXPath
				*)
						tell me to set savePath to savePath as «class furl»
						save theAttachment in savePath
					end try
				end repeat # theAttachment
			end repeat # theMessage
			set read status of theMessages to true
		end tell # Mail
	end perform mail action with messages
end using terms from

#=====
(*
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 as text
	set AppleScript's text item delimiters to oTIDs
	return t
end remplace

#=====



But it only seems to work when having just one action connected to the Mail Rule, that is “Run Applescript” so Im not able to move the message to a specific email folder or even mark it as read…

(This is just my thoughts, but it appears when adding more actions to the rule in mail, Mail.app sort the actions uncorrectly, i.e moving the first action (that I would like to be) “Run Applescript” to the bottom if I add “Mark email as read” and “Move to different folder”. So thats why Mail.app looses the references to the mail that applescript is supposed to be working with later on… But I don’t know how to confirm that.)

Im too novice with Applescript, been using it for about a month or two, I’ve just googled and picked up bits and pieces from here and there and put together my script :slight_smile: But its a lot of fun :slight_smile: Maybe there is a way to solve this with the unique message id’s, I wouldn’t know how to do that yet.

Niklas

I will try to run the system in English to see if the oddity striking here is linked to the French localisation.
The problem has nothing common with the inability to write in a custom folder which is solved by the
close access (open for access savePath) # THE TIP instruction.
Here, when the script is triggered by a rule it fails when it reach the instruction :
repeat with theAttachment in theMessage’s mail attachments
It’s to try to see what is failing that I split the instruction in two parts:
set theAttachments to theMessage’s mail attachments
repeat with theAttachment in theAttachments.

When testing in Script Debugger I discovered that repeat with theAttachment in theMessage’s mail attachments has no raw format (chevrons one).
This is why I edited as :
set theAttachments to every attachment of theMessage
which, when I ask for raw syntax appears as :
set theAttachments to every «class atts» of theMessage

Everything behaves well when the script is called by:

tell application "Mail"
   set test_list to selection
   tell me to perform mail action with messages (test_list)
end tell

When triggered by a rule which does no other task, the script execute the say “point 7” instruction but fails to execute the instruction
set theAttachments to every attachment of theMessage.
The fact that the script fails if you ask the rule to do what you described before isn’t too surprising.
Changing the read status isn’t a problem but when the rule move a message in a mailbox, it seems that the script receive the original location, not the new one.

In the message #16 you may find the instructions to add to your code so that it change the read status and moves the message in a dedicated mailbox.

         end repeat # theAttachment # existing instruction

           set read status of theMessage to true
           -- tell me to say "point 15 message " & pass
           move theMessage to mailbox "aaa/aac" # EDIT to fit your needs
           -- tell me to say "point 16 message " & pass

       end repeat # theMessage # existing instruction

I’m a bit skeptical because the instruction
set attachmentsFolder to “Users:niklas:Dropbox:folder1:folder2:”
doesn’t define a valid path. The name of the volume is missing !

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) samedi 14 janvier 2017 19:27:38

It’s perfectly foolish.
I re-tested with the code borrowed from your late message and the attachment was not moved.

I re-booted in English and the same code behaved flawlessly so I thought that there is a bug dedicated to French localization.

I re-booted in French and to be sure I made an other attempt. This time the attachments were correctly saved.

I must assume that something was odd in a file which was repaired when I ran in English.

I took time to test the script with the added instructions listed before.
It worked flawlessly. Of course you will have to edit the instruction moving to a mailbox because I doubt that “aaa/aac” exists on your machine.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) samedi 14 janvier 2017 20:05:54

At last I found why your script seems to work.
It’s not working upon the list of messages massed by the rule, it’s working upon what is selected in Mail.

    tell application "Mail"
           set theMessages to the selection # THE INSTRUCTION WHICH FOOLED US
           set selectedMessages to theMessages

Remove the instruction grabbing the selection and you will not get the attachments saved.
If we want to save files attached to selected messages, a script triggered by a rule isn’t the good scheme.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) dimanche 15 janvier 2017 17:00:03

I stand corrected.

My script with Mail.app rules does not work - all the time.

It doesn’t save the attachment as its supposed to. Its not stable.

Back to the drawing board :slight_smile:

Niklas

The script does its duty when it is called from the Scripts Editor. It fails when it’s called by a Mail’s rule.
It’s not the only one behaving this way.
There is at least one other thread about this kind of problem.
My understanding is that it’s clearly an Apple problem.

The tip :
close access (open for access savePath)
solves only the oddity due to the inability to save in most of the folders.
This time the problem is that when triggered by a rule, Mail fails to execute several actions upon the attached files.
I’m afraid that we will have to live with that for “some” time.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) mercredi 18 janvier 2017 11:30:26

I have solved this problem using an Automator script, running once every night (with the help of iCal / Calendar.app)

Until the guys at Apple fix this issue.

Niklas

With the current version of beta 10.12.4 the tip :
close access (open for access savePath)
is no longer needed (but it doesn’t hurt)

Yvan KOENIG running Sierra 10.12.3 in French (VALLAURIS, France) vendredi 17 février 2017 13:46:44

Nice… does that mean they are fixing this issue for the coming OS X release ?

Niklas

A part of the problem will be solved, the one forbidding to save the attachment without using the TIP.
At this time, the problem related to the rule which doesn’t pass the correct list continue to strike and I’m afraid that it will strike for long.

Yvan KOENIG running Sierra 10.12.3 in French (VALLAURIS, France) vendredi 17 février 2017 17:13:41

Hi,
I’ve tried implementing the solutions in this thread but can’t seem to make this work. Admittedly I don’t really understand applesctipt. I’ve tried inserting the tip to my script but no joy. It seems as though no matter what I do applecript will not recognize that an attachment exists. (I tried inserting a say statement before the “if theMessage’s mail attachments is not {} then” line but it never triggers. This is a script that I found here years ago and keep adjusting slightly with each release when it stops working.

I get an email every day with an attachment titled OPIS_Pricing.csv. I need to save that in my downloads directory and kick off a script. I wish apple would simply include a damn rule for saving attachments and save me this recurring headache. /rant

Any help would be much appreciated.


using terms from application "Mail"
	on perform mail action with messages theMessages --for rule theRule
		-- Only use hardcoded path! DO NOT USE choose folder. It will crash Mail if you do
		
		-- Script modified for 10.8 on 09/12/13 MV
		-- as of Mac OS X 10.8 Mail.app applescripts can only save to the downloads directory
		-- the shell script below moves the file from downloads to its proper place for processing
		set theOutputFolder to ("/Users/me/Downloads/") as rich text -- replace quoted text with your desired path
		set theNextDelivery to "OPIS_Pricing.csv"
		tell application "Mail"
			repeat with theMessage in theMessages
				if theMessage's mail attachments is not {} then
					repeat with theAttachment in theMessage's mail attachments
						set theFileName to theOutputFolder & theNextDelivery
						try
							save theAttachment in theFileName
						on error errnum
						end try
					end repeat
				end if
			end repeat
		end tell
		do shell script "/Users/me/Documents/Scripts/CurrentFuelCost/CurrentFuelCostScript_OPIS.sh"
	end perform mail action with messages
end using terms from

It seems that you are facing what I wrote in message #26:
A part of the problem will be solved (is solved under 10.12.4), the one forbidding to save the attachment without using the TIP.
At this time, the problem related to the rule which doesn’t pass the correct list continue to strike and I’m afraid that it will strike for long.

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) samedi 15 avril 2017 12:24:10

I cobbled together a temporary workaround in this find command

/usr/bin/find "/Users/me/Library/Mail/V4/" -type f -name "opis_racks_6to6.csv"| sed 's/.*/"&"/' | xargs ls -lTU |sort -k 9,9n -k 6,6M -k 7,7n | awk '{ print $10,$11 }' |tail -1 | xargs -I{} cp "{}" /tmp/opis_racks_6to6.csv

The only weird thing is that it doesn’t seem to find the attachment right away. The email comes in at 7:07ish every day. Sometimes when I run the file at 8 it works flawlessly. Other days I am forced to re-run it the next morning. The timestamp on the file in the /Mail/V4/… directory indicates it wasn’t created till 10 PM

Now I have a LaunchDaemon set to make sure mail is running at 6PM so it never misses the email. The timestamp on the email is 7:07 PM. The timestamp on the opis_racks_6to6.csv file is 22:12. Can someone explain this behavior to me? I’d like to run the script at 8PM the latest. Currently I’m forced to run it at 5AM just to be certain the the correct file actually exists.

NickeZ28, could you let us know how you created the Automator work-around for this?

I have set up an Automator workflow that gets the selected messages in Mail and saves their attachments to the documents folder. The limitation is that the emails must first be selected in Mail. Is it possible for a Mail rule AppleScript to pass the received emails as input to the Automator workflow?

Hi all,
I managed to work out how to do this by actually reading what has been discussed above. Yvan’s TIP did the trick for me (thanks Yvan). The problem was that Mail was refusing to save the attachments due to some kind of permissions issue. The following code sorted it out:


set savePath to attachmentsFolder & originalName
close access (open for access savePath)
set savePath to savePath as «class furl»
save theAttachment in savePath