(* Requirements: https://discussions.apple.com/thread/8480829 Quick hints 1) Apple hids the User's library folder. You will want to make it visible. http://osxdaily.com/2013/10/28/show-user-library-folder-os-x-mavericks/ 2) Place this Applescript in this folder with name ShortMailDownload.scpt. You may not use the App extension. "/Users/mac/Library/Application Scripts/com.apple.mail" 2) The first "on" statement needs to be on perform mail action with messages theMessages 3) Mail > Preferences... > Rules > Add Rule "Perform the following actions" [ buttons should be ] "Run AppleScript" "ShortMailDownload" 4) When saving you may get a warning message that some other app, Mail, has made changes to the script. When ever the script is run, the script changes a global variable which modifies the script. Ignore and save any way. Limitation: Yosemite, 10.10.5, Mail Version 8.2 (2104) only supports extracting one attachment. No problem in High Sierra, 10.13.6. off topic. Clicking on menu item: tell application "System Events" to click menu item "Date" of menu "Sort By" of menu item "Sort By" of menu "View" of menu bar item "View" of menu bar 1 of process "Mail" Based on the example " Sample Rule Action Script.scpt" script in macOS 10.6.8 found in folder "/Library/Scripts /Library/Scripts/Mail Scripts/Rule Actions" Sample Rule Action Script.scpt I think "on perform mail action" needs to be the first "on" routine. -- If run as an ordinary script, instead of directly from the Scripts -- menu, it will call the default handler instead. on r u n tell application "Mail" to set selectedMessages to selection tell me to perform mail action with messages (selectedMessages) end r u n http://hints.macworld.com/article.php?story=20070215145127300 There is lots of outdated advice on download attachments found by Google. https://stackoverflow.com/questions/39882312/how-to-download-an-attachment-from-mail-via-applescript-and-mail-rules Copyright 2018 rccharles Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *) (* Uncomment when you want to debug this script in the Script Editor. Go into Mail and select a message. Run this script. *) (* on run tell application "Mail" try set theSelectedMessages to selection log "class of theSelectedMessages is " & class of theSelectedMessages log "count of theSelectedMessages is " & (count of theSelectedMessages) on error errMsg number n display dialog "Silly, you need to select a message in Mail." & return & "on error the errMsg is " & errMsg & " number is " & n giving up after 8 return end try -- not used, don't know how to pass to perform ... set theRule to {name:"dummyRule"} tell me to perform mail action with messages theSelectedMessages -- for theRule end tell -- app "Mail" end run *) (**) using terms from application "Mail" on perform mail action with messages theMessages --for rule theRule -- Suggest this code be the first "on" in file, no on r u n & no assumed on r u n either -- Required by debug routine. global debugRunning set debugRunning to "" -- debug status for displaying messages in this routine. set debugSwitch to true log "debugSwitch is " & debugSwitch -- initialize set longWait to 20 (* Where to place files *) -- intermediate folder in Home folder -- must be Downloads for later version of macOS set intermediateDownloadFolder to "Downloads:" -- Folder inside of Downloads folder set intermediateSubFolder to "subFolderForMailDownload" -- final destination folder in Home folder set finalDestination to "Students" set startMsg to "perform mail with " & getMyName() & " ====== " & ((current date) as string) & " ====== perform mail " my debugLog(startMsg) --display dialog startMsg giving up after 2 set intermediateFolder to intermediateDownloadFolder & intermediateSubFolder log "intermediateFolder is " & intermediateFolder if (count of theMessages) = 0 then log "odd no messages to process." display dialog "Odd, no messages to process." giving up after longWait return 1 end if -- mail requires download to the Downloads or subfolders of Downloads -- for some reason, this gets the path to this app when invoked by mail. set pathToHome to (path to home folder) as string log ("pathToHome is " & pathToHome) set listOfPathToHome to textToList(pathToHome, ":") set pathToHome to item 1 of listOfPathToHome & ":" & item 2 of listOfPathToHome & ":" & item 3 of listOfPathToHome & ":" log ("revised pathToHome is " & pathToHome) -- make intermediate folder try tell application "Finder" set newfolder to make new folder at (pathToHome & intermediateDownloadFolder) with properties {name:intermediateSubFolder} my debugLog("Created folder " & pathToHome & intermediateDownloadFolder & intermediateSubFolder) end tell on error errMsg number n -- It's ok if the folder already exits [ -48 ]. Put out warning for all other errors. if n is not -48 then set commonError to "on error " & intermediateSubFolder & " the errMsg is " & errMsg & " number is " & n display dialog commonError giving up after longWait my debugLog(commonError) return 2 end if end try -- make final destination folder try tell application "Finder" set newfolder to make new folder at pathToHome with properties {name:finalDestination} my debugLog("Created final destination folder " & pathToHome & finalDestination) end tell on error errMsg number n -- It's ok if the folder already exits [ -48 ]. Put out warning for all other errors. if n is not -48 then set commonError to "on error " & finalDestination & " errMsg is " & errMsg & " number is " & n display dialog commonError giving up after longWait my debugLog(commonError) return 3 end if end try tell application "Mail" --activate log "was activated" -- look throught the messages we selected. repeat with aMessage in theMessages -- loop through each message log "class of aMessage is " & class of aMessage -- get the senders email address set fromMail to aMessage's sender my debugLog("fromMail is " & fromMail) -- Create folder for the individual student. -- ---- Set final target folder ----> set pathToStudents to pathToHome & finalDestination & ":" my debugLog("pathToStudents is " & pathToStudents) repeat 1 times -- sinulate continue try tell application "Finder" set newStudent to make new folder at pathToStudents with properties {name:fromMail} my debugLog("Created final destination subfolder " & pathToStudents & fromMail) end tell on error errMsg number n -- It's ok if the folder already exits [-48 ]. Put out warning for all other errors. if n is not -48 then set commonError to "attempting to create " & pathToStudents & fromMail & return & " on error the errMsg is " & errMsg & " number is " & n display dialog commonError giving up after longWait my debugLog(commonError) exit repeat -- look at next message. simulate iterate here. end if end try set pathToTheStudent to pathToStudents & fromMail & ":" my debugLog("pathToTheStudent is " & pathToTheStudent) -- repeat with aFile in aMessage's mail attachments -- set ourList to every mail attachment of aMessage repeat with aFile in (mail attachments of aMessage) log "save aFile" repeat 1 times -- sinulate continue --if (downloaded of aFile) then -- check if file is already downloaded set attachmentName to name of aFile my debugLog("attachmentName is " & attachmentName) set {fileName, fileExt} to my getNameExt(attachmentName) log "fileName is " & fileName & " fileExt is " & fileExt -- ---- Intermediate folder target ----> -- apple changed mail to require the download to be in the downloads folder :-( set pathForDownload to pathToHome & intermediateFolder & ":" set clearCount to 0 set destPath to pathForDownload & attachmentName set finalDestPath to pathToStudents & fromMail & ":" & attachmentName log "check for free name. clearCount is " & clearCount & return & " destPath is " & destPath & return & " finalDestPath is " & finalDestPath try repeat while my fileExists(destPath) or my fileExists(finalDestPath) if clearCount > 300 then set commonError to "could not find free name when searching for free name. clearCount is " & clearCount & " clean up intermediate and final foldrs. " & return & destPath & return & finalDestPath my debugLog(commonError) -- have to throw an error to get out of this repeat. Could have set a switch I guess. display dialog commonError giving up after longWait error "cannot find free file name" number 8110 end if set clearCount to clearCount + 1 log "clearCount is " & clearCount set destPath to pathForDownload & fileName & "#" & clearCount & "." & fileExt set finalDestPath to pathToStudents & fromMail & ":" & fileName & "#" & clearCount & "." & fileExt my debugLog("searching for free name. clearCount is " & clearCount & return & " destPath is " & destPath & return & " finalDestPath is " & finalDestPath) end repeat -- while on error errMsg number n if n is not 8110 then set commonError to "attempting to create free filename " & return & " on error the errMsg is " & errMsg & " number is " & n display dialog commonError giving up after longWait my debugLog(commonError) end if exit repeat -- look at next message. simulate iterate here. end try my debugLog("found usable name. destPath is " & destPath) save aFile in destPath as native format log "file was saved." -- move it right on to file location. -- debuggin to see if we got the right name my debugLog("Saved to intermediate. destPath is " & destPath) if my fileExists(destPath) then tell application "Finder" try -- from file filename path to full folder name path :-(. move destPath to pathToTheStudent my debugLog("saved to final folder pathToTheStudent is " & pathToTheStudent) on error msg number n set outMsg to "Got error message while moving " & destPath & " to " & pathToTheStudent & return & "message is " & msg & return & "number is " & n my debugLog(outMsg) display dialog outMsg giving up after longWait -- might as well try the next attachment exit repeat -- look at next message. simulate iterate here. end try end tell else -- very bad, folder doesn't exist as expected. my debugLog("very bad, folder " & destPath & " doesn't exist as expected.") exit repeat -- look at next message. simulate iterate here. end if end repeat -- one time to make like a continue statment end repeat -- next attached file end repeat -- continue end repeat -- next message end tell --tell app "Mail" return 0 end perform mail action with messages end using terms from (* ======================== Subroutines ======================= *) -- ------------------------------------------------------ (* *) on appendToFile(fileId, theData) local theSize, writeWhere set theSize to (get eof fileId) set writeWhere to theSize + 1 as integer write theData to fileId starting at writeWhere end appendToFile -- ------------------------------------------------------ (* debug() Write messages to a log file. -- Need to place these two lines in the calling routine. global debugRunning set debugRunning to "" -- references appendToFile() -- example: debug("start program. Reading from " & listOfFiles) found here: /Users/mac/Documents/BJ\ Prior\ Years/BJ2004/sendmailapp2\ copy *) on debug(theMessage) -- return global debugRunning local theSize, startupDiskName, pathToLog, fileReference set pathToLog to (path to home folder as text) & "tryAttachmentsLog.txt" -- log "pathToLog is " & pathToLog -- display dialog "pathToLog is " & pathToLog giving up after 4 try -- Complete the path. set pathToLog to pathToLog as text set fileReference to (open for access file pathToLog ¬ with write permission) if debugRunning = "" then set theSize to (get eof fileReference) if theSize > 0 then appendToFile(fileReference, " " & return) end if appendToFile(fileReference, " --- debug on " & ((current date) as string) & " --- " & return) set debugRunning to "running" end if -- log "theMessage " & theMessage -- display dialog "in debug..." & return & "theMessage " & theMessage giving up after 3 appendToFile(fileReference, theMessage & return) close access fileReference tell application "Finder" set the creator type of the file pathToLog ¬ to "R*ch" end tell on error mes number n try set commonErr to "error ... " & mes & " error number is " & n log commonErr close access fileReference display dialog commonErr giving up after 4 end try end try -- log "end of debug" end debug (* write log message to script editor log and to our file log *) on debugLog(theMessage) log "debugLog: " & theMessage return debug(theMessage) end debugLog -- ------------------------------------------------------ (* ideas from: https://stackoverflow.com/questions/3469389/applescript-testing-for-file-existence use the alias way. *) on fileExists(theFile) -- (String) as Boolean (* "System Events" and "Finder" checking for file existance revealed problems. l*) set debugging to false if debugging then log " fileExists: theFile is " & theFile try set theAlias to theFile as alias set theExistance to true on error errMsg number n if debugging then log " fileExists: n is " & n -- File or folder doesn't exist. if n is not -43 then set commonError to "on error the errMsg is " & errMsg & " number is " & n if debugging then log " fileExists: " & commonError display dialog commonError giving up after 10 -- cause grief above. error "Failure of alias." number -1 else set theExistance to false end if end try if debugging then log " fileExists: theExistance is " & theExistance return theExistance end fileExists (* Philip Regan https://stackoverflow.com/questions/3469389/applescript-testing-for-file-existence *) (*on fileExists(theFile) -- (String) as Boolean tell application "System Events" if exists file theFile then return true else return false end if end tell end fileExists*) -- ------------------------------------------------------ (* Yvan Koenig https://macscripter.net/viewtopic.php?id=43133 with mods for no extension present *) on getExt(theName) if (offset of "." in theName) is greater than 0 then set saveTID to AppleScript's text item delimiters set AppleScript's text item delimiters to {"."} set theExt to last text item of theName set AppleScript's text item delimiters to saveTID if theExt ends with ":" then set theExt to text 1 thru -2 of theExt else set theExt to "" end if return theExt end getExt -- ------------------------------------------------------ (* Input: a file with or without an extension. hhas https://forums.macrumors.com/threads/applescript-to-get-file-name.927338/ may not work with folders with extensions like apps. Test cases: set fileName to " " set fileExt to my getExt(attachmentName) log "fileName is " & fileName & " fileExt is " & fileExt set fileExt to my getExt("testfileNo") log "fileName is " & fileName & " fileExt is " & fileExt set fileExt to my getExt("path:to:testfileNo:") log "fileName is " & fileName & " fileExt is " & fileExt log ">>>>>>>>>>>>>>>>>>>>>>" set {fileName, fileExt} to my getNameExt(attachmentName) log "fileName is " & fileName & " fileExt is " & fileExt set {fileName, fileExt} to my getNameExt("testfileNo") log "fileName is " & fileName & " fileExt is " & fileExt set {fileName, fileExt} to my getNameExt("path:to:testfileNo:") log "fileName is " & fileName & " fileExt is " & fileExt *) on getNameExt(fileName) set saveTID to AppleScript's text item delimiters set AppleScript's text item delimiters to "." if fileName contains "." then set {displayName, nameExt} to {text 1 thru text item -2, text item -1} of fileName else set {displayName, nameExt} to {fileName, ""} end if set AppleScript's text item delimiters to saveTID return {displayName, nameExt} end getNameExt -- ------------------------------------------------------ (* modified to let the extension be. by mklement0 https://stackoverflow.com/questions/5770384/how-find-the-file-name-of-an-executing-applescript *) on getMyName() local myAlias, myName tell application "System Events" set myAlias to path to me -- alias to the file/bundle of the running script set myName to name of myAlias -- filename with extension, if any. -- leave extension alone. end tell return myName end getMyName -- ------------------------------------------------------ (* ls_l is list file with options the format is best for debuging. example usage: set {fileExists, fromUnix} to ls_l(attachmentNamePath, "-l") log "attachmentNamePath fileExists is " & fileExists & return & " fromUnix is " & fromUnix *) on ls_l(attachmentNamePath, options) --log "ls_l" --log options set unixAttachmentNamePath to POSIX path of attachmentNamePath --log "unixDesktopPath = " & unixAttachmentNamePath set quotedUnixAttachmentNamePath to quoted form of unixAttachmentNamePath --log "quoted form is " & quotedUnixAttachmentNamePath try set fromUnix to do shell script "ls " & options & " " & quotedUnixAttachmentNamePath set fromUnix to "ls " & options & return & fromUnix set fileExists to true on error errMsg number n set fromUnix to "ls " & options & " error..." & errMsg & " with number " & n set fileExists to false end try return {fileExists, fromUnix} end ls_l -- ------------------------------------------------------ (* textToList seems to be what you are trying to do thisText is the input string delim is what to split on returns a list of strings. - textToList was found here: - http://macscripter.net/viewtopic.php?id=15423 *) on textToList(thisText, delim) set resultList to {} set {tid, my text item delimiters} to {my text item delimiters, delim} try set resultList to every text item of thisText set my text item delimiters to tid on error set my text item delimiters to tid end try return resultList end textToList