Example 2 :: listing the macros of a named KM group
Listing the macros of a named KM Group.kmmacros (24.4 KB)
JS Source
(() => {
"use strict";
// Reading the KM XML from KMEngine.getmacros()
// Example two :: listing the macros of a named group.
// Rob Trew @2021
// main :: IO ()
const main = () => {
const
kme = Application("Keyboard Maestro Engine"),
groupName = kme.getvariable("groupName"),
fpTemp = writeTempFile("km.xml")(
kme.getmacros({
asstring: true
})
);
return either(
msg => alert("Listing named KM group")(msg)
)(
report => report
)(
bindLR(
readPlistArrayFileLR(fpTemp)
)(
groups => {
const
groupIndex = groups.findIndex(
group => groupName === group.name
);
return -1 !== groupIndex ? (() => {
const
group = groups[groupIndex],
macros = group.macros,
title = `${groupName} group:`,
listing = macros.map(macro => {
const
name = macro.name,
n = macro.used,
units = plural("time")(n);
return `\t- ${name} (used ${n} ${units})`;
}).join("\n");
return Right(`${title}\n\n${listing}`);
})() : Left(`No group found with name: ${groupName}`);
}
)
);
};
// ----------------------- JXA -----------------------
// alert :: String => String -> IO String
const alert = title =>
s => {
const sa = Object.assign(
Application("System Events"), {
includeStandardAdditions: true
});
return (
sa.activate(),
sa.displayDialog(s, {
withTitle: title,
buttons: ["OK"],
defaultButton: "OK"
}),
s
);
};
// --------------------- GENERIC ---------------------
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// doesFileExist :: FilePath -> IO Bool
const doesFileExist = fp => {
const ref = Ref();
return $.NSFileManager.defaultManager
.fileExistsAtPathIsDirectory(
$(fp)
.stringByStandardizingPath, ref
) && 1 !== ref[0];
};
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
// Application of the function fl to the
// contents of any Left value in e, or
// the application of fr to its Right value.
fr => e => e.Left ? (
fl(e.Left)
) : fr(e.Right);
// filePath :: String -> FilePath
const filePath = s =>
// The given file path with any tilde expanded
// to the full user directory path.
ObjC.unwrap(ObjC.wrap(s)
.stringByStandardizingPath);
// last :: [a] -> a
const last = xs =>
// The last item of a list.
0 < xs.length ? (
xs.slice(-1)[0]
) : null;
// plural :: String -> Int -> String
const plural = k =>
// Singular or plural EN inflection
// of a given word.
n => 1 !== n ? (
`${k}s`
) : k;
// readPlistArrayFileLR :: FilePath -> Either String Object
const readPlistArrayFileLR = fp =>
bindLR(
doesFileExist(fp) ? (
Right(filePath(fp))
) : Left(`No file found at path:\n\t${fp}`)
)(fpFull => {
const
e = $(),
maybeDict = (
$.NSArray
.arrayWithContentsOfURLError(
$.NSURL.fileURLWithPath(fpFull),
e
)
);
return maybeDict.isNil() ? (() => {
const msg = ObjC.unwrap(e.localizedDescription);
return Left(`readPlistFileLR:\n\t${msg}`);
})() : Right(ObjC.deepUnwrap(maybeDict));
});
// takeBaseName :: FilePath -> String
const takeBaseName = fp =>
("" !== fp) ? (
("/" !== fp[fp.length - 1]) ? (() => {
const fn = fp.split("/").slice(-1)[0];
return fn.includes(".") ? (
fn.split(".").slice(0, -1)
.join(".")
) : fn;
})() : ""
) : "";
// takeExtension :: FilePath -> String
const takeExtension = fp => (
fs => {
const fn = last(fs);
return fn.includes(".") ? (
`.${last(fn.split("."))}`
) : "";
}
)(fp.split("/"));
// writeFile :: FilePath -> String -> IO ()
const writeFile = fp => s =>
$.NSString.alloc.initWithUTF8String(s)
.writeToFileAtomicallyEncodingError(
$(fp)
.stringByStandardizingPath, false,
$.NSUTF8StringEncoding, null
);
// writeTempFile :: String -> String -> IO FilePath
const writeTempFile = template =>
// File name template -> string data -> IO temporary path
txt => {
const
fp = ObjC.unwrap($.NSTemporaryDirectory()) +
takeBaseName(template) + Math.random()
.toString()
.substring(3) + takeExtension(template);
return (writeFile(fp)(txt), fp);
};
// MAIN ---
return main();
})();