Last active
November 3, 2025 04:19
-
Star
(129)
You must be signed in to star a gist -
Fork
(12)
You must be signed in to fork a gist
-
-
Save lancethomps/a5ac103f334b171f70ce2ff983220b4f to your computer and use it in GitHub Desktop.
Revisions
-
lancethomps revised this gist
Oct 24, 2025 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -329,7 +329,7 @@ function run(input, parameters) { let attempts = 1; while (maybeMore && new Date().getTime() - startTime <= 1000 * 30) { try { let closeResult = closeNextGroup(getNotificationCenterGroups(true), closedCount); maybeMore = closeResult[0]; if (maybeMore) { closedCount = closedCount + closeResult[1]; -
lancethomps revised this gist
Oct 10, 2025 . 1 changed file with 8 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -49,7 +49,7 @@ function run(input, parameters) { const V10_OR_LESS = !V11_OR_GREATER; const V12 = SYS_VERSION[0] === 12; const V15_OR_GREATER = SYS_VERSION[0] >= 15; const V15_2_OR_GREATER = SYS_VERSION[0] >= 16 || (SYS_VERSION[0] >= 15 && SYS_VERSION[1] >= 2); const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? 'AXStaticText' : 'AXImage'; const NOTIFICATION_SUB_ROLES = ['AXNotificationCenterAlert', 'AXNotificationCenterAlertStack']; const hasAppNames = notNullOrEmpty(appNames); @@ -84,6 +84,8 @@ function run(input, parameters) { } }; logVerbose(`SYS_VERSION: ${SYS_VERSION}`); const getLogLines = () => { return logs.join('\n'); }; @@ -110,14 +112,18 @@ function run(input, parameters) { return []; } if (V10_OR_LESS) { logVerbose('getNotificationCenterGroups: V10_OR_LESS'); return notificationCenter.windows(); } if (V12) { logVerbose('getNotificationCenterGroups: V12'); return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } if (V15_2_OR_GREATER) { logVerbose('getNotificationCenterGroups: V15_2_OR_GREATER'); return findNotificationCenterAlerts([], notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements()); } logVerbose('getNotificationCenterGroups: no version specific logic'); return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements(); } catch (err) { logError('Could not get NotificationCenter groups'); @@ -132,6 +138,7 @@ function run(input, parameters) { }; const findNotificationCenterAlerts = (alerts, elements) => { logVerbose(`Finding alerts for ${elements.length} elements...`); for (let elem of elements) { let subrole = elem.subrole(); if (NOTIFICATION_SUB_ROLES.indexOf(subrole) > -1) { -
lancethomps revised this gist
Jan 16, 2025 . No changes.There are no files selected for viewing
-
lancethomps revised this gist
Dec 13, 2024 . 1 changed file with 13 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -116,7 +116,7 @@ function run(input, parameters) { return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } if (V15_2_OR_GREATER) { return findNotificationCenterAlerts([], notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements()); } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements(); } catch (err) { @@ -131,6 +131,18 @@ function run(input, parameters) { } }; const findNotificationCenterAlerts = (alerts, elements) => { for (let elem of elements) { let subrole = elem.subrole(); if (NOTIFICATION_SUB_ROLES.indexOf(subrole) > -1) { alerts.push(elem); } else if (elem.uiElements.length > 0) { findNotificationCenterAlerts(alerts, elem.uiElements()); } } return alerts; }; const isClearButton = (description, name) => { return description === 'button' && name === CLEAR_ALL_ACTION_TOP; }; -
lancethomps revised this gist
Dec 12, 2024 . 1 changed file with 9 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -44,11 +44,14 @@ function run(input, parameters) { return systemVersionGreaterThanOrEqualTo(11); }; const SYS_VERSION = systemVersion(); const V11_OR_GREATER = isBigSurOrGreater(); const V10_OR_LESS = !V11_OR_GREATER; const V12 = SYS_VERSION[0] === 12; const V15_OR_GREATER = SYS_VERSION[0] >= 15; const V15_2_OR_GREATER = SYS_VERSION[0] >= 15 && SYS_VERSION[1] >= 2; const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? 'AXStaticText' : 'AXImage'; const NOTIFICATION_SUB_ROLES = ['AXNotificationCenterAlert', 'AXNotificationCenterAlertStack']; const hasAppNames = notNullOrEmpty(appNames); const hasSkipAppNames = notNullOrEmpty(skipAppNames); const hasAppNameFilters = hasAppNames || hasSkipAppNames; @@ -112,6 +115,9 @@ function run(input, parameters) { if (V12) { return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } if (V15_2_OR_GREATER) { return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements[0].uiElements(); } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements(); } catch (err) { logError('Could not get NotificationCenter groups'); @@ -184,7 +190,7 @@ function run(input, parameters) { } if (V15_OR_GREATER) { let subrole = group.subrole(); if (NOTIFICATION_SUB_ROLES.indexOf(subrole) === -1) { return false; } } else if (V11_OR_GREATER && description !== 'group') { -
lancethomps revised this gist
Dec 11, 2024 . 1 changed file with 70 additions and 41 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,14 +1,13 @@ function run(input, parameters) { const appNames = []; const skipAppNames = []; const verbose = true; const scriptName = 'close_notifications_applescript'; const CLEAR_ALL_ACTION = 'Clear All'; const CLEAR_ALL_ACTION_TOP = 'Clear'; const CLOSE_ACTION = 'Close'; const notNull = (val) => { return val !== null && val !== undefined; @@ -31,7 +30,10 @@ function run(input, parameters) { }; const systemVersion = () => { return Application('Finder') .version() .split('.') .map((val) => parseInt(val)); }; const systemVersionGreaterThanOrEqualTo = (vers) => { @@ -43,24 +45,26 @@ function run(input, parameters) { }; const V11_OR_GREATER = isBigSurOrGreater(); const V10_OR_LESS = !V11_OR_GREATER; const V12 = systemVersion()[0] === 12; const V15_OR_GREATER = systemVersionGreaterThanOrEqualTo(15); const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? 'AXStaticText' : 'AXImage'; const hasAppNames = notNullOrEmpty(appNames); const hasSkipAppNames = notNullOrEmpty(skipAppNames); const hasAppNameFilters = hasAppNames || hasSkipAppNames; const appNameForLog = hasAppNames ? ` [${appNames.join(',')}]` : ''; const logs = []; const log = (message, ...optionalParams) => { let message_with_prefix = `${new Date().toISOString().replace('Z', '').replace('T', ' ')} [${scriptName}]${appNameForLog} ${message}`; console.log(message_with_prefix, optionalParams); logs.push(message_with_prefix); }; const logError = (message, ...optionalParams) => { if (isError(message)) { let err = message; message = `${err}${err.stack ? ' ' + err.stack : ''}`; } log(`ERROR ${message}`, optionalParams); }; @@ -78,20 +82,20 @@ function run(input, parameters) { }; const getLogLines = () => { return logs.join('\n'); }; const getSystemEvents = () => { let systemEvents = Application('System Events'); systemEvents.includeStandardAdditions = true; return systemEvents; }; const getNotificationCenter = () => { try { return getSystemEvents().processes.byName('NotificationCenter'); } catch (err) { logError('Could not get NotificationCenter'); throw err; } }; @@ -102,18 +106,18 @@ function run(input, parameters) { if (notificationCenter.windows.length <= 0) { return []; } if (V10_OR_LESS) { return notificationCenter.windows(); } if (V12) { return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements(); } catch (err) { logError('Could not get NotificationCenter groups'); if (retryOnError) { logError(err); log('Retrying getNotificationCenterGroups...'); return getNotificationCenterGroups(false); } else { throw err; @@ -122,7 +126,7 @@ function run(input, parameters) { }; const isClearButton = (description, name) => { return description === 'button' && name === CLEAR_ALL_ACTION_TOP; }; const matchesAnyAppNames = (value, checkValues) => { @@ -138,30 +142,59 @@ function run(input, parameters) { return false; }; const matchesAppName = (value) => { if (hasAppNames) { return matchesAnyAppNames(value, appNames); } return !matchesAnyAppNames(value, skipAppNames); }; const getAppName = (group) => { if (V15_OR_GREATER) { for (let action of group.actions()) { if (action.description() === 'Remind Me Tomorrow') { return 'reminders'; } } return ''; } if (V10_OR_LESS) { if (group.role() !== APP_NAME_MATCHER_ROLE) { return ''; } return group.description(); } let checkElem = group.uiElements[0]; if (checkElem.value().toLowerCase() === 'time sensitive') { checkElem = group.uiElements[1]; } if (checkElem.role() !== APP_NAME_MATCHER_ROLE) { return ''; } return checkElem.value(); }; const notificationGroupMatches = (group) => { try { let description = group.description(); if (V11_OR_GREATER && isClearButton(description, group.name())) { return true; } if (V15_OR_GREATER) { let subrole = group.subrole(); if (subrole !== 'AXNotificationCenterAlertStack' && subrole !== 'AXNotificationCenterAlert') { return false; } } else if (V11_OR_GREATER && description !== 'group') { return false; } if (V10_OR_LESS) { let matchedAppName = !hasAppNameFilters; if (!matchedAppName) { for (let elem of group.uiElements()) { if (matchesAppName(getAppName(elem))) { matchedAppName = true; break; } @@ -175,11 +208,7 @@ function run(input, parameters) { if (!hasAppNameFilters) { return true; } return matchesAppName(getAppName(group)); } catch (err) { logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`); logErrorVerbose(err); @@ -190,22 +219,22 @@ function run(input, parameters) { const findCloseActionV10 = (group, closedCount) => { try { for (let elem of group.uiElements()) { if (elem.role() === 'AXButton' && elem.title() === CLOSE_ACTION) { return elem.actions['AXPress']; } } } catch (err) { logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`); logErrorVerbose(err); return null; } log('No close action found for notification'); return null; }; const findCloseAction = (group, closedCount) => { try { if (V10_OR_LESS) { return findCloseActionV10(group, closedCount); } let checkForPress = isClearButton(group.description(), group.name()); @@ -218,7 +247,7 @@ function run(input, parameters) { break; } else if (description === CLOSE_ACTION) { closeAction = action; } else if (checkForPress && description === 'press') { clearAllAction = action; break; } @@ -233,7 +262,7 @@ function run(input, parameters) { logErrorVerbose(err); return null; } log('No close action found for notification'); return null; }; @@ -257,23 +286,23 @@ function run(input, parameters) { } return false; } catch (err) { logError('Could not run closeNextGroup'); throw err; } }; try { let groupsCount = getNotificationCenterGroups(true).filter((group) => notificationGroupMatches(group)).length; if (groupsCount > 0) { logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${groupsCount > 1 ? 's' : ''}`); let startTime = new Date().getTime(); let closedCount = 0; let maybeMore = true; let maxAttempts = 2; let attempts = 1; while (maybeMore && new Date().getTime() - startTime <= 1000 * 30) { try { let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount); maybeMore = closeResult[0]; @@ -282,7 +311,7 @@ function run(input, parameters) { } } catch (innerErr) { if (maybeMore && closedCount === 0 && attempts < maxAttempts) { log(`Caught an error before anything closed, trying ${maxAttempts - attempts} more time(s).`); attempts++; } else { throw innerErr; -
lancethomps revised this gist
Jul 18, 2023 . 1 changed file with 5 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -175,8 +175,11 @@ function run(input, parameters) { if (!hasAppNameFilters) { return true; } let checkElem = group.uiElements[0]; if (checkElem.value().toLowerCase() === "time sensitive") { checkElem = group.uiElements[1]; } return matchesAppName(checkElem.role(), checkElem.value()); } catch (err) { logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`); logErrorVerbose(err); -
lancethomps revised this gist
Jan 7, 2023 . No changes.There are no files selected for viewing
-
lancethomps revised this gist
Jan 6, 2023 . 1 changed file with 5 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -43,6 +43,7 @@ function run(input, parameters) { }; const V11_OR_GREATER = isBigSurOrGreater(); const V12 = systemVersion()[0] === 12; const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage"; const hasAppNames = notNullOrEmpty(appNames); const hasSkipAppNames = notNullOrEmpty(skipAppNames); @@ -104,7 +105,10 @@ function run(input, parameters) { if (!V11_OR_GREATER) { return notificationCenter.windows(); } if (V12) { return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements(); } catch (err) { logError("Could not get NotificationCenter groups"); if (retryOnError) { -
lancethomps revised this gist
Mar 2, 2022 . 1 changed file with 58 additions and 27 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,7 @@ function run(input, parameters) { const appNames = []; const skipAppNames = []; const verbose = true; const scriptName = "close_notifications_applescript"; @@ -11,69 +12,79 @@ function run(input, parameters) { const notNull = (val) => { return val !== null && val !== undefined; }; const isNull = (val) => { return !notNull(val); }; const notNullOrEmpty = (val) => { return notNull(val) && val.length > 0; }; const isNullOrEmpty = (val) => { return !notNullOrEmpty(val); }; const isError = (maybeErr) => { return notNull(maybeErr) && (maybeErr instanceof Error || maybeErr.message); }; const systemVersion = () => { return Application("Finder").version().split(".").map(val => parseInt(val)); }; const systemVersionGreaterThanOrEqualTo = (vers) => { return systemVersion()[0] >= vers; }; const isBigSurOrGreater = () => { return systemVersionGreaterThanOrEqualTo(11); }; const V11_OR_GREATER = isBigSurOrGreater(); const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage"; const hasAppNames = notNullOrEmpty(appNames); const hasSkipAppNames = notNullOrEmpty(skipAppNames); const hasAppNameFilters = hasAppNames || hasSkipAppNames; const appNameForLog = hasAppNames ? ` [${appNames.join(",")}]` : ""; const logs = []; const log = (message, ...optionalParams) => { let message_with_prefix = `${new Date().toISOString().replace("Z", "").replace("T", " ")} [${scriptName}]${appNameForLog} ${message}`; console.log(message_with_prefix, optionalParams); logs.push(message_with_prefix); }; const logError = (message, ...optionalParams) => { if (isError(message)) { let err = message; message = `${err}${err.stack ? (" " + err.stack) : ""}`; } log(`ERROR ${message}`, optionalParams); }; const logErrorVerbose = (message, ...optionalParams) => { if (verbose) { logError(message, optionalParams); } }; const logVerbose = (message) => { if (verbose) { log(message); } }; const getLogLines = () => { return logs.join("\n"); }; const getSystemEvents = () => { let systemEvents = Application("System Events"); systemEvents.includeStandardAdditions = true; return systemEvents; }; const getNotificationCenter = () => { try { @@ -82,7 +93,7 @@ function run(input, parameters) { logError("Could not get NotificationCenter"); throw err; } }; const getNotificationCenterGroups = (retryOnError = false) => { try { @@ -98,20 +109,40 @@ function run(input, parameters) { logError("Could not get NotificationCenter groups"); if (retryOnError) { logError(err); log("Retrying getNotificationCenterGroups..."); return getNotificationCenterGroups(false); } else { throw err; } } }; const isClearButton = (description, name) => { return description === "button" && name === CLEAR_ALL_ACTION_TOP; }; const matchesAnyAppNames = (value, checkValues) => { if (isNullOrEmpty(checkValues)) { return false; } let lowerAppName = value.toLowerCase(); for (let checkValue of checkValues) { if (lowerAppName === checkValue.toLowerCase()) { return true; } } return false; }; const matchesAppName = (role, value) => { if (role !== APP_NAME_MATCHER_ROLE) { return false; } if (hasAppNames) { return matchesAnyAppNames(value, appNames); } return !matchesAnyAppNames(value, skipAppNames); }; const notificationGroupMatches = (group) => { try { @@ -123,7 +154,7 @@ function run(input, parameters) { return false; } if (!V11_OR_GREATER) { let matchedAppName = !hasAppNameFilters; if (!matchedAppName) { for (let elem of group.uiElements()) { if (matchesAppName(elem.role(), elem.description())) { @@ -137,7 +168,7 @@ function run(input, parameters) { } return false; } if (!hasAppNameFilters) { return true; } let firstElem = group.uiElements[0]; @@ -147,7 +178,7 @@ function run(input, parameters) { logErrorVerbose(err); } return false; }; const findCloseActionV10 = (group, closedCount) => { try { @@ -163,7 +194,7 @@ function run(input, parameters) { } log("No close action found for notification"); return null; }; const findCloseAction = (group, closedCount) => { try { @@ -197,7 +228,7 @@ function run(input, parameters) { } log("No close action found for notification"); return null; }; const closeNextGroup = (groups, closedCount) => { try { @@ -222,7 +253,7 @@ function run(input, parameters) { logError("Could not run closeNextGroup"); throw err; } }; try { let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length; -
lancethomps revised this gist
Apr 6, 2021 . 1 changed file with 52 additions and 30 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,6 +17,10 @@ function run(input, parameters) { return !notNull(val); } const isError = (maybeErr) => { return notNull(maybeErr) && (maybeErr instanceof Error || maybeErr.message); } const systemVersion = () => { return Application("Finder").version().split(".").map(val => parseInt(val)); } @@ -42,12 +46,16 @@ function run(input, parameters) { } const logError = (message, ...optionalParams) => { if (isError(message)) { let err = message; message = `${err}${err.stack ? (' ' + err.stack) : ''}`; } log(`ERROR ${message}`, optionalParams); } const logErrorVerbose = (message, ...optionalParams) => { if (verbose) { logError(message, optionalParams); } } @@ -68,18 +76,33 @@ function run(input, parameters) { } const getNotificationCenter = () => { try { return getSystemEvents().processes.byName("NotificationCenter"); } catch (err) { logError("Could not get NotificationCenter"); throw err; } } const getNotificationCenterGroups = (retryOnError = false) => { try { let notificationCenter = getNotificationCenter(); if (notificationCenter.windows.length <= 0) { return []; } if (!V11_OR_GREATER) { return notificationCenter.windows(); } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } catch (err) { logError("Could not get NotificationCenter groups"); if (retryOnError) { logError(err); return getNotificationCenterGroups(false); } else { throw err; } } } const isClearButton = (description, name) => { @@ -177,33 +200,32 @@ function run(input, parameters) { } const closeNextGroup = (groups, closedCount) => { try { for (let group of groups) { if (notificationGroupMatches(group)) { let closeAction = findCloseAction(group, closedCount); if (notNull(closeAction)) { try { closeAction.perform(); return [true, 1]; } catch (err) { logErrorVerbose(`(group_${closedCount}) Caught error while performing close action, window is probably closed: ${err}`); logErrorVerbose(err); } } return [true, 0]; } } return false; } catch (err) { logError("Could not run closeNextGroup"); throw err; } } try { let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length; if (groupsCount > 0) { logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`); -
lancethomps revised this gist
Mar 5, 2021 . 1 changed file with 15 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -211,11 +211,22 @@ function run(input, parameters) { let startTime = new Date().getTime(); let closedCount = 0; let maybeMore = true; let maxAttempts = 2; let attempts = 1; while (maybeMore && ((new Date().getTime() - startTime) <= (1000 * 30))) { try { let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount); maybeMore = closeResult[0]; if (maybeMore) { closedCount = closedCount + closeResult[1]; } } catch (innerErr) { if (maybeMore && closedCount === 0 && attempts < maxAttempts) { log(`Caught an error before anything closed, trying ${maxAttempts - attempts} more time(s).`) attempts++; } else { throw innerErr; } } } } else { -
lancethomps revised this gist
Feb 9, 2021 . 1 changed file with 13 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -99,17 +99,24 @@ function run(input, parameters) { if (V11_OR_GREATER && description !== "group") { return false; } if (!V11_OR_GREATER) { let matchedAppName = !hasAppName; if (!matchedAppName) { for (let elem of group.uiElements()) { if (matchesAppName(elem.role(), elem.description())) { matchedAppName = true; break; } } } if (matchedAppName) { return notNull(findCloseActionV10(group, -1)); } return false; } if (!hasAppName) { return true; } let firstElem = group.uiElements[0]; return matchesAppName(firstElem.role(), firstElem.value()); } catch (err) { -
lancethomps revised this gist
Feb 9, 2021 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,7 +18,7 @@ function run(input, parameters) { } const systemVersion = () => { return Application("Finder").version().split(".").map(val => parseInt(val)); } const systemVersionGreaterThanOrEqualTo = (vers) => { -
lancethomps revised this gist
Feb 9, 2021 . 1 changed file with 100 additions and 29 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,6 +3,12 @@ function run(input, parameters) { const appName = ""; const verbose = true; const scriptName = "close_notifications_applescript"; const CLEAR_ALL_ACTION = "Clear All"; const CLEAR_ALL_ACTION_TOP = "Clear"; const CLOSE_ACTION = "Close"; const notNull = (val) => { return val !== null && val !== undefined; } @@ -11,12 +17,26 @@ function run(input, parameters) { return !notNull(val); } const systemVersion = () => { return Application("Finder").version().split(".").map(parseInt); } const systemVersionGreaterThanOrEqualTo = (vers) => { return systemVersion()[0] >= vers; } const isBigSurOrGreater = () => { return systemVersionGreaterThanOrEqualTo(11); } const V11_OR_GREATER = isBigSurOrGreater(); const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage"; const hasAppName = notNull(appName) && appName !== ""; const appNameForLog = hasAppName ? ` [${appName}]` : ""; const logs = []; const log = (message, ...optionalParams) => { let message_with_prefix = `${new Date().toISOString().replace("Z", "").replace("T", " ")} [${scriptName}]${appNameForLog} ${message}`; console.log(message_with_prefix, optionalParams); logs.push(message_with_prefix); } @@ -56,40 +76,83 @@ function run(input, parameters) { if (notificationCenter.windows.length <= 0) { return []; } if (!V11_OR_GREATER) { return notificationCenter.windows(); } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } const isClearButton = (description, name) => { return description === "button" && name === CLEAR_ALL_ACTION_TOP; } const matchesAppName = (role, value) => { return role === APP_NAME_MATCHER_ROLE && value.toLowerCase() === appName.toLowerCase(); } const notificationGroupMatches = (group) => { try { let description = group.description(); if (V11_OR_GREATER && isClearButton(description, group.name())) { return true; } if (V11_OR_GREATER && description !== "group") { return false; } if (!hasAppName) { return true; } if (!V11_OR_GREATER) { for (let elem of group.uiElements()) { if (matchesAppName(elem.role(), elem.description())) { return true; } } return false; } let firstElem = group.uiElements[0]; return matchesAppName(firstElem.role(), firstElem.value()); } catch (err) { logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`); logErrorVerbose(err); } return false; } const findCloseActionV10 = (group, closedCount) => { try { for (let elem of group.uiElements()) { if (elem.role() === "AXButton" && elem.title() === CLOSE_ACTION) { return elem.actions["AXPress"]; } } } catch (err) { logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`); logErrorVerbose(err); return null; } log("No close action found for notification"); return null; } const findCloseAction = (group, closedCount) => { try { if (!V11_OR_GREATER) { return findCloseActionV10(group, closedCount); } let checkForPress = isClearButton(group.description(), group.name()); let clearAllAction; let closeAction; for (let action of group.actions()) { let description = action.description(); if (description === CLEAR_ALL_ACTION) { clearAllAction = action; break; } else if (description === CLOSE_ACTION) { closeAction = action; } else if (checkForPress && description === "press") { clearAllAction = action; break; } } if (notNull(clearAllAction)) { @@ -126,28 +189,36 @@ function run(input, parameters) { return false; } try { let notificationCenter = getNotificationCenter(); if (notificationCenter.windows.length <= 0) { logError("No notifications found."); return getLogLines(); } let groupsCount = getNotificationCenterGroups().filter(group => notificationGroupMatches(group)).length; if (groupsCount > 0) { logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`); let startTime = new Date().getTime(); let closedCount = 0; let maybeMore = true; while (maybeMore && ((new Date().getTime() - startTime) <= (1000 * 30))) { let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount); maybeMore = closeResult[0]; if (maybeMore) { closedCount = closedCount + closeResult[1]; } } } else { throw Error(`No${appNameForLog} notifications found...`); } } catch (err) { logError(err); logError(err.message); getLogLines(); throw err; } return getLogLines(); -
lancethomps revised this gist
Jan 8, 2021 . 1 changed file with 71 additions and 44 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,20 +1,34 @@ function run(input, parameters) { const appName = ""; const verbose = true; const notNull = (val) => { return val !== null && val !== undefined; } const isNull = (val) => { return !notNull(val); } const hasAppName = notNull(appName) && appName !== ""; const appNameForLog = hasAppName ? ` [${appName}]` : ""; const logs = []; const log = (message, ...optionalParams) => { let message_with_prefix = `${new Date().toISOString().replaceAll("Z", "").replaceAll("T", " ")} [close_notifications]${appNameForLog} ${message}`; console.log(message_with_prefix, optionalParams); logs.push(message_with_prefix); } const logError = (message, ...optionalParams) => { log(`ERROR ${message}`, optionalParams); } const logErrorVerbose = (message, ...optionalParams) => { if (verbose) { log(`ERROR ${message}`, optionalParams); } } const logVerbose = (message) => { @@ -23,6 +37,49 @@ function run(input, parameters) { } } const getLogLines = () => { return logs.join("\n"); } const getSystemEvents = () => { let systemEvents = Application("System Events"); systemEvents.includeStandardAdditions = true; return systemEvents; } const getNotificationCenter = () => { return getSystemEvents().processes.byName("NotificationCenter"); } const getNotificationCenterGroups = () => { let notificationCenter = getNotificationCenter(); if (notificationCenter.windows.length <= 0) { return []; } return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements(); } const notificationGroupMatches = (group) => { if (!hasAppName) { return true; } try { for (let elem of group.uiElements()) { if (hasAppName && elem.role() === "AXStaticText" && elem.value().toLowerCase() === appName.toLowerCase()) { return true; } } } catch (err) { logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`); logErrorVerbose(err); } return false; } const CLEAR_ALL_ACTION = "Clear All"; const CLOSE_ACTION = "Close"; const findCloseAction = (group, closedCount) => { try { let clearAllAction; @@ -41,48 +98,26 @@ function run(input, parameters) { return closeAction; } } catch (err) { logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`); logErrorVerbose(err); return null; } log("No close action found for notification"); return null; } const closeNextGroup = (groups, closedCount) => { for (let group of groups) { if (notificationGroupMatches(group)) { let closeAction = findCloseAction(group, closedCount); if (notNull(closeAction)) { try { closeAction.perform(); return [true, 1]; } catch (err) { logErrorVerbose(`(group_${closedCount}) Caught error while performing close action, window is probably closed: ${err}`); logErrorVerbose(err); } } return [true, 0]; @@ -91,18 +126,10 @@ function run(input, parameters) { return false; } let notificationCenter = getNotificationCenter(); if (notificationCenter.windows.length <= 0) { logError("No notifications found."); return getLogLines(); } let groupsCount = getNotificationCenterGroups().filter(group => notificationGroupMatches(group)).length; @@ -123,5 +150,5 @@ function run(input, parameters) { throw Error(`No${appNameForLog} notifications found...`); } return getLogLines(); } -
lancethomps created this gist
Jan 7, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,127 @@ function run(input, parameters) { const notNull = (val) => { return val !== null && val !== undefined; } const appName = ""; const verbose = true; const CLEAR_ALL_ACTION = "Clear All"; const CLOSE_ACTION = "Close"; const hasAppName = notNull(appName) && appName !== ""; const appNameForLog = hasAppName ? (" " + appName) : ""; const log = (message, ...optionalParams) => { console.log("[close_notifications] " + message, optionalParams); } const logVerbose = (message) => { if (verbose) { log(message); } } const findCloseAction = (group, closedCount) => { try { let clearAllAction; let closeAction; for (let action of group.actions()) { if (action.description() === CLEAR_ALL_ACTION) { clearAllAction = action; break; } else if (action.description() === CLOSE_ACTION) { closeAction = action; } } if (notNull(clearAllAction)) { return clearAllAction; } else if (notNull(closeAction)) { return closeAction; } } catch (err) { logVerbose(`${closedCount}: Caught error while searching for close action, window is probably closed.`); logVerbose(err); return null; } log("No close action found for notification"); return null; } const notificationGroupMatches = (group) => { if (!hasAppName) { return true; } logVerbose(`Checking UI elements of group...`); try { for (let elem of group.uiElements()) { if (hasAppName && elem.role() === "AXStaticText" && elem.value().toLowerCase() === appName.toLowerCase()) { return true; } } } catch (err) { logVerbose(`Caught error while checking window, window is probably closed.`); logVerbose(err); } return false; } const closeNextGroup = (groups, closedCount) => { for (let group of groups) { if (notificationGroupMatches(group)) { logVerbose(`${closedCount}: FIND_CLOSE_ACTION`); let closeAction = findCloseAction(group, closedCount); if (notNull(closeAction)) { logVerbose(`${closedCount}: CLOSING`); try { closeAction.perform(); logVerbose(`${closedCount}: CLOSE_PERFORMED`); return [true, 1]; } catch (err) { logVerbose(`${closedCount}: Caught error while performing close action, window is probably closed.`); logVerbose(err); } } return [true, 0]; } } return false; } const getNotificationCenter = () => { let systemEvents = Application("System Events"); return systemEvents.processes.byName("NotificationCenter"); } const getNotificationCenterGroups = () => { return getNotificationCenter().windows[0].uiElements[0].uiElements[0].uiElements(); } let notificationCenter = getNotificationCenter(); if (notificationCenter.windows.length <= 0) { return input; } let groupsCount = getNotificationCenterGroups().filter(group => notificationGroupMatches(group)).length; if (groupsCount > 0) { logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`); let closedCount = 0; let maybeMore = true; while (maybeMore) { let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount); maybeMore = closeResult[0]; if (maybeMore) { closedCount = closedCount + closeResult[1]; } } } else { throw Error(`No${appNameForLog} notifications found...`); } return input; }