But you have to be a bit careful. Suppose you schedule the abortModal method to be called after 30 seconds, and the user hits a button almost immediately. There might be another dialog showing when the 30 seconds are up, and it would be dismissed instead. So once the alert has been dismissed, you need to check whether it could have been due to a timeout (if giveUp > 0), and if so, whether the return code tells you it aborted (NSModalResponseAbort). If the former and not the latter, you need to cancel the still-scheduled call to abortModal, which you do using a special method of the NSObject class, cancelPreviousPerformRequestsWithTarget:::.
Then when you get the button number, if it is 0 you know the alert timed out, and in this case the handler sets the button name to "Gave Up".
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit" -- required for NSAlert
-- check we are running in foreground
if not (current application's NSThread's isMainThread()) as boolean then
display alert "This script must be run from the main thread." buttons {"Cancel"} as critical
error number -128
end if
-- for styleNum, 0 = warning, 1 = informational, 2 = critical; for givingUpAfter, 0 means never
on displayAlert:mainText message:theExplanaton asStyle:styleNum buttons:buttonsList suppression:showSuppression givingUpAfter:giveUp
set buttonsList to reverse of buttonsList -- because they get added in reverse order cf AS
-- create an alert
set theAlert to current application's NSAlert's alloc()'s init()
-- set up alert
tell theAlert
its setAlertStyle:styleNum
its setMessageText:mainText
its setInformativeText:theExplanaton
repeat with anEntry in buttonsList
(its addButtonWithTitle:anEntry)
end repeat
its setShowsSuppressionButton:showSuppression
end tell
-- if giveUp value > 0, tell the app to abort any modal event loop after that time, and thus close the panel
if giveUp > 0 then current application's NSApp's performSelector:"abortModal" withObject:(missing value) afterDelay:giveUp inModes:{current application's NSModalPanelRunLoopMode}
-- show alert in modal loop
set returnCode to theAlert's runModal()
-- if a giveUp time was specified and the alert didn't timeout, cancel the pending abort request
if giveUp > 0 and returnCode is not current application's NSModalResponseAbort then current application's NSObject's cancelPreviousPerformRequestsWithTarget:(current application's NSApp) selector:"abortModal" object:(missing value)
-- get values after alert is closed
set suppressedState to theAlert's suppressionButton()'s state() as boolean
set buttonNumber to returnCode mod 1000 + 1 -- where 1 = right-most button
if buttonNumber = 0 then
set buttonName to "Gave Up"
else
set buttonName to item buttonNumber of buttonsList
end if
return {buttonName, suppressedState}
end displayAlert:message:asStyle:buttons:suppression:givingUpAfter:
set {buttonName, suppressedState} to (my displayAlert:"Decision time" message:("Yae or nay?") asStyle:2 buttons:{"Cancel", "Maybe", "OK"} suppression:false givingUpAfter:3.0)
return buttonName