Saving a Mail attachment

I have found a couple of posts already here on this topic, neither with replies. Is there a method to script the saving of an attachment in Mail? The native Mail rules have nothing available, the Mail dictionary does not seem to cover the topic, and this script below does not do it either:

set to_day to short date string of (current date)
set today_folder to CheckOrMakeFolder(to_day)
today_folder
using terms from application "Mail"
	on perform mail action with messages The_Messages for rule theRule
		repeat with This_Message in The_Messages
			tell application "Mail"
				set PDF_name to words 2 thru 3 of This_Message's content as string
				save first mail attachment of This_Message in (today_folder & PDF_name & ".pdf")
			end tell
		end repeat
	end perform mail action with messages
end using terms from
----------------------------------------------------------------------
on CheckOrMakeFolder(date_string)
	set dp to ((path to documents folder as Unicode text) & "Cathouse:Payroll:Paystubs:" & date_string & ":")
	tell application "Finder"
		if not (the folder dp exists) then
			do shell script "mkdir " & (quoted form of POSIX path of dp)
		end if
	end tell
	return dp
end CheckOrMakeFolder

Every two weeks, I get a bunch of single emails with attached PDFs of the paystubs for my employees. I want them all saved to a single folder, so I can save them, print them, whatever. As it stands currently, I have to open each one, save the file (with a new filename, since they all have the same name), and print each individually.

Any advice or assistance is appreciated.

IMHO i don’t think there is,
i spent quite a lot of time searching and testing , trying to get something to work.

but maybe im wrong ( i hope);:slight_smile:

Hi guys.

There are a couple of issues here.

The first is that the local variable today_folder is defined in the (implicit) run handler, rather than in the perform mail action with messages subroutine.

So when the mail action is performed, it results in an error number -2753 i[/i]. However, instead of presenting the usual error message “The variable today_folder is not defined.”, the script fails silently. (Small wonder, then, that folks sometimes have difficulty scripting stuff like this.) :confused:

Something like this should work for you:

using terms from application "Mail"
	on perform mail action with messages The_Messages
		set today_folder to CheckOrMakeFolder((current date)'s short date string)
		tell application "Mail" to repeat with This_Message in The_Messages
			set PDF_name to text from word 2 to word 3 of (get This_Message's content)
			save first mail attachment of This_Message in (today_folder & PDF_name & ".pdf")
		end repeat
	end perform mail action with messages
end using terms from
----------------------------------------------------------------------
on CheckOrMakeFolder(date_string)
	set dp to (path to documents folder as Unicode text) & "Cathouse:Payroll:Paystubs:" & date_string & ":"
	tell application "Finder" to if not (exists folder dp) then do shell script "mkdir -p " & quoted form of POSIX path of dp
	dp
end CheckOrMakeFolder

Edit: Corrected subroutine name above. (Write 100 lines: I must not post messages when tired. I must not…) :wink:

Works great, kai, thanks a million. This is my first Mail script to use as part of a rule, and I appreciate you pointing out that variable definition issue. Is there a reason that today_folder needs to be defined within the run handler, yet the CheckOrMakeFolder handler does not need to be referenced with [my]?

I was reading in the Mail dictionary, and wondered what the difference would be if I changed from the repeat to simply referencing [item 1 of The_Messages], since it is activated with a single email message; a list of 1 item. In all honesty, I cannot understand why the [perform mail actions with messages] run handler expects list in the first place. What situation could trigger the development of a list of mulitple email messages? I would really like to know, since this very situation of mine starts off with a group of emails that need to be processed.

One final query and I will post this thing and get on with life. Once I have my little folder of PDF files, I have a lovely script that will print them all out. The only problem is that I cannot think of how to tag something onto the end of this script to call that script, since the number of emails varies every pay period. Sometimes there will be 3, other times there will be 5. The emails come blasting in all at once, however, so I considered simply installing a 10 or 15 minute delay (at least 5 minutes more than the interval between Mail’s checking for new messages), but then I thought the script may be triggered 3 or 4 or 5 times, resulting in way too many copies of printouts. Do you have any suggestions on a solution? Should I use a property that is defined on the first run, (similar to the make folder thing), so that the followup printing script is only triggered once? Some kind of log file written to the folder? Am I babbling?

Delighted to hear it works for you, Craig. :smiley:

Just to be clear on this, unless a variable is declared as global, or defined as a script property, it usually needs to be either passed to a handler as a parameter - or defined within the subroutine itself. However, I think the question of where the variable is defined may be confused slightly by the presence of the using terms from block. This is really only required to allow the perform mail action with messages handler to compile. So the reason you don’t need to qualify the CheckOrMakeFolder call with my, is that it’s not actually in a tell block. (I may have added to the confusion, by using an incorrect reference to the subroutine - something which I’ve now corrected above.)

Your question about using item 1, rather than a repeat loop, is a good one. Much of the time, Mail certainly seems to process incoming messages individually. However, since I have come across instances where more than one message has been processed at a time, I’d say it’s generally safer to stick to the repeat method.

Finally, there are several ways in which you could approach the question of printing. You might, for example, create a stay-open script application to carry out the batch printing - something like this:

property printPath : missing value
property printNow : false

to setPrintPath(dp)
	set printPath to dp
end setPrintPath

to printFiles()
	(* your print statements here *)
end printFiles

on idle
	if printNow then
		set printNow to false
		printFiles()
		return quit
	else
		set printNow to true
		15 * minutes
	end if
end idle

For this example, I’ve called the saved application “Paystubs Print Idler”. Your Mail script would then need to be modified something like this:

using terms from application "Mail"
	on perform mail action with messages The_Messages
		set today_folder to CheckOrMakeFolder((current date)'s short date string)
		tell application "Mail" to repeat with This_Message in The_Messages
			set PDF_name to text from word 2 to word 3 of (get This_Message's content)
			save first mail attachment of This_Message in (today_folder & PDF_name & ".pdf")
		end repeat
	end perform mail action with messages
end using terms from
----------------------------------------------------------------------
on CheckOrMakeFolder(date_string)
	set dp to (path to documents folder as Unicode text) & "Cathouse:Payroll:Paystubs:" & date_string & ":"
	tell application "Finder" to set newFolder to not (exists folder dp)
	if newFolder then
		do shell script "mkdir -p " & quoted form of POSIX path of dp
		launch application "Paystubs Print Idler"
		tell application "Paystubs Print Idler" to setPrintPath(dp)
	end if
	dp
end CheckOrMakeFolder

Another (possibly simpler) method might be to incorporate a print handler within the mail script itself, so that an individual print command is issued for each message processed:

to printFile(PDF_file)
	(* your individual print PDF_file statements here *)
end printFile

using terms from application "Mail"
	on perform mail action with messages The_Messages
		set today_folder to CheckOrMakeFolder((current date)'s short date string)
		tell application "Mail" to repeat with This_Message in The_Messages
			set PDF_name to text from word 2 to word 3 of (get This_Message's content)
			set PDF_file to today_folder & PDF_name & ".pdf"
			save first mail attachment of This_Message in PDF_file
			my printFile(PDF_file)
		end repeat
	end perform mail action with messages
end using terms from
----------------------------------------------------------------------
on CheckOrMakeFolder(date_string)
	set dp to (path to documents folder as Unicode text) & "Cathouse:Payroll:Paystubs:" & date_string & ":"
	tell application "Finder" to if not (exists folder dp) then do shell script "mkdir -p " & quoted form of POSIX path of dp
	dp
end CheckOrMakeFolder

The variable PDF_file may need to coerced from a path to an alias (or whatever) for your printing subroutine - but that should give you the general idea… :slight_smile:

Thank you again, kai, for your detailed explanation; it continues to be extremely helpful for me. I am trying to get more concise with my one-liner’s, but you always manage to shove more apostrophe’s and parentheses onto a line (Lynne Truss would be proud of you) and force me to really read it through to figure it out. That is great, and I thoroughly enjoy the challenge of trying to understand.

I like the idea of sending each attachment to the printer (I am using the [lpr] shell script) as they come in; I actually thought of that in the middle of the night, so I am glad that both our tired brains get fuzzy once in awhile.

Thanks for the advice on the loop; I agree with your reasoning. Heaven forbid that one of my people misses their paystub because Mail decided to process a group of messages THIS time… Of course, it would be even simpler if they would just have them emailed to their own accounts, but alas, nothing can be done here about that.

:lol: I certainly admire what Lynne has achieved in terms of raising awareness about punctuation (or perhaps that should be mis-punctuation). By coincidence, she and I both live in the same town - so maybe there’s something in the water around here… :wink:

:lol::lol::lol::lol::lol::lol:

Just in case anyone is interested, here is the final script, which works flawlessly:

--As each paystub comes in, this should make a new folder (named for the date), rename the file based on the employee's name, and print it out.

using terms from application "Mail"
	on perform mail action with messages The_Messages
		set today_folder to CheckOrMakeFolder((current date)'s short date string)
		tell application "Mail" to repeat with This_Message in The_Messages
			set PDF_name to text from word 2 to word 3 of (get This_Message's content)
			set PDF_file to today_folder & PDF_name & ".pdf"
			save first mail attachment of This_Message in PDF_file
			my printFile(PDF_file)
		end repeat
	end perform mail action with messages
end using terms from
----------------------------------------------------------------------
on CheckOrMakeFolder(date_string)
	set dp to (path to documents folder as Unicode text) & "Cathouse:Payroll:Paystubs:" & date_string & ":"
	tell application "Finder" to if not (exists folder dp) then do shell script "mkdir -p " & quoted form of POSIX path of dp
	dp
end CheckOrMakeFolder
----------------------------------------------------------------------
to printFile(b)
	set Print_er to "Deskjet_5400_series" --Must use underscores
	set file_to_print to POSIX path of (b as Unicode text)
	try
		do shell script "lpr -P " & (quoted form of Print_er) & " " & (quoted form of file_to_print)
	end try
end printFile

I’m trying to learn from what you guys have done here and an example script provided by Apple to save attachments. So far I’ve got:

(*
	Just trying to get the attachments to save to somewhere.
*)

using terms from application "Mail"
	on perform mail action with messages selectedMsgs
		tell application "Mail"
			repeat with counter from 1 to (count of selectedMsgs)
				set msg to item counter of selectedMsgs
				set theAttachments to (count of mail attachments of msg)
				say "There are " & theAttachments & " attachments."
				set dp to (path to documents folder rule type Unicode text) & "attachment1.jpg"
				
				--save first mail attachment of msg in dp
				
				repeat with attach_counter from 1 to theAttachments
					set thefilename to name of mail attachment attach_counter of msg
					set theid to id of mail attachment attach_counter of msg
					
					--save mail attachment
				end repeat
			end repeat
		end tell
	end perform mail action with messages
end using terms from

-- If run as an ordinary script, instead of directly from the Scripts
-- menu, it will call the default handler instead.
using terms from application "Mail"
	on run
		tell application "Mail" to set sel to selection
		tell me to perform mail action with messages (sel)
	end run
end using terms from

See where it says path to documents folder rule type Unicode text? That was originally written as you had it in your script path to documents folder as Unicode text. Applescript keeps substituting “as” for “rule type” and then it won’t compile. If I skip compilation and just run, it lets me run the script once without error. If I run it again, it fails on “save first attachment etc.” and if I try to run it a third time, it won’t run at all and says that “rule type” is Expected ", " but found property.

So I’m stuck. I have a sample email message with 4 attachments. I can get the script to count how many, say how many and save the first one (ultimately I want to save all of them, and you can see me getting some structure in there to do that), but I can’t get past this weird as/rule type flipping it’s doing.

Model: dual core G5 2GHz
AppleScript: 1.10.3
Browser: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.1) Gecko/20060214 Camino/1.0
Operating System: Mac OS X (10.4)

Darcy:

First, let me apologize that I haven’t time to help you more right this moment. The primary issue is where you set the variable [dp]. I am not sure why, but you cannot set the path as Unicode text (or any other text) within the Mail tell section. You have to use a handler to get out first, then set the variable:

(*
	Just trying to get the attachments to save to somewhere.
*)

using terms from application "Mail"
	on perform mail action with messages selectedMsgs
		tell application "Mail"
			repeat with counter from 1 to (count of selectedMsgs)
				set msg to item counter of selectedMsgs
				set theAttachments to (count of mail attachments of msg)
				say "There are " & theAttachments & " attachments."
				repeat with attach_counter from 1 to theAttachments
					set thefilename to name of mail attachment attach_counter of msg
					set theid to id of mail attachment attach_counter of msg
					
					my SaveAttachments(thefilename)
				end repeat
			end repeat
		end tell
	end perform mail action with messages
end using terms from
-------------
on SaveAttachments(x)
	set dp to (path to documents folder as Unicode text) & x
	
	--save first mail attachment of msg in dp
end SaveAttachments

-- If run as an ordinary script, instead of directly from the Scripts
-- menu, it will call the default handler instead.
using terms from application "Mail"
	on run
		tell application "Mail" to set sel to selection
		tell me to perform mail action with messages (sel)
	end run
end using terms from

It is frustrating, and I don’t like having to set the variable every time an attachment is saved, but that is the way it is. I will have some time later on to work more on this, but perhaps this will get you started, or someone else will stop by first.

Thanks for taking the time to reply Craig. I’ll be giving your suggestion a try tonight. Being new Applescript, it really threw me for a loop to watch it modify the code like that on me. I was tempted to give up on apple’s editor and stay in TextWrangler.

Darcy:

Try this script out:

(*
	Just trying to get the attachments to save to somewhere.
*)

using terms from application "Mail"
	on perform mail action with messages selectedMsgs
		tell application "Mail"
			repeat with counter from 1 to (count of selectedMsgs)
				set msg to item counter of selectedMsgs
				set theAttachments to (count of mail attachments of msg)
				say "There are " & theAttachments & " attachments."
				repeat with attach_counter from 1 to theAttachments
					set thefilename to SaveAttachments(name of mail attachment attach_counter of msg)
					--set theid to id of mail attachment attach_counter of msg--I don't think  you need this.
					save mail attachment attach_counter of msg in thefilename --The full path is returned from the handler, and the file is saved.
				end repeat
			end repeat
		end tell
	end perform mail action with messages
end using terms from
-------------
on SaveAttachments(x) --This creates the path to the documents folder to save the attachment, then adds the name of the file.
	set dp to (path to documents folder as Unicode text) & x
	return dp
end SaveAttachments

-- If run as an ordinary script, instead of directly from the Scripts
-- menu, it will call the default handler instead.
using terms from application "Mail"
	on run
		tell application "Mail" to set sel to selection
		tell me to perform mail action with messages (sel)
	end run
end using terms from

I think we could clean it up some more, but I believe it will work. Please test it out and let me know how it is going.

I tried it two ways: I tried using it running from the script editor and from the script menu while Mail was active and it didn’t work. When I ran it from the editor, an error comes up, Mail got an error: Can’t continue SaveAttachments and it highlights this section of script: SaveAttachments(name of mail attachment attach_counter of msg) .

Here is a little script derived from the one discussed here, that allows pdf attachments to be printed as they are received in a specific mailbox. The pdf attachment is also saved in a specific folder. The sender string is used to check whether the pdf attachment is sent by a specific send such as a fax service.
This script can be launched automatically from within a rule added to your Mail application and your pdf file will be automatically printed by the finder.

set myMbx to "your mailbox name" -- Change this to any mailbox name you want
set myFolder to "your folder name" -- Change this to any folder name you want in your "Documents" folder
set myFax to "j2" -- Change this to any other string your mail's sender string may begin with (this is for faxes sent by the j2-connect system)
set att to ""
tell application "Mail"
	set this_message to last message in mailbox myMbx
	if the sender of this_message begins with myFax then
		set att to first mail attachment of this_message
		set filename to name of first mail attachment of this_message
		set fic to (((path to documents folder) as Unicode text) & myFolder & ":" & filename)
		if fic exists then
			log fic & " already exists"
		else
			save att in (((path to documents folder) as Unicode text) & myFolder & ":" & filename)
			tell application "Finder" to print file fic
		end if
	end if
end tell

Wow this is almost what I need. Can I call from a rule a script that simply saves and prints the attachment of the message that raised the rule? Does exists a reference to this “current” message that satisfies rules conditions? I see that you take the last message of a specified mailbox and you test its sender, but I need to test my conditions within the rule and just save and print its attachment. Any suggestions?
THanx

Sav:

Try my posted script a few messages up. The date and time of that posting is: 2006-01-23 12:59:22.

What that script does is takes every message that triggers it, prints out the attachment, and saves the attachment in another folder, created at that time. If you don’t want to save the attachment somewhere else, it can be easily modified.

hello all,
this is my first post here and im quite new to applescript. Ive been using it to help save my RSI and to minimize my repetition. anyway, ive been trying to adapt this script to run one time on one email using Mail rules. so, when a new mail comes i want to take that attachment and send it to a directory which is already created. then, i want to replace the file that is already there with the new, so there will always only be one file in the folder and it will be replaced everytime a new email comes in… so far i have this but it does nothing…

using terms from application "Mail"
	on perform mail action with messages The_Messages
		set today_folder to CheckOrMakeFolder()
		tell application "Mail" to repeat with This_Message in The_Messages
			set FP_name to "task_manager"
			set FP_file to today_folder & PDF_name & ".fp7"
			save first mail attachment of This_Message in FP_file
		end repeat
	end perform mail action with messages
end using terms from
----------------------------------------------------------------------
on CheckOrMakeFolder()
	set dp to (path to documents folder as Unicode text) & "task management:" & ":"
	tell application "Finder" to if not (exists folder dp) then do shell script "mkdir -p " & quoted form of POSIX path of dp
	dp
end CheckOrMakeFolder

please help if you find the time, thanks a lot.

cinegod

Hi cinegod and welcome to the forum.

The first problem I see is that “PDF_name” is not defined. In Craig’s (post #9) script, that was done with this line:

           set PDF_name to text from word 2 to word 3 of (get This_Message's content)

The second was this line:

set dp to (path to documents folder as Unicode text) & "task management:" & ":"

which resulted in a path with 2 colons - “task management::”

This will work for now. You can run the script from the editor on a single selected message until you are satisfied with it, then you can make it a rule again.


set PDF_name to "Test Name"
set today_folder to CheckOrMakeFolder()
tell application "Mail"
	set This_Message to item 1 of (get selection)
	set FP_name to "task_manager"
	set FP_file to today_folder & PDF_name & ".fp7"
	save first mail attachment of This_Message in FP_file
end tell
----------------------------------------------------------------------
on CheckOrMakeFolder()
	set dp to (path to documents folder as Unicode text) & "task management:"
	tell application "Finder" to if not (exists folder dp) then do shell script "mkdir -p " & quoted form of POSIX path of dp
	dp
end CheckOrMakeFolder

Edited three times (so far) for typos.

Thank you CapitalJ, i will give it a shot.