Skip to content

Instantly share code, notes, and snippets.

@lancethomps
Last active November 3, 2025 04:19
Show Gist options
  • Select an option

  • Save lancethomps/a5ac103f334b171f70ce2ff983220b4f to your computer and use it in GitHub Desktop.

Select an option

Save lancethomps/a5ac103f334b171f70ce2ff983220b4f to your computer and use it in GitHub Desktop.

Revisions

  1. lancethomps revised this gist Oct 24, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion close_notifications_applescript.js
    Original 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(), closedCount);
    let closeResult = closeNextGroup(getNotificationCenterGroups(true), closedCount);
    maybeMore = closeResult[0];
    if (maybeMore) {
    closedCount = closedCount + closeResult[1];
  2. lancethomps revised this gist Oct 10, 2025. 1 changed file with 8 additions and 1 deletion.
    9 changes: 8 additions & 1 deletion close_notifications_applescript.js
    Original 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] >= 15 && SYS_VERSION[1] >= 2;
    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) {
  3. lancethomps revised this gist Jan 16, 2025. No changes.
  4. lancethomps revised this gist Dec 13, 2024. 1 changed file with 13 additions and 1 deletion.
    14 changes: 13 additions & 1 deletion close_notifications_applescript.js
    Original 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 notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements[0].uiElements[0].uiElements();
    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;
    };
  5. lancethomps revised this gist Dec 12, 2024. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions close_notifications_applescript.js
    Original 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 = systemVersion()[0] === 12;
    const V15_OR_GREATER = systemVersionGreaterThanOrEqualTo(15);
    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 (subrole !== 'AXNotificationCenterAlertStack' && subrole !== 'AXNotificationCenterAlert') {
    if (NOTIFICATION_SUB_ROLES.indexOf(subrole) === -1) {
    return false;
    }
    } else if (V11_OR_GREATER && description !== 'group') {
  6. lancethomps revised this gist Dec 11, 2024. 1 changed file with 70 additions and 41 deletions.
    111 changes: 70 additions & 41 deletions close_notifications_applescript.js
    Original 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 scriptName = 'close_notifications_applescript';

    const CLEAR_ALL_ACTION = "Clear All";
    const CLEAR_ALL_ACTION_TOP = "Clear";
    const CLOSE_ACTION = "Close";
    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));
    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 APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage";
    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 appNameForLog = hasAppNames ? ` [${appNames.join(',')}]` : '';

    const logs = [];
    const log = (message, ...optionalParams) => {
    let message_with_prefix = `${new Date().toISOString().replace("Z", "").replace("T", " ")} [${scriptName}]${appNameForLog} ${message}`;
    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) : ""}`;
    message = `${err}${err.stack ? ' ' + err.stack : ''}`;
    }
    log(`ERROR ${message}`, optionalParams);
    };
    @@ -78,20 +82,20 @@ function run(input, parameters) {
    };

    const getLogLines = () => {
    return logs.join("\n");
    return logs.join('\n');
    };

    const getSystemEvents = () => {
    let systemEvents = Application("System Events");
    let systemEvents = Application('System Events');
    systemEvents.includeStandardAdditions = true;
    return systemEvents;
    };

    const getNotificationCenter = () => {
    try {
    return getSystemEvents().processes.byName("NotificationCenter");
    return getSystemEvents().processes.byName('NotificationCenter');
    } catch (err) {
    logError("Could not get NotificationCenter");
    logError('Could not get NotificationCenter');
    throw err;
    }
    };
    @@ -102,18 +106,18 @@ function run(input, parameters) {
    if (notificationCenter.windows.length <= 0) {
    return [];
    }
    if (!V11_OR_GREATER) {
    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");
    logError('Could not get NotificationCenter groups');
    if (retryOnError) {
    logError(err);
    log("Retrying getNotificationCenterGroups...");
    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;
    return description === 'button' && name === CLEAR_ALL_ACTION_TOP;
    };

    const matchesAnyAppNames = (value, checkValues) => {
    @@ -138,30 +142,59 @@ function run(input, parameters) {
    return false;
    };

    const matchesAppName = (role, value) => {
    if (role !== APP_NAME_MATCHER_ROLE) {
    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 (V11_OR_GREATER && description !== "group") {
    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 (!V11_OR_GREATER) {
    if (V10_OR_LESS) {
    let matchedAppName = !hasAppNameFilters;
    if (!matchedAppName) {
    for (let elem of group.uiElements()) {
    if (matchesAppName(elem.role(), elem.description())) {
    if (matchesAppName(getAppName(elem))) {
    matchedAppName = true;
    break;
    }
    @@ -175,11 +208,7 @@ 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());
    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"];
    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");
    log('No close action found for notification');
    return null;
    };

    const findCloseAction = (group, closedCount) => {
    try {
    if (!V11_OR_GREATER) {
    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") {
    } 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");
    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");
    logError('Could not run closeNextGroup');
    throw err;
    }
    };

    try {
    let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length;
    let groupsCount = getNotificationCenterGroups(true).filter((group) => notificationGroupMatches(group)).length;

    if (groupsCount > 0) {
    logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);
    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))) {
    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).`)
    log(`Caught an error before anything closed, trying ${maxAttempts - attempts} more time(s).`);
    attempts++;
    } else {
    throw innerErr;
  7. lancethomps revised this gist Jul 18, 2023. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions close_notifications_applescript.js
    Original file line number Diff line number Diff line change
    @@ -175,8 +175,11 @@ function run(input, parameters) {
    if (!hasAppNameFilters) {
    return true;
    }
    let firstElem = group.uiElements[0];
    return matchesAppName(firstElem.role(), firstElem.value());
    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);
  8. lancethomps revised this gist Jan 7, 2023. No changes.
  9. lancethomps revised this gist Jan 6, 2023. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion close_notifications_applescript.js
    Original 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();
    }
    return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements();
    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) {
  10. lancethomps revised this gist Mar 2, 2022. 1 changed file with 58 additions and 27 deletions.
    85 changes: 58 additions & 27 deletions close_notifications_applescript.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    function run(input, parameters) {

    const appName = "";
    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 hasAppName = notNull(appName) && appName !== "";
    const appNameForLog = hasAppName ? ` [${appName}]` : "";
    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) : ''}`;
    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) => {
    return role === APP_NAME_MATCHER_ROLE && value.toLowerCase() === appName.toLowerCase();
    }
    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 = !hasAppName;
    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 (!hasAppName) {
    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;
  11. lancethomps revised this gist Apr 6, 2021. 1 changed file with 52 additions and 30 deletions.
    82 changes: 52 additions & 30 deletions close_notifications_applescript.js
    Original 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) {
    log(`ERROR ${message}`, optionalParams);
    logError(message, optionalParams);
    }
    }

    @@ -68,18 +76,33 @@ function run(input, parameters) {
    }

    const getNotificationCenter = () => {
    return getSystemEvents().processes.byName("NotificationCenter");
    try {
    return getSystemEvents().processes.byName("NotificationCenter");
    } catch (err) {
    logError("Could not get NotificationCenter");
    throw err;
    }
    }

    const getNotificationCenterGroups = () => {
    let notificationCenter = getNotificationCenter();
    if (notificationCenter.windows.length <= 0) {
    return [];
    }
    if (!V11_OR_GREATER) {
    return notificationCenter.windows();
    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;
    }
    }
    return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements();
    }

    const isClearButton = (description, name) => {
    @@ -177,33 +200,32 @@ function run(input, parameters) {
    }

    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);
    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 [true, 0];
    }
    return false;
    } catch (err) {
    logError("Could not run closeNextGroup");
    throw err;
    }
    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;
    let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length;

    if (groupsCount > 0) {
    logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);
  12. lancethomps revised this gist Mar 5, 2021. 1 changed file with 15 additions and 4 deletions.
    19 changes: 15 additions & 4 deletions close_notifications_applescript.js
    Original 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))) {
    let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount);
    maybeMore = closeResult[0];
    if (maybeMore) {
    closedCount = closedCount + closeResult[1];
    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 {
  13. lancethomps revised this gist Feb 9, 2021. 1 changed file with 13 additions and 6 deletions.
    19 changes: 13 additions & 6 deletions close_notifications_applescript.js
    Original 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 (!hasAppName) {
    return true;
    }
    if (!V11_OR_GREATER) {
    for (let elem of group.uiElements()) {
    if (matchesAppName(elem.role(), elem.description())) {
    return true;
    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) {
  14. lancethomps revised this gist Feb 9, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion close_notifications_applescript.js
    Original 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(parseInt);
    return Application("Finder").version().split(".").map(val => parseInt(val));
    }

    const systemVersionGreaterThanOrEqualTo = (vers) => {
  15. lancethomps revised this gist Feb 9, 2021. 1 changed file with 100 additions and 29 deletions.
    129 changes: 100 additions & 29 deletions close_notifications_applescript.js
    Original 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().replaceAll("Z", "").replaceAll("T", " ")} [close_notifications]${appNameForLog} ${message}`;
    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 notificationGroupMatches = (group) => {
    if (!hasAppName) {
    return true;
    }
    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 {
    for (let elem of group.uiElements()) {
    if (hasAppName && elem.role() === "AXStaticText" && elem.value().toLowerCase() === appName.toLowerCase()) {
    return true;
    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 CLEAR_ALL_ACTION = "Clear All";
    const CLOSE_ACTION = "Close";
    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()) {
    if (action.description() === CLEAR_ALL_ACTION) {
    let description = action.description();
    if (description === CLEAR_ALL_ACTION) {
    clearAllAction = action;
    break;
    } else if (action.description() === CLOSE_ACTION) {
    } 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;
    }

    let notificationCenter = getNotificationCenter();
    if (notificationCenter.windows.length <= 0) {
    logError("No notifications found.");
    return getLogLines();
    }
    try {
    let notificationCenter = getNotificationCenter();
    if (notificationCenter.windows.length <= 0) {
    logError("No notifications found.");
    return getLogLines();
    }

    let groupsCount = getNotificationCenterGroups().filter(group => notificationGroupMatches(group)).length;
    let groupsCount = getNotificationCenterGroups().filter(group => notificationGroupMatches(group)).length;

    if (groupsCount > 0) {
    logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);
    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];
    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...`);
    }
    } else {
    throw Error(`No${appNameForLog} notifications found...`);
    } catch (err) {
    logError(err);
    logError(err.message);
    getLogLines();
    throw err;
    }

    return getLogLines();
  16. lancethomps revised this gist Jan 8, 2021. 1 changed file with 71 additions and 44 deletions.
    115 changes: 71 additions & 44 deletions close_notifications_applescript.js
    Original 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 appName = "";
    const verbose = true;

    const CLEAR_ALL_ACTION = "Clear All";
    const CLOSE_ACTION = "Close";
    const isNull = (val) => {
    return !notNull(val);
    }

    const hasAppName = notNull(appName) && appName !== "";
    const appNameForLog = hasAppName ? (" " + appName) : "";
    const appNameForLog = hasAppName ? ` [${appName}]` : "";

    const logs = [];
    const log = (message, ...optionalParams) => {
    console.log("[close_notifications] " + 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) {
    logVerbose(`${closedCount}: Caught error while searching for close action, window is probably closed.`);
    logVerbose(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 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);
    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;
    }

    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;
    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 input;
    return getLogLines();
    }
  17. lancethomps created this gist Jan 7, 2021.
    127 changes: 127 additions & 0 deletions close_notifications_applescript.js
    Original 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;
    }