Saklad5 Saklad5 - 1 year ago 73
AppleScript Question

Why is this AppleScript idle timer not triggering correctly?

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"
set winlist to every window
repeat with win in winlist
set numtabs to 0
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 "" 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
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"
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

foo foo
Answer Source

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
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.