AppleScript: How to extract numbers from a string? - applescript

I am writing a script to go to the NYT website on Corona, get the US data, extract numbers (total, death), and to send me a notification. I am close, but when I extract numbers and display them, they are put together (ie 700021 instead of 7000,21). My question is:
How do I extract the numbers so that they are delineated?
Here is the code:
set theURL to "https://www.nytimes.com/interactive/2020/world/coronavirus-maps.html?action=click&pgtype=Article&state=default&module=styln-coronavirus&variant=show&region=TOP_BANNER&context=storyline_menu"
tell application "Safari" to make new document with properties {URL:theURL}
tell application "System Events"
repeat until exists (UI elements of groups of toolbar 1 of window 1 of application process "Safari" whose name = "Reload this page")
delay 0.5
end repeat
end tell
to getInputByClass(theClass, num)
tell application "Safari"
set input to do JavaScript "
document.getElementsByClassName('" & theClass & "')[" & num & "].innerText;" in document 1
end tell
return input
end getInputByClass
set myVar to getInputByClass("g-body ", 5)
on returnNumbersInString(inputString)
set s to quoted form of inputString
do shell script "sed s/[a-zA-Z\\']//g <<< " & s
set dx to the result
set numlist to {}
repeat with i from 1 to count of words in dx
set this_item to word i of dx
try
set this_item to this_item as number
set the end of numlist to this_item
end try
end repeat
return numlist
end returnNumbersInString
set theNums to returnNumbersInString(myVar) as text
display notification "COVID-19 UPDATE" subtitle theNums sound name "glass"
tell application "Safari"
close its front window
end tell

You are getting a list of numbers from the returnNumbersInString handler, but just coercing the list to text doesn't normally provide any kind of formatting. One solution would be to use text item delimiters to specify the text to use when joining the list items. For example, when converting to text for the notification you could do something like:
set tempTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to ", "
set theNums to returnNumbersInString(myVar) as text
set AppleScript's text item delimiters to tempTID

Similar to your other question I helped you with, the target data is already in a table and as such I'd use the table data to get the information as its structure layout is not likely to change where target 'g-body ' of 5 may not always be the United States.
I get my data a little different way:
set theURL to "https://www.nytimes.com/interactive/2020/world/coronavirus-maps.html?action=click&pgtype=Article&state=default&module=styln-coronavirus&variant=show&region=TOP_BANNER&context=storyline_menu"
tell application "Safari" to make new document with properties {URL:theURL}
tell application "System Events"
repeat until exists ¬
(UI elements of groups of toolbar 1 of window 1 of ¬
application process "Safari" whose name = "Reload this page")
delay 0.5
end repeat
end tell
tell application "Safari" to tell document 1 to set CountriesTable to ¬
do JavaScript "document.getElementsByClassName('svelte-f9sygj')[0].innerText;"
tell application "Safari" to close its front window
set awkCommand to ¬
"awk '/United States/{print $3,\"Cases &\",$4,\"Deaths\"}'"
set notificationMessage to ¬
do shell script awkCommand & "<<<" & CountriesTable's quoted form
display notification notificationMessage subtitle "US COVID-19 UPDATE" sound name "glass"
NOTE: The code used to determine when the page in Safari has finished loading works in macOS Mojave and later, however, for macOS High Sierra and some earlier versions, add the words buttons of in front of UI elements ... in the repeat until exists ¬ ... code.
Note: The example AppleScript code is just that and does not contain any error handling as may be appropriate. The onus is upon the user to add any error handling as may be appropriate, needed or wanted. Have a look at the try statement and error statement in the AppleScript Language Guide. See also, Working with Errors. Additionally, the use of the delay command may be necessary between events where appropriate, e.g. delay 0.5, with the value of the delay set appropriately.

Related

How can I list the available AirPlay output devices on macOS?

I'm trying to retrieve a list of available AirPlay output audio devices, I don't have a preference on which language to use.
Before Ventura I used the following Apple Script:
set devices to {}
tell application "System Preferences"
reveal pane id "com.apple.preference.sound"
end tell
tell application "System Events"
tell application process "System Preferences"
repeat until exists tab group 1 of window "Sound"
end repeat
tell tab group 1 of window "Sound"
click radio button "Output"
tell table 1 of scroll area 1
set selected_row to (first UI element whose selected is true)
set currentOutput to value of text field 1 of selected_row as text
repeat with r in rows
try
set deviceName to value of text field 1 of r as text
set deviceType to value of text field 2 of r as text
set end of devices to { deviceName, deviceType }
end try
end repeat
end tell
end tell
end tell
end tell
if application "System Preferences" is running then
tell application "System Preferences" to quit
end if
return [ devices, "currentOutput", currentOutput ]
But since the Ventura update this broken and it seems like there's no way to adapt it to work with Apple Script anymore.
Does anyone know how to update it to work with Ventura or could point me to the documentation of either Swift or ObjC to retrieve this list?
You can get system information for audio devices in the XML form, then parse it someway for audio devices names. I don't think following parsing is very good, but it works for me at least:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set theXML to do shell script "system_profiler SPAudioDataType -xml"
set {theXMLDoc, theError} to current application's NSXMLDocument's alloc()'s initWithXMLString:theXML options:(current application's NSXMLDocumentTidyHTML) |error|:(reference)
set {theMatches, theError} to (theXMLDoc's nodesForXPath:("//array") |error|:(reference))
if theMatches = missing value then error (theError's localizedDescription() as text)
set temp to (theMatches's firstObject()'s valueForKey:"stringValue") as text
set ATID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"_name", "coreaudio", "_properties"}
set tempItems to text items of temp
set AppleScript's text item delimiters to ATID
set audioDevicesNames to {}
repeat with anItem in tempItems
set anItem to contents of anItem
if anItem is "" or anItem begins with "_" then
-- do nothing
else
set end of audioDevicesNames to anItem
end if
end repeat
audioDevicesNames

Removing a word from a variable in Applescript

I'm pretty newbie at Applescript and I can't work out how to remove a word from a variable if the word contains a “#” in it.
My script gets this error -> "Can’t make word into type integer." number -1700 from word to integer
Here's my script so far:
activate application "Grids"
delay 2
tell application "System Events"
keystroke "a" using command down
delay 0.25
keystroke "c" using command down
delay 0.25
set Description to the clipboard
if any word in Description contains "#" then delete that word
return Description
end tell
Any pointers?
Cheers,
Chris
To get text out of the clipboard, use (clipboard as text). The clipboard can contain almost anything, even multiple objects, in multiple formats, so as text gives you a string to work with.
And watch out: 'Description' appears to be part of some existing appleScript 'terminology', at least on the Mac I have right here, so I am changing your identifier to desc here:
activate application "Grids"
delay 2
tell application "System Events"
keystroke "a" using command down
delay 0.25
keystroke "c" using command down
delay 0.25
set desc to the clipboard as text
end tell
set out to {}
set tids to AppleScript's text item delimiters
set AppleScript's text item delimiters to " "
repeat with anItem in (text items of desc)
set str to (anItem as string)
if (str does not contain "#") then
set end of out to str
end if
end repeat
set outStr to out as string
set AppleScript's text item delimiters to tids
return outStr
This code just returns the text you are looking for. It does not re-insert the groomed string, or do anything else interesting.
I assume you're going to tell System Events to paste it via cmd-v. (Remember to set the clipboard to outStr before you paste!)
AppleScript's text item delimiters allows the string to be split and reassembled using a space (or any other token you wish). For code hygiene reasons, it's wise practice to store it before changing it, then reset it to its original value afterwards, as shown here, otherwise odd things might happen in scripts which expect it to have the default value.

How to click button in AppleScript?

Writing an AppleScript to open Image Capture and click the Import All button.
tell application "Image Capture"
activate
tell application "System Events"
tell process "Image Capture"
click button "Import All" of group 1 of splitter group 1 of window 1
end tell
end tell
end tell
Image Capture opens but the script throws an error message saying it couldn't find the button "Import All".
Have followed advice on other threads on how to check the location in Accessibility Inspector and how to translate that to AppleScript instructions.
What's missing?
To get the button and group numbers, you have 2 ways: use the utility aplication provided by Apple in the developper toolkit "Accessibility Inspector" or use small scripts to find the number yourselves.
I prefer using script method. it is a bit longer sometime, but it always works. Just write a script with instruction get UI elements. Here is a small example of such script:
-- return lis of UI elements of active application and paste result in Excel
property tab : ASCII character 9
global T
-- to find last active application
tell application "System Events"
set frontmostProcess to first process where it is frontmost
set visible of frontmostProcess to false
repeat while (frontmostProcess is frontmost)
delay 0.2
end repeat
set secondFrontmost to name of first process where it is frontmost
set frontmost of frontmostProcess to true
end tell
set ActifProcess to secondFrontmost as text
tell application ActifProcess to activate -- set active the last actived application
delay 1
-- recursive loop to list all elements of active window
tell application "System Events" to tell process ActifProcess to set myWindow to front window
set T to ""
getUI(myWindow, 1)
set the clipboard to T
display dialog "Result is in clipboard. paste in Excel or text document"
on getUI(UIObjet, myLevel) -- get recursively the list of UI elements
set AppleScript's text item delimiters to {"//"}
tell application "System Events" to set UIliste to UI elements of UIObjet
repeat with oneUI in UIliste
tell application "System Events"
set UItext to ("Level=" & myLevel & tab & "Name=" & (name of oneUI) & tab & (description of oneUI) & tab & "Class=" & (class of oneUI) as string) & tab & "Title=" & (title of oneUI) as string
set UItext to (UItext & tab & "Value=" & (value of oneUI) as string) & tab
try
set UItext to (UItext & "Position=" & (position of oneUI) as text)
end try
set UItext to UItext & return
try
set NbSub to count of UI elements of oneUI
on error
set NbSub to 0
end try
set T to T & return & UItext
end tell
if NbSub > 0 then
getUI(oneUI, myLevel + 1) -- there are other sub UI elements, get them
end if
end repeat
end getUI
Copy this script in Script Editor. Make active the window/application you want to get UI elements. Make this script active and run.
The result is sometime not easy to interpret because developper of the application/window you're looking for may not have use UI element clear names or titles which describe what they are. Then you will have to look their relative position in the window.
The "import all" button is "button 3 of group 2 of splitter group 1 of window 1" for image capture version 6.6. Also, I prefer to use button number, instead of button name to make sure the script works with any language.
tell application "Image Capture"
activate
tell application "System Events"
tell process "Image Capture"
click button 3 of group 2 of group 1 of splitter group 1 of window 1
end tell
end tell
end tell
Please note that any next changes done by Apple on Image Capture will impact your script.

Interact WITH a running Applescript script/program without leaving the current program (Hotkey, sent keystroke etc)

The general question from my specific problem is how to interact WITH a running Applescript without leaving the program you a currently in. Its kind of the opposite question of how to USE Applescript to interact with other programs.
I need to go through a lot of web pages and input information from them into a spreadsheet. I have somewhat automated the process with an Applescript that takes an webpage as input and then opens its relevant links one by one, waiting for a click in a dialog box before proceeding to the next.
If you want to run the script, use http://www.resultat.dk/tennis/challenger-singler/champaign/resultater/ as input to the dialog box
--Let the user input the the main webpage
display dialog "Linkzor" default answer ""
set tunering to text returned of result
--get the javascript of the web page
tell application "Safari"
tell window 1
open location tunering
end tell
delay 5
set listen to "" as string
tell front document to set ¬
startText to {name, do JavaScript "window.document.documentElement.outerHTML"}
end tell
set listen to {}
set teksten to startText as string
--Gets all the relevant link variables of the web page
repeat with i from 1 to 500
set forekomst to text (nthOffset(teksten, "g_2_", i) + 4) thru (nthOffset(teksten, "g_2_", i) + 11) of teksten
if nthOffset(teksten, "g_2_", i) = 0 then exit repeat
set punkt to "http://www.resultat.dk/kamp/" & forekomst & "/#kampreferat"
set listen to listen & punkt
end repeat
set lengde to count of listen
--opens the links one by one.
repeat with x from 1 to lengde
tell application "Safari"
tell window 1
set URL of document 1 to item x of listen
end tell
end tell
--THIS IS THE PART I WANT TO INTERACT WITH FROM OUTSIDE THE SCRIPT
set question to display dialog "forsæt?" buttons {"Ja", "nej"} default button 1
set answer to button returned of question
if answer is "nej" then exit repeat
end repeat
on nthOffset(myText, subString, n)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to subString
-- There will be one more text item than there are instances of the substring in the string, so:
if (count myText's text items) > n then
-- There are at least n instances of the substring in the text.
-- The first character of the nth instance comes immediately after the last
-- character of the nth text item.
set o to (count text from text item 1 to text item n of myText) + 1
else
-- There isn't an nth instance of the substring in this text.
set o to 0
end if
set AppleScript's text item delimiters to astid
return o
end nthOffset
Now I would like to automate it a bit further so I didn´t have to leave the spreadsheet to click the dialog box when going to the next webpage. Is there a way to force the Applescript proceed without me actually clicking the dialog box? I have tried to save the script as an application ("hent2") and made another script to sent an "enter" keystroke to it while it is running (and then activate the new script through some kind of hotkey):
tell application "System Events"
tell application "hent2" to activate
key code 36
end tell
It does´t do anything.
Any suggestions on how to proceed? Either with the "sent keystroke to Applescript" or a more elegant solution?
EDIT:
I go it to work with the "Hitting a button" solution from Jerry below.
But I can´t get the handler solution to work. It clearly communicating with the main script because it tells me it can´t find the list "listen" of urls:
"error "hent3 got an error: The variable listen is not defined." number -2753 from "listen""
I define and assign values to "listen" in the part that is autorun when I save and run the script as a program. But when I call the subroutine from another Applescript it knows nothing about "listen"
Here is my attempt:
--Let the user input the the main webpage
display dialog "Linkzor" default answer ""
set tunering to text returned of result
--get the javascript of the web page
tell application "Safari"
tell window 1
open location tunering
end tell
delay 5
tell front document to set ¬
startText to {name, do JavaScript "window.document.documentElement.outerHTML"}
end tell
set listen to {} --HERE I DEFINE THE VARIABLE
set teksten to startText as string
--Gets all the relevant link variables of the web page
repeat with i from 1 to 500
set forekomst to text (nthOffset(teksten, "g_2_", i) + 4) thru (nthOffset(teksten, "g_2_", i) + 11) of teksten
if nthOffset(teksten, "g_2_", i) = 0 then exit repeat
set punkt to "http://www.resultat.dk/kamp/" & forekomst & "/#kampreferat"
set listen to listen & punkt -HERE I ASSIGN VALUES TO IT
end repeat
set lengde to count of listen
set i to 1
on ContinueOnward()
tell application "Safari"
tell window 1
set URL of document 1 to item i of listen --HERE IT ISN`T RECOGNISED
end tell
end tell
set i to i + 1
if i > lengde then
display notification "slut"
end if
end ContinueOnward
on nthOffset(myText, subString, n)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to subString
-- There will be one more text item than there are instances of the substring in the string, so:
if (count myText's text items) > n then
-- There are at least n instances of the substring in the text.
-- The first character of the nth instance comes immediately after the last
-- character of the nth text item.
set o to (count text from text item 1 to text item n of myText) + 1
else
-- There isn't an nth instance of the substring in this text.
set o to 0
end if
set AppleScript's text item delimiters to astid
return o
end nthOffset
Hitting a button
First, when scripting dialogs, if you want to press a button, use System Event’s click button:
tell application "System Events"
tell application process "hent2"
tell window 1
click button "Ja"
end tell
end tell
end tell
This works even if the process being hit is not front-most, so it might be all you need.
Activating via handler
You can also activate handlers in one AppleScript via a tell in another AppleScript. For example, save this as Test Handler, as Application and Stay Open After Run:
on ContinueOnward()
display notification "Continuing Onward"
end ContinueOnward
Then, in Script Editor, write the following script:
tell application "Test Handler"
ContinueOnward()
end tell
You should see a notification pop up saying “Continuing Onward” when you run that script. In your own script, of course, you could use that handler to move on to the next URL.
In order to maintain variables across different handlers, you will need to use either properties or globals or some combination. Properties maintain their values across runs of the application; globals only maintain their values across one run of the application. As I read your needs, you will need globals. In every handler that needs access to the global, use:
global variablename
And you’ll also need to initialize the variable outside of the handler.
For example, this application (Save As Application, Stay Open) will change its notification message, and then quit, after the fourth time:
set currentCount to 0
on ContinueOnward()
global currentCount
set currentCount to currentCount + 1
if currentCount < 5 then
display notification "Continuing with item " & currentCount
else
display notification "Quitting"
quit
end if
end ContinueOnward
In your case, you will want to make i, listen, and lengde be global variables. Put:
global i, listen, lengde
at the top of your handler, as I did with currentCount at the top of the example ContinueOnward handler.

Applescript get list of running apps?

Applescript newbie question again :) I am trying to create a small applescript that will allow me to select multiple items from a list of currently running applications and then quit those selected apps. Something like this works but rather than having to click on each dialog it would be much easier to chose from a list.
tell application "System Events"
repeat with p in every process
if background only of p is false then
display dialog "Would you like to quit " & name of p & "?" as string
end if
end repeat
end tell
Any and all help would be greatly appreciated!
Thanks!
Try this:
tell application "System Events"
set listOfProcesses to (name of every process where background only is false)
tell me to set selectedProcesses to choose from list listOfProcesses with multiple selections allowed
end tell
--The variable `selectedProcesses` will contain the list of selected items.
repeat with processName in selectedProcesses
do shell script "Killall " & quoted form of processName
end repeat
tell application "System Events"
set processList to get the name of every process whose background only is false
set processNameList to choose from list processList with prompt "Select process to quit" with multiple selections allowed
if the result is not false then
repeat with processName in processNameList
do shell script "Killall " & quoted form of processName
end repeat
end if
end tell
you can use this script which is much simpler
tell application "Finder"
get the name of every process whose visible is true
end tell
You can try this
tell application "System Events"
set AppName to name of every process whose background only is false
choose from list AppName OK button name "Ok" cancel button name "Cancel"
end
& (name of every process whose (name is "AppName") can be added to Michele Percich's and Parag Bafna's solutions to include specific menu bar applications by name.
tell application processName to quit can be used instead of do shell script "Killall " & quoted form of processName.
tell application "System Events"
set processList to ¬
(name of every process where background only is false) & ¬
(name of every process whose ¬
(name is "AppName") or ¬
(name is "AnotherAppName"))
tell me to set selectedProcesses to choose from list processList with prompt "Select process(es) to quit:" with multiple selections allowed
end tell
if the result is not false then
repeat with processName in selectedProcesses
tell application processName to quit
end repeat
end if
I wrote this following AppleScript code a few years back. I consider it to be a “Must Have” because I use it almost every single day.
This code will generate a list of Visible and Hidden application processes, allowing multiple items to be selected to kill their processes. The first items in the list will be visible application processes (not sorted alphabetically), then an empty list item (used to separate the visible from the hidden processes), and the remaining list items will be the hidden application processes (sorted alphabetically)
use framework "Foundation"
use scripting additions
property appsToKill : missing value
property NSArray : a reference to current application's NSArray
listAllAppProcesses()
activate
set killApp to (choose from list ¬
appsToKill with title "Choose The App To Kill" with prompt ¬
"Choose The App/Apps To Kill" & linefeed & linefeed ¬
& "The Empty List Item Separates The Visible From The Hidden Applications" OK button name ¬
"OK" cancel button name "CANCEL" with multiple selections allowed)
set pidList to {}
if killApp is not false then
tell application "System Events"
repeat with i from 1 to count of killApp
set thisItem to item i of killApp
tell application process thisItem
set thePID to unix id
set end of pidList to thePID
end tell
end repeat
end tell
else
return
end if
set text item delimiters to space
do shell script ({"kill -9", pidList} as text)
on listAllAppProcesses()
tell application "System Events"
set visibleAppsToKill to name of every application process ¬
where visible is true
set invisibleAppsToKill to name of every application process ¬
where visible is false
set aList to ((NSArray's arrayWithArray:invisibleAppsToKill)'s ¬
sortedArrayUsingSelector:"caseInsensitiveCompare:") as list
set appsToKill to visibleAppsToKill & "" & aList
end tell
end listAllAppProcesses
If you want it from Terminal, you can use a simple script like this quit.rb
The following example AppleScript code is pretty straight forward and will gracefully quit the selected application(s), providing the selected application(s) is (are) in a stable state:
tell application "System Events" to ¬
set appList to the name of ¬
every process whose visible is true
set quitAppList to ¬
choose from list appList ¬
with multiple selections allowed
repeat with thisApp in quitAppList
quit application thisApp
end repeat
When I present a list to choose from, I prefer to have it in alphabetical order and to that end I use a handler to first sort the list before presenting it:
on SortList(thisList)
set indexList to {}
set sortedList to {}
set theCount to (count thisList)
repeat theCount times
set lowItem to ""
repeat with i from 1 to theCount
if i is not in the indexList then
set thisItem to item i of thisList as text
if lowItem is "" then
set lowItem to thisItem
set lowItemIndex to i
else if thisItem comes before lowItem then
set lowItem to thisItem
set lowItemIndex to i
end if
end if
end repeat
set end of sortedList to lowItem
set end of indexList to lowItemIndex
end repeat
return the sortedList
end SortList
To use this with the first block of code presented I typically add handlers at the bottom of my code and then to use it, add the following example AppleScript code between the tell application "Finder" to ¬ and set quitAppList to ¬ statements:
set appList to SortList(appList)
Note: I acquired this particular handler somewhere on the Internet too many years ago to remember and unfortunately lost who to credit it to. My apologies to whomever you are.

Resources