Skip to content

Commit

Permalink
[#3] Fix DND for macos-12 (#4)
Browse files Browse the repository at this point in the history
* fix: try adjustment to applescript
* ci: record during ci
* chore: bump package version
  • Loading branch information
cmorten authored Dec 3, 2022
1 parent f0b4c46 commit c71bb2c
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 60 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ jobs:
node-version: 16
- run: yarn install --frozen-lockfile
- run: yarn ci
- uses: actions/upload-artifact@v2
if: always()
with:
name: artifacts
path: |
**/recordings/**/*
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ If you are using GitHub Actions, check out the dedicated [`guidepup/setup-action
uses: guidepup/[email protected]
```
## Debugging
If you are encountering errors in CI you can pass a `--record` flag to the command which will output a screen-recording of the setup to a `./recordings/` directory.

## See Also 🐶

Check out some of the other Guidepup modules:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@guidepup/setup",
"version": "0.4.1",
"version": "0.5.0",
"description": "Setup your environment for screen-reader automation.",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand All @@ -25,7 +25,7 @@
],
"scripts": {
"build": "yarn clean && yarn compile",
"ci": "yarn clean && yarn lint && yarn build && yarn start --ci",
"ci": "yarn clean && yarn lint && yarn build && yarn start --ci --record",
"clean": "rimraf lib",
"compile": "tsc",
"dev": "ts-node ./src/index.ts",
Expand Down
53 changes: 23 additions & 30 deletions src/macOS/enableDoNotDisturb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,29 @@ killall usernoted && killall ControlCenter

// source https://www.reddit.com/r/applescript/comments/r9nnil/enable_do_not_disturb_on_monterey/
const enableFocusModeAppleScript = `
tell application "System Preferences"
activate
end tell
set timeoutSeconds to 5.0
tell application "System Events"
tell process "System Preferences"
repeat 25 times
if (exists window "System Preferences") then
click button "Notifications\n& Focus" of scroll area 1 of window "System Preferences"
repeat 25 times
if (exists window "Notifications & Focus") then
click radio button "Focus" of tab group 1 of window "Notifications & Focus"
set focusSwitch to checkbox 1 of group 1 of tab group 1 of window "Notifications & Focus"
tell focusSwitch
if not (its value as boolean) then click focusSwitch
end tell
tell application "System Preferences"
quit
end tell
return
end if
delay 0.2
end repeat
error number 1
end if
delay 0.2
end repeat
error number 1
end tell
end tell
set openSystemPreferences to "tell application \\"System Preferences\\" to activate"
set clickNotificationAndFocusButton to "click UI Element \\"Notifications
& Focus\\" of scroll area 1 of window \\"System Preferences\\" of application process \\"System Preferences\\""
set clickFocusTab to "click radio button \\"Focus\\" of tab group 1 of window \\"Notifications & Focus\\" of application process \\"System Preferences\\""
set enableDoNotDisturb to "
set doNotDisturbToggle to checkbox 1 of group 1 of tab group 1 of window \\"Notifications & Focus\\" of application process \\"System Preferences\\"
tell doNotDisturbToggle
if not (its value as boolean) then click doNotDisturbToggle
end tell"
set closeSystemPreferences to "tell application \\"System Preferences\\" to quit"
my withTimeout(openSystemPreferences, timeoutSeconds)
my withTimeout(clickNotificationAndFocusButton, timeoutSeconds)
my withTimeout(clickFocusTab, timeoutSeconds)
my withTimeout(enableDoNotDisturb, timeoutSeconds)
my withTimeout(closeSystemPreferences, timeoutSeconds)
`;

export async function enableDoNotDisturb() {
Expand All @@ -57,7 +50,7 @@ export async function enableDoNotDisturb() {
// From MacOS 12 Monterey (Darwin 21) there is no known way to enable DND via system defaults
try {
await runAppleScript(enableFocusModeAppleScript);
} catch (_) {
} catch (e) {
throw new Error(ERR_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB);
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/macOS/record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { execSync, spawn } from "child_process";
import { dirname } from "path";
import { unlinkSync } from "fs";

/**
* Start a screen recording.
*
* @param {string} filepath The file path to save the screen recording to.
* @returns {Function} A function to stop the screen recording.
*/
export function record(filepath: string): () => void {
execSync(`mkdir -p ${dirname(filepath)}`);

try {
unlinkSync(filepath);
} catch (_) {
// file doesn't exist.
}

const screencapture = spawn("/usr/sbin/screencapture", [
"-v",
"-C",
"-k",
"-T0",
"-g",
filepath,
]);

return () => {
screencapture.stdin.write("q");
};
}
24 changes: 23 additions & 1 deletion src/macOS/runAppleScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,29 @@ export async function runAppleScript<T = string | void>(
script: string,
{ timeout = DEFAULT_TIMEOUT } = { timeout: DEFAULT_TIMEOUT }
): Promise<T> {
const scriptWithTimeout = `with timeout of ${timeout} seconds\n${script}\nend timeout`;
const scriptWithTimeout = `
with timeout of ${timeout} seconds
${script}
end timeout
on withTimeout(uiScript, timeoutSeconds)
set endDate to (current date) + timeoutSeconds
repeat
try
run script "tell application \\"System Events\\"
" & uiScript & "
end tell"
exit repeat
on error errorMessage
if ((current date) > endDate) then
error errorMessage & "\n\nFor script: " & uiScript
end if
end try
delay 0.2
end repeat
end doWithTimeout
`;

return (await new Promise<string | void>((resolve, reject) => {
const child = execFile(
Expand Down
64 changes: 37 additions & 27 deletions src/macOS/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,56 @@ import { setVoiceOverEnabledViaUi } from "./setVoiceOverEnabledViaUi";
import { logInfo } from "../logging";
import { ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION } from "../errors";
import { enableDoNotDisturb } from "./enableDoNotDisturb";
import { record } from "./record";

const isCi = process.argv.includes("--ci");
const isRecorded = process.argv.includes("--record");

export async function setup(): Promise<void> {
checkVersion();
enableAppleScriptControlSystemDefaults();
disableSplashScreenSystemDefaults();
disableDictationInputAutoEnable();
const stopRecording = isRecorded
? record(`./recordings/macos-setup-${+new Date()}.mov`)
: () => null;

try {
updateTccDb();
} catch (e) {
if (isCi) {
throw e;
checkVersion();
enableAppleScriptControlSystemDefaults();
disableSplashScreenSystemDefaults();
disableDictationInputAutoEnable();

try {
updateTccDb();
} catch (e) {
if (isCi) {
throw e;
}
}
}

if (isCi) {
await enableDoNotDisturb();
}
if (isCi) {
await enableDoNotDisturb();
}

if (!isSipEnabled()) {
writeDatabaseFile();
if (!isSipEnabled()) {
writeDatabaseFile();

return;
}
return;
}

if (await isAppleScriptControlEnabled()) {
return;
}
if (await isAppleScriptControlEnabled()) {
return;
}

if (isCi) {
throw new Error(ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
}
if (isCi) {
throw new Error(ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
}

const credentials = await askUserToControlUi();
const credentials = await askUserToControlUi();

logInfo("");
logInfo("Starting UI control...");
logInfo("Please refrain from interaction until the script has completed");
logInfo("");
logInfo("Starting UI control...");
logInfo("Please refrain from interaction until the script has completed");

await setVoiceOverEnabledViaUi(credentials);
await setVoiceOverEnabledViaUi(credentials);
} finally {
stopRecording();
}
}

0 comments on commit c71bb2c

Please sign in to comment.