Saving Mail message attachments

how-to

(Mark Alldritt) #1

Here is a snippet of code showing how to save email message attachments using the Mail application. The script processes all selected messages and saves each attachment to the Desktop:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

--
--	Save attachments of selected Mail messages to the desktop
--

set desktopPath to (path to desktop)'s POSIX path

tell application "Mail"
	repeat with aMessage in (get selection)
		repeat with anAttachment in mail attachments of aMessage
			set attachmentName to name of anAttachment
			
			save anAttachment in POSIX file (desktopPath & attachmentName)
		end repeat
	end repeat
end tell

Saving a Mail Attachment
#2

And if you want to return a value (e.g. a list of all the filePaths saved to)

(to enable notification, for example, or composition within some larger process)

then concatMap (combined with list-wrapping of returned filePaths), allows (by concatenation), for a pruned list – with no ‘empties’ where where selected messages turned out to have no attachments.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

--
--    Save attachments of selected Mail messages to the desktop
--

set folderPath to (path to desktop)'s POSIX path

tell application "Mail"
    script attachmentsSaved
        on |λ|(mailItem)
            script fpSaved
                on |λ|(x)
                    set fp to folderPath & (name of x)
                    save x in POSIX file fp
                    {fp}
                end |λ|
            end script
            concatMap(fpSaved, get mail attachments of mailItem)
        end |λ|
    end script
    
    -- Return value: file paths of any saved files
    my concatMap(attachmentsSaved, get selection)

    --> {"/Users/houthakker/Desktop/labour draft.docx", "/Users/houthakker/Desktop/Socialist_dress_outline.isf"}
end tell


-- GENERIC  ------------------------------------------------------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & |λ|(item i of xs, i, xs)
        end repeat
    end tell
    return acc
end concatMap

-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

(Vince Angeloni) #3

how is this better than using Mark’s script with this as the fist script line:

set myFilePathList to {}

and putting this in the deepest repeat loop for the attachment move:

set myFilePathList to myFilePathList & posix file of (desktopPath & attachmentName)

?


(Mark Alldritt) #4

7 posts were merged into an existing topic: Using map, filter and reduce or fold in Applescript


#5

Hello Mark,

Thanks, I was able to take your script and modify it slightly to function when invoked from a mail rule.

Tim


Using map, filter and reduce or fold in Applescript
(Vince Angeloni) #6

Oddly enough, when I run this using POSIX file, Mail throws an error that some objects were not the correct type. It works fine, if I change it to this:

`save anAttachment in file (desktopPath & attachmentName)`

The entire script which works on the selection without using POSIX:

set desktopPath to (path to desktop) as string
tell application "Mail"
	set selectedMessages to the selection
	repeat with aMessage in selectedMessages
		repeat with anAttachment in mail attachments of aMessage
			set attachmentName to name of anAttachment
			save anAttachment in file (desktopPath & attachmentName)
		end repeat
	end repeat
end tell

OS X 10.13.6
Mail Version 11.5 (3445.9.1)


(Jim Underwood) #7

Would you mind sharing your final script? I have the same requirement I’ve been struggling with.

Thanks.