Mark all messages as read in mail: WHY SO SLOW?

Hi All,

For those who’d like it, here’s a nifty script for marking all messages as read. One catch though, it’s too damn slow…

Can anyone tell me why Mail is so slow to mark messages as read? I get that it has to search the whole mailbox to find unread messages before it can mark them as read, but once it starts marking them as read, it’s so painfully slow, marking about 4 messages per second. The same happens if I simply do the inbox like,

tell application "Mail" to set read status of every message of inbox to true

Why is it that I can select a arbitrary number of messages manually and mark them all nearly instantaneously, but using applescript it can’t reproduce that behavior? With how slow this is, it would be faster to GUI script Mail to activate each mailbox, select all, and hit the mark as read hotkey.

Thoughts?

Thanks,

Tim

on run
	tell application "Mail" to set AllAccounts to every account
	repeat with AnAccount in AllAccounts
		tell application "Mail"
			set TheMailboxes to every mailbox of AnAccount
		end tell
		repeat with TheMailbox in TheMailboxes
			try
				tell application "Mail"
					if unread count of TheMailbox > 0 then set (read status of every message of TheMailbox whose read status is false) to true
				end tell
			end try
		end repeat
	end repeat
	return 0
end run

So the slowness issue disappears if an account is offline, so it seems to be related to Mail syncing with the account (IMAP and iCloud accounts, at least) after it changes each message’s read status. So the obvious solution is to take an account offline before you marked its messages as read, except that particular function isn’t scriptable for some odd reason. You can disable the account, but then you lose access to its messages too, so that’s no good.

Enter GUI scripting (sigh…). Here’s an updates version that is WAY faster, marking hundreds of messages a second instead of 3 or 4. The overkill GUI scripting is to try and overcome its finickiness. Here’s two versions, one with no display of any kind, and another that shows Mr. Stanley’s awesome progress bar for you.

Takes less than 2 seconds to run if there’s no unread messages for my 70 mailboxes and 30000 messages. took a little over a minute to mark ~1000 messages as read amongst all the mailboxes. The more unread messages, and the more messages that are in the mailbox with the unread ones, the longer it will take to get the list of messages to mark.

Simple version:

on run
	tell application "Mail" to set AllAccounts to every account
	set AccountNum to 1
	set AccountsOnline to true
	repeat with AnAccount in AllAccounts
		tell application "Mail"
			set TheMailboxes to every mailbox of AnAccount
		end tell
		repeat with TheMailbox in TheMailboxes
			try
				with timeout of 600 seconds
					tell application "Mail"
						if unread count of TheMailbox > 0 and (name of TheMailbox does not contain "Draft") then
							if AccountsOnline then
								tell application "System Events" to tell process "Mail"
									repeat 10 times
										try
											click menu item "Take All Accounts Offline" of menu "Mailbox" of menu bar 1
										end try
									end repeat
								end tell
								delay 1
								set AccountsOnline to false
							end if
							set (read status of every message of TheMailbox whose read status is false) to true
						end if
					end tell
				end timeout
			end try
		end repeat
		set AccountNum to AccountNum + 1
	end repeat
	if not AccountsOnline then
		tell application "System Events" to tell process "Mail"
			repeat 10 times
				try
					click menu item "Take All Accounts Online" of menu "Mailbox" of menu bar 1
				end try
			end repeat
		end tell
	end if
	return 0
end run

ASObjC Runner progress window version (Go Shane!!):

on run
	tell application "Mail" to set AllAccounts to every account
	set BoxCount to 0
	repeat with AnAccount in AllAccounts
		tell application "Mail" to set BoxCount to BoxCount + ((count of every mailbox) of AnAccount)
	end repeat
	tell application "ASObjC Runner"
		reset progress
		set properties of progress window to {name:"Mark All Read", message:"Marking all messages as read. Press Cancel to quit.", max value:BoxCount, current value:0, button visible:true, button title:"Cancel"}
		show progress
		activate
	end tell
	set BoxNum to 1
	set AccountNum to 1
	set AccountsOnline to true
	repeat with AnAccount in AllAccounts
		tell application "Mail"
			set AccountName to name of AnAccount
			set TheMailboxes to every mailbox of AnAccount
		end tell
		repeat with TheMailbox in TheMailboxes
			tell application "Mail" to set CurrentBox to (AccountName & "'s " & name of TheMailbox)
			tell application "ASObjC Runner"
				set properties of progress window to {current value:BoxNum, detail:"Processing " & CurrentBox}
				if button was pressed of progress window then
					hide progress
					return 0
				end if
			end tell
			try
				with timeout of 600 seconds
					tell application "Mail"
						if unread count of TheMailbox > 0 and (name of TheMailbox does not contain "Draft") then
							if AccountsOnline then
								tell application "System Events" to tell process "Mail"
									repeat 10 times
										try
											click menu item "Take All Accounts Offline" of menu "Mailbox" of menu bar 1
										end try
									end repeat
								end tell
								delay 1
								set AccountsOnline to false
							end if
							set (read status of every message of TheMailbox whose read status is false) to true
						end if
					end tell
				end timeout
			end try
			set BoxNum to BoxNum + 1
		end repeat
		set AccountNum to AccountNum + 1
	end repeat
	if not AccountsOnline then
		tell application "System Events" to tell process "Mail"
			repeat 10 times
				try
					click menu item "Take All Accounts Online" of menu "Mailbox" of menu bar 1
				end try
			end repeat
		end tell
	end if
	tell application "ASObjC Runner" to hide progress
	return 0
end run