(Slightly updating the code in the earlier macro):
Once you have:
- copied some actions as a JSON string,
- obtained some JavaScript object(s) from the string with JSON.parse(strJSON)
- made any run-time adjustments to the parameter values in those objects
You can then execute it/them as a JavaScript Array (or Dictionary, for a single action), with a piece of code like:
// jsoDoScript :: Object (Dict | Array) -> IO ()
const jsoDoScript = jso => {
const strPath = tempFilePath('tmp.plist');
return (
Application('Keyboard Maestro Engine')
.doScript((
$(Array.isArray(jso) ? jso : [jso])
.writeToFileAtomically(
$(strPath)
.stringByStandardizingPath,
true
),
readFile(strPath)
)),
true
);
};
For example, to execute this sequence of two Keyboard Maestro actions, copied as JSON, or constructed / adjusted at run-time.
[{
"MacroActionType": "SpeakText",
"Rate": "Default",
"IsDisclosed": true,
"TimeOutAbortsMacro": true,
"Text": "This is a simple example",
"IsActive": true
}, {
"MacroActionType": "PlaySound",
"Volume": 74,
"IsDisclosed": true,
"TimeOutAbortsMacro": true,
"IsActive": true,
"Path": "/System/Library/Sounds/Glass.aiff",
"DeviceID": "SOUNDEFFECTS"
}]
The whole script might look like this:
(() => {
'use strict';
// GENERIC ---------------------------------------------------------------
// readFile :: FilePath -> IO String
const readFile = strPath => {
var error = $(),
str = ObjC.unwrap(
$.NSString.stringWithContentsOfFileEncodingError(
$(strPath)
.stringByStandardizingPath,
$.NSUTF8StringEncoding,
error
)
);
return typeof error.code !== 'string' ? (
str
) : 'Could not read ' + strPath;
};
// takeExtension :: FilePath -> String
const takeExtension = strPath => {
const
xs = strPath.split('.'),
lng = xs.length;
return lng > 1 ? (
'.' + xs[lng - 1]
) : '';
};
// takeBaseName :: FilePath -> String
const takeBaseName = strPath =>
strPath !== '' ? (
strPath[strPath.length - 1] !== '/' ? (
strPath.split('/')
.slice(-1)[0].split('.')[0]
) : ''
) : '';
// File name template -> temporary path
// (Random digit sequence inserted between template base and extension)
// tempFilePath :: String -> IO FilePath
const tempFilePath = template =>
ObjC.unwrap($.NSTemporaryDirectory()) +
takeBaseName(template) + Math.random()
.toString()
.substring(3) + takeExtension(template)
// JXA -------------------------------------------------------------------
// actions :: [Dict]
const actions = [{
"MacroActionType": "SpeakText",
"Rate": "Default",
"IsDisclosed": true,
"TimeOutAbortsMacro": true,
"Text": "This is a simple example",
"IsActive": true
}, {
"MacroActionType": "PlaySound",
"Volume": 74,
"IsDisclosed": true,
"TimeOutAbortsMacro": true,
"IsActive": true,
"Path": "/System/Library/Sounds/Glass.aiff",
"DeviceID": "SOUNDEFFECTS"
}];
// jsoDoScript :: Object (Dict | Array) -> IO ()
const jsoDoScript = jso => {
const strPath = tempFilePath('tmp.plist');
return (
Application('Keyboard Maestro Engine')
.doScript((
$(Array.isArray(jso) ? jso : [jso])
.writeToFileAtomically(
$(strPath)
.stringByStandardizingPath,
true
),
readFile(strPath)
)),
true
);
};
// MAIN ------------------------------------------------------------------
jsoDoScript(actions);
})();
As ever:
Je n’ai fait celle-ci plus longue que parce que je n’ai pas eu le loisir de la faire plus courte.
Blaise Pascal (Lettres Provinciales) 1657
(This (script) is long, simply for want of the leisure to shorten it.)
I also find that snapping things together from a set of unpruned Lego bricks (pre-fab functions, automatically pasted from a library), is worth, not just in speed of preparation but also in ease of maintenance, whatever it happens to cost in length and redundancy : -)
(Brevity is certainly nothing to be ashamed of, but perhaps the expenditure of time which it takes to create is not always something to be proud of either : -)