Previously working script stopped working after upgrade to Sequoia

Hello! I posted this to the Apple Developers Forum and got no replies. This community seems more lively and I am hoping to get some help…

I don’t know much about AppleScripr. I found this script (see code below) on a RayCast site. It is used to dismiss notifications from Notification Center. It worked fine on Sonoma, but stopped working after an upgrade to Sonoma. Here is the code:

tell application "System Events"
	tell process "NotificationCenter"
		if not (window "Notification Center" exists) then return
		set alertGroups to groups of first UI element of first scroll area of first group of window "Notification Center"
		repeat with aGroup in alertGroups
			try
				perform (first action of aGroup whose name contains "Close" or name contains "Clear")
			on error errMsg
				log errMsg
			end try
		end repeat
		-- Show no message on success
		return ""
	end tell
end tell

Please help! This script was super useful.

Since AppleScript is running on macOS Sequoia, the title you set is incorrect and contains incorrect content.

In general, many members are very likely to ignore the title alone. The title is not good.

This AppleScript seems to be aimed at erasing notification messages, not operations on RayCast, and the description itself is wrong.

thanks for the feedback. edited the title. I was under the impression that “AppleScript” can also refer to a script.

getting scripts to run in Raycast is pretty trivial. Raycast is just a launcher. It’s not a Raycast issue. If I try to run the script from the CLI in Sequoia, it fails. The same exact code works under Sonoma.

The code is precisely used to erase notification messages. That is the intended function.

I did forget to include the returned error, which is:

Can’t get scroll area 1 of group 1 of window “Notification Center” of process “NotificationCenter”. Invalid index. (-1719)

Any help would be appreciated. Again I am not knowledgable in AppleScript in general so if I use the wrong terminology, apologies in advance.

I don’t have a solution, just some general info:

This script is based on UI scripting, which is unreliable by nature. I guess in Sequoia some UI elements in Notification Center have changed, which broke the script. Maybe other members with more experience in UI scripting will be able to help.

1 Like

AppleScript can also refer to the script, but adding extraneous information can drive readers away. On more focused forums such as Apple Developers, the moment they see “RayCast”, if they don’t happen to use that then they move on to the next topic. The same thing could apply if they are not running Sequoia.

In your case the problem is that, as mentioned, using GUI scripting is fragile. Any given application has its user interface grouped into various UI elements - the main window or menu, for example. These UI elements can contain other UI elements, which can also contain UI elements, and so on. There are no standards for how these UI elements are arranged - it is entirely up to the developer to use whatever design (if any) they want, and Apple loves to rearrange their user interfaces.

In order to script these UI elements, whatever layout is used will need to be figured out so that the proper reference can be used. One way to look at this hierarchy would be to compare it with entering a cave - the only way to know what is down the various dark passageways is to explore them (wearing the appropriate headgear so you don’t lose too much hair), mapping out where you are going and what is there. This can be done by using the Accessibility Inspector (included with Xcode) or a script.

For a script, you can start by using app-specific scripting terms to reveal the target UI element (if needed), and get its UI elements. From there, pick one of the elements and add it to the chain of tell statement to get its UI elements. Repeat with each item of interest, backing up and choosing different items if needed, to get to the desired element. Automator’s Watch Me Do action is also surprisingly effective at getting UI element references if you get stuck in a corner.

I am not running Sequoia, but a script to start with would be something like:

on run -- example
   tell application "System Events" to click menu bar item 1 of menu bar 1 of application process "Control Center"
   delay 0.5 -- allow the window to appear
   spelunk("NotificationCenter")
end run

to spelunk(appName)
   tell application appName to activate -- target application
   tell application "System Events"
      tell application process appName ¬
         to tell window 1 ¬
         to tell group 1 ¬
         --> add sub elements as desired - note that 'to' and line continuations (option-return) are being used to keep things a little shorter and indentation from making a mess
         
         set elements to its UI elements
         if elements is {} then
            return its properties -- current element info
         else -- more to explore
            return elements -- contained elements
         end if
      end tell
   end tell
end spelunk

The final object reference or whatever desired pieces can be copied from the editor’s events window.

2 Likes

I’ve got Sequoia running in a VM, and I’ve done a bunch of UI Scripting in the past.
Here’s what I’ve found so far:
The close button is the following UI element:
button 2 of first UI element of first scroll area of first group of first group of window "Notification Center"
Here’s the fun part: that button does NOT EXIST until you hover the pointer over the notification. Until you do, there is only “button 1” (I think that’s the “Show” button).
And, none of the UI elements have an “accessibility description” property, nor name, nor nothing. It’s as if Apple decided “Notifications are irrelevant to blind/low-vision people”.
BUT, after a bit of searching, I came across a post at StackExchange where someone wrote Apple Event code (in Javascript) that closes ALL notifications, and grabbed the relevant concept: search for the actions of the Notification Center window. The action is there even when the buton isn’t and you can call that directly.
So, here’s code that worked in my Sequoia VM:

-- create a test notification:
display notification "Test Notification To Find its Close Button." with title "Test Notify!" subtitle "Good luck! Seems like that button doesn't exist until you hover over this notification." sound name "Frog"
delay 1

tell application "System Events"
	tell process "NotificationCenter"
		tell window "Notification Center"
			set nestedNotifActions to actions of buttons of first UI element of first scroll area of first group of first group
			-- flatten the nest list of actions so we can loop over them. 
			set notifActions to {}
			repeat with i in nestedNotifActions
				set notifActions to notifActions & i
			end repeat
			
			repeat with oneAction in notifActions
				set oneAction to contents of oneAction
				if name of oneAction contains "Close" then perform oneAction
			end repeat
		end tell
		
		-- Show no message on success
		return ""
	end tell
end tell

Note that this will close all open notifications, since it flattens the nested list of actions. If you send up 2 notifications, one right after the other, the 2nd pushes the 1st into the non-visible list.
So, you might want to play around with this.
When I made the following change, a 2nd notification created right after the 1st stuck around:

			set notifActions to item 1 of actions of buttons of first UI element of first scroll area of first group of first group
			repeat with oneAction in notifActions
				set oneAction to contents of oneAction
				if name of oneAction contains "Close" then perform oneAction
			end repeat

The update from 15 to 15.2 broke my notification management scripts. Here’s the working one that dismisses the most recent notification:

tell application "System Events"
	tell process "NotificationCenter"
		
		set G1 to group 1 of scroll area 1 of group 1 of group 1 of window "Notification Center"
		set collapsedNotificationGroup to false
		
		-- If the top element is a single notification, click it
		if (get subrole of G1) is "AXNotificationCenterAlert" then
			log ("Top element is a single notification")
			click G1
			log ("Clicked the only notification")
			
		else -- The top element is a notification group
			
			-- If the top element is a collapsed notification group
			if (count (groups of G1)) is 1 then
				set collapsedNotificationGroup to true
				log ("Top element is collapsed group")
			else
				log ("Top element is expanded group")
			end if
			
			-- If the top element is a collapsed notification group, expand it and click the top notification
			if collapsedNotificationGroup then
				-- Expand group
				click group 1 of G1
				log ("Expanded notification group")
				-- For some reason this is necessary, otherwise the next call to notificationElements doesn't get a refreshed view
				delay 0.5
			end if
			
			-- Click the top notification
			set topgroup to (my getItemWithSmallestYValue(get groups of G1))
			click (topgroup)
			log ("Clicked top notification of group")
			
			
		end if
	end tell
end tell


on getYPosition(element)
	tell application "System Events"
		set xyPositions to (get position of element)
	end tell
	return item 2 of xyPositions
end getYPosition

on getItemWithSmallestYValue(elementList)
	set lowestY to 99999
	set lowestYElement to item 1 of elementList
	repeat with element in elementList
		set newY to getYPosition(element)
		set oldY to getYPosition(lowestYElement)
		if newY < oldY then
			set lowestYElement to element
		end if
	end repeat
	return lowestYElement
end getItemWithSmallestYValue

I also have a version that opens the most recent notification and one that closes the top notification group (expanded or collapsed), if that’s useful: scripting/applescript/notification-management/15.2 sequoia at main · seren/scripting · GitHub

In addition to the general bemoaning of UI Scripting, I have to ask why you need a script to do this in the first place.

It seems to me that the purpose of this script is to close the notification window. To be fair, it’s not clear how this script is intended to be invoked, but to me, if you don’t want to see the notifications, then System Settings → Notifications → Off and you’re done, no?

If the notifications come from your scripts, I suggest sticking with the StandardAdditions implementation or using the more customizable Unix program alerter | GitHub. At this point, managing the system notification with GUI scripting is akin to beating a dead horse.