Applescript Errors with Mail - error-handling

I made this quick Applescript application and it gave me an error, here is the coding:
set buyannounce to "AUTOMATIC SERVER EMAIL : BUY"
set transferannounce to "AUTOMATIC SERVER EMAIL : TRANSFER"
set signupannounce to "AUTOMATIC SERVER EMAIL : SIGNUP"
set sellannounce to "AUTOMATIC SERVER EMAIL : SELL"
set nowsignup to "0"
set announce to "AUTOMATIC SERVER EMAIL"
set ¬
currentSignups ¬
to the text returned ¬
of (display dialog ¬
¬
"How many current signups?" default answer "")
tell application "Mail"
set unreadMessages to (get every message of mailbox "INBOX" of account "Yahoo" whose read status is true)
repeat with eachMessage in unreadMessages
if announce is in (get content of eachMessage) then
if buyannounce is in (get content of eachMessage) then
--buy email
end if
if transferannounce is in (get content of eachMessage) then
--transfer email
end if
if signupannounce is in (get content of eachMessage) then
set nowsignup to nowsignup + 1
set eachMessageTrimmed to ((characters 33 thru -1 of (get content of eachMessage)) as string)
display dialog nowsignup
set filepath to POSIX path of "/Users/obleopold/Dropbox/Accounts.csv"
try
set command to "open " & quoted form of filepath
do shell script command
end try
delay 10
repeat currentSignups times
tell "System Events" to keystroke return
end repeat
repeat nowsignup times
tell "System Events"
keystroke eachMessageTrimmed --here is where I am getting the error
end tell
tell "System Events" to keystroke return
currentSignups = currentSignups + nowsignup
set nowsignup to "0"
end repeat
end if
if sellannounce is in (get content of eachMessage) then
--sell email
end if
end if
end repeat
end tell
end
end
end
end

I did not check if your code works properly but here's one way to write your code to avoid the problems. You can use subroutines. When you call a subroutine from within a "tell" block of code you need to preface it with the word "my". This tells the script that the subroutine belongs to the script and not the application you are telling.
Notice that you can optimize your code by getting the message content once. Every time you "get content of eachMessage" there's a performance hit... so do it only once. Notice also that I added some delays between your keystroke commands. Normally you need these small delays to prevent errors when you are doing things like this. It gives your computer a little extra time to perform the keystrokes.
Good luck.
set buyannounce to "AUTOMATIC SERVER EMAIL : BUY"
set transferannounce to "AUTOMATIC SERVER EMAIL : TRANSFER"
set signupannounce to "AUTOMATIC SERVER EMAIL : SIGNUP"
set sellannounce to "AUTOMATIC SERVER EMAIL : SELL"
set nowsignup to "0"
set announce to "AUTOMATIC SERVER EMAIL"
set currentSignups to the text returned of (display dialog "How many current signups?" default answer "")
tell application "Mail"
set unreadMessages to (get every message of mailbox "INBOX" of account "Yahoo" whose read status is true)
repeat with eachMessage in unreadMessages
set messageContent to content of eachMessage
if announce is in messageContent then
if buyannounce is in messageContent then
--buy email
end if
if transferannounce is in messageContent then
--transfer email
end if
if signupannounce is in messageContent then
set nowsignup to nowsignup + 1
set eachMessageTrimmed to my trimMessageText(messageContent)
display dialog nowsignup
my openFilePath(POSIX path of "/Users/obleopold/Dropbox/Accounts.csv")
delay 10
repeat currentSignups times
my keystrokeSomething(return)
delay 0.2
end repeat
repeat nowsignup times
my keystrokeSomething(eachMessageTrimmed)
delay 0.2
my keystrokeSomething(return)
delay 0.2
currentSignups = currentSignups + nowsignup
set nowsignup to "0"
end repeat
end if
if sellannounce is in messageContent then
--sell email
end if
end if
end repeat
end tell
(************* SUBROUTINES *****************)
on trimMessageText(messageText)
try
return text 33 thru -1 of messageText
on error
return messageText
end try
end trimMessageText
on keystrokeSomething(something)
tell application "System Events"
keystroke something
end tell
end keystrokeSomething
on openFilePath(filepath)
try
do shell script "open " & quoted form of filepath
end try
end openFilePath
The other way to separate your tell blocks would be something like this. But in your case it seems subroutines are easier.
tell application "Mail"
-- do something
end tell
set eachMessageTrimmed to text 33 thru -1 of messageText
tell application "System Events"
-- do something
end tell
do shell script "something"
tell application "Mail"
-- do something
end tell

System Events is an application, so for your tell blocks you need
tell application "System Events"
Instead of
tell "System Events"
Furthermore, to prevent nested tell blocks you can use:
tell application "System events"
tell process "Mail"
keystroke "My Keystroke"
end tell
end tell
Or in one line:
tell application "System Events" to tell process "Mail" to keystroke "My Keystroke"

Related

How to suppress an OS X error dialog in applescript or how to make sure a remote share is connected and sharing before using a mount volume command

Okay so I've been a few days on this, and I've only found one similar issue. Unfortunately it wasn't resolved fully.
I have a script that checks for a network connection, checks for the remote computer, and mounts the volumes.
The script works as expected and is error handled with a try blocks but in the mountVolume() handler I get the same error dialog box that Daniel from the other post gets when the share is unavailable. e.g. external drive is unplugged from remote computer or hasn't finished connecting yet.
Once I dismiss OS X's error dialog(s), my error dialog comes up. I could just get rid of my dialog but the thing is that for every share (4 now) I've tried to mount that fails, I get a separate OS X error dialog that I have to dismiss.
If I can get the script to suppress these boxes I can use one box of my own in the script for all errors.
If I can't then I would like a way to check if the share exists on the REMOTE computer before I try to use mount volume thus circumnavigating the error all together.
Thanks any ideas would be appreciated.
Here is my code:
global userName
global userPass
global ipAddress
set ipAddress to "###.###.###.###"
set userName to short user name of (system info)
set userPass to do shell script ("security find-internet-password -a " & userName & " -s " & ipAddress & " -g")
on FileExists(theFile)
tell application "System Events"
if exists file theFile then
return true
else
return false
end if
end tell
end FileExists
on FolderExists(theFolder)
tell application "System Events"
if exists folder theFolder then
return true
else
return false
end if
end tell
end FolderExists
on doCleanUp()
if FolderExists("/Volumes/SHARENAME") then
tell application "Finder" to eject disk "SHARENAME"
end if
set checkPath to ((path to home folder as text) & "SHARENAME")
if FileExists(checkPath) then
do shell script ("rm ~/SHARENAME")
end if
end doCleanUp
on checkNet()
try
do shell script ("nc -z " & ipAddress & " 445")
return true
on error
return false
end try
end checkNet
on mountVolume()
try
mount volume "smb://" & ipAddress & "/SHARENAME"
return true
on error errText number errNum
log {errText, errNum}
return false
end try
end mountVolume
on makeAlias()
if FolderExists("/Volumes/SHARENAME") then
set checkPath to ((path to home folder as text) & "SHARENAME")
tell application "Finder"
if not (exists file checkPath) then
make new alias to disk "SHARENAME" at path to home folder
end if
end tell
end if
end makeAlias
set tryAgain to 0
set ipValid to false
set doRetry to true
doCleanUp()
repeat while doRetry
repeat 3 times
if not ipValid then
set ipValid to checkNet()
end if
end repeat
if ipValid then
set volMounted to mountVolume()
if volMounted then
set aliasCreated to makeAlias()
if aliasCreated then
return
else
set notificationMessage to "Could not create alias."
display alert "An error has occurred." message notificationMessage as critical
return
end if
else
set notificationMessage to "Could not mount remote volume."
display alert "An error has occurred." message notificationMessage as critical
return
end if
else
set retryCheck to display alert "Can't connect. Do you want to retry?" buttons {"Yes", "No"} default button 1
set doRetry to button returned of retryCheck as boolean
if not doRetry then
doCleanUp()
set notificationMessage to "Could not connect to Local Area Network."
display alert "An error has occurred." message notificationMessage as critical
end if
end if
end repeat
The error dialog is being generated outside of AppleScript, so you can’t trap it with a try statement. The only way I know of to avoid the dialog is to create the mount point and mount the volume yourself with the mount shell script instead of the mount volume command, for example:
do shell script "mount -t smbfs //169.254.0.0/SHARENAME /path/to/sharepoint“
If there is an error the try statement will still catch it, just without the external dialog.

How to save variables after quitting and reopening in AppleScript?

I am writing a very complicated AppleScript application that requires variables to be saved after quitting. So if I set the variable while it is running, close it, reopen it the variable will still be the same.
This script is intended to bring up a setup menu on first run. Then save the preferences for after the application is closed. More Technical explanation:
When it starts (on run) it will check if isSetup is false if it is it will go to the function setup(). The setup() function sets preferences and sets isSetup to true. When I quit and reopen the application is runs the setup() function again.
I know I am not supposed to copy and paste full scripts but I can't find the replicate the error without it. Here it is:
--AppleScript: menu bar script -- Created 2017-03-03 by Takaaki Naganoya adapted by ----
--2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
--http://piyocast.com/as/archives/4502
property aStatusItem : missing value
global theToggle
global theMenuTitle
global aTag
global aTitle
global isSetup
global usrName
global usrPass
global usrProtocol
property usrName : missing value
property usrPass : missing value
property isSetup : false
property usrProtocol : missing value
on run
if isSetup is false then
setup()
else
set theToggle to "Connect"
set theMenuTitle to "Server Helper"
init()
end if
end run
on init()
set aList to {theToggle, "Preferences", "Change Password", "", "Quit"}
set aStatusItem to current application's NSStatusBar's systemStatusBar()'s statusItemWithLength:(current application's NSVariableStatusItemLength)
aStatusItem's setTitle:theMenuTitle
aStatusItem's setHighlightMode:true
aStatusItem's setMenu:(createMenu(aList) of me)
end init
on createMenu(aList)
set aMenu to current application's NSMenu's alloc()'s init()
set aCount to 1
repeat with i in aList
set j to contents of i
if j is not equal to "" then
set aMenuItem to (current application's NSMenuItem's alloc()'s initWithTitle:j action:"actionHandler:" keyEquivalent:"")
else
set aMenuItem to (current application's NSMenuItem's separatorItem())
end if
(aMenuItem's setTarget:me)
(aMenuItem's setTag:aCount)
(aMenu's addItem:aMenuItem)
if j is not equal to "" then
set aCount to aCount + 1
end if
end repeat
return aMenu
end createMenu
on setup()
display dialog " Welcome to the Server setup Utility.
To Begin click " & quote & "Continue" & quote & " below." buttons {"Cancel", "Continue"} default button 2
set theButton to the button returned of the result
if theButton is "Continue" then
display dialog "Please enter your " & quote & "Username" & quote & " for the Brown Server." default answer "Username" buttons {"Continue"} default button 1
set usrName to the text returned of the result
display dialog "Please enter your " & quote & "Password" & quote & " for the Brown Server." default answer "" buttons {"Continue"} default button 1 with hidden answer
set usrPass to the text returned of the result
set listDeProtocols to {"AFP", "SMB", "WebDav", "FTP"}
set usrProtocol to (choose from list listDeProtocols with prompt "Choose Your Prefered Protocol. AFP is recomended. If AFP does not work try SMB. All others are not supported at this time")
set isSetup to true
postSet()
end if
end setup
on postSet()
if isSetup is false then
setup()
else
set theToggle to "Connect"
set theMenuTitle to "Server Helper"
init()
end if
end postSet
on changePref()
end changePref
on pref()
set length1 to the length of usrPass
set p1 to ""
set p2 to ""
repeat length1 times
set p1 to "•"
set p2 to p1 & p2
end repeat
display dialog "These are your following preferences. Click the " & quote & "Change" & quote & " to change.
Username: " & usrName & "
Password: " & p2 & "
Prefered Protocol: " & usrProtocol buttons {"Back", "Change"}
set theButton to the button returned of the result
if theButton is "Change" then
changePref()
end if
end pref
on actionHandler:sender
set aTag to tag of sender as integer
set aTitle to title of sender as string
if aTitle is not equal to "Quit" then
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
if aTitle is "Connect" then
set theToggle to "Disconnect"
init()
end if
if aTitle is "Disconnect" then
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
set theToggle to "Connect"
init()
end if
if aTitle is "Preferences" then
pref()
end if
if aTitle is "Change Password" then
changePass()
end if
else
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
end if
end actionHandler:
Update: doesn't work
YES!!!! I finally found the answer. You have to remove the property aStatusItem : missing value from the stop of the script. This will prevent aStatusItem from being used between functions. Because of this the menu bar won't be removed when you press quit. To fix that problem at the end change current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem to tell me to quit. This quits the application resulting in the menu bar item being removed.

AppleScript : Search safari tab and open tab

I would like to find a safari tab, and focus on this tab
This is not working, however the tab is found alright, I just can't open this tab.
tell application "Safari"
repeat with t in tabs of windows
tell t
if name starts with "facebook" then open tab
end tell
end repeat
end tell
Also I wonder can I found some text from a another tab without focus on this tab ?
to getInputByClass75(theClass, num)
tell application "Safari"
set input to do JavaScript "
document.getElementsByClassName('" & theClass & "')[" & num & "].innerHTML;" in document 1
end tell
return input
end getInputByClass75
getInputByClass75("Order", 0)
set theText to Unicode text
set theSource to getInputByClass75("Order", 0)
property leftEdge75 : "<a class=\"columns\" href=\"/Web"
property rightEdge75 : "\">Display</a>"
set saveTID to text item delimiters
set text item delimiters to leftEdge75
set classValue to text item 2 of theSource
set text item delimiters to rightEdge75
set OrderLink to text item 1 of classValue
set text item delimiters to saveTID
OrderLink
The right phrase to bring a tab to foreground is
tell window x to set current tab to tab y
Try this
tell application "Safari"
repeat with w in windows
if name of w is not "" then --in case of zombie windows
repeat with t in tabs of w
if name of t starts with "facebook" then
tell w to set current tab to t
set index of w to 1
end if
end repeat
end if
end repeat
end tell
--
You can execute javascripts in background tabs. Specify the tab like this:
tell application "Safari"
do JavaScript "document....." in tab 1 of window 1
end tell

Issue setting returned text to variable within repeat loop - OS X 10.9.x

The goal of this script is to:
Ask the user how many text replacement (System Preferences>Keyboard>Text) shortcuts they'd like to have. The text returned is set to my variable "gTextReplacementNum" as number. See my second handler "HowMany()"
Have the user provide the text replacement shortcut and the text to do the replacing for the number of shortcuts they wanted. See my third handler "GetText()"
Take the user provided text contained in a variable to create a new AppleScript doc that does all the heavy lifting for them. Code not yet written; not within scope of question.
Then they have a personalized AppleScript Application Bundle they may launch on their Mac to auto-populate the text replacement preferences pane.
I am having trouble getting this to work properly. I need the loop to keep adding the answers to a variable as a list or to a variable that increments its name according to the loop instance (e.g. TextReturned_i, TextReturned_i+1, etc).
Have I adequately explain this?
global gTextReplacementNum
set gTextReplacementNum to 0
# Main Logic Begins
try
Start()
HowMany()
GetText()
on error errText number errNum
display alert "Error " & errNum message errText
end try
# Main Logic Ends
# Handlers Begin
-- First Handler
on Start()
display alert "Automated Text Replacement v1.0" message "Created by: Me
myemail#domain.com" buttons {} giving up after 4
display alert "About" message "This app will have you provide a text 'short cut' to replace with and replacement text. It then compiles all this into an application that can be run on any Mac.
Would you like to continue?" buttons {"No", "Yes"} cancel button 1 default button 2
end Start
-- Second Handler
on HowMany()
display dialog "How many text replacement shortcuts would you like?
Please enter numericals only. (1, 2, 3)" default answer "" buttons {"Cancel", "Okay"} default button 2 cancel button 1
copy the result as list to {ButtonPressed, TextReturned}
set gTextReplacementNum to TextReturned as number
end HowMany
-- Third Handler
on GetText()
repeat with i from 1 to gTextReplacementNum as number
display dialog "What text would you like to replace?
(this is your shortcut)" default answer "" buttons {"Cancel", "Okay"} default button 2 cancel button 1
set TextShortcut to text returned of result as list
display dialog "What is the replacement text?
(this is what the shortcut fills out)" default answer "" buttons {"Cancel", "Okay"} default button 2 cancel button 1
set TextReplaced to text returned of result as list
end repeat
end GetText
# Handlers End
In your GetText() handler, you are replacing the value TextShortcut and TextReplaced each time. You need to
set aList to aList & newValue
to build a list in a repeat loop.
Also, as is, this handler never returns the value of these two lists. So, I'd suggest, using your scheme, make these two variables globals as well.
So, the full changes are:
1. Add to the declarations:
global gTextReplacementNum
global gTextShortcut
global gTextReplaced
set gTextReplacementNum to 0
set gTextShortcut to {}
set gTextReplaced to {}
and 2. edit your GetText() handler:
-- Third Handler
on GetText()
repeat with i from 1 to gTextReplacementNum as number
display dialog "What text would you like to replace?
(this is your shortcut)" default answer "" buttons {"Cancel", "Okay"} default button 2 cancel button 1
set gTextShortcut to gTextShortcut & (text returned of result)
display dialog "What is the replacement text?
(this is what the shortcut fills out)" default answer "" buttons {"Cancel", "Okay"} default button 2 cancel button 1
set gTextReplaced to gTextReplaced & (text returned of result)
end repeat
end GetText
An alternate method would be to read a tab delim file and work from that with a standard script. Something like:
property fileName : "shortcuts.txt"
set filePath to (path to desktop as string) & fileName
set theData to read file filePath
set theRecords to paragraphs of theData
set oldDelim to AppleScript's text item delimiters
set AppleScript's text item delimiters to tab
repeat with thisPair in theRecords
set {theShortcut, theReplacement} to text items of thisPair
setKeyboardPref(theShortcut, theReplacement)
end repeat
set AppleScript's text item delimiters to oldDelim
on setKeyboardPref(theShortcut, theReplacement)
-- set up the pair
display dialog theShortcut & return & theReplacement
end setKeyboardPref

AppleScript: Main Script reading from random folder

How do you bundle an applescript that has a main script which is reading scripts randomly from a folder?
Check out this code: http://macosxautomation.com/applescript/linktrigger/index.html.
It's doing exactly that (besides other things) and you can use the sub-routine there for the script-handling.
Here is a full script that works when you follow these steps: save it as an application and put all your scripts into the "Resources" folder of that application (as described at the webpage, "Show Package Contents"). Good Luck.
property MyUserName : ""
if MyUserName is "" then
display dialog "User Name:" default answer "" buttons {"Cancel", "Continue…"} default button 2
copy the result as list to {returnedButton, returnedText}
if returnedButton is "Cancel" then return
-- What to do when the user did not input any text?
if returnedText is "" then return -- we stop.
set MyUserName to returnedText
say "Hello," & MyUserName & "!" using "Karen"
else
display dialog "User name: " & MyUserName buttons {"Ok"} default button 1 with icon 1 giving up after 10
end if
say "For your information, please start the Amuse App everytime you log on... and I will speak to you at random times during your visit with me." using "Karen"
delay 20
try
repeat 105 times
set rnd to (random number from 1 to 105)
set rndFileName to (rnd as text) & ".scpt"
if my run_scriptfile(rndFileName) is false then -- if the script execution fails,…
error number -128 -- … stop this app
end if
end repeat
on error the error_message number the error_number
display dialog "Error: " & the error_number & ". " & the error_message buttons {"OK"} default button 1
return
end try
on run_scriptfile(this_scriptfile)
-- This handler will execute a script file
-- located in the Resources folder of this applet
try
set the script_file to path to resource this_scriptfile
return (run script script_file)
on error
return false
end try
end run_scriptfile
This is how it looks with (only) 3 of the scripts you wanna randomly execute:
Your question is clear but here's an script that loads randomly a script from a folder, if the folder contains any.
set theFolder to choose folder as string
set theFiles to do shell script "ls " & quoted form of posix path of theFolder & " | grep '.scpt$' || true"
if theFiles = "" then return
set theFile to some item of (paragraphs of theFiles)
set randomScript to load script file (theFolder & theFile)