C-Command Software Forum

Outlook Mac - strange moving of emails

I have MS Outlook for Mac ver 16. I set up a rule to move emails from a specific sender to a specific folder. About 6 emails came through and were in my in-box. (I assume they went through the spamsieve program (which involves apple scripts and the “Outlook - Filter Mailboxes” pre-made script). I ‘applied rules’ to move the emails from the inbox to the specific folder (I selected all emails and applied all rules). All the emails in my inbox disappeared. Then they returned to the inbox, except the ones for the new rule (and maybe others - not sure). I looked in the specific folder and two of the emails (that were the target of my rule) were in that folder. I could not find the others in any folder on my hard-drive or mail server. A while later, these missing emails were in the specific folder, but then they disappeared again.

Any idea if this is related to spamsieve? Is there a hierarchy or sequence that needs to happen? If an email gets moved from the Inbox on the server to another server folder does it get sent through spamsieve sequence again?
I am using IMAP to access my account. There is one account. I have specific folders on both the server and my hard-drive, as I might access some emails from other devices.

If you want messages to go to a certain folder (e.g. bypass SpamSieve) you should put your rule above the InboxSpamSieveRule. Otherwise (automatically or when you manually apply rules), Outlook will move the messages to the InboxSpamSieve folder and then SpamSieve’s Outlook Filter Mailboxes will move the good ones to the inbox and the spam ones to Junk. SpamSieve does not delete any messages.

No.

I put the spamsieve rule first on the list, but the emails still disappear then reappear. After a while they end up in the inbox folder or spam folder (all of them I believe). But the behavior is troubling.

As I said above, the other rule should be above the SpamSieve rule.

So I had to spam filter at the very top of the list of rules, and at the very bottom of the list of rules, and I’m getting the same behavior.

The rules send the messages to folders that are also on the server, not on my hard drive. I would think that has nothing to do with the issue. All of the folders including the Spamsieve inbox folder are on the same level as inbox.

The SpamSieve rule only moves messages out of the inbox. So if your other rule is before the SpamSieve one, the message should no longer be in the inbox by the time Outlook gets to the SpamSieve rule, so it won’t do anything. Please make sure that you have “Do not apply other rules…” checked in your rules above the SpamSieve one. It might also be worth manually applying your other rule (from the Message menu) to make sure that it actually matches the messages in question.

I have checked each rule like you suggested. They work as intended. And each rule has checked ‘Do not apply other rules to messages that meet these conditions’.
I don’t think this is the issue though, because all of the rules have this checked, and therefore the second and later rules should not execute, no matter the order (unless there is some resetting of the messages after the script handles them. And-Somehow Spamsieve rule knows not to reprocess rules when the script moves the good email back to the Inbox - maybe at that time the other rules might process? I am not sure how this works and wonder if this is related to the issue.

The other rules do not have a requirement for the email to be of the folder Inbox, which is the only condition of the Spamsieve rule.

My understanding is that when a message is downloaded from the server into the Outlook Inbox, the Outlook rule moves the messages out of the Inbox folder and into the InboxSpamSieve folder - this is what the rule states.
Then the script tests the InboxSpamSieve contents and moves ‘not-spam’ back to the inbox, and spam to the junk folder.

Also, the Inbox folder is synced with the server (it is not the Inbox that is on my hard-drive - (I can delete or move an email out of the Inbox folder using Outlook, and that move will be reflected in the server when using an internet browser of phone mail app to look at emails. The other folders are also folders on the server. I am not sure if that is causing some problem.

Here is the spamsieve script, if it helps.

-- Outlook - Filter Mailboxes
-- https://c-command.com/scripts/spamsieve/outlook-filter-mailboxes
-- Summary: Periodically filter new messages in the inbox with SpamSieve.
-- Requires: SpamSieve, Outlook 365
-- Install Location: /Applications
-- Last Modified: 2019-01-25



global gDebug, gSecondsBetweenChecks, gInboxSpamSieveName, gGoodCategoryName, gGoodFolderName
set _keys to {"OutlookScriptDebug", "OutlookFilterMailboxesSecondsBetweenChecks", "OutlookFilterMailboxesInboxSpamSieveName", "OutlookFilterMailboxesGoodCategoryName", "OutlookFilterMailboxesGoodFolderName"}
set _defaultValues to {false, 1 * 60, "InboxSpamSieve", "Good", "", "Inbox"}
set {gDebug, gSecondsBetweenChecks, gInboxSpamSieveName, gGoodCategoryName, gGoodFolderName} to my lookupDefaults(_keys, _defaultValues)
my filterMailboxes()

on idle
	-- This is executed periodically when the script is run as a stay-open application.
	my filterMailboxes()
	return gSecondsBetweenChecks
end idle

on reopen
	-- This is executed when you click on the Dock icon after the script app is already running.
	my filterMailboxes()
end reopen

on filterMailboxes()
	if application "Microsoft Outlook" is not running then
		my debugLog("Outlook is not running")
		return
	end if
	try
		set _mailboxes to my mailboxesToFilter()
		repeat with _mailbox in _mailboxes
			set _messages to my messagesToFilterFromMailbox(_mailbox)
			if gDebug then
				my debugLog((count of _messages) & " messages to filter in " & describeFolder(_mailbox))
			end if
			repeat with _message in _messages
				set _score to scoreMessage(_message)
				if _score ≥ 50 then
					my processSpamMessage(_message, _score)
				else
					my processGoodMessage(_message, _score)
				end if
			end repeat
		end repeat
	on error _error number _errorNumber
		my logToConsole("Error: " & _error)
		if _errorNumber is -1743 then -- errAEEventNotPermitted
			set _alertMessage to "You can give “Outlook Filter Mailboxes” access to control Outlook 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
end filterMailboxes

on mailboxesToFilter()
	tell application "Microsoft Outlook"
		set _result to every mail folder whose name starts with gInboxSpamSieveName
		if _result is not {} then return _result
		
		set _result to {}
		set _accounts to imap accounts & pop accounts & exchange accounts -- Just "accounts" does not work.
		repeat with _account in _accounts
			copy _account's inbox to end of _result
		end repeat
		copy inbox to end of _result -- For Google accounts
	end tell
	return _result
end mailboxesToFilter

on messagesToFilterFromMailbox(_mailbox)
	tell application "Microsoft Outlook"
		if name of _mailbox starts with gInboxSpamSieveName then
			with timeout of 2 * 60 seconds
				return messages of _mailbox -- Faster than checking unread and category
			end timeout
		end if
		set _messages to my unreadMessagesFromMailbox(_mailbox)
		set _result to {}
		repeat with _message in _messages
			if my shouldFilterMessage(_message) then
				copy _message to end of _result
			end if
		end repeat
		return _result
	end tell
end messagesToFilterFromMailbox

on unreadMessagesFromMailbox(_mailbox)
	set _startDate to current date
	tell application "Microsoft Outlook"
		try
			with timeout of 2 * 60 seconds
				-- Using "whose" clause seems to make Outlook unresponsive.
				set _allMessages to messages of _mailbox -- whose is read is false
			end timeout
			set _messages to {}
			repeat with _message in _allMessages
				if _message's is read is false then
					copy _message to end of _messages
				end if
			end repeat
		on error _error number _errorNumber
			my logToConsole("Outlook reported error “" & _error & "” (number " & _errorNumber & ") getting the messages from " & my describeFolder(_mailbox))
			return {}
		end try
	end tell
	set _endDate to current date
	set _duration to _endDate - _startDate
	set _statusMessage to "Outlook took " & _duration & " seconds to get " & (count of _messages) & " unread messages out of " & (count of _allMessages) & " total messages from " & my describeFolder(_mailbox)
	if _duration > 3 then
		my logToConsole(_statusMessage)
	else
		my debugLog(_statusMessage)
	end if
	return _messages
end unreadMessagesFromMailbox

on shouldFilterMessage(_message)
	if gGoodCategoryName is "" then
		return true
	end if
	return not my doesMessageHaveCategoryNamed(_message, gGoodCategoryName)
end shouldFilterMessage

on doesMessageHaveCategoryNamed(_message, _categoryName)
	tell application "Microsoft Outlook"
		set _categories to _message's category
		repeat with _category in _categories
			if _category's name is _categoryName then
				return true
			end if
		end repeat
		return false
	end tell
end doesMessageHaveCategoryNamed

on scoreMessage(_message)
	tell application "Microsoft Outlook"
		set _source to _message's source
	end tell
	if _source is missing value then
		my logToConsole("Outlook could not get the source of message: " & my subjectOfMessage(_message))
		return 49
	else
		tell application "SpamSieve"
			return score message _source
		end tell
	end if
end scoreMessage

on processSpamMessage(_message, _score)
	my debugLogMessage("Predicted Spam (" & _score & ")", _message)
	if my isSpamScoreUncertain(_score) then
		my applyCategoryNamed(_message, "Uncertain Junk")
	else
		my applyCategoryNamed(_message, "Junk")
	end if
	my moveToSpamFolder(_message)
end processSpamMessage

on moveToInbox(_message)
	tell application "Microsoft Outlook"
		try
			set _inbox to inbox of _message's account
		on error _errorMessage -- Will fail for new Google accounts that aren't scriptable.
			my logToConsole("Error getting inbox of message so using generic inbox: " & _errorMessage)
			try
				move _message to inbox
			on error _errorMessage
				my logToConsole("Error moving message to fallback generic inbox: " & _errorMessage)
			end try
			return
		end try
		my debugLogMessage("Moving to inbox " & my describeFolder(_inbox), _message)
		move _message to _inbox
	end tell
end moveToInbox

on destinationFolderForMessage(_message)
	tell application "Microsoft Outlook"
		set _inboxFolderName to name of _message's folder
		set AppleScript's text item delimiters to ""
		set _start to (length of gInboxSpamSieveName) + 1
		set _destinationName to (characters _start through -1 of _inboxFolderName) as Unicode text
		set _account to _message's account
		if _account is missing value then
			return mail folder _destinationName
		else
			try
				tell _account
					return mail folder _destinationName
				end tell
			on error -- If message is POP, the above won't search nested On My Computer folders, but this will.
				return mail folder _destinationName
			end try
		end if
	end tell
end destinationFolderForMessage

on moveToFolder(_message)
	tell application "Microsoft Outlook"
		try
			set _folder to my destinationFolderForMessage(_message)
		on error _errorMessage
			my logToConsole("Error getting destination for message, so moving to inbox: " & _errorMessage)
			my moveToInbox(_message)
			return
		end try
		my debugLogMessage("Moving to folder " & my describeFolder(_folder), _message)
		move _message to _folder
	end tell
end moveToFolder

on moveToSpamFolder(_message)
	tell application "Microsoft Outlook"
		set _destFolder to my junkFolderForMessage(_message)
		my debugLogMessage("Moving to junk mailbox " & my describeFolder(_destFolder), _message)
		move _message to _destFolder
		try
			if _message's folder is not _destFolder then
				my debugLogMessage("Removing to junk mailbox " & my describeFolder(junk mail), _message)
				move _message to junk mail
			end if
		end try
	end tell
end moveToSpamFolder

on junkFolderForMessage(_message)
	tell application "Microsoft Outlook"
		try
			set _destFolder to junk mail of _message's account
			if _destFolder is not missing value then return _destFolder
		end try
		try
			set _destFolder to junk mail
			if _destFolder is not missing value then return _destFolder
		end try
		try
			set _destFolder to folder "Junk E-mail" of _message's account
			if _destFolder is not missing value then return _destFolder
		end try
		try
			set _destFolder to folder "Junk" of _message's account
			if _destFolder is not missing value then return _destFolder
		end try
		display dialog "Could not find the “Junk E-mail” folder"
		return junk mail
	end tell
end junkFolderForMessage

on processGoodMessage(_message, _score)
	my debugLogMessage("Predicted Good (" & _score & ")", _message)
	tell application "Microsoft Outlook"
		set _folderName to name of _message's folder
		if _folderName is gInboxSpamSieveName then
			my moveToInbox(_message)
		else if _folderName starts with gInboxSpamSieveName then
			my moveToFolder(_message)
		else
			if gGoodCategoryName is not "" then
				my applyCategoryNamed(_message, gGoodCategoryName)
			end if
			if gGoodFolderName is not "" then
				my moveToFolderNamed(_message, gGoodFolderName)
			end if
		end if
	end tell
end processGoodMessage

on isSpamScoreUncertain(_score)
	tell application "SpamSieve"
		set _keys to {"Border", "OutlookUncertainJunk"}
		set _defaults to {75, true}
		try
			set {gUncertainThreshold, gUncertainJunk} to lookup keys _keys default values _defaults
		on error
			set {gUncertainThreshold, gUncertainJunk} to _defaults
		end try
	end tell
	return _score < gUncertainThreshold and gUncertainJunk
end isSpamScoreUncertain

on subjectOfMessage(_message)
	tell application "Microsoft Outlook"
		try
			return (_message's subject) as Unicode text
		on error
			return "<Error getting subject of message id " & _message's id & ">"
		end try
	end tell
end subjectOfMessage

on moveToFolderNamed(_message, _folderName)
	tell application "Microsoft Outlook"
		try
			move _message to folder _folderName of _message's account
		on error _error
			-- Folder probably doesn't exist. Not sure how to create it.
			my logToConsole("Error moving to " & _folderName & ": " & _error)
		end try
	end tell
end moveToFolderNamed

-- Categories

on categoryForName(_categoryName)
	tell application "Microsoft Outlook"
		try
			-- "exists category _categoryName" sometimes lies
			return category _categoryName
		on error
			try
				-- getting by name doesn't always work
				repeat with _category in categories
					if _category's name is _categoryName then return _category
				end repeat
			end try
			set _category to make new category with properties {name:_categoryName}
			if _categoryName is gGoodCategoryName then
				set _category's color to {0, 0, 0}
			end if
		end try
		return category _categoryName
	end tell
end categoryForName

on applyCategoryNamed(_message, _categoryName)
	tell application "Microsoft Outlook"
		set _categoryToApply to my categoryForName(_categoryName)
		set _categories to _message's category
		repeat with _category in _categories
			if _category's id is equal to _categoryToApply's id then return
		end repeat
		set category of _message to {_categoryToApply} & category of _message
	end tell
end applyCategoryNamed

-- Logging

on debugLogMessage(_string, _message)
	if not gDebug then return
	tell application "Microsoft Outlook"
		try
			set _location to my describeFolder(_message's folder)
		on error
			set _location to "<error getting message's mailbox>"
		end try
		set _subject to my subjectOfMessage(_message)
	end tell
	my debugLog(_string & ": " & _location & "] " & _subject)
end debugLogMessage


on describeFolder(_folder)
	tell application "Microsoft Outlook"
		set _container to _folder's container -- For some reason, this doesn't work inside the "try"
		try
			set _containerName to my describeFolder(_container)
		on error
			try
				return name of _folder's account
			on error
				return "[Unknown/OnMyComputer/Google]"
			end try
		end try
		set _folderName to _folder's name
		return _containerName & "/" & _folderName
	end tell
end describeFolder

on debugLog(_message)
	if gDebug then my logToConsole(_message)
end debugLog

on logToConsole(_message)
	set _prefix to "SpamSieve [Outlook Filter Mailboxes]"
	set _logMessage to _prefix & " " & _message
	try
		do shell script "/usr/bin/logger -s " & _logMessage's quoted form
	on error _error number _errorNumber
		set _alertMessage to "An error occurred while logging the message:" & return & return & _message & return & return & "to Console. Error " & _errorNumber & ": " & return & return & _error & return & return & "This error message will dismiss itself after 10 seconds so that your filtering is not interrupted."
		display alert _prefix message _alertMessage giving up after 10
	end try
end logToConsole

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
			try
				return lookup keys _keys default values _defaultValues
			on error _errorMessage
				my logToConsole("Error getting fallback defaults: " & _errorMessage)
				return _defaultValues
			end try
		end try
	end tell
end lookupDefaults

If the rules are correct and Outlook is working properly, the first matching rule should move the message and stop the processing. So the message should never go to InboxSpamSieve and the script should never see it.

My suggestion would be to test this by temporarily removing SpamSieve from the equation. You can do this by quitting the Outlook Filter Mailboxes app. Then see where the messages end up, and you’ll be able to see what the Outlook rules did.

Outlook should know that the messages are not new, and so it won’t re-apply the rules. But this should be irrelevant because the messages in question should never be seen by the SpamSieve rule, due to the earlier rule matching them first.

Right.

Right.

That sounds normal to me.