Search code examples
applescriptosx-elcapitanidle-timer

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

Solution

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