-- Apple Mail - Remote Training -- https://c-command.com/scripts/spamsieve/apple-mail-remote-training -- Summary: Training script for setting up a spam filtering drone with Apple Mail. -- Requires: SpamSieve, Apple Mail -- Install Location: ~/Library/Application Scripts/com.apple.mail or ~/Library/Scripts/Applications/Mail/ -- Last Modified: 2022-12-27 property pMarkSpamMessagesRead : false property pColorSpamMessages : true property pFlagSpamMessages : false property pMarkGoodMessagesUnread : false property pMoveBySettingMailbox : true property pSpamMailboxName : "Spam" property pEnableDebugLogging : false global useJunkMailbox on accountNamesForDrone() -- Enter your account names here. If you have more than one, separate with commas: {"Account 1", "Account 2"} -- The account name comes from the "Description" field in the Accounts tab of Mail's preferences. return {"Account 1"} end accountNamesForDrone on spamMailboxNamesByAccount() -- You can specify pairs here, e.g. {{"Work Account", "Junk"}, {"Personal Account", "Spam"}} -- to have different spam mailbox names for each account. If an account is not specified, -- it defaults to pSpamMailboxName. return {} end spamMailboxNamesByAccount on hostNameForDrone() return "" -- Remove the above line if iCloud rule syncing is working incorrectly, -- so that your remote training rule cannot be made inactive on the -- non-drone Macs. set {_host} to my lookupDefaults({"AppleMailEnabledHostName"}, {""}) return _host end hostNameForDrone on debugLog(_message) if pEnableDebugLogging then my logToConsole(_message) end debugLog on logToConsole(_message) set _logMessage to "SpamSieve [Apple Mail Remote Training MJTLog] " & _message do shell script "/usr/bin/logger -s " & _logMessage's quoted form end logToConsole on run -- This is executed when you run the script directly. my doRemoteTraining() end run on idle -- This is executed periodically when the script is run as a stay-open application. my doRemoteTraining() return 60 * 5 -- Run again in 5 minutes. end idle on shouldDisableOnThisMac() set _droneHost to my hostNameForDrone() if _droneHost is "" then return false set _currentHost to do shell script "/usr/bin/uname -n" if _droneHost is _currentHost then return false my debugLog("Drone disabled on host: " & _currentHost) return true end shouldDisableOnThisMac using terms from application "Mail" on perform mail action with messages _messages -- This is executed when Mail runs the rule. my doRemoteTraining() end perform mail action with messages end using terms from on doRemoteTraining() if application "Mail" is not running then return if my shouldDisableOnThisMac() then return tell application "SpamSieve" set useJunkMailbox to lookup single key "AppleMailUseJunkMailbox" without default value end tell try tell application "Mail" to get version on error _error number _errorNumber if _errorNumber is -1743 then -- errAEEventNotPermitted set _alertMessage to "You can give “Apple Mail - Remote Training” access to control Mail and SpamSieve from System Preferences > Security & Privacy > Privacy > Automation. For more information, please see: https://c-command.com/spamsieve/help/security-privacy-acce" display alert _error message _alertMessage end if end try tell application "Mail" set _accounts to my accountsToCheck() repeat with _account in _accounts my debugLog("Checking account: " & _account's name) try my trainMessagesInAccount(_account) on error _error my logToConsole("Error training from account “" & _account's name & "”: " & _error) end try end repeat end tell end doRemoteTraining on accountsToCheck() tell application "Mail" set _result to {} if useJunkMailbox then repeat with _account in accounts try if _account's enabled then my findMailbox("TrainSpam", _account) copy _account to end of _result end if on error -- Avoid creating a mailbox end try end repeat else repeat with _accountName in my accountNamesForDrone() set _account to account _accountName copy _account to end of _result end repeat end if return _result end tell end accountsToCheck on trainMessagesInAccount(_account) tell application "Mail" set _messages to my messagesFromMailbox("TrainSpam", _account) repeat with _message in _messages set _source to my sourceFromMessage(_message) tell application "SpamSieve" to add spam message _source set _message's junk mail status to true if pColorSpamMessages then set _message's background color to blue end if if pFlagSpamMessages then set _message's flag index to 6 end if if pMarkSpamMessagesRead then set _message's read status to true end if my moveMessage(_message, my spamMailboxForAccount(_account), true) end repeat set _messages to my messagesFromMailbox("TrainGood", _account) repeat with _message in _messages set _source to my sourceFromMessage(_message) tell application "SpamSieve" to add good message _source set _message's junk mail status to false if pColorSpamMessages then set _message's background color to none end if if pFlagSpamMessages then set _message's flag index to -1 end if if pMarkGoodMessagesUnread then set _message's read status to false end if my moveMessage(_message, my inboxForAccount(_account), false) end repeat end tell end trainMessagesInAccount on messagesFromMailbox(_mailboxName, _account) tell application "Mail" set _accountName to name of _account try set _mailbox to my findMailbox(_mailboxName, _account) on error tell _account make new mailbox with properties {name:_mailboxName} end tell set _mailbox to mailbox _mailboxName of _account end try my debugLog(my makeLogMessage("Getting messages in mailbox", _mailbox, "This can take a long time if there are many messages.")) with timeout of 3 * 60 seconds set _messages to messages of _mailbox whose deleted status is false end timeout my debugLog(my makeLogMessage("Messages in mailbox", _mailbox, count of _messages)) return _messages end tell end messagesFromMailbox on findMailbox(_mailboxName, _account) tell application "Mail" try return mailbox _mailboxName of _account on error -- my debugLog("Looking for nested mailbox " & _mailboxName & " in account " & _account's name) set _result to my findNestedMailbox(_mailboxName, mailboxes of _account) if _result is not missing value then my debugLog("Found nested mailbox " & _mailboxName & " in account " & _account's name) return _result end if my debugLog("No nested mailbox " & _mailboxName & " in account " & _account's name) error "No mailbox named " & _mailboxName end try end tell end findMailbox on findNestedMailbox(_mailboxName, _mailboxes) tell application "Mail" repeat with _mailbox in _mailboxes if name of _mailbox is _mailboxName then return _mailbox end if end repeat repeat with _mailbox in _mailboxes set _result to my findNestedMailbox(_mailboxName, mailboxes of _mailbox) if _result is not missing value then return _result end if end repeat return missing value end tell end findNestedMailbox 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, _isSpam) tell application "Mail" if not useJunkMailbox then my debugLog(my makeLogMessage("Moving message to", _mailbox, _message's subject)) set _message's mailbox to _mailbox return end if if _isSpam then my debugLog("Moving message to Junk: " & _message's subject) move _message to junk mailbox else if pMoveBySettingMailbox then my debugLog(my makeLogMessage("Moving message to", _mailbox, _message's subject)) set _message's mailbox to _mailbox else -- Not used by default because for many users copies rather than moves messages that Mail reports are in Gmail's All Mail, however some users report that this works better (#kZMU4ZD8QOzpruuManwLChA). my debugLog("Moving message to Inbox: " & _message's subject) move _message to inbox end if end if end tell end moveMessage 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 on spamMailboxForAccount(_account) if useJunkMailbox then return missing value tell application "Mail" repeat with _pair in my spamMailboxNamesByAccount() if item 1 of _pair is name of _account then set _name to item 2 of _pair return mailbox _name of _account end if end repeat return mailbox pSpamMailboxName of _account end tell end spamMailboxForAccount on inboxForAccount(_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 inboxForAccount 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