Applescript & Rules in Mail - applescript

I'm trying to execute the following Applescript within a Rule I set up in Mac Mail. I'm running OS 10.10.1 Yosemite on my iMac. My Rule looks for a particular email I receive every day, then instructs Mail to execute my Applescript. The Rule works perfectly when I manually highlight the email and click on "Apply Rules"; however, when I start up my computer in the morning and receive my first mail of the day. The Applescript program gets caught in never ending loop, which is evidenced by the whirling icon that appears in the menu bar. BTW: My automator routine works perfectly. My thinking is that the Applescript gets confused trying to execute while Mail is downloading all my mail???? Any suggestions?? Oh, I'm a novice...Thx
tell application "System Events"
tell process "Mail"
-- Select the Print menu item
click (first menu item of menu "File" of menu bar 1 whose name begins with "Print")
tell window 1
-- Wait until the print sheet appears
repeat until sheet 1 exists
end repeat
tell sheet 1
-- Click the PDF button
click menu button "PDF"
-- Select the PDF to SBS Dropbox menu item
click (first menu item of menu 1 of menu button "PDF" whose name begins with "PDF to SBS Dropbox")
end tell
end tell
end tell
end tell

First you need to use the Applescript handler called by mail rules:
on perform mail action with messages theMessages for rule theRule
Inside this handler you have access to the mails that matched your mail rule conditions and do whatever you like. Here is the skeleton you can use to add your code:
using terms from application "Mail"
on perform mail action with messages theMessages for rule theRule
tell application "Mail"
repeat with thisMessage in theMessages
-- place your code here
end repeat
end tell
end perform mail action with messages
end using terms from
In your special case I think we can try to put your code inside the repeat-loop and let mail open the mail before execution and close it after printing:
using terms from application "Mail"
on perform mail action with messages theMessages for rule theRule
tell application "Mail"
-- walk through all matching messages
repeat with thisMessage in theMessages
-- open the message
set openedMail to open thisMessage
-- perform your UI scripting
tell application "System Events"
tell process "Mail"
-- Select the Print menu item
click (first menu item of menu "File" of menu bar 1 whose name begins with "Print")
tell window 1
-- Wait until the print sheet appears
repeat 30 times
if sheet 1 exists then exit repeat
delay 0.5
end repeat
tell sheet 1
-- Click the PDF button
click menu button "PDF"
-- Select the PDF to SBS Dropbox menu item
click (first menu item of menu 1 of menu button "PDF" whose name begins with "PDF to SBS Dropbox")
end tell
end tell
end tell
end tell
-- close the message
close openedMail
end repeat
end tell
end perform mail action with messages
end using terms from
It's not tested yet! And I prevent the script from running in an infinite loop when trying to access the print sheet now.
Give it a try! Enjoy, Michael / Hamburg

Related

Notes export all as PDF

Ability to export all Notes in macOS Notes.app as PDFs.
execution error: Notes got an error: AppleEvent handler failed. (-10000)
Multiple scripts, latest below.
tell application "Notes"
activate
repeat with theFolder in every folder
repeat with theNote in every note of theFolder
tell application "System Events"
tell process "Notes"
set dockPrefs to dock preferences
set appearancePrefs to appearance preferences
delay 1
display dialog "Foo"
tell menu bar 1 of process "Notes"
click menu bar item "File"
click menu item "Export as PDF..." of menu "File" of menu bar of process "Notes"
end tell
click button "Save" of sheet 1 of window "Notes" of process "Notes"
delay 1
key code 125
end tell
end tell
end repeat
end repeat
end tell
execution error: Notes got an error: AppleEvent handler failed. (-10000)
There are a few problems in there:
You are already targeting the Notes process, so including that in
the click statements is adding another process target - use one or the other, but if you are
doing a lot of menu clicks you might look at using a general
purpose handler;
The export menu item uses an ellipse (a single character), not three
periods;
By placing a display dialog statement in the System Events tell statement, you are moving the focus away from the application.
Also note that the text field is selected and the save button is the default in the sheet, so you can use keystrokes instead of trying to click UI elements. A cleaned up example (tested in Mojave) would look something like:
tell application "Notes"
launch -- seems to work better than 'activate'
repeat with aFolder in folders
repeat with aNote in notes of aFolder
set noteName to (name of aNote)
try -- keep the name a reasonable length
set noteName to text 1 thru 20 of noteName
end try
tell (current date) to set timeStamp to text 2 thru -1 of (get (1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as text) -- hhmmss
tell application "System Events"
#display dialog noteName -- testing?
tell process "Notes"
set frontmost to true -- retarget the Notes app
delay 0.5
click menu item "Export as PDF…" of menu "File" of menu bar item "File" of menu bar 1
repeat until exists sheet 1 of window 1 -- wait for the sheet
delay 0.02
end repeat
end tell
keystroke noteName & "_" & timeStamp -- update the name, trying to avoid duplicates
delay 0.5
keystroke return -- dismiss the sheet
delay 0.5
key code 125
end tell
end repeat
end repeat
end tell

How can I bypass an index error or have the script run again?

I am encountering an index error that appears when the app in use has an overlay or notification appear. To provide a better description, the app will occasionally show an alert if something needs to be acknowledged or dismissed. When that happens, the script is unable to return the value from the designated location in the GUI, and returns the following error message: "Can’t get group 4 of toolbar 1 of window 1 of process "App I'm Using". Invalid index.System Events got an error: Can’t get group 4 of toolbar 1 of window 1 of process "App I'm Using". Invalid index. (-1719)"
The behavior is expected, but I would like to adjust the script to where it will either delay trying again for 30 seconds or so, or just not display said error at all.
I've been toying around with using an 'on error' statement, but I can't get it to take with the 'tell' statement that it's referring to, for example:
on error error_message number error_number
if error_number = -1719 then
wait 30
end if
I'm unsure of how I can use the 'on error' function with the section of the script below, but if I can make it try again in 30 - 45 seconds without displaying an error, it would be perfect.
on idle
-- Update the status item's text here.
tell application "System Events"
if not (exists process appName) then
display alert "Application " & appName & " is not running" as warning giving up after 6
quit me
end if
tell process appName
tell first window's first toolbar's fourth group's first group's first menu button
set activityState to first item of (value as list) as text
end tell
end tell
end tell
end idle
I believe the error is encountered when the script reaches "tell window's first toolbar's fourth group's..." before it is supposed to "set activityState to first item...".
I have used the 'on error' function with 'try' statements successfully, but I'm having issues moving forward with this one.
Or you can try this approach which will remain in the repeat loop until first window's first toolbar's fourth group's first group's first menu button becomes available.
on idle
-- Update the status item's text here.
tell application "System Events"
if not (exists process appName) then
display alert "Application " & appName & " is not running" as warning giving up after 6
quit me
end if
tell process appName
repeat until exists of first window's first toolbar's fourth group's first group's first menu button
delay 0.2
end repeat
tell first window's first toolbar's fourth group's first group's first menu button
set activityState to first item of (value as list) as text
end tell
end tell
end tell
end idle
You can’t split statements (such as if or tell). The try statement needs to wrap around the complete statement(s) you want it to work with, for example, the statement telling the app process:
on idle
tell application "System Events"
if not (exists process appName) then
---
end if
try
tell process appName
---
end tell
on error errmess number errnum
return 30 -- try the idle handler again in 30 seconds
end try
end tell
end idle
You can use the exists keyword to check if an element is present before trying to access it. exists won't through an error if the element isn't there, and will let you skip over the problematic lines:
on idle
-- Update the status item's text here.
tell application "System Events"
if not (exists process appName) then
display alert "Application " & appName & " is not running" as warning giving up after 6
quit me
end if
tell process appName
-- assuming the window and toolbar are always going to be there
try
tell first window's first toolbar
-- check to see if the UI element exists
if exists fourth group's first group's first menu button then
-- only get the activity state if it does
tell fourth group's first group's first menu button
set activityState to first item of (value as list) as text
end tell
end if
end tell
on error errstr
(*
this code is in an 'idle' handler, so on any error we
just return 30 to idle for another 30 seconds and try again.
*)
return 30
end try
end tell
end tell
end idle

AppleScript Not Recognizing Application Index Addresses

I am creating an AppleScript to control some basic standard actions in SAP and everything as written works fine from the main menu page of available transactions. The current script fills out the Command Text Field and opens a new existing transaction code. However, once SAP has gone to the new transaction code page, when I run the AppleScript a second time it no longer recognizes the index of the Command Text Field. Instead it pastes the transaction code into whatever text field happens to be the current focus. When I run the script, it should always go to the same text field because the overall index address hasn't changed, but it's not.
I've attempted to open the Script Dictionary for SAP, but it appears that my company's flavor of SAP has had the scripting dictionary disabled, so I am forced to use indexing addresses to fill text fields and click buttons
on chooseSAPTransaction()
global sapTransaction, transactionCode
set transactionCode to "" -- intialize the variable
set transactionList to choose from list {"Find Document", "Change Document", "Look up Product", "Correct Product"} with prompt "SAP Action:" default items {""}
if transactionList is false then
error number -128
else
set choosenTransaction to item 1 of transactionList
end if
if choosenTransaction is "Find Document" then
set transactionCode to "code1"
chooseCode1()
end if
if choosenTransaction is "Change Document" then
set transactionCode to "code2"
chooseCode2()
end if
if choosenTransaction is "Look up Product" then
set transactionCode to "code3"
chooseCode3()
end if
if choosenTransaction is "Correct Product" then
set transactionCode to "code4"
chooseCode4()
end if
set sapTransaction to "/n" & transactionCode
activate application "SAP"
delay 0.1
tell application "System Events"
tell process "SAP"
tell front window to tell toolbar 1 to tell text field 1
keystroke sapTransaction
end tell
tell front window to tell toolbar 1 to tell button 1 to perform action "AXPress"
end tell
end tell
end chooseSAPTransaction
It's skipping tell front window to tell toolbar 1 to tell text field 1 and going right to keystroke sapTransaction on subsequent script executions
("/n" & transactionCode creates the string that tells SAP the new transaction to go to. When typing it manually, it always takes you to the new transaction, regardless of how many layers deep you might be in the current transaction)
I expect the indexing tell to go to the same element on every new instance of running the script, but once it's run once, AppleScript isn't finding the element address anymore.
edit:
I just tried moving the pasting action into its own unique handler so that it can be called before attempting to go to the next step, but that didn't work either.
--snip--
if choosenTransaction is "Find Document" then
set transactionCode to "code1"
set sapTransaction to "/n" & transactionCode
pasteTransaction()
chooseCode1()
end if
--snip--
on pasteTransaction()
global serialNumber, sapTransaction, transactionCode
activate application "SAPGUI"
delay 0.1
tell application "System Events"
tell process "SAPGUI"
tell front window to tell toolbar 1 to tell text field 1
keystroke sapTransaction
end tell
tell front window to tell toolbar 1 to tell button 1 to perform action "AXPress"
end tell
end tell
end pasteTransaction
edit 2:
I got it working by forcing SAP to go through the menu. Woo!
on pasteTransaction()
global serialNumber, sapTransaction, transactionCode
activate application "SAPGUI"
delay 0.1
tell application "System Events"
click menu item ¬
"Target Command Field" of menu 1 of menu bar item ¬
"Edit" of menu bar 1 of application process "SAPGUI"
keystroke sapTransaction
tell process "SAPGUI"
tell front window to tell toolbar 1 to tell button 1 to perform action "AXPress"
end tell
end tell
end pasteTransaction
unfortunately, that doesn't fix the bigger issue of AppleScript not finding the correct index address for text fields, because getting this part working is only step one. Now I have to get text strings into various text fields within each transaction.
So if anyone knows how to address that original Index Address issue, that would be great.
I wish I knew how to actually write AppleScript instead of cobbling code together one action at a time from various sources. It's not elegant, and functionality is hit-or-miss.
GUI scripting is always a hack, and without seeing the application in action it's hard to diagnose problems. Obviously something is changing in the interface that is throwing off the view hierarchy you're trying to leverage, but... I will point out that menus and toolbars tend to be dynamic, which makes things dicier. But here's a couple of diagnostics and alternate approaches you can try.
First, let me rewrite the GUI code like this, which I find clearer:
tell application "System Events"
tell process "SAP"
tell front window
tell toolbar 1
tell text field 1
keystroke sapTransaction
end tell
tell button 1
perform action "AXPress"
end tell
end tell
end tell
end tell
end tell
So first — assuming you are targeting the correct textfield — you might try this instead:
tell toolbar 1
tell text field 1
set value to sapTransaction
end tell
tell button 1
perform action "AXPress"
end tell
end tell
keystroke can be intercepted by whatever UI element has the focus, but setting the text field's value ought to go directly into the correct element. If that doesn't work, you can try this diagnostic in Script Editor:
tell toolbar 1
properties of every text field
end tell
This should give you a list in the Script Editor's log of all of the text fields in toolbar 1. With that, you can see if the text field has a name property (which would make addressing easier: i.e. tell text field "someName"), or if some other text field has been added to the toolbar higher in the hierarchy, changing the index of the text field you're after. If worse comes to worst, you can try this:
tell toolbar 1
entire contents
end tell
Which will list out all the UI elements of the toolbar. You can do a before/after comparison to see what (if anything) changed.
Hope that helps.

Selecting Pop Up Menu Buttons in AppleScript

I want to automate clicking a specific pop down menu's item.
For Example, I want to change the Value of "Message receive Sound" to something else. How can I do this with AppleScript? And how can I do this with other pop down menus in AppleScript?
(To open the iMessage Settings menu, shown in the image, type CMD COMMA, once you open iMessage)
Note: I have successfully done this Automator, I just want to do it in applescript.
It's called GUI scripting. You have to identify the reference to the UI element(s).
GUI scripting strongly depends on the system version. If an update changes the UI structure the script will brake.
This selects the sound "Popcorn" in the sound popup menu. It's for El Capitan. In systems < 10.11 the UI elements may be different and the process name might be "iChat"
tell application "System Events"
tell process "Messages"
set frontmost to true
if not (exists (1st window whose value of attribute "AXIdentifier" is "MessagesPreferencesWindow")) then
keystroke "," using command down
repeat until exists (1st window whose value of attribute "AXIdentifier" is "MessagesPreferencesWindow")
delay 0.1
end repeat
end if
tell (1st window whose value of attribute "AXIdentifier" is "MessagesPreferencesWindow")
tell pop up button 4 of group 1
click
delay 0.2
click menu item "Popcorn" of menu 1
end tell
end tell
end tell
end tell

How to Close an open email using Applescript

I've been able to achieve 95% success using the following script on my Mac OS X 10.10.1 However, I can't get the email that I've opened to "Close". Any suggestions???
Here's the Applescript:
using terms from application "Mail"
on perform mail action with messages theMessages for rule theRule
tell application "Mail"
-- walk through all matching messages
repeat with thisMessage in theMessages
-- open the message
set openedMail to open thisMessage
-- perform your UI scripting
tell application "System Events"
tell process "Mail"
-- Select the Print menu item
click (first menu item of menu "File" of menu bar 1 whose name begins with "Print")
tell window 1
-- Wait until the print sheet appears
repeat 30 times
if sheet 1 exists then exit repeat
delay 0.5
end repeat
tell sheet 1
-- Click the PDF button
click menu button "PDF"
-- Select the PDF to SBS Dropbox menu item
delay 0.5
click (first menu item of menu 1 of menu button "PDF" whose name begins with "PDF to SBS Dropbox")
delay 4
end tell
end tell
end tell
end tell
-- close the message
close openedMail
end repeat
end tell
end perform mail action with messages
end using terms from
If your code doesn't work then why not try the same technique you used earlier with system events...
click (first menu item of menu "File" of menu bar 1 whose name begins with "Close")
Or you can just tell mail to do it with...
close window 1

Resources