Perform Mail Action With Messages – no result

I have a mail rule created with AppleScript. I wanted to use the command perform mail action with messages put inside the tell block with Mail as a target to make it delete junk mail periodically. The script below (in the picture) compiles and runs fine but produces no result. I’m aware of the eponymous handler fitted with on…end clauses that’s usually enclosed in using terms from but since it’s a standard command in Mail’s dictionary the handler variant fails to compile inside Mail’s tell block but goes OK as a command: used outside the block it compiles successfully but errors out because its direct parameter requires messages which are Mail’s object.

  1. Is the use correct and what am I missing?
  2. What’s the difference between these two incarnations of the command?
  3. When and how should I use either of them?
    3.1 Do I have to include the property run script in rule scripts?

The script in question

The rule (a GUI version)

Model: MacBook Pro
AppleScript: 2.3.2
Browser: Safari 537.86.7
Operating System: macOS 10.9

First, save following script with name “Delete Junk messages.scpt” at Mail scripts location (~Library/Applications Scripts/com.apple.mail/)


using terms from application "Mail"
	on perform mail action with messages JunkMessages for rule this_rule
		tell application "Mail"
			repeat with aMsg in JunkMessages
				if (all headers of aMsg contains "X-Spam_Flag") or (all headers of aMsg contains "X-Suspected-Spam_Flag") then delete contents of aMsg
			end repeat
		end tell
	end perform mail action with messages
end using terms from

Now, make following rule and confirm appling it. Done.

Thank you. But my question was what’s the difference between perform mail action with messages as a Mail command and a standalone handler?

Model: MacBook Pro
AppleScript: 2.3.2
Browser: Safari 537.86.7
Operating System: macOS 10.9

You will not be able to invoke the action correctly because you will not be able to pass filtered messages to the rule. It is not the action that sends filtered messages to the rule, but the rule sends them to the action.

Therefore, you have such a clumsy rule in post #1, which also filters messages incorrectly.

Please, refrain from using emotionally charged language as in “you rule is clumsy” since it doesn’t explain why it’s supposedly “clumsy”, what “clumsy” means exactly in the discussed context, and you don’t offer any easily testable alternatives.

I only understood the part that using simple perform… command doesn’t work because the rule action is a recipient of messages, not vice versa.

Also, the part “for rule theRule” in the handler’s clause: does theRule denote an existing mail rule? If the rule is defined and has conditions why do I need to do that once again inside the handler (if… then delete )?

I faced an additional issue with the mail rule script I mentioned in #1 (it’s not the perform mail action thing). I can add an AppleScript file manually to a rule via Mail preferences but the scriptable way throws an error even though I supply a correct class. I made sure it’s correct by adding AppleScript action manually and then getting the value of the run script value in ScriptEditor. My rule creating script goes as follows:

tell application "Mail"
	
	
	
	set TargetRules to every rule whose name contains "Delete Junk Monthly"
	if TargetRules is not {} then
		repeat with aRule in TargetRules
			delete aRule
		end repeat
	end if
	
	tell (make new rule with properties {name:"Delete Junk Monthly", delete message:true, all conditions must be met:false})
		tell (make new rule condition with properties {header:"X-Spam-Flag"})
			
			set qualifier to equal to value
			set expression to "yes"
		end tell
		
		tell (make new rule condition with properties {header:"X-Suspected-Spam"}) to set {qualifier, expression} to {equal to value, "true"}
		
		
		set enabled to true
		set stop evaluating rules to false
		
	end tell
	set run script to file "OS X Mavericks:Users:home:Library:Application Scripts:com.apple.mail:Perform Mail Action.scpt"
	save
	
end tell



Result:
error “Mail got an error: Can’t set run script to file "OS X Mavericks:Users:home:Library:Application Scripts:com.apple.mail:Perform Mail Action.scpt".” number -10006 from run script

When I get the value of run script I get the file reference file “OS X Mavericks:Users:home:Library:Scripts:Applications:Mail:Perform Mail Action.scpt”

home is a placeholder and not an actual name of my home folder.

Mail actions are whatever function(s) the rule will perform on meeting its conditions; there’s no discernable reason to use “perform mail action with messages” as a command; that would be a fait accompli, if the rule triggered. As a handler, it allows for additional actions before executing preset ones.

It does denote an existing rule, but you don’t have to re-specify conditions in the script that the rule was told to do and vice versa. My experience is that including the “for rule ” portion breaks the handler’s functionality, and I’m not sure if that’s a bug or a flaw in my understanding its usage.

I think you’ve just anonymized the home name in your last question. The file part may just need to be parenthesized to work—I didn’t test it— but you could also choose that file in the appropriate folder.


set scriptFile to (choose file default location alias ((path to library folder from user domain as text) & "Application Scripts:com.apple.mail")) as text

tell application "Mail"
	make rule with properties {run script:file scriptFile}
end tell

edited to simplify and correct script

I was able to resolve that part: I misunderstood the for rule TheRule part of the handler and supplied the actual rule instead of the variable, which was what caused that error.
In the separate, perform mail action with messages script consisting only of this handler I omitted for rule TheRule and it compiled successfully. If we look into the dictionary entry on perform mail action with messages then for rule is optional too. However, as I understand in order to have a rule run an AppleScript script as its action you have to set the run script property which is a scriptable analogue of setting the action to run AppleScript using the GUI. If it’s responsible for attaching a mail action to the rule does getting rid of it makes a difference?

However, scripting it fails. I re-wrote my original script as

property RunScriptPath : (((path to library folder from user domain) as text) & "Application Scripts:com.apple.mail:Clean Out Junk.scpt")
tell application "Mail"
	
	
	
	set TargetRules to every rule whose name contains "Delete Junk Monthly"
	if TargetRules is not {} then
		repeat with aRule in TargetRules
			delete aRule
		end repeat
	end if
	
	tell (make new rule with properties {name:"Delete Junk Monthly", delete message:true, all conditions must be met:false, run script:file RunScriptPath})
		tell (make new rule condition with properties {header:"X-Spam-Flag"})
			
			set qualifier to equal to value
			set expression to "yes"
		end tell
		
		tell (make new rule condition with properties {header:"X-Suspected-Spam"}) to set {qualifier, expression} to {equal to value, "true"}
		
		set enabled to true
		set stop evaluating rules to false
		
	end tell
	save
	
end tell

It compiles and runs splendidly creating the rule, however the Run AppleScript action is not really set and requires choosing the script file manually which completely defeats the purpose of this script :point_up:

Is it a bug or have I missed something?

Model: MacBook Pro
AppleScript: 2.3.2
Browser: Safari 537.86.7
Operating System: macOS 10.9

The pared down version, below, sets it on my machine. Try that first and then add the other stuff back.

As a side-note, “path to ” 's coercion really is properly within the parentheses as a parameter; although it doesn’t change the outcome, here, it may affect speed in some situations.

set RunScriptPath to ((path to library folder from user domain as text) & "Application Scripts:com.apple.mail:Clean Out Junk.scpt") 
tell application "Mail"
	(make rule with properties {name:"Delete Junk Monthly", delete message:true, all conditions must be met:false, run script:file RunScriptPath})
end tell

I ran your script and the result is still the same: the action isn’t set. macOS Mojave.

Model: MacBook Pro 9,1 (mid-2012 15") Core i7 2.3 GHz, 16 GB RAM, 1TB SSD
AppleScript: 2.7
Browser: Safari 605.1.15
Operating System: macOS 10.14

I’m also on a Mojave machine—10.14.6. Perhaps something is wrong with the script you’re trying to link.

using terms from application "Mail"
	on perform mail action with messages JunkMessages for rule CleanOutJunk
		tell application "Mail"
			repeat with Acc in every account
				tell Acc
					tell mailbox "Junk"
						repeat with aMsg in (get every message whose date received ≥ (current date) - 5 * weeks)
							tell aMsg's contents
								delete aMsg's contents
								
							end tell
						end repeat
						
					end tell
				end tell
			end repeat
			
			
			
		end tell
		
	end perform mail action with messages
end using terms from

Model: MacBook Pro
AppleScript: 2.3.2
Browser: Safari 537.86.7
Operating System: macOS 10.9

Your script file handler doesn’t appear to be needed at all.

[format]using terms from application “Mail”
on perform mail action with messages JunkMessages [/format]
The first line is useful when the application is not directly addressed, and the latter is so you can iterate whatever JunkMessages may be.

The rule named in your script file—“CleanOutJunk”—doesn’t match the rule name you’re creating—“Delete Junk Monthly”—or the one in the attached image—“Delete Junk,” and I’m not certain you’re testing the correct item for changes. Try using a script to test placement:

set scriptFile to POSIX file ((choose file default location alias ((path to library folder from user domain as text) & "Application Scripts:com.apple.mail"))'s POSIX path)
tell application "Mail"
	(make rule with properties {name:"TEST", run script:file scriptFile})
	if rule -1's run script is not missing value then display dialog "Success!"
end tell

I changed my rule’s name to “Delete Junk” just to test. On the other hand, I removed both handlers’ clauses in the script being attached effectively turning it into a basic script file and ran your rule-creating script’s pared-down version without a hitch but it still failed to select a script file (“No script selected”) as before.

Update I think I found one more nasty bug by Apple. There’s a plist file at /Users/home/Library/Mail/V[any digit]/MailData/SyncedRules.plist containing all rules, including user-created ones. I deleted my rule manually, then replaced it by running my original script (that deletes it if the rule exists - for debugging purposes). In all cases that plist file gets updated properly but not the GUI panel that manages mail rules unless you set the script action manually. Every time adding and deleting a rule I saw it added or removed in SyncedRules.plist too (together with every condition and action I scripted showing up as XML key-value pairs), so it works on this end. After setting it manually in the rules panel I removed the rule, then re-added it via the scripted method and now that selection stuck as expected. I then moved SyncedRules.plist to Trash; I made sure that rules were gone at the GUI level and quitted Mail. I put the deleted plist back, ran my script again and got back to square 1. The list updated timely but the rules pane did not. Dead end.


As I see from your picture, it isn’t updated properly. The correct string value of AppleScript key should be only name of script, and not its full posix path. In your case it should be “Clean Out Junk.scpt”

In any case, it’s either a bug in the run script property, or Apple doesn’t want this functionality to be automated programatically. Note that the value of single POSIX path property of run script property is read-only.

If that was the case then this property would be marked as such (read only, or r/o) in the Dictionary. And what makes you think the value shouldn’t be the full POSIX path?

It is easy to check my statement. Select the script manually in the rule, and see how Mail.app writes (correctly) the AppleScript key in the plist file.

As for the run script property, Apple gives you minimal control over it: you can set it to missing value, which allows you to programmatically remove the bound script. That is, it is not just for reading. But that’s all. You can programmatically remove a script, but not add a new one.

This approach leads me to conclude that Apple is wary of unauthorized programmatic AppleScript addition. I can create Mail.app’s file object programatically, but its POSIX file property is empty value:

tell application "Mail" to set mailFile to a reference to file ((choose file) as string)

There does appear to be something amiss. Yesterday, the GUI on my machine reflected the selected item, but today it doesn’t. The failed items from today show their full POSIX path in the synced plist, where working ones use only the displayed name. My test script reports the file as being linked in both cases. As an experiment, I removed the non-name path portion in the plist, and then the GUI immediately updated. Perhaps the problem is a corrupt preference file. At any rate, simply writing to the plist appears to be a workaround.

Here’s the final draft of my script which creates the rule and works around the GUI shortcoming using System Event’s Property List File Suite. The latter presented yet another difficulty layer because there isn’t an easy way of referencing nested properties since in the case of such files as SyncedRules.plist parent property list items’ names that correspond to the rule’s name are nothing but missing values (not to mention that values of some properties are records inside records where the keys are not object’s properties but standard variable identifiers that, in turn, makes them non-referenceable): to overcome this obstacle we need to set the run script property to the file reference to identify both the last created rule and its property list item “AppleScript” to target the correct property list item by its value which is the POSIX path and then re-set it to the legitimate one.

property SyncedRules : (((path to library folder from user domain) as text) & "Mail:V2:MailData:SyncedRules.plist")

property ScriptToRun : (((path to library folder from user domain) as text) & "Application Scripts:com.apple.mail:Clean Out Junk.scpt") -- or can be selected with the Finder chooser.
set text item delimiters to ":"
set ScriptFile to the last text item of ScriptToRun
set text item delimiters to ""

set SyncedRulesPosixPath to quoted form of POSIX path of alias SyncedRules

tell application "Mail"
	
	
	
	set TargetRules to every rule whose name contains "Delete Junk Monthly"
	if TargetRules is not {} then
		repeat with aRule in TargetRules
			delete aRule
		end repeat
	end if
	
	tell (make new rule with properties {name:"Delete Junk Monthly", all conditions must be met:false, run script:file ScriptToRun})
		tell (make new rule condition with properties {header:"X-Spam-Flag"})
			
			set qualifier to equal to value
			set expression to "yes"
		end tell
		
		tell (make new rule condition with properties {header:"X-Suspected-Spam"}) to set {qualifier, expression} to {equal to value, "true"}
		
		set enabled to true
		set stop evaluating rules to false
		
	end tell
	save
	delay 2
	quit
end tell

delay 1

ignoring white space and case
	try
		tell application "System Events"
			tell property list file SyncedRules
				get the last property list item
				tell (the result)
					if (exists property list item "AppleScript") then
						tell property list item "AppleScript"
							set ScriptPath to (get its value)
							if ScriptPath contains ScriptFile then set TargetPLI to get it
						end tell
					end if
				end tell
				
				tell TargetPLI
					set its value to ScriptFile
					
				end tell
			end tell
		end tell
	end try
end ignoring

tell application "Mail" to activate

Model: MacBook Pro
AppleScript: 2.3.2
Browser: Safari 537.86.7
Operating System: macOS 10.9