How to allow an Applescript to be exited at any time - applescript

I am trying to write a script which presses the "w" and "q" keys rapidly for a prolonged period (the purpose is to perform a repetitive activity in a game's practice mode).
I know next to nothing about Applescripts, but I was able to piece together the following which kind of gets the job done:
tell application "System Events"
delay 5
repeat with i from 1 to 50
delay 0.25
keystroke "w"
delay 0.25
keystroke "q"
end repeat
end tell
Basically I press "run", alt-tab to the game, and let the script run.
The problem is that I have no way to force-exit the script. I tried the recommendation here to use command+option+escape, but I can't manage to click "force quit" and it won't close.
I'm looking at the documentation for handlers, but I'm not sure exactly what I'm supposed to do. I don't want to close the game application, just stop the script.
Ideally, I could implement some sort of "on key pressed" listener that kills the Applescript.
edit with the suggestion in the comment I now have:
use sys : application "System Events"
use framework "Cocoa"
tell application "System Events"
delay 5
repeat with i from 1 to 50
delay 0.25
keystroke "w"
delay 0.25
keystroke "q"
if modifierKeydown() then exit repeat # Exit on keypresswq
end repeat
end tell
# Returns true if any of
# { function, control, option, command }
# are depressed; false otherwise
on modifierKeydown()
set __m to current application's ¬
NSEvent's modifierFlags() as any
return (__m > 262143)
end modifierKeydown
This raises:
error "System Events got an error: Can’t continue modifierKeydown." number -1708

Using my answer to a similar question, here's a semi-tailored solution to your specific situation:
use sys : application "System Events"
use framework "Cocoa"
# Bring your game window into focus
tell application "The Name of Your Game" to activate
# Might be needed if your game window takes a long time to
# enter focus. But if it's already running, it should be instant
# and this little loop won't be needed.
# repeat while process "The Name of Your Game" is not frontmost
# end
# Get frontmost window of the frontmost application
set P to a reference to (the first process whose frontmost is true)
set W to the front window of P
repeat with i from 1 to 50
# Terminate loop if the focus changes to another window
if the front window of P is not equal to W then exit repeat
delay 0.25
tell sys to keystroke "w"
delay 0.25
tell sys to keystroke "q"
if modifierKeydown() then exit repeat # Terminate loop if modifier key is pressed
end repeat
return
# Returns true if any of
# { function, control, option, command }
# are depressed; false otherwise
on modifierKeydown()
set __m to current application's ¬
NSEvent's modifierFlags() as any
return (__m > 262143)
end modifierKeydown

Related

AppleScript to turn off the monitor if not in use for more than 15 mins

I would like to turn off monitor if not in use for more than 15 mins. I have this script below:
tell application "System Events"
set frontApp to name of first application process whose frontmost is true
set mouseMoved to false
repeat
set currentPosition to get the mouse location
if currentPosition is not equal to {-1, -1} then
if currentPosition is not in {{0, 0}, {0, 1079}} then
set mouseMoved to true
end if
end if
if mouseMoved is false then
tell application "System Preferences"
activate
set current pane to pane "com.apple.preference.displays"
end tell
tell application "System Events" to tell process "System Preferences"
click button "Display" of tab group 1 of window 1
delay 2
click radio button "Turn off after: 15 min" of tab group 1 of window 1
end tell
keystroke "q" using {command down}
end if
delay 60
end repeat
end tell
When I test it I am getting: Expected end of line but found class name.
I looked online for solutions but still no clue how to fix this. Any advices would be appreciated. Thanks
Setting display to sleep after N minutes (when no any activity registered) is the job for the Power Management utility (pmset). I recommend to use following script as run-only, for security reasons.
displaySleepAfterMiunutes(15)
on displaySleepAfterMiunutes(sleepAfterMinutes)
set thePassword to "*******" -- set your ADMIN password here
set UserName to do shell script "echo $USER"
do shell script "pmset displaysleep " & sleepAfterMinutes user name UserName password thePassword with administrator privileges
end displaySleepAfterMiunutes
Full safe approach:
do shell script "pmset displaysleep 15" with administrator privileges

How «Wait until application launch» applescript?

How to write the code correctly?
I run the application Photoshop in the automator
I'm waiting for it to fully load
Then I press 10 times Tab and press Enter.
I've tried that:
enter image description here
Looks like that part doesn't work. Because Tab starts to click before the application is fully loaded. What's wrong? Thanks!
repeat until application launch
delay 0.5 end repeat delay 0.5
Most likely, the OP does not understand the main thing: GUI scripting (in this case, sending 10 tabs, and then Enter, that is, keystroke tab and keystroke return in AppleScript language) only works with the frontmost window. And the launch command launches an application without bringing its window to the front.
The correct approach is 1) use the activate application "Photoshop" command 2) use the make new document command, 3) check if the new window exists, 4) send keystroke commands. In the Automator, the Run AppleScript action should be something like this:
on run {input, parameters}
tell application "Photoshop"
activate
make new document with properties {name:"myNewDocument"}
repeat until window "myNewDocument" exists
delay 0.1
end repeat
end tell
tell application "System Events"
repeat 10 times
delay 0.1
keystroke tab
end repeat
keystroke return
end tell
return input
end run
NOTE: not tested, because PhotoShop.app is not installed on my Mac. I am ready to correct my script, if needed. In general, the question is not quite clear.
I don't know much about Photoshop, but I know that it has a loading screen. I tried the following code in Affinity Photo which is a similar product to Photoshop.
tell application "Photoshop"
launch
set theBool to false
repeat until theBool
tell application "System Events" to ¬
if menu item "Close" of ¬
menu 1 of ¬
menu bar item "File" of ¬
menu bar 1 of ¬
application process "Photoshop" exists then ¬
set theBool to true
delay 0.2
end repeat
end tell
The repeat until theBool checks if the loading screen is over by checking if some menu item exists which isn't available when the loading screen is open. If the "Close" and the "File" don't work in Photoshop, you may choose something else.
This is the answer:
tell application "Your app"
launch
activate
end tell

Applescript for auto scrolling in Font Book. I could use some expert guidance

As a graphic designer, I am constantly having to identify which fonts to use in a document or match a font. Currently I have over 5000 different fonts. I don't keep all 5000 installed on my system. However, I usually do have to scroll through custom collections of 1000 fonts or more using the down arrow key. Sometimes the whole process takes me the better part of an hour
I created a script for auto scrolling in Font Book and saved it as an application named “FontBook_Auto_Scroll.app”. Basically it opens a dialog window giving me three options. If I select “arrow down”, it brings Font Book to the front and pushes the arrow down key 35 times in increments of half a second.
Then the dialogue window opens again. If I select “arrow up”, it brings Font Book to the front and pushes the arrow up key 7 times, etc. But here is the problem. If in the process of “scrolling down”, I see the font I want to use and it happens to appear as the second font in the “scrolling down” cycle, I would prefer not to have to wait until the 35 arrow key down entries are completed.
I'm still playing around with this script and making revisions as I continue reading AppleScript help documents. This is what I have so far.
property selectedFontFamily : missing value
tell application "Font Book"
activate
delay 5
try
set (selected collections) to font domain "AllFonts"
on error errMsg number errNum
set (selected collections) to font domain "AllFonts"
end try
try
set (selected font families) to font family 1
on error errMsg number errNum
set (selected font families) to font family 1
end try
end tell
tell application "System Events"
repeat 2 times
key code 48
end repeat
end tell
delay 1
repeat 40 times
activate
display dialog "Font Book Scrolling" buttons {"Arrow Down", "Arrow Up", "Cancel"} default button 1 giving up after 7
set the button_pressed to the button returned of the result
if the button_pressed is "" then
tell application "Font Book"
activate
delay 1
set (selected collections) to font domain "AllFonts"
tell application "System Events"
key code 37 using {command down, option down}
end tell
delay 1
set selectedFontFamily to (selected font families)
end tell
tell application "System Events"
delay 3
repeat 55 times
delay 0.6
key code 125
end repeat
delay 1
end tell
tell application "Font Book"
set selectedFontFamily to (selected font families)
tell application "System Events"
key code 37 using {command down, option down}
end tell
end tell
else if the button_pressed is "Arrow Down" then
tell application "Font Book"
activate
set (selected collections) to font domain "AllFonts"
tell application "System Events"
key code 37 using {command down, option down}
end tell
set selectedFontFamily to (selected font families)
end tell
tell application "System Events"
delay 3
repeat 55 times
delay 0.6
key code 125
end repeat
delay 1
end tell
tell application "Font Book"
set selectedFontFamily to (selected font families)
tell application "System Events"
key code 37 using {command down, option down}
end tell
end tell
else if the button_pressed is "Arrow Up" then
tell application "Font Book"
activate
set (selected collections) to font domain "AllFonts"
tell application "System Events"
key code 37 using {command down, option down}
end tell
set selectedFontFamily to (selected font families)
end tell
tell application "System Events"
delay 1
repeat 15 times
delay 0.7
key code 126
end repeat
delay 1
end tell
tell application "Font Book"
set selectedFontFamily to (selected font families)
tell application "System Events"
key code 37 using {command down, option down}
end tell
end tell
else if the button_pressed is "Cancel" then
tell application "Font Book"
quit
end tell
return
end if
end repeat
quit
end
on quit
tell application "Font Book"
quit
end tell
continue quit -- allows the script to quit
end quit
It's been my experience that once an AppleScript application starts running its script, sans coded exit points, the only way to get out of a loop is to force quit the application.
Because one may have more then one AppleScript application running at a time and the executable's name, regardless of what one named the application, is applet, you don't want to use a command like do shell script "kill -9 $(pgrep applet)", as it will kill all running AppleScript applications.
I'd have second AppleScript application handy, e.g. "Terminate - FontBook_Auto_Scroll.app", in the Dock for quick access, to isolate the PID of the target AppleScript application, using the following command syntax:
do shell script "kill -9 $(ps -x | awk '/[N]ame.app/{print $1}'); exit 0"
In the case of your "FontBook_Auto_Scroll.app", the command would be:
do shell script "kill -9 $(ps -x | awk '/[F]ontBook_Auto_Scroll.app/{print $1}'); exit 0"
The first character of the application's name is in square braces so as not to confuse the PID returned of the awk query that has the target AppleScript application's name in it.
The ; exit 0 it there so if you accidentally run the AppleScript application that terminates the target AppleScript application when it's not running, it does not error out.
Then when you want to stop the scrolling, use the "Terminate - FontBook_Auto_Scroll.app" AppleScript application to terminate the "FontBook_Auto_Scroll.app" AppleScript application.
BTW Looking at the coding of your AppleScript application, the issue you're going to run into is while it's in the loop, if you set focus elsewhere, the key code events are going to go to whatever has focus.
Update:
Here is some example code, using cliclick, to programmatically mathematically calculate, based on the properties of the UI Elements, where to click.
Tested under macOS 10.12.5, this code will click the All Fonts collection, then the first Font in that collection.
Note: Change the value of the cliclick variable based on where it's located on your system.
set cliclick to POSIX path of (path to home folder as string) & "bin/cliclick"
tell application "Font Book"
activate
-- delay 1
tell application "System Events"
set position of window 1 of application process "Font Book" to {0, 22}
set size of window 1 of application process "Font Book" to {800, 622}
set theFontBookAllFontsProperties to ¬
get properties ¬
of static text 1 ¬
of UI element 1 ¬
of row 2 ¬
of outline 1 ¬
of scroll area 1 ¬
of splitter group 1 ¬
of window 1 ¬
of application process "Font Book"
set theFontBookAllFontsPosition to position in theFontBookAllFontsProperties
set theFontBookAllFontsSize to size in theFontBookAllFontsProperties
set theXpos to (item 1 of theFontBookAllFontsPosition) + (item 1 of theFontBookAllFontsSize) / 2 as integer
set theYpos to (item 2 of theFontBookAllFontsPosition) + (item 2 of theFontBookAllFontsSize) / 2 as integer
tell current application
-- delay 0.25
do shell script cliclick & " c:" & theXpos & "," & theYpos
end tell
set theFontBookAllFontsFirstFontsProperties to ¬
get properties ¬
of static text 1 ¬
of UI element 1 ¬
of row 1 ¬
of outline 1 ¬
of scroll area 2 ¬
of splitter group 1 ¬
of window 1 ¬
of application process "Font Book"
set theFontBookAllFontsFirstFontsPosition to position in theFontBookAllFontsFirstFontsProperties
set theFontBookAllFontsFirstFontsSize to size in theFontBookAllFontsFirstFontsProperties
set theXpos to (item 1 of theFontBookAllFontsFirstFontsPosition) + (item 1 of theFontBookAllFontsFirstFontsSize) / 2 as integer
set theYpos to (item 2 of theFontBookAllFontsFirstFontsPosition) + (item 2 of theFontBookAllFontsFirstFontsSize) / 2 as integer
tell current application
-- delay 0.25
do shell script cliclick & " c:" & theXpos & "," & theYpos
end tell
end tell
end tell
Note: The delay commands may or may not be necessary and or may or may not need to have the value of the delay modified. Uncomment and set as appropriate to the needs.

applescript copy paste between textedit and 3rd party application not working

I have searched all over this site and cannot find an answer to why this code isn't doing what I need it to do. I have a textedit doc with a list of numbers. I want to copy 1 number at a time paste that number into a 3rd party application at a specific place in url, and then hit some buttons in the ui of that application. I need this process to repeat for each individual number in the textedit document.
Here is what I came up with after researching applescript.
tell application "TextEdit" to activate
tell application "System Events"
tell process "TextEdit"
key code 124 using {shift down, command down}
keystroke "c" using command down
key code 125
end tell
end tell
delay 1.0
tell application "import.io" to activate
tell application "System Events"
tell process "import.io"
keystroke tab
keystroke tab
key code 124
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 51
keystroke "v" using command down
keystroke tab
key code 76
end tell
end tell
-- Make a selection from the popupbutton.
delay 2.231426
set timeoutSeconds to 10.0
set uiScript to "click pop up button 1 of window \"Save\" of application process \"import.io\""
my doWithTimeout(uiScript, timeoutSeconds)
return input
end run
on doWithTimeout(uiScript, timeoutSeconds)
set endDate to (current date) + timeoutSeconds
repeat
try
run script "tell application \"System Events\"
" & uiScript & "
end tell"
exit repeat
on error errorMessage
if ((current date) > endDate) then
error "Can not " & uiScript
end if
end try
end repeat
end doWithTimeout
-- Click the “<fill in title>” checkbox.
delay 1.496275
set timeoutSeconds to 10.0
set uiScript to "click checkbox 1 of window \"Save\" of application process \"import.io\""
my doWithTimeout(uiScript, timeoutSeconds)
return input
-- Type “Data” into the text field.
delay 7.290406
set timeoutSeconds to 10.0
set uiScript to "click text field 1 of group 17 of list 1 of scroll area 1 of scroll area 1 of browser 1 of splitter group 1 of splitter group 1 of group 2 of window \"Save\" of application process \"import.io\""
keystroke "Data"
keystroke "."
tell application "System Events" to tell process "import.io"
keystroke "v" using command down
end tell
my doWithTimeout(uiScript, timeoutSeconds)
return input
-- Click the “Save” button.
delay 1.475013
set timeoutSeconds to 10.0
set uiScript to "click UI Element \"Save\" of window \"Save\" of application process \"import.io\""
my doWithTimeout(uiScript, timeoutSeconds)
return input
my textedit document is formatted like this:
50
100
150
200
etc
When I run the script this is what it does to my textedit document:
50
50
100
150
200
etc
Any idea what is going on here? I can't make heads or tails of it.
Looking for result in your TextEdit file, first you must start to simplify the textEdit copy part. I assumed that the TextEdit document is already open.
The script bellow provides simplification for copy part :
tell application "TextEdit"
activate
set myNumbers to every paragraph of front document
end tell
repeat with aNumber in myNumbers -- loop through each number
set the clipboard to aNumber
-- insert here the paste instructions in your third party application
end repeat
You must insert the instruction about the paste in the loop, after the 'set clipboard...' instruction which fills the clipboard with a number.
I can't help you for the paste part, because I don't know your third party application, however, your program calling other scripts in doWithTimeout routine does not seems to be very clean and efficient.
If this third party application is not scriptable, at least you may have to go via GUI scripting or java (if java based application).
For instance, instead of doing all the key arrows to move to correct input cell, try to address that cell directly by its GUI properties.
Same for the checkbox, you can also use GUI click function.
The disadvantage of GUI scripting is that application interface must not changed. But this is already the case in your script.

Run Javascript on the specific process id of Safari

I have the script like this here:
on run argv
set pid to item 1 of argv
set fileLocation to item 2 of argv
tell application "System Events"
set theprocs to every process whose unix id is pid
repeat with proc in theprocs
set the frontmost of proc to true
using terms from application "Safari"
set theScript to "document.querySelector(\"input[name='fileField']\").click()"
tell application "Safari" to do JavaScript theScript in current tab of first window
end using terms from
tell proc
keystroke "G" using {command down, shift down}
delay 2
key code 51
delay 2
keystroke fileLocation
delay 10
key code 52
delay 5
key code 52
delay 5
end tell
end repeat
end tell
end run
which is trying to do the file upload process on the Safari browser. The main goal is to run the script against the given process id ( as many Safari will be running on the machine).
I have problem here with the line do JavaScript from:
tell application "Safari" to do JavaScript theScript in current tab of first window
this is executing the click call on the different Safari browser, rather than the one given in the pid.
I just want to make sure that the script runs against the given Safari PID.
How can I do that?

Resources