Move Apple Mail to Specified Finder Folder

Move Apple Mail to Specified Finder Folder

@Nige_S @ComplexPoint
Risking axes, pitchforks, torches, and howls to kill the beast, here is another blackbox Frankenstein monster, courtesy of our beloved creator and rapist of small children and animals, ChatGPT, for your egg and rotten vegetable throwing pleasure.

But seriously, I would like your critical eye to see if you can optimize this AppleScript code as there is a 1-2 second delay when the script runs. I also have a parallel set of macros that opens the Finder location so I can watch and confirm the mail item shows up. At some point when I fully trust the script, I’ll simplify this by adding an action to delete the selected mail item and just move to the next message.

The script takes the selected Mail message and (I guess) moves/creates a copy in the specified Finder folder. There is the location of the Finder folder that needs to be specified for each instance of the script and three other parameters that need to be updated in the script each time a new folder is selected within a new script.

Doing this via AppleScript is a big upgrade from using the UI scripting I had in place before getting this script to work.

It took around 15 iterations to get it to work and sanitize the file names, ensure a unique file name, and handle duplicates.

All your input and comments are appreciated.

Note about how the script shows up here:
I did put ``AppleScript before and ``` after (three ticks before doesn't show up so I deleted one) the script per Using the Keyboard Maestro Forum but the script still comes up without the indentations and with spaces between each line. The code is copy/pasted from Script Debugger. :man_shrugging:

AppleScript ( expand / collapse )
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
tell application "Mail"
	set selectedMessages to selection
	if selectedMessages is {} then
		display dialog "No email selected. Please select one or more emails and try again." buttons {"OK"} default button "OK"
		return
	end if
	-- Full path to the target folder
	set FolderDT to "/Users/bernshanfield2/Desktop/" -- Absolute full path
	-- Ensure the folder exists
	do shell script "mkdir -p " & quoted form of FolderDT
	-- Process each selected email
	repeat with msg in selectedMessages
		try
			-- Retrieve the email subject
			set messageSubject to subject of msg
			if messageSubject is "" then
				set messageSubject to "Untitled Email"
			end if
			-- Sanitize the subject to create a valid filename
			set safeSubject to my makeSafeFileName(messageSubject)
			set filePath to FolderDT & safeSubject & ".eml"
			-- Ensure unique filename
			set uniqueFilePath to my makeUniqueFilePath(filePath)
			-- Get the raw source of the email
			set rawSource to source of msg
			if rawSource is missing value then
				error "The email source could not be retrieved."
			end if
			-- Write the email source to the file
			set fileRef to open for access (POSIX file uniqueFilePath as rich text) with write permission
			try
				write rawSource to fileRef
			on error errMsg
				close access fileRef
				error errMsg
			end try
			close access fileRef
		on error errMsg
			-- Handle any errors during the selection or writing process
			display dialog "Error saving email: " & safeSubject & ". Error: " & errMsg buttons {"OK"} default button "OK"
		end try
	end repeat
end tell
-- Function to sanitize the subject for use in file names
on makeSafeFileName(inputString)
	set forbiddenCharacters to ":/\\?%*|\"<>"
	set cleanedString to inputString
	-- Replace forbidden characters with underscores
	repeat with char in forbiddenCharacters
		set AppleScript's text item delimiters to char
		set cleanedString to text items of cleanedString
		set AppleScript's text item delimiters to "_"
		set cleanedString to cleanedString as string
	end repeat
	-- Replace emojis and other non-standard characters with an underscore
	set AppleScript's text item delimiters to ""
	set cleanedString to my replaceNonStandardChars(cleanedString)
	return cleanedString
end makeSafeFileName
-- Function to replace non-standard characters (like emojis) with underscores
on replaceNonStandardChars(inputString)
	set safeString to inputString
	-- Define a list of common non-standard characters (including emojis)
	set nonStandardChars to {"💡", "★", "❤", "☺", "😊", "🎉", "🔔"} -- Add other emojis as needed
	repeat with char in nonStandardChars
		set safeString to my replaceText(char, "_", safeString)
	end repeat
	return safeString
end replaceNonStandardChars
-- Function to replace text in a string
on replaceText(findString, replaceString, inputString)
	set {oldDelims, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ""}
	set AppleScript's text item delimiters to findString
	set inputString to text items of inputString
	set AppleScript's text item delimiters to replaceString
	set inputString to inputString as string
	set AppleScript's text item delimiters to oldDelims
	return inputString
end replaceText
-- Function to ensure the file has a unique name
on makeUniqueFilePath(originalPath)
	set uniquePath to originalPath
	set fileCounter to 1
	set baseName to do shell script "basename " & quoted form of originalPath & " .eml"
	set dirPath to do shell script "dirname " & quoted form of originalPath
	-- Check for file existence and append a counter if necessary
	repeat while (do shell script "test -e " & quoted form of uniquePath & "; echo $?") is "0"
		set uniquePath to dirPath & "/" & baseName & "_" & fileCounter & ".eml"
		set fileCounter to fileCounter + 1
	end repeat
	return uniquePath
end makeUniqueFilePath

Here is the simple Execute script file macro to point to each new script needed for each Finder file location:

Macro ( expand / collapse )

01)DT.kmmacros (38 KB)

And this is the UI in Mail:

To be clear -- I'm not saying you shouldn't use LLMs for this sort of thing. Their potential utility is obvious and, to be honest, I make just as many mistakes and often write far ropier code than they output!

What I am urging you to do is at least "sniff test" the code before you run it. AppleScript is very "readable", it mainly does what it says, and it would be sensible to at least look for an accidental "then delete all my files..." before you hit the "Execute" button...

I haven't done it myself, but I've heard good reports about the "...and now explain this code..." method, where you ask the LLM to explain its output before you use it. That, some common sense, and a little practice should protect you from the most egregious errors. Optimisation etc is just fluff for most of what we're doing -- good practice, of course, but a working solution now is usually far better than an optimal solution next week :wink:

With that disclaimer out of the way...

Doing things in/to Mail will take as long as it takes. Similarly for the two search-and-replace routines -- and "text item delimiter" operations are blazingly fast on shorter strings like these, so probably not worth redoing as calls to the KM Engine regex operation.

So it's really those multiple do shell script calls. And while, for example, doing a Finder-based test:

tell application "Finder"
	if not (exists (POSIX file folderDT as text)) then
		do shell script "mkdir -p " & quoted form of folderDT
	end if
end tell

...takes a third of the time that the simpler

-- Full path to the target folder
set FolderDT to "/Users/bernshanfield2/Desktop/" -- Absolute full path
-- Ensure the folder exists
do shell script "mkdir -p " & quoted form of FolderDT

...takes whenever the folder is already present, we're still only talking fractions of a second for a single op.

So you could look at replacing the shell script functions with Finder-based equivalents wherever possible but I don't know if, even all added together, they'd account for your "1-2 second delay".

Have you tried just putting the AppleScript into the action, rather than reading it from a file? It's an easy thing to check, and may give you the biggest bang for your buck.

It must be something that Script Debugger does—there was Markdown formatting around many of the words in the script, but that's exactly what using the triple-backtick does. You just need the plain text of the script itself. That's because within a triple-backtick code block, you cannot use any Markdown; the block itself is doing all the formatting when you say "```AppleScript" on the first line; all Markdown is ignored and just shown as input.

Using Apple's built-in Script Editor, that's what you get if you copy and paste ... you might see if Script Debugger has an option to copy as plain text or something?

I've edited the script to remove all those bits, and it formats as expected now.

-rob.

Thank you (and sorry) for taking your time to do that and for the explanation. I'll use Script Editor going forward.

Yes, the "Execute text script" option works noticeably faster than using the "Execute script file" option.

Thank you!

Also, thank you for what you said about using the scratch and stiff method for inspecting the LLM's poopy code. It made it clear and actionable for me when you said to look for "then delete all my files..." code. Without that specificity it just seemed like you had a serious ax to grind and I never considered that such insanity would be generated. Ahh, innocent lamb to slaughter.

And again, because it can't be said often enough, thank you for contributing your time, knowledge, and expertise to the community and in particular to me here. I appreciate it very much.