As part of a larger macro to block all emails from a sender, I wrote this AppleScript, which works, but I would rather skip over to my unread emails, not go to the first one.
My second attempt does not work, and I don't know why. Any help would be appreciated.
tell application "System Events"
tell application process "Mail"
select row 1 of table 1 of scroll area 1 of splitter group 1 of splitter group 1 of window 1
end tell
end tell
set downArrowKeyCode to 125
set theSender to "None"
tell application "System Events"
tell application process "Mail"
repeat with currentRow from 1 to 50
display dialog "current row " & currentRow
select row currentRow of table 1 of scroll area 1 of splitter group 1 of splitter group 1 of window 1
delay 1.0
set theSelectedMessage to selection
set msgCount to (count of theSelectedMessage)
if (msgCount = 1) then
set theMsg to item (currentRow) of theSelectedMessage
set theSender to (sender of theMsg)
display dialog "Sender is: " & theSender
if read status of theMsg is false then key code downArrowKeyCode
set currentRow to currentRow + 1
exit repeat
else
display dialog "giving up for now"
end if
end repeat
end tell
end tell
tell application "System Events"
tell application process "Mail"
tell window 1
tell splitter group 1
tell splitter group 1
tell scroll area 1
tell table 1
set readRowList to rows where its UI element 2's accessibility description is not "unread"
end tell
end tell
end tell
end tell
end tell
end tell
end tell
This gets me to a row, but “accessibility description” does not seem to be a reliable way to decide whether or not the corresponding message is read or unread. That field is often “missing value”. If you replace "is not unread" with "is read" you'll see what I mean.
I’m having trouble getting from the row to the message’s state (read or unread). Any other suggestions?
Here is what I tried:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
tell application "System Events"
tell application process "Mail"
tell window 1
tell splitter group 1
tell splitter group 1
tell scroll area 1
tell table 1
set readRowList to rows where its UI element 2's accessibility description is not "unread"
-- set readRowList to rows where its UI element 2's selected is true
-- set readRowList to rows where its UI element 2's selected is false
end tell
repeat with aRow in readRowList
-- display dialog "Name of " & (name of UI element of aRow)
end repeat
end tell
end tell
end tell
end tell
end tell
end tell
Missing value means read. This is silly, but we're dealing with Apple here...
Set various messages to read and unread then run the script.
# Collect read-status of messages.
tell application "System Events"
tell application process "Mail"
tell window 1
tell splitter group 1
tell splitter group 1
tell scroll area 1
tell table 1
tell rows
set readList to accessibility description of UI element 2
end tell
end tell
end tell
end tell
end tell
end tell
end tell
end tell
It reliably shows unread where applicable, and when the email is read the result is an empty-string not missing value.
tell application "System Events"
tell application process "Mail"
tell window 1
tell splitter group 1
tell splitter group 1
tell scroll area 1
tell table 1
set firstReadRow to first row where its UI element 2's accessibility description is not "unread"
set selected of firstReadRow to true
end tell
end tell
end tell
end tell
end tell
end tell
end tell
This works but has a built-in annoyance – the viewer will not scroll to show the selection.
So if the selection will not naturally be in view you have to play with things to force it.
I'm not sure why you're monkeying around with UI-Scripting when Mail is pretty scriptable.
# This won't work on certain containers on on Smart-Mailboxes.
tell application "Mail"
tell (some message viewer whose index is 1)
set selectedMailboxList to (get selected mailboxes)
if selectedMailboxList ≠{} then
set theMailbox to item 1 of selectedMailboxList
if class of theMailbox = container then
tell theMailbox
set readMessageList to messages whose read status is true
end tell
end if
end if
end tell
end tell
Why don't you describe what your overall goal is, and we can see if there's a more efficient means to get you there.
I'm not certain if this is sufficiently comparable, but I'll share it and let ikenassi decide if this macro is of use.
Since I keep the Dock hidden, I had a menu bar app that displayed a badge when there was new mail. That app is no longer compatible with more recent versions of macOS. To conveniently check for new mail without switching to Mail I whipped up this macro, which is the kind of thing someone like me does since I don't have scripting skills like CCStone has.
All this macro does is check for new email. If there are new messages, then
Mail comes to the front,
the Inbox is selected,
the topmost message is selected,
and then it is opened.
Really, though, the AppleScript checks for new mail, and most everything else is just Keyboard Maestro simulating keystrokes.
If there are no new messages, I get a system alert.
One key to this macro is that the Favorites Bar is used so that the macro can go directly to a desired mailbox by simulating a keystroke. In my macro, that's Command-1 for the Inbox, which is leftmost in the Favorites Bar. The Favorites Bar supports any Command-[1 through 9] keyboard shortcut for a corresponding mailbox, with the counting going from left to right in the Favorites Bar.
Another key to this setup is the use of Option-[Up Arrow]. This is a built-in Mac shortcut for going to the top of a list (Option-[Down Arrow] being the direct way to the bottom of a list). Most native apps should support this. Since my Inbox is sorted such that most recent messages are at the top, Option-[Up Arrow] is thus selecting the most recent email in the Inbox.
The Return key is just opening the selected message.
As I said, I don't have scripting skills like CCStone. But simulating keystrokes can also be a means to navigating an app like Mail.
So first, I want to thank everyone for their responses to my request. Much appreciated. Here is the complete macro "Block and Junk" to deal with the increasing amount of spam I'm getting.
The purpose of this macro is to block the sender of a selected mail message, and then select the next message in the list of messages. It mostly works, except for selecting the next message after the one whose sender was blocked. It's quite workable the way it is, but I would like to automate the last step, i.e., to select the next message in the message list. That's what I haven't figured out how to do.
I know how to block a sender. That's not the problem. And yes, to your other question, mail is very scriptable. But going from the UI of the mail app to the internal Mail db state is where I have a problem. For example, this script works, but I can't figure out how to select the next unread message. On the other hand, it's not a big deal, so unless there's an obvious answer, I'll drop it. Check out this script to show the unread messages in the inbox:
tell application "Mail"
set myAccount to account "Nassi"
set myMailbox to mailbox "INBOX" of myAccount
set myCount to unread count of myMailbox
-- display dialog "Count of unread messages in inbox " & myCount
repeat with i from myCount to 1 by -1
set thisMsg to message i of myMailbox
if read status of thisMsg is false then
-- select thisMsg-- but how???
-- exit repeat
set f to extract name from sender of thisMsg
set d to date sent of thisMsg
set d to (month of d as integer) & "/" & day of d & "/" & year of d
set s to subject of thisMsg
display dialog ("From: " & f & ¬
"
Date: " & d & ¬
"
Subj: " & s)
end if
end repeat
end tell
Mail in Mojave is a little flakey about how it handles first unread in some circumstances. For instance:
Run the script.
Mark the message as Read.
Run the script again.
The same bleeping message is selected.
Hopefully that's been fixed, but I wouldn’t bet on it.
Try this:
tell application "Mail"
# Don't change the inbox – that's a reference to the inbox container for all accounts.
set firstUnread to first message of inbox whose read status is false
tell (some message viewer whose index is 1)
try
set selectedMailboxes to selected mailboxes
on error errMsg number errNum
if errNum = -10000 then
set selectedMailboxes to {missing value}
end if
end try
if selectedMailboxes ≠{inbox} then
set selected mailboxes to {inbox}
delay 0.2
end if
set selected messages to firstUnread
end tell
end tell