Apple Mail - Move If Spam
Summary: Of the selected messages, moves the spam ones to the Junk mailbox.
Requires: SpamSieve, Apple Mail
Install Location: ~/Library/Scripts/Applications/Mail or ~/Library/Application Scripts/com.apple.mail
Last Modified: 2021-07-15
Description
Normally, SpamSieve uses a plug-in and a special rule to move spam messages to the Junk mailbox. This script is an alternative way of using SpamSieve from Apple Mail that is useful in two main circumstances:
-
Sometimes it is not possible to use SpamSieve’s Apple Mail plug-in because of a new OS version, an incompatibility with another plug-in, or because you are trying to troubleshoot a crash in Mail.
To use the script in this way, open the Library folder and install the script in the ~/Library/Application Scripts/com.apple.mail folder. Then create a rule in Apple Mail called Move If Spam (do not put “SpamSieve” in the name of the rule) with conditions Every Message and actions Run AppleScript. Select the Apple Mail - Move If Spam.scpt file as the script.
Normally, to re-apply SpamSieve to a bunch of selected messages, you would use the Message ‣ Apply Rules command. This script is useful when you want to re-apply SpamSieve without applying all the other rules.
To use the script in this way, open the Library folder and install the script in the ~/Library/Scripts/Applications/Mail folder. You can invoke the script via the system’s script menu.
Disadvantages of this script compared to the SpamSieve rule are:
- The script is slower than the SpamSieve rule.
- With the SpamSieve rule, Mail will not apply subsequent rules if the message is spam. With a rule that runs the script, Mail will keep applying the rest of the rules.
Installation Instructions · Download in Compiled Format · Download in Text Format
Script
property pMarkSpamMessagesRead : false
property pChangeJunkStatus : true
property pColorSpamMessages : true
property pFlagSpamMessages : false
property pMoveBlueMessagesToTrash : false
property pMoveGrayMessagesToTrash : false
property pMovePurpleMessagesToTrash : false
property pMoveRedMessagesToTrash : false
property pMoveOrangeMessagesToTrash : false
property pMoveYellowMessagesToTrash : false
property pMoveGoodMessagesToInbox : false
property pMarkGoodMessagesUnread : false
property pEnableDebugLogging : false
global useJunkMailbox
on run
tell application "Mail"
set _messages to the selection as list
my processMessages(_messages)
end tell
end run
using terms from application "Mail"
on perform mail action with messages _messages
-- This is executed when Mail runs the rule.
my processMessages(_messages)
end perform mail action with messages
end using terms from
on processMessages(_messages)
try
set {useJunkMailbox, _spamFolderName, _accountName} to my lookupDefaults({"AppleMailUseJunkMailbox", "AppleMailTrainSpamName", "AppleMailSpamAccountName"}, {false, "Spam", ""})
repeat with _message in _messages
my processMessage(_message, _spamFolderName, _accountName)
end repeat
on error _errorMessage
my logToConsole("Error processing message: " & _errorMessage)
end try
end processMessages
on processMessage(_message, _spamFolderName, _accountName)
tell application "Mail"
set _spamMailbox to my spamMailboxForMessage(_message, _spamFolderName, _accountName)
set _isSpam to my processMessageIfSpam(_message, _spamMailbox)
if not _isSpam then
my processGoodMessage(_message)
end if
end tell
end processMessage
on spamMailboxForMessage(_message, _spamFolderName, _accountName)
tell application "Mail"
if _accountName is "" then
return mailbox _spamFolderName
end if
try
set _account to account _accountName
try
-- Use per-account if possible
set _account to (account of mailbox of _message)
if _account is missing value then
set _account to account _accountName
end if
end try
return mailbox _spamFolderName of _account
on error _error
if not (exists mailbox _spamFolderName) then
make new mailbox with properties {name:_spamFolderName}
end if
return mailbox _spamFolderName
end try
end tell
end spamMailboxForMessage
on processMessageIfSpam(_message, _spamMailbox)
set _source to my sourceFromMessage(_message)
tell application "SpamSieve"
set _score to score message _source
end tell
tell application "Mail"
my debugLog("Spam score of message is " & _score & ": " & _message's subject)
set _isSpam to _score ≥ 50
if pChangeJunkStatus then
set _message's junk mail status to _isSpam
end if
if _isSpam and pMarkSpamMessagesRead then
set _message's read status to true
end if
set _moveToTrash to my colorMessageAndDecideIfShouldMoveToTrash(_message, _score)
if _moveToTrash then
delete _message
else if _isSpam then
my moveMessage(_message, _spamMailbox)
end if
return _isSpam
end tell
end processMessageIfSpam
on colorMessageAndDecideIfShouldMoveToTrash(_message, _score)
tell application "Mail"
set _table to {¬
{99, blue, pMoveBlueMessagesToTrash, 6}, ¬
{95, gray, pMoveGrayMessagesToTrash, 5}, ¬
{88, purple, pMovePurpleMessagesToTrash, 4}, ¬
{81, red, pMoveRedMessagesToTrash, 3}, ¬
{75, orange, pMoveOrangeMessagesToTrash, 2}, ¬
{50, yellow, pMoveYellowMessagesToTrash, 1}, ¬
{0, none, false, -1}}
-- Flag colors chosen so that messages sort by spamminess: gray, purple, blue, green, yellow, orange, none
repeat with _row in _table
set {_threshold, _color, _moveToTrash, _flagColor} to _row
if _score ≥ _threshold then
if pColorSpamMessages then
set _message's background color to _color
end if
if pFlagSpamMessages then
set _message's flag index to _flagColor
end if
return _moveToTrash
end if
end repeat
end tell
end colorMessageAndDecideIfShouldMoveToTrash
on processGoodMessage(_message)
tell application "Mail"
try
set junk mail status of _message to false
end try
if pMarkGoodMessagesUnread then
try
set read status of _message to true
end try
end if
if pMoveGoodMessagesToInbox then
set _message's mailbox to my inboxForMessage(_message)
end if
end tell
end processGoodMessage
on inboxForMessage(_message)
tell application "Mail"
set _accounts to accounts where its enabled is true
set _account to my accountFromMessage(_message, _accounts)
set _mailbox to my inboxFromAccount(_account)
return _mailbox
end tell
end inboxForMessage
on accountFromMessage(_message, _accounts)
tell application "Mail"
try
set _recipientAddresses to my recipientAddressesFromMessage(_message)
repeat with _account in _accounts
set _accountAddresses to my addressesForAccount(_account)
repeat with _recipientAddress in _recipientAddresses
ignoring case
if _recipientAddress is in _accountAddresses then return _account
end ignoring
end repeat
end repeat
end try
try
-- Only do this as a fallback, because some people have one remote Spam mailbox but multiple inboxes.
set _account to account of mailbox of _message
if _account is not missing value then return _account -- Can happen for local mailboxes.
end try
return item 1 of _accounts
end tell
end accountFromMessage
on inboxFromAccount(_account)
tell application "Mail"
set _names to {"INBOX", "Inbox", "innboks", "Posteingang", "Boite de reception"}
repeat with _name in _names
try
set _mailbox to mailbox _name of _account
return _mailbox
end try
end repeat
return inbox
end tell
end inboxFromAccount
on recipientAddressesFromMessage(_message)
tell application "Mail"
set _addresses to {}
set _addresses to _addresses & my toAddressesFromMessage(_message)
set _addresses to _addresses & my ccAddressesFromMessage(_message)
set _addresses to _addresses & my extraAddressesFromMessage(_message)
if _addresses is not {} then return _addresses
return my parseAddressesFromMessage(_message)
end tell
end recipientAddressesFromMessage
on toAddressesFromMessage(_message)
tell application "Mail"
set _addresses to {}
if (count _message's to recipients) > 20 then
-- Mail in 10.3.5 will hang if we ask it to get too many "to recipients"
set _recipients to {_message's first to recipient}
else
set _recipients to _message's to recipients
end if
repeat with _recipient in _recipients
copy _recipient's address to end of _addresses
end repeat
return _addresses
end tell
end toAddressesFromMessage
on ccAddressesFromMessage(_message)
tell application "Mail"
set _addresses to {}
if (count _message's cc recipients) > 20 then
set _recipients to {_message's first cc recipient}
else
set _recipients to _message's cc recipients
end if
repeat with _recipient in _recipients
copy _recipient's address to end of _addresses
end repeat
return _addresses
end tell
end ccAddressesFromMessage
on extraAddressesFromMessage(_message)
tell application "Mail"
set _addresses to {}
set _headerNames to {"envelope-to", "x-original-to", "x-envelope-to", "delivered-to"}
try
set _headers to _message's headers
repeat with _header in _headers
try
set _name to name of _header
ignoring case
if _name in _headerNames then
copy content of _header to end of _addresses
end if
end ignoring
on error
-- Accessing headers seems broken on 10.7.0
end try
end repeat
end try
return _addresses
end tell
end extraAddressesFromMessage
on parseAddressesFromMessage(_message)
tell application "Mail"
set _addresses to {}
try
set _allHeaders to all headers of _message
repeat with _headerName in _headerNames
set AppleScript's text item delimiters to _headerName & ": "
try
ignoring case
set _value to paragraph 1 of text item 2 of _allHeaders
end ignoring
copy _value to end of _addresses
end try
end repeat
end try
return _addresses
end tell
end parseAddressesFromMessage
on lookupDefaults(_keys, _defaultValues)
tell application "SpamSieve"
try
set _result to {}
repeat with _i from 1 to count of _keys
set _key to item _i of _keys
set _defaultValue to item _i of _defaultValues
set _value to lookup single key _key default value _defaultValue
copy _value to end of _result
end repeat
return _result
on error -- SpamSieve 2.9.15 and earlier
return lookup keys _keys default values _defaultValues
end try
end tell
end lookupDefaults
-- Logging
on debugLog(_message)
if pEnableDebugLogging then my logToConsole(_message)
end debugLog
on logToConsole(_message)
set _logMessage to "SpamSieve [Apple Mail Move If Spam] MJTLog " & _message
do shell script "/usr/bin/logger -s " & _logMessage's quoted form
end logToConsole
on makeLogMessage(_action, _mailbox, _detail)
return _action & " " & my describeMailbox(_mailbox) & ": " & _detail
end makeLogMessage
on describeMailbox(_mailbox)
tell application "Mail"
set _mailboxName to _mailbox's name
try
set _accountName to name of _mailbox's account
on error
set _accountName to "On My Mac"
end try
return "“" & _accountName & "” / “" & _mailboxName & "”"
end tell
end describeMailbox
-- Logging Helpers
on sourceFromMessage(_message)
tell application "Mail"
my debugLog(my makeLogMessage("Getting source of message in", _message's mailbox, _message's subject))
return _message's source
end tell
end sourceFromMessage
on moveMessage(_message, _mailbox)
tell application "Mail"
if useJunkMailbox then
my debugLog("Moving message to Junk: " & _message's subject)
move _message to junk mailbox
else
my debugLog(my makeLogMessage("Moving message to", _mailbox, _message's subject))
set _message's mailbox to _mailbox
end if
end tell
end moveMessage