-- Apple Mail - Discard Spam -- https://c-command.com/scripts/spamsieve/apple-mail-discard-spam -- Summary: Completely deletes the messages in the Spam mailbox. -- Requires: SpamSieve, Apple Mail -- Install Location: ~/Library/Scripts/Applications/Mail -- Last Modified: 2021-08-09 property pEnableDebugLogging : false property pTrashingDelay : 0 property pRetryDelay : 0 on run {} tell application "Mail" my completelyDeleteMessagesFromMailbox(my spamMailbox()) end tell end run on spamMailbox() set _keys to {"AppleMailTrainSpamName", "AppleMailLocalSpamMailbox"} set _defaultValues to {"Spam", true} set {_spamFolderName, _isLocal} to my lookupDefaults(_keys, _defaultValues) tell application "Mail" if _isLocal then return mailbox _spamFolderName set _accounts to accounts repeat with _account in accounts try return mailbox _spamFolderName of _account end try end repeat error "No spam mailbox named “" & _spamFolderName & "” found." end tell end spamMailbox on completelyDeleteMessagesFromMailbox(_mailbox) tell application "Mail" -- "message id" (from the Message-ID header) is not actually unique, but "id" changes when the message is moved. -- Possibly a race condition here, but AppleScript sometimes fails (and is always much slower) if we try to get the "message id" from a list of messages. set {_messages, _messageIDs} to {messages of _mailbox, message id of messages of _mailbox} my debugLog("Found " & (count of _messages) & " messages in mailbox “" & name of _mailbox & "”.") repeat with _message in _messages my moveMessageToTrash(_message) end repeat delay pTrashingDelay set _failedMessagesIDs to my deleteMessageIDsFromTrash(_messageIDs) if _failedMessagesIDs is not {} then set AppleScript's text item delimiters to ", " my logToConsole("Mail did not delete the messages: " & _failedMessagesIDs) delay pRetryDelay set _failedMessagesIDs to my deleteMessageIDsFromTrash(_messageIDs) if _failedMessagesIDs is not {} then my logToConsole("After retrying, Mail still did not delete the messages: " & _failedMessagesIDs) else my debugLog("After retrying, Mail did delete the messages: " & _failedMessagesIDs) end if else my debugLog("Mail deleted all the moved messages from the trash") end if end tell end completelyDeleteMessagesFromMailbox on moveMessageToTrash(_message) tell application "Mail" try delete _message my debugLog("Moved message to trash: " & message id of _message) on error _error try set _messageID to message id of _message on error _innerError my logToConsole("Mail encountered an error moving unknown message to trash: " & _error) my logToConsole("Message is unknown because of error getting Message-ID: " & _innerError) return -- Give up on this message, but let it continue trying to move other messages to trash. end try try delete _message my logToConsole("Mail encountered a temporary error moving message " & _messageID & " to the trash: " & _error) on error _error my logToConsole("Mail could not move the message " & _messageID & " to the trash because of an error: " & _error) end try end try end tell end moveMessageToTrash on deleteMessageIDsFromTrash(_messageIDs) tell application "Mail" try set _trashedMessages to messages of trash mailbox -- Includes contents of child mailboxes. on error _error number _errorNumber if _errorNumber is -1712 then -- errAETimeout display dialog "Mail took too long to respond, probably because there are too many messages in the trash. Please try empting the trash before using Discard Spam again." end if error _error number _errorNumber end try set _deletedMessageIDs to {} repeat with _trashedMessage in _trashedMessages try -- Coercion prevents building up a list of references that can make the script fail. set _messageIDToDelete to (message id of _trashedMessage) as Unicode text if _messageIDToDelete is in _messageIDs then my debugLog("Deleting message from trash: " & _messageIDToDelete) delete _trashedMessage copy _messageIDToDelete to end of _deletedMessageIDs end if on error _error my logToConsole("Error deleting message from trash: " & _error) -- Sometimes Mail reports an error getting "_trashedMessage's message id", saying it can't find the message. end try end repeat set _failedMessageIDs to {} repeat with _messageID in _messageIDs try if _messageID is not in _deletedMessageIDs then -- Coercion prevents building up a list of references that can make the script fail. set _messageID to _messageID as Unicode text copy _messageID to end of _failedMessageIDs end if on error _error my logToConsole("Error finding failed messages: " & _error) end try end repeat return _failedMessageIDs end tell end deleteMessageIDsFromTrash on logToConsole(_message) set _logMessage to "SpamSieve [Discard Spam Script] " & _message do shell script "/usr/bin/logger -s " & _logMessage's quoted form end logToConsole on debugLog(_message) if pEnableDebugLogging then my logToConsole(_message) end debugLog 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