Why is this AppleScript idle timer not triggering correctly? - applescript

I am trying to create a timer that increments at a certain time (in this case, whenever the time is xx:03:24) and sends a notification when it reaches a user-inputted value. Once it does so, it resets the increment and repeats until manually quit.
However, this code is not reliably triggering when it is supposed to. Does anyone have a guess as to why?
on quit
continue quit
end quit
tell application "Notifications Scripting"
set event handlers script path to (path to me)
end tell
global actions, newActions
using terms from application "Notifications Scripting"
on notification activated
tell application "Safari"
activate
set winlist to every window
repeat with win in winlist
set numtabs to 0
try
set tablist to every tab of win
set numtabs to length of tablist
end try
if numtabs is greater than 0 then
repeat with t in tablist
if "fallenlondon.storynexus.com" is in (URL of t as string) then
tell application "System Events"
tell window id (id of win as integer) of application "Safari" to set current tab to tab (index of t as integer)
end tell
end if
end repeat
end if
end repeat
end tell
end notification activated
end using terms from
display dialog "How many actions do you want to build up before being notified?" default answer "1"
set actions to (text returned of result) as number
set newActions to 0
on idle
if ((seconds of (current date)) = 24) and ((minutes of (current date)) mod 10 = 3) then
set newActions to (newActions + 1)
set delayTime to 540
else
set delayTime to 0.5
end if
if newActions ≥ actions then
if newActions = 1 then
tell application "Notifications Scripting" to display notification "Fallen London" message "You have " & newActions & " new action!" sound name "Glass"
else
tell application "Notifications Scripting" to display notification "Fallen London" message "You have " & newActions & " new actions!" sound name "Glass"
end if
set newActions to 0
end if
return delayTime
end idle

I would not expect your idle implementation to work reliably. It is not a precision timing mechanism; therefore the following assumption is unsound:
if ((seconds of (current date)) = 24) and ((minutes of (current date)) mod 10 = 3) then
You're attempting to hit a 1-second window in a 10-minute loop here, and if you miss that window it'll be another 10 minutes till you get to try again.
A reliable way to do it is to store a date representing the time upon which to next trigger, then periodically check if the current date is now after that date and trigger if it does:
to nextTriggerDate() -- returns next xx:x3:23 date
set d to current date
set {d's minutes, d's seconds} to {((d's minutes) div 10 * 10) + 3, 23}
if d < (current date) then set d to d + 10 * minutes
d
end nextTriggerDate
property _triggerDate : nextTriggerDate()
on idle
if (current date) > _triggerDate then
set _triggerDate to nextTriggerDate()
-- DO STUFF HERE...
end if
return 1
end idle
Alternatively, you could use NSTimer via the AppleScript-ObjC bridge, which is designed specifically for this type of task, or set up a launchd script to run an AppleScript on the specified times if you don't want to be tied to a stay-open applet.

I think you have a few things working against you with your code.
First thing to note is that only the code within the idle handler will be called on each idle event. In other words, if you're expecting the main body of your script to run with each idle process, it will not.
try this as an example...
on run
display dialog "hello!" giving up after 3
end run
on idle
display dialog "Idle test!" giving up after 3
return 5
on idle
When you save the code above as a stay open script, you'll get one dialog that says "hello" then you'll get the "Idle test!" message several times, until you quit the script. Remember to save it as "Stay Open"
To fix this, if you expect all the code outside of the idle handler to run, create a custom function from it that you call from your idle handler.
on yourCustomFunction()
-- All the code you want to run prior when the idle function is called
end yourCustomFunction
on idle
yourCustomFunction()
-- any other additional code you want to run here
return 5
end idle
Another thing I'd like to point out. You don't need to tell application "Notifications Scripting", you can just display notification as shown below.
on idle
if ((seconds of (current date)) = 24) and ((minutes of (current date)) mod 10 = 3) then
set newActions to (newActions + 1)
set delayTime to 540
else
set delayTime to 0.5
end if
if newActions ≥ actions then
if newActions = 1 then
display notification "You have " & newActions & " new action!" with title "Fallen London" sound name "Glass"
else
display notification "You have " & newActions & " new actions!" with title "Fallen London" sound name "Glass"
end if
set newActions to 0
end if
return delayTime
end idle
Lastly, I would recommend adding an on run handler rather than just writing your code in the script without it.

Related

Is there a reason my AppleScript is running so slow?

I have a script to run from a command line prompt. The command,
followed by some text, will set a reminder in the Reminder.app with a
due date of next friday. The script is taking about 100 seconds to run
which seem very high.
on nextWeekday(wd)
set today to current date
set twd to weekday of today
if twd is wd then
set d to 7
else
set d to (7 + (wd - twd)) mod 7
end if
return today + (d * days)
end nextWeekday
on run argv
set nextFriday to nextWeekday(Friday)
tell application "Reminders"
set newLable to argv as string
set myList to list "Do."
tell myList
set newReminder to make new reminder
set name of newReminder to newLable
#set remind me date of newReminder to theDay
set allday due date of newReminder to nextFriday
end tell
end tell
end run
The code in the .bash_profile is as follows:
function reme() {
cd
cd /Users/jamieauburn/Documents/projects/reminder
osax reme.scpt $1
}
Have I got something wrong in the script?
You have a lot of unnecessary things here. Your script can be written much more compactly, and it becomes about 4 times faster.
on run argv
tell (current date) to set nextFriday to it + (6 - (its weekday)) * days
tell application "Reminders" to tell list "Do."
make new reminder with properties {name:(argv as string), allday due date:nextFriday}
end tell
end run

Applescript - Check if Page Content of URLs List contain THIS_TEXT | Output all these URLs

Trying the following:
My script returns approx. 20 URLs as variable Single_URLs
Check if these URLs contain THIS_TEXT
Keep URL's containing THIS_TEXT
Delete the other URLs from the result
Pure Applescript or Shell.
My example script just checks IF the provided URL contains THIS_TEXT as i could not get any further by now.
--Open Pages
set site_url to "https://teespring.com/shop/CLASSIC-DODGE-CHARGER-MOP?aid=marketplace&tsmac=marketplace&tsmic=search#pid=212&cid=5819&sid=front"
tell application "Safari"
activate
open location site_url
end tell
-- wait until page loaded
property testingString : "CLASSIC DODGE CHARGER" --Text on website to look for
set pageLoaded to false
tell application "Safari"
repeat while pageLoaded is false
set readyState to (do JavaScript "document.readyState" in document 1)
set pageText to text of document 1
if (readyState is "complete") and (pageText contains testingString) then set pageLoaded to true
delay 0.2
end repeat
end tell
-- get number of links
set theLinks to {}
tell application "Safari" to set num_links to (do JavaScript "document.links.length" in document 1)
set linkCounter to num_links - 1
-- retrieve the links
repeat with i from 0 to linkCounter
tell application "Safari" to set end of theLinks to do JavaScript "document.links[" & i & "].href" in document 1
end repeat
theLinks
set nonExcludedURLs to {}
pageLoaded
This is a a charitable project to help Artists not being cheated. Every help is very welcome, Thanks.
Here's a script that would do that with AppleScript:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
on run
set URLFoundItems to {}
set SearchItemsList to {"CLASSIC DODGE CHARGER"}
set URLList to {"https://teespring.com/shop/CLASSIC-DODGE-CHARGER-MOP?aid=marketplace&tsmac=marketplace&tsmic=search#pid=212&cid=5819&sid=front"}
repeat with i from 1 to count of URLList
set URLv to item i of URLList
tell application "Safari"
try
tell window 1
set current tab to (make new tab with properties {URL:URLv})
end tell
on error
make new document with properties {URL:URLv}
end try
set readyState to (do JavaScript "document.readyState" in document 1)
set pageLoaded to false
repeat while pageLoaded is false
set readyState to (do JavaScript "document.readyState" in document 1)
set SearchIn to source of document 1
if (readyState is "complete") and SearchIn ≠ "" then
set pageLoaded to true
else
delay 0.2
end if
end repeat
repeat with z from 1 to count of SearchItemsList
set SearchString to item z of SearchItemsList
set x to offset of SearchString in SearchIn
if x > 0 then
set URLFoundItems to URLFoundItems & URLv & " (" & SearchString & ")" as string
end if
end repeat
tell window 1
close current tab
end tell
end tell
end repeat
return URLFoundItems
end run
I hope this helps.

Spotify giving wrong value of current track (AppleScript)

I am currently trying to make an application that gets the duration of the current song playing in Spotify. To do this I am using AppleScript.
Here is my code:
tell application "Spotify"
return the duration of the current track
end tell
The length of the song is 2.52 minutes, the code is telling me 172026 seconds.
By looking at the return value, it looks like it is returning the milliseconds and not the seconds of the track length. This I can fix easily by doing milliseconds*1000. The problem is 172026ms is not 2.52mins, instead it is 2.8671mins.
How am I meant to get the true value of the song duration?
Here is the Spotify AppleScript documentation:
track n : A Spotify track.
duration (integer, r/o) : The length of the track in seconds.
Just found that I needed to do a little bit of maths.
Here is the code:
tell application "Spotify"
set tM to round (((duration of current track) / 1000) / 60) rounding down
set tS to round (((duration of current track) / 1000) mod 60) rounding down
set myTime to ((tM as text) & "," & tS as text)
return myTime
end tell
Thanks to dronir for his code at https://github.com/dronir/SpotifyControl
You can also use the following code to read song & artist name (if you need it):
"inline": "if application \"Spotify\" is running then\rtell application \"Spotify\"\rreturn \" \" & (get artist of current track) & \" – \" & (get name of current track)\rend tell\rend if\rreturn \"\"\r"
(thanks to Toxblh from https://github.com/Toxblh/MTMR/blob/master/Resources/aadi_vs_anand.json)

Mavericks AppleScript count every desktop always returns 1

I've got a script taken from GitHub that is supposed to set the wallpaper of every desktop to a certain image depending on the time of day. (I have modified it from the original code to include more time ranges, issue shows in both versions)
The script attempts to count the number of desktops in order to change more than just the current desktop. It does this by first telling System Events the following
set theDesktops to a reference to every desktop
And then, in order to loop through every desktop, it does the following:
if ((count theDesktops) > 1) then
repeat with x from 2 to (count theDesktops)
--some code removed, see full code below
end repeat
end if
The issues is that count theDesktops always returns a 1, no matter how many desktops there are, as seen in the following screenshot.
http://ss.kobitate.com/2013-12-28_0922_2.png
What can be done to fix this? Here is the full code
(*
Script by Philip Hutchison, April 2013
http://pipwerks.com
MIT license http://pipwerks.mit-license.org/
This script assumes:
1. You have a folder named "Wallpapers" in your Pictures folder
2. You have a subfolder named "Time of Day" in Wallpapers
3. You have six subfolders inside "Time of Day", with names that match the variables below.
* If you decide to use different folder names, you must change the variables to match the new folder names
4. You have images inside each folder
For example:
/Users/YOUR_USER_NAME/Pictures/Wallpapers/Time of Day/Afternoon Early/image.jpg
GeekTool can execute this script for you at specified intervals. Use this line in the command field:
osascript ~/Pictures/Wallpapers/Time\ of\ Day/wallpaper.scpt
*)
-- BEGIN USER CONFIGURATION
-- supply folder names
set morningEarly to "Morning Early"
set morningLate to "Morning Late"
set afternoonEarly to "Afternoon Early"
set afternoonLate to "Afternoon Late"
set eveningEarly to "Evening Early"
set eveningLate to "Evening Late"
set nightEarly to "Night Early"
set nightLate to "Night Late"
-- for multiple monitor support.
-- set to true to display the same image on all desktops, false to show unique images on each desktop
set useSamePictureAcrossDisplays to true
-- END USER CONFIGURATION
-- get current hour
set h to hours of (current date)
-- set default periodOfDay
set periodOfDay to nightLate
-- change value of periodOfDay based on current time
if (h > 6 and h < 8) then
set periodOfDay to morningEarly
else if (h ≥ 8 and h < 10) then
set periodOfDay to morningLate
else if (h ≥ 10 and h < 12) then
set periodOfDay to afternoonEarly
else if (h ≥ 12 and h < 16) then
set periodOfDay to afternoonLate
else if (h ≥ 16 and h < 18) then
set periodOfDay to eveningEarly
else if (h ≥ 18 and h < 20) then
set periodOfDay to eveningLate
else if (h ≥ 20 and h < 22) then
set periodOfDay to nightEarly
else if (h ≥ 22) then
set periodOfDay to nightLate
end if
-- helper function ("handler") for getting random image
on getImage(folderName)
tell application "Finder"
return some file of folder ("Pictures:Wallpapers:Time of Day:" & folderName) of home as text
end tell
end getImage
tell application "Finder"
-- wrapped in a try block for error suppression
try
-- determine which picture to use for main display
set mainDisplayPicture to my getImage(periodOfDay)
-- set the picture for additional monitors, if applicable
tell application "System Events"
-- get a reference to all desktops
set theDesktops to a reference to every desktop
-- handle additional desktops
if ((count theDesktops) > 1) then
-- loop through all desktops (beginning with the second desktop)
repeat with x from 2 to (count theDesktops)
-- determine which image to use
if (useSamePictureAcrossDisplays is false) then
set secondaryDisplayPicture to my getImage(periodOfDay)
else
set secondaryDisplayPicture to my mainDisplayPicture
end if
-- apply image to desktop
set picture of item x of the theDesktops to secondaryDisplayPicture
end repeat
end if
end tell
-- set the primary monitor's picture
-- due to a Finder quirk, this has to be done AFTER setting the other displays
set desktop picture to mainDisplayPicture
end try
end tell
Edit: Fixed an unrelated mistake I found in the code
This seems to be fixed now. I remember testing this when #KobiTate posted it.
I am now on 10.9.1 but not sure when it was fixed
tell application "System Events"
set theDesktops to count of (a reference to every desktop)
set theDesktops2 to count every desktop
end tell
log "count of (a reference to every desktop) = " & theDesktops
log "count every desktop = " & theDesktops2
Returns:
(count of (a reference to every desktop) = 2)
(count every desktop = 2)

Save and restore applications and layout

I'm looking for ways to reduce the wasted time spent to open all the applications needed, position windows, open urls/files/change directories/etc. before actual coding starts.
In the perfect world there would be 2 buttons marked 'SAVE STATE' and 'RESTORE STATE' per 'project'. The kind of feature you find in some games.
I'm on a mac and just spent afew hours banging my head with 'Automator' (which for some reason has problems to even open firefox from the dock) and then applescript (which gives me the feeling i'm in for a long ride).
Searching on the net led me to this script:
http://snipt.net/Fotinakis/applescript-to-save-and-restore-window-positions/
#!/usr/bin/osascript
-- Usage:
-- $ osacompile -o windowPositions.compiled.scpt windowPositions.scpt
-- $ osascript windowPositions.compiled.scpt --save
-- $ osascript windowPositions.compiled.scpt --restore
-- Change this to be the list of windows you want to save/restore
property affectedProcesses : {"Chrome", "Adium", "Eclipse", "Terminal"}
property windowRecord : {}
on run argv
if (count of argv) is equal to 0 then
log "Please specify one of --save or --restore."
return
end if
tell application "System Events"
if (item 1 of argv is equal to "--save") then
set windowRecord to {}
repeat with i from 1 to count affectedProcesses
set end of windowRecord to {0, {}, {}}
end repeat
repeat with p from 1 to count affectedProcesses
set processName to (item p of affectedProcesses)
if exists process processName then
log "Process '" & processName & "' exists"
tell process processName
set numWindows to count windows
set item 1 of item p of windowRecord to numWindows
repeat with i from 1 to numWindows
set end of item 2 of item p of windowRecord to position of window i
set end of item 3 of item p of windowRecord to size of window i
end repeat
end tell
end if
end repeat
else
repeat with p from 1 to count affectedProcesses
set processName to (item p of affectedProcesses)
if exists process processName then
log "Process '" & processName & "' exists"
tell process processName
set numWindows to item 1 of item p of windowRecord
repeat with i from 1 to numWindows
set position of window i to (item i of item 2 of item p of windowRecord)
set size of window i to (item i of item 3 of item p of windowRecord)
end repeat
end tell
end if
end repeat
end if
end tell
end run
It does half the job (resize and position current windows) but falls apart on a multi-monitor multi-desktops setup. There is no contact info for the original author to ask for help or feedback.
Can anyone shed some light on saving and restoring applications and layout? It feels like such a common task that ought to have some helper utilities. The best I have is the 'sleep mode' but it seems I have to to do a full restart every other day and I have different applications and layout for different projects.
This is a feature of Lion (Mac OS X 10.7) ... I wouldn't kill yourself over a feature Apple has seen the need for and has implemented rather seamlessly...

Resources