Using Real Java Script in BTT
NOTE: If you are using the floating webview action in BTT have a look here instead: Floating Webview JavaScript
This feature is new in BetterTouchTool version 3.333. I think it is the most powerful and easiest way to script BetterTouchtool.
It's basically just modern Java Script (so you can use async await and the like) however it has a few important additions:
- It can call any Apple Script - and get the result
- It can call any Shell Script - and get the result.
- It can trigger all functions available in BetterTouchTool's scripting interface.
By combining these three you can automate almost any task on your Mac.
- Using Real Java Script in BTT
- Available BetterTouchTool Scripting Interfaces
- Example script function calls:
- trigger_named
- trigger_named_async_without_response
- cancel_delayed_named_trigger_execution
- update_touch_bar_widget
- update_menubar_item
- update_stream_deck_widget
- trigger_action
- execute_assigned_actions_for_trigger
- refresh_widget
- update_trigger
- get_trigger
- get_triggers
- add_new_trigger
- delete_trigger
- get_clipboard_content
- set_persistent_string_variable
- set_string_variable
- set_persistent_number_variable
- set_number_variable
- get_number_variable
- get_string_variable
- export_preset
- display_notification
- paste_text
- reveal_element_in_ui
- get_menu_item_details
- set_clipboard_content
- get-selected-text
1.) Running Standard Apple Scripts
To run a Apple Script from within the Java Script code, use the runAppleScript(scriptCode) function. The function takes a string that defines the Apple Script, and returns a promise which resolves to the result value.
I'm always using it with async await, but you can also use classic Promise syntax.
Example: Run Apple Script from within Java Script
// I always put my code into a self executing async function, because top level await is not allowed in JavaScript.
(async () => {
// put the Apple Script into a string (back ticks are great for multi -line strings)
let appleScript = `
set theDialogText to "The curent date and time is " & (current date) & "."
set result to display dialog theDialogText
return result
`;
// this will execute the Apple Script and store the result in the result variable.
let result = await runAppleScript(appleScript);
// do whatever you want with the result
// at the end you always need to call returnToBTT to exit the script / return the value to BTT.
returnToBTT(result);
// it is important that this function self-executes ()
})();
1.) Running Java Script For Automation (JXA) Scripts
To run a Apple Script from within the Java Script code, use the runAppleScript(scriptCode) function. The function takes a string that defines the Apple Script, and returns a promise which resolves to the result value.
I'm always using it with async await, but you can also use classic Promise syntax.
Example: Run JavaScript For Automation (JXA) from within Java Script (requires BTT >= 4.903)
// I always put my code into a self executing async function, because top level await is not allowed in JavaScript.
(async () => {
// put the Apple Script into a string (back ticks are great for multi -line strings)
let jxaScript = `
var app = Application.currentApplication()
app.includeStandardAdditions = true
var response = app.displayDialog("Test?", {
defaultAnswer: "",
withIcon: "note",
buttons: ["Cancel", "Continue"],
defaultButton: "Continue"
})
// Result: {"buttonReturned":"Continue", "textReturned":"Test"}
app.displayDialog("Hello, " + (response.textReturned) + ".");
`;
// this will execute the JXA Apple Script and store the result in the result variable.
let result = await runJXA(jxaScript);
// do whatever you want with the result
// at the end you always need to call returnToBTT to exit the script / return the value to BTT.
returnToBTT(result);
// it is important that this function self-executes ()
})();
3.) Running Shell Scripts
Example: Run Shell Script from within Java Script
// I always put my code into a self executing async function, because top level await is not allowed in JavaScript.
(async () => {
// put the shell script into a string (single backticks are great for multiline strings)
let shellScript = `say hello world`;
let shellScriptWrapper = {
script: shellScript, // mandatory
launchPath: '/bin/bash', //optional - default is /bin/bash
parameters: '-c', // optional - default is -c. If you use multiple parameters please separate them by ;; e.g. -c;;date
environmentVariables: '' //optional e.g. VAR1=/test/;VAR2=/test2/;
};
// this will execute the Apple Script and store the result in the result variable.
let result = await runShellScript(shellScriptWrapper);
// do whatever you want with the result
// at the end you always need to call returnToBTT to exit the script / return the value to BTT.
returnToBTT(result);
// it is important that this function self-executes ()
})();
4.) Running Shortcuts From Apple's Shortcuts App:
You can provide the name of the shortcut and some input (optional).
(async () => {
let shortcut = "some name";
let input = "some input";
let result = await runAppleShortcut({name: shortcut, input: input});
returnToBTT(result);
})();
If you want to provide multiple files as input, please separate them with two semicolons. For example
(async () => {
runAppleShortcut({name: 'multi-file-test', input: '~/Downloads/test1.png;;~/Downloads/test2.png'});
})();
5.) Read and Write Files
readFile(path: string, readAsBase64: Boolean)
async function doSomething() {
return readFile("~/Library/Application Support/BetterTouchTool/app-cache.json");
}
6.) Load the contents of a URL as base64
fetchURLAsBase64(path: string, readAsBase64: Boolean)
async function loadURL() {
return fetchURLAsBase64("https://folivora.ai/favicon.ico");
}
writeStringToFile(dataString: string, path: string, stringIsBase64: Boolean)
async function doSomething() {
return writeStringToFile(
JSON.stringify(menuItems),
"~/Library/Application Support/BetterTouchTool/app-cache.json"
}
6.) Using the JavaScript to call BTT scripting functions
All BetterTouchTool scripting functions can be accessed from Java Script.
Available BetterTouchTool Scripting Interfaces
The available scripting functions are:
- trigger_named
- update_touch_bar_widget
- update_menubar_item
- update_stream_deck_widget
- trigger_action
- execute_assigned_actions_for_trigger
- refresh_widget
- get_trigger
- get_triggers
- update_trigger (to create or if exists update a trigger)
- add_new_trigger
- delete_trigger
- delete_triggers
- trigger_named
- trigger_named_async_without_response
- cancel_delayed_named_trigger_execution
- get_dock_badge_for
- get_active_touch_bar_group
- is_true_tone_enabled
- get_location
- set_persistent_string_variable
- set_string_variable
- set_persistent_number_variable
- set_number_variable
- get_number_variable
- get_string_variable
- export_preset
- display_notification
- paste_text
- set_clipboard_content
- get-selected-text
Floating Menus:
- update_menu_item
- get_menu_item_value
- set_menu_item_value
- webview_menu_item_load_html_url_js
Example script function calls:
trigger_named
This method will trigger the specified named trigger (which can be configured in the "Other" tab in BetterTouchTool.)
Example
(async ()=> {
let result = await trigger_named({trigger_name: 'Action5', wait_for_reply: true});
// Currently only the Apple Script and Shell Script actions return results, if you don't care about the result, setting wait_for_reply to false can make execution a bit faster (default is true)
returnToBTT(result);
})();
trigger_named_async_without_response
This method will trigger the specified named trigger (which can be configured in the "Other" tab in BetterTouchTool.).
Optional parameter: delay This lets you set a delay before the named trigger is executed (in seconds). While the trigger has not been executed you can cancel the execution by calling the cancel_delayed_named_trigger_execution function
Example
(async ()=> {
trigger_named_async_without_response({trigger_name: 'Action5', wait_for_reply: false});
// Currently only the Apple Script and Shell Script actions return results.
returnToBTT("done");
})();
cancel_delayed_named_trigger_execution
This method will cancel the execution of a delayed named trigger (see previous function)
Example
(async ()=> {
cancel_delayed_named_trigger_execution({trigger_name: 'Action5'});
// Currently only the Apple Script and Shell Script actions return results.
returnToBTT("done");
})();
update_touch_bar_widget
This method will temporarily update the contents of a Touch Bar Script Widget (identified by its uuid). You can provide a new text to show, a new icon and a new background color.
For the icon you can either provide it directly using the icon_data parameter (must be base64 encoded) or you can provide a file path (via the icon_path parameter) that points to the new icon.
You can get the uuid by right-clicking any script widget in BTT.
Example:
(async ()=> {
let widgetConfig = {
text: "hi there!",
icon_path: "/Users/andi/Desktop/test.png",
background_color: "200,100,100,255"
};
update_touch_bar_widget({uuid: '9990CE09-9820-4D67-9C52-8BABAB263056', json: widgetConfig});
returnToBTT('done');
})();
update_menubar_item
This method will temporarily update the contents of a Menubar Status Item (identified by its uuid). You can provide a new text to show, a new icon and a new background color.
For the icon you can either provide it directly using the icon_data parameter (must be base64 encoded) or you can provide a file path (via the icon_path parameter) that points to the new icon.
You can get the uuid by right-clicking any script widget in BTT.
Example:
(async ()=> {
let widgetConfig = {
text: "hi there!",
icon_path: "/Users/andi/Desktop/test.png",
background_color: "200,100,100,255"
};
update_menubar_item({uuid: '9990CE09-9820-4D67-9C52-8BABAB263056', json: widgetConfig});
returnToBTT('done');
})();
update_stream_deck_widget
This method will temporarily update the contents of a Stream Deck Script Widget (identified by its uuid). The changes will not be persisted. You can provide a new text to show on the button and you can update any other of the button's properties. To see what properties are possible right-click an existing & configured Strem Deck button in BTT and choose "copy". When pasting this copied button into some text editor you'll see the possible values in the "BTTTriggerConfig" section.
Example:
(async ()=> {
let widgetConfig = {
text: "some new title!",
BTTStreamDeckBackgroundColor: "200,100,100,255",
BTTStreamDeckSFSymbolName : "bitcoinsign.circle.fill",
};
update_stream_deck_widget({uuid: '9990CE09-9820-4D67-9C52-8BABAB263056', json: JSON.stringify(widgetConfig)});
returnToBTT('done');
})();
trigger_action
This method will trigger any of BetterTouchTool's predefined actions (or any combination of them).
You need to provide a JSON description of the action you want to trigger as a string. The easiest way to get such a JSON description is to right-click the trigger you have configured in BetterTouchTool and choose "Copy JSON". This will copy the complete JSON (including the configuration for the trigger itself), but this action will ignore anything that's not needed. (or you can delete the not needed parts)
Example:
(async ()=> {
let actionDefinition = {
"BTTPredefinedActionType" : 153,
"BTTPredefinedActionName" : "Move Mouse To Position",
"BTTMoveMouseToPosition" : "{100, 100}",
"BTTMoveMouseRelative" : "6"
};
let result = await trigger_action({json: JSON.stringify(actionDefinition), wait_for_reply: false});
returnToBTT(result);
})();
execute_assigned_actions_for_trigger
This method execute all the assigned actions for a given trigger (i.e. gesture, shortcut, drawing etc.) identified by its uuid.
You can get the uuid by right-clicking any configured trigger in BetterTouchTool.
Example
(async ()=> {
let result = await execute_assigned_actions_for_trigger({uuid: '2F34005D-4537-464D-94E9-A7F42DA39DF1'});
// Currently only the Apple Script and Shell Script actions return results.
returnToBTT(result);
})();
refresh_widget
This method will execute all scripts assigned to a script widget and update its contents accordingly.
The widget is identified by its uuid, which you can get by right-clicking the widget in BetterTouchTool.
Example
(async ()=> {
refresh_widget({uuid: '2F34005D-4537-464D-94E9-A7F42DA39DF1'});
returnToBTT('done');
})();
update_trigger
This method will create or update the configuration of any specified trigger (i.e. gestures, shortcuts, touchbar items etc.).
You need to provide the uuid of the trigger you want to update (get by right-clicking it in BTT) and a JSON object defining the updates. To know how the JSON object should look like, right-click the trigger in BTT and choose "Copy JSON".
Optional Parameter: trigger_parent_uuid (if you want to add the trigger to a group)
Example:
Hint: don't forget to use JSON.stringify() before passing the updateDefinition object.
(async ()=> {
var updateDefinition = JSON.stringify(
{
"BTTTriggerConfig": {
"BTTTouchBarButtonName" : "New Name",
"BTTTouchBarItemIconHeight": 25
}
}
);
update_trigger({uuid: 'ED631459-115C-4D8C-940A-D428E4EAE086', json: updateDefinition});
returnToBTT('done');
})();
get_trigger
This allows you to retrieve the json representation of any trigger (e.g. gesture, Touch Bar button, keyboard shortcut etc.) identified by the given UUID.
You can get the UUID by right-clicking any trigger in BTT.
(async ()=> {
let result = await get_trigger({uuid:'123e4567-e89b-12d3-a456-426655440000'})
returnToBTT(result);
})();
get_triggers
This allows you to retrieve the JSON description of multiple triggers in BTT (e.g. Touch Bar item, Gesture, Keyboard Shortcut etc.) based on a few properties.
You can get the properties of a specific trigger by selecting it in BTT and copy pasting it to some text editor.
The get_triggers() function supports these parameters (all optional):
- trigger_type (e.g. BTTTriggerTypeMagicMouse)
- trigger_id (e.g. 643 for named triggers)
- trigger_parent_uuid (if you want to get the items of a folder)
- trigger_uuid (to get a specific trigger)
- trigger_app_bundle_identifier (to get triggers assigned to a specific app)
(async ()=> {
let allNamedTriggersJSONString = await get_triggers({trigger_id: 643});
let allTriggersJSONArray = JSON.parse(allNamedTriggersJSONString);
let allNamedTriggerNames = [];
for(let trigger of allTriggersJSONArray) {
allNamedTriggerNames.push(trigger["BTTTriggerName"]);
}
returnToBTT(allNamedTriggerNames);
})();
add_new_trigger
This method will add a new trigger to BTT (i.e. gestures, shortcuts, touchbar items etc.).
You need to provide a JSON object defining the new trigger. To know how the JSON object should look like, right-click any existing trigger in BTT and choose "Copy JSON".
Optional Parameter: trigger_parent_uuid (if you want to add the trigger to a group)
Example
(async ()=> {
var newTriggerDefinition = {
"BTTTriggerClass" : "BTTTriggerTypeKeyboardShortcut",
"BTTPredefinedActionType" : 5,
"BTTPredefinedActionName" : "Mission Control",
"BTTAdditionalConfiguration" : "1179648",
"BTTTriggerOnDown" : 1,
"BTTEnabled" : 1,
"BTTShortcutKeyCode" : 2,
"BTTShortcutModifierKeys" : 1179648,
"BTTOrder" : 3
}
add_new_trigger({json: JSON.stringify(newTriggerDefinition)});
returnToBTT('done');
})();
delete_trigger
This method will delete a trigger from BetterTouchTool. You need to provide the uuid of the trigger you want to delete (get by right-clicking it in BTT).
Example
(async ()=> {
let result = await add_new_trigger({uuid: '2F34005D-4537-464D-94E9-A7F42DA39DF1'});
returnToBTT(result);
})();
get_clipboard_content
Gives you the current text content of the clipboard.
Example
(async ()=> {
let result = get_clipboard_content({});
returnToBTT(result);
})();
set_persistent_string_variable
This allows you to set a variable to a given string that persists over BTT relaunches.
Java Script for Automation Example:
(async ()=> {
let result = await set_persistent_string_variable({variable_name: 'test', to: "this is a test value"});
returnToBTT(result);
})();
set_string_variable
This allows you to set a variable to a given string that persists over BTT relaunches.
(async ()=> {
let result = await set_string_variable({variable_name: 'test', to: "this is a test value"});
returnToBTT(result);
})();
set_persistent_number_variable
This allows you to set a variable to a given string that persists over BTT relaunches.
(async ()=> {
let result = await set_persistent_number_variable({variable_name: 'test', to: 12345});
returnToBTT(result);
})();
set_number_variable
This allows you to set a variable to a given string that persists over BTT relaunches.
(async ()=> {
let result = await set_number_variable({variable_name:'OutputVolume' to: 0.5});
returnToBTT(result);
})();
get_number_variable
This allows you to retrieve the contents of a number variable with the given name
(async ()=> {
let result = await get_number_variable({variable_name:'OutputVolume'})
returnToBTT(result);
})();
get_string_variable
This allows you to retrieve the contents of a string variable with the given name
(async ()=> {
let result = await get_string_variable({variable_name:'BTTActiveAppBundleIdentifier'})
returnToBTT(result);
})();
export_preset
This allows you to export a specific preset
(async ()=> {
let result = await export_preset({name:'the_preset_name', compress: 1, includeSettings: 0, outputPath: '~/Downloads', comment: 'this is a great preset', link: 'https://someoptionallink', minimumVersion: '4.123'})
returnToBTT(result);
})();
display_notification
This shows a notification
(async ()=> {
let result = await display_notification({title:'notification title', subTitle: 'some subttitle', soundName: 'frog', imagePath: '~/Downloads/image.png})
returnToBTT(result);
})();
paste_text
This pastes some text
Parameters:
text: The text to paste.
insert_by_pasting: If set to true BTT will use cmd+v to paste the text. Otherwise it will actually try to type it (only possible for standard formats)
move_cursor_left_by_x_after_pasting: If specified BTT will move the cursor by the given amount to the left.
format: Optional. The format the text will be interpreted as when pasting (e.g. if you want to paste a table, you can use set the string to <table>...</table>
and specify the format NSPasteboardTypeHTML)
Default formats are (more are possible):
- NSPasteboardTypeString
- NSPasteboardTypeTIFF
- NSPasteboardTypePNG
- NSPasteboardTypeRTF
- NSPasteboardTypeRTFD
- NSPasteboardTypeHTML
- NSPasteboardTypeTabularText
- NSPasteboardTypeFont
- NSPasteboardTypeRuler
- NSPasteboardTypeColor
- NSPasteboardTypeSound
- NSPasteboardTypeMultipleTextSelection
- NSPasteboardTypeTextFinderOptions
- NSPasteboardTypeURL
- NSPasteboardTypeFileURL
(async ()=> {
let result = await paste_text({text:'<strong>Hello world</strong>', format: 'NSPasteboardTypeHTML'})
returnToBTT(result);
})();
reveal_element_in_ui
This will show a BTT trigger identified by it's UUID in the BetterTouchTool user interface.
Parameters: The UUID of the trigger (you can get the UUID of a trigger by right-clicking it or e.g. via the "get_triggers" scripting function)
async function someJavaScriptFunction() {
await reveal_element_in_ui("39CDAF30-98BB-4F54-B87F-273B6081149E");
return "done"";
}
get_menu_item_details
Gets the details (available, enabled, checked) of a given menubar menu item. This can be helpful to perform actions conditionally if a menu item is enabled/disabled.
Parameters: The path to the menu bar item E.g. "Edit;Cut" or "Window;Move & Resize; Left"
async function someJavaScriptFunction() {
let menuItemDetailsString = await get_menu_item_details("Edit;Cut");
let menuItemDetails = JSON.parse(menuItemDetailsString);
// The object looks like this: {"available": false, "enabled": false, "checked": false}
return menuItemDetails.enabled;
}
set_clipboard_content
Changes the content of the clipboard
Parameters:
content: The content to put in your clipboard.
format: Optional. The format the content will be interpreted as when pasting (e.g. if you want to paste a table, you can use set the string to <table>...</table>
and specify the format NSPasteboardTypeHTML)
Default formats are (more are possible):
- NSPasteboardTypeString
- NSPasteboardTypeTIFF
- NSPasteboardTypePNG
- NSPasteboardTypeRTF
- NSPasteboardTypeRTFD
- NSPasteboardTypeHTML
- NSPasteboardTypeTabularText
- NSPasteboardTypeFont
- NSPasteboardTypeRuler
- NSPasteboardTypeColor
- NSPasteboardTypeSound
- NSPasteboardTypeMultipleTextSelection
- NSPasteboardTypeTextFinderOptions
- NSPasteboardTypeURL
- NSPasteboardTypeFileURL
(async ()=> {
let result = await set_clipboard_content({content:'<strong>Hello world</strong>', format: 'NSPasteboardTypeHTML'})
returnToBTT(result);
})();
get-selected-text
To access the currently selected text, make use of the selected_text variable like this:
(async ()=> {
let selectedText = await get_string_variable({variable_name:'selected_text'})
returnToBTT(selectedText);
})();