Last active
November 26, 2021 17:31
-
-
Save mmazzarolo/45971692a851eef05f18dfb907b8fc0f to your computer and use it in GitHub Desktop.
Deno script to create a reminder from CLI (in Apple's Reminders app)
This file contains 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 characters
// Deno CLI script to add reminders to Apple's Reminders app parsing the | |
// due date with natural language. | |
// Requires reminders-cli (https://github.com/keith/reminders-cli) to be | |
// installed in order to run. Uses reminders-cli instead of Applescript/JXA | |
// because reminders-cli is much faster. | |
// Uses chrono-node (https://github.com/wanasit/chrono) to parse the date (which | |
// in my opinion works better than reminders-cli natural lanaguage parser). | |
// | |
// Example usage: | |
// ``` | |
// quick-reminder Doctor appointment in 2 weeks at 5pm | |
// ``` | |
import chrono from "https://esm.sh/chrono-node"; | |
import { iter } from "https://deno.land/std/io/util.ts"; | |
import * as Colors from "https://deno.land/std/fmt/colors.ts"; | |
import { format } from "https://deno.land/std/datetime/mod.ts"; | |
// Helper function to run CLI script from Deno. | |
interface ExecOptions { | |
verbose?: boolean; | |
} | |
const decoder = new TextDecoder(); | |
async function exec(cmd: string, options: ExecOptions = {}) { | |
const { verbose } = options; | |
const p = Deno.run({ | |
cmd: ["/bin/sh", "-c", cmd], | |
stdout: "piped", | |
stderr: "piped", | |
}); | |
let stdout = ""; | |
let stderr = ""; | |
const stdoutPromise = (async function () { | |
for await (const chunk of iter(p.stdout)) { | |
const decoded = decoder.decode(chunk); | |
if (verbose) await Deno.stdout.write(chunk); | |
stdout += decoded; | |
} | |
p.stdout.close(); | |
})(); | |
const stderrPromise = (async function () { | |
for await (const chunk of iter(p.stderr)) { | |
const decoded = decoder.decode(chunk); | |
if (verbose) await Deno.stderr.write(chunk); | |
stderr += decoded; | |
} | |
p.stderr.close(); | |
})(); | |
const { success } = await p.status(); | |
await Promise.all([stdoutPromise, stderrPromise]); | |
p.close(); | |
if (!success) { | |
throw new Error(stderr); | |
} | |
return stdout.trim(); | |
} | |
// Use reminders-cli (https://github.com/keith/reminders-cli) to add a reminder. | |
async function run() { | |
// Ensure reminders-cli is installed. | |
try { | |
await exec(`reminders`); | |
} catch (_err) { | |
throw new Error( | |
`Reminders CLI doesn't seem to be installed. See https://github.com/keith/reminders-cli.` | |
); | |
} | |
// Get input args. | |
// For a better UX, we allow writing the entire string without quotes. | |
const inputStr = Deno.args.join(" "); | |
if (!inputStr) { | |
throw new Error( | |
`Missing input. Example usage: $ quick-reminder Doctor appointment tomorrow` | |
); | |
} | |
// Get default reminders list. | |
// Since the reminders.app API doesn't expose the "default" list, we'll pick | |
// the first one available from the list of reminders list (which follows the | |
// order set in the Reminder app). | |
const remindersCLIShowListsStdout = await exec("reminders show-lists"); | |
const availableReminderListNames = remindersCLIShowListsStdout.split("\n"); | |
const defaultReminderListName = availableReminderListNames[0]; | |
if (!defaultReminderListName) { | |
throw "Unable to load reminder lists"; | |
} | |
// Extract the natural-lanaguage date from the input string using chrono-node. | |
const chronoParsingResult = chrono.parse(inputStr)[0]; | |
const naturalLanguageDueDate = chronoParsingResult?.text || ""; | |
const reminderText = inputStr.replace(naturalLanguageDueDate, "").trim(); | |
const reminderDueDate = chronoParsingResult?.start?.date(); | |
// Use reminders-cli to add the reminder to reminders.app. | |
await exec( | |
reminderDueDate | |
? `reminders add ${defaultReminderListName} "${reminderText}" --due-date "${reminderDueDate}"` | |
: `reminders add ${defaultReminderListName} "${reminderText}"` | |
); | |
const prettyCheckMark = Colors.green("✔"); | |
const prettyReminderText = Colors.magenta(reminderText); | |
const prettyReminderListName = Colors.magenta(defaultReminderListName); | |
if (reminderDueDate) { | |
const prettyReminderDueDate = Colors.magenta( | |
format(reminderDueDate, "yyyy-MM-dd HH:mm:ss") | |
); | |
console.log( | |
`${prettyCheckMark} Added a new "${prettyReminderText}" reminder to the "${prettyReminderListName}" list with due date ${prettyReminderDueDate}` | |
); | |
} else { | |
console.log( | |
`${prettyCheckMark} Added a new "${prettyReminderText}" reminder to the "${prettyReminderListName}"` | |
); | |
} | |
} | |
try { | |
await run(); | |
} catch (err) { | |
console.error(`${Colors.red("✘")} ${err?.message}`); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Requires
reminders-cli
to be globally installed.Usage: