Hi @Black_Simorgh,
If I understand your post correctly, you're trying to figure out how to setup a Trigger in BTT that would run some Actions to check if there are any updates available in BTT? Essentially, an automated way of clicking on the Check For Regular Updates or the Check For Alpha Version Updates menu items in the BTT menu bar like in the screenshot below. Is this correct?
First, regarding your issue with the "selecting from menu" trigger not working with BTT itself - that's an interesting problem. You're right that it works with other apps like Finder but not with BTT. While we wait for @Andreas_Hegenberg to consider your feature request for a direct update shortcut, I have an alternative solution that might be even more reliable.
I'll share with you a shell script I wrote to automate the process of sharing my BTT device details which includes information about whether my installed version of BTT is behind for any regular or alpha updates. The advantage of this approach is that it directly checks the update sources online rather than depending on UI elements, making it more reliable than menu selection.
#!/bin/bash
###############################################################################
# BetterTouchTool Version Checker CLI - With Improved Structured Logging
###############################################################################
# Purpose: This script checks your installed BetterTouchTool version and compares
# it with the latest available alpha and regular versions. It provides formatted
# output for easy sharing of your BTT environment information.
#
# INSTALLATION & USAGE INSTRUCTIONS:
# 1. Save this file as "get_BTT_info.sh" in a location of your choice
# 2. Make the script executable by running this command in Terminal:
# chmod +x /path/to/get_BTT_info.sh
# 3. Run the script using one of these methods:
# a) ./get_BTT_info.sh (if you're in the same directory)
# b) /path/to/get_BTT_info.sh (using the full or relative path)
# c) bash /path/to/get_BTT_info.sh (if you didn't make it executable)
#
# By default, output is copied to clipboard in markdown format for easy forum posting
# Use -h or --help to see all available options
###############################################################################
# Default configurations
log_level="NONE" # Options: NONE, ERROR, WARNING, INFO, DEBUG
output_format="markdown" # Options: markdown, plain, json
copy_to_clipboard=true # Set to false with '-n' or '--no-copy'
colorize_logs=false # Enable colored logging with '--color'
# Initialize variables for installed version and build
installed_version=""
installed_build=""
# Track if we had to increment the build
incremented_build=false
original_installed_build=""
# Required utilities that must be available on the system
required_utilities=(
"curl" # For fetching online version information
"grep" # For text pattern matching
"awk" # For text processing
"sed" # For string manipulation
"ioreg" # For hardware information
"defaults" # For reading macOS property lists
"/usr/libexec/PlistBuddy" # For advanced property list manipulation
"sw_vers" # For macOS version information
"pbcopy" # For copying output to clipboard
)
# ANSI Color Codes (if --color is enabled)
RED="\033[0;31m"
GREEN="\033[0;32m"
BLUE="\033[0;34m"
YELLOW="\033[0;33m"
WHITE="\033[0;37m"
RESET="\033[0m"
# Display help and usage information
usage() {
cat << EOF
Usage: $0 [options]
Basic Options:
-l, --log-level LEVEL Set the logging level (NONE, ERROR, WARNING, INFO, DEBUG). Default: NONE
-o, --output-format FORMAT Set output format (markdown, plain, json). Default: markdown
-c, --copy Copy output to clipboard (enabled by default)
-n, --no-copy Do not copy output to clipboard
--color Enable colored logs
-h, --help Display this help message and exit
Advanced/Testing Options:
-v, --installed-version VERSION Manually specify BTT version (bypasses auto-detection)
-b, --installed-build BUILD Manually specify BTT build (bypasses auto-detection)
Examples:
$0 # Auto-detect installed BTT version
$0 -l DEBUG -o plain # Set log level to DEBUG and output format to plain text
$0 -o json -n # Set output format to JSON and do not copy to clipboard
$0 --color # Enable colored logs
$0 -v 4.702 -b 2024091705 # Testing: Simulate a specific BTT version/build
EOF
exit 1
}
# Convert log level string to numeric value for comparison
log_level_num() {
case "$1" in
"NONE") echo -1 ;;
"ERROR") echo 0 ;;
"WARNING") echo 1 ;;
"INFO") echo 2 ;;
"DEBUG") echo 3 ;;
*) echo -1 ;; # Default to NONE
esac
}
# Logging function with configurable levels and formatting
log() {
# Field widths for alignment
local level_width=20
local func_width=25
local caller_function="${FUNCNAME[1]}"
[ -z "$caller_function" ] && caller_function="main"
local level=$1
shift
local message="$*"
local current_level
current_level=$(log_level_num "$log_level")
local message_level
message_level=$(log_level_num "$level")
# Only output if message level is less than or equal to current log level
if [ "$current_level" -ge "$message_level" ]; then
local color=""
if [ "$colorize_logs" = true ]; then
case "$level" in
"ERROR") color="$RED" ;;
"WARNING") color="$YELLOW" ;;
"INFO") color="$GREEN" ;;
"DEBUG") color="$BLUE" ;;
*) color="$WHITE" ;;
esac
printf "%-${level_width}b %-${func_width}b %b\n" \
"[$color$level$RESET]" \
"[$caller_function]" \
"${WHITE}$message$RESET" >&2
else
printf "%-${level_width}s %-${func_width}s %s\n" \
"[$level]" \
"[$caller_function]" \
"$message" >&2
fi
fi
}
# Check if all required utilities are available on the system
check_requirements() {
local missing_utilities=()
for util in "${required_utilities[@]}"; do
util_path=$(command -v "$util")
if [ -x "$util_path" ]; then
log "DEBUG" "Found utility '$util' at '$util_path'"
else
log "ERROR" "Required utility '$util' not found in PATH."
missing_utilities+=("$util")
fi
done
# Exit if any required utilities are missing
if [ "${#missing_utilities[@]}" -ne 0 ]; then
log "ERROR" "Missing utilities: ${missing_utilities[*]}"
exit 1
fi
}
# Compare two version numbers (e.g., 4.702 vs 4.705)
# Returns 1 if ver1 > ver2, 0 if equal, -1 if ver1 < ver2
version_compare() {
local ver1="$1"
local ver2="$2"
IFS='.' read -ra ver1_parts <<< "$ver1"
IFS='.' read -ra ver2_parts <<< "$ver2"
local max_index=$(( ${#ver1_parts[@]} > ${#ver2_parts[@]} ? ${#ver1_parts[@]} : ${#ver2_parts[@]} ))
# Compare each part of the version number
for ((i=0; i<max_index; i++)); do
local part1=${ver1_parts[i]:-0}
local part2=${ver2_parts[i]:-0}
if ((10#$part1 > 10#$part2)); then
log "DEBUG" "$ver1 > $ver2"
echo 1
return
elif ((10#$part1 < 10#$part2)); then
log "DEBUG" "$ver1 < $ver2"
echo -1
return
fi
done
log "DEBUG" "$ver1 == $ver2"
echo 0
}
# Get installed BTT version from the application bundle
get_installed_version() {
if [ -z "$installed_version" ] || [ -z "$installed_build" ]; then
# Read version and build from BTT's Info.plist
installed_version=$(defaults read "/Applications/BetterTouchTool.app/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null)
installed_build=$(defaults read "/Applications/BetterTouchTool.app/Contents/Info.plist" CFBundleVersion 2>/dev/null)
fi
# Exit if BTT is not installed or inaccessible
if [ -z "$installed_version" ] || [ -z "$installed_build" ]; then
log "ERROR" "BetterTouchTool not installed or Info.plist inaccessible."
exit 1
fi
log "DEBUG" "installed_version: $installed_version"
log "DEBUG" "installed_build: $installed_build"
original_installed_build="$installed_build"
installed_version_full="$installed_version ($installed_build)"
log "DEBUG" "installed_version_full: $installed_version_full"
}
# Fetch available alpha versions from the BTT website
fetch_alpha_versions() {
# Parse the releases page to extract alpha version information
available_versions=$(curl -s https://folivora.ai/releases/ | grep -o '"btt[0-9]\+\.[0-9]\+-[0-9]\+\.zip"')
formatted_versions=$(echo "$available_versions" | sed -E 's/"btt([0-9]+\.[0-9]+)-([0-9]+)\.zip"/\1 (\2)/')
# Get the latest alpha version (first in the list)
latest_alpha_version=$(echo "$formatted_versions" | head -n 1)
# Log available alpha versions for debugging
number_of_alpha_versions_to_log=10
log "DEBUG" "Available alpha versions (displaying $number_of_alpha_versions_to_log most recent): $(echo "$formatted_versions" | head -n $number_of_alpha_versions_to_log | tr '\n' ' ')"
}
# Determine status relative to alpha versions
determine_alpha_status() {
# Find the position of installed version in the available alpha versions list
installed_index=$(echo "$formatted_versions" | awk -v installed="$installed_version_full" '
{
if ($0 == installed) {
print NR;
exit
}
}')
# Set the message based on whether the installed version was found
if [ -z "$installed_index" ]; then
log "WARNING" "Installed version not found in available alpha versions."
alpha_version_message="Installed alpha version not found."
else
log "DEBUG" "Installed version found at index $installed_index"
alpha_updates_behind=$((installed_index - 1))
alpha_version_message="$alpha_updates_behind update(s) behind latest Alpha version $latest_alpha_version."
fi
}
# Fetch the latest regular version from the BTT website
fetch_regular_version() {
log "INFO" "Fetching latest regular version..."
# Extract version numbers from the release notes page
regular_versions=$(curl -s https://updates.folivora.ai/bettertouchtool_release_notes.html | grep -Eo 'BetterTouchTool >= [0-9]+\.[0-9]+' | sed -E 's/BetterTouchTool >= //g' | sort -V)
latest_regular_version=$(echo "$regular_versions" | tail -n 1)
log "INFO" "Latest regular version: $latest_regular_version"
}
# Compare installed version with the latest regular version
determine_regular_status() {
# Compare versions using the version_compare function
comparison_result=$(version_compare "$installed_version" "$latest_regular_version")
# Set message based on comparison result
if [ "$comparison_result" -eq 0 ]; then
regular_version_message="Up to date with latest Regular version $latest_regular_version."
elif [ "$comparison_result" -eq 1 ]; then
regular_version_message="Installed version is ahead of latest Regular version $latest_regular_version."
else
regular_version_message="Installed version is behind latest Regular version $latest_regular_version."
fi
log "INFO" "$regular_version_message"
}
# Get system information (device model and macOS version)
get_system_info() {
# Get device model information
device_info="$(/usr/libexec/PlistBuddy -c 'Print :0:product-name' /dev/stdin <<< "$(ioreg -arc IOPlatformDevice -k product-name)")"
# Get macOS version information
macos_build="$(sw_vers -buildVersion)"
macos_product_version="$(sw_vers -productVersion)"
# Determine if running a beta version of macOS
if [[ "$macos_build" =~ [a-zA-Z]$ ]]; then
macos_version="$macos_product_version Beta ($macos_build)"
else
macos_version="$macos_product_version ($macos_build)"
fi
log "DEBUG" "device_info: $device_info"
log "DEBUG" "macos_version: $macos_version"
}
# Generate the final output in the specified format
generate_output() {
# Handle special case where build number was incremented
if [ "$incremented_build" = true ]; then
installed_version_full="$installed_version ($original_installed_build)"
alpha_version_message="1 uninstallable update behind latest Alpha version $latest_alpha_version"
fi
# Generate output based on the selected format
if [ "$output_format" == "markdown" ]; then
output_content=$(cat << EOF
- **Device**: $device_info
- **macOS**: $macos_version
- **BTT Version**: $installed_version_full
- **Alpha**: $alpha_version_message
- **Regular**: $regular_version_message
EOF
)
elif [ "$output_format" == "plain" ]; then
output_content=$(cat << EOF
Device: $device_info
macOS: $macos_version
BTT Version: $installed_version_full
Alpha: $alpha_version_message
Regular: $regular_version_message
EOF
)
elif [ "$output_format" == "json" ]; then
output_content=$(printf "{\n \"Device\": \"%s\",\n \"macOS\": \"%s\",\n \"BTT_Version\": \"%s\",\n \"Alpha\": \"%s\",\n \"Regular\": \"%s\"\n}" \
"$device_info" \
"$macos_version" \
"$installed_version_full" \
"$alpha_version_message" \
"$regular_version_message")
else
log "ERROR" "Unsupported output format: $output_format"
exit 1
fi
# Print the output to standard output
echo "$output_content"
# Copy to clipboard if enabled
if [ "$copy_to_clipboard" = true ]; then
echo "$output_content" | pbcopy
log "INFO" "Output copied to clipboard."
fi
}
# Main function to orchestrate the script execution
main() {
# Parse command-line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
-v|--installed-version)
installed_version="$2"
shift 2
;;
-b|--installed-build)
installed_build="$2"
shift 2
;;
-l|--log-level)
log_level="$2"
shift 2
;;
-o|--output-format)
output_format="$2"
shift 2
;;
-c|--copy)
copy_to_clipboard=true
shift
;;
-n|--no-copy)
copy_to_clipboard=false
shift
;;
--color)
colorize_logs=true
shift
;;
-h|--help)
usage
;;
*)
log "ERROR" "Unknown parameter passed: $1"
usage
;;
esac
done
# Validate log level
case "$log_level" in
NONE|ERROR|WARNING|INFO|DEBUG)
;;
*)
log "ERROR" "Invalid log level: $log_level"
usage
;;
esac
# Validate output format
case "$output_format" in
markdown|plain|json)
;;
*)
log "ERROR" "Invalid output format: $output_format"
usage
;;
esac
# Log the configuration
log "INFO" "Configuration: log_level=$log_level, output_format=$output_format, copy_to_clipboard=$copy_to_clipboard, installed_version=$installed_version, installed_build=$installed_build, colorize_logs=$colorize_logs"
# Execute the main workflow
check_requirements
get_installed_version
fetch_alpha_versions
determine_alpha_status
# Handle the case where installed version wasn't found in alpha versions
if [[ "$alpha_version_message" == "Installed alpha version not found." ]]; then
log "DEBUG" "Installed version not found. Trying with incremented build number..."
installed_build=$((installed_build + 1))
installed_version_full="$installed_version ($installed_build)"
incremented_build=true
log "DEBUG" "Retesting with incremented installed_build: $installed_version_full"
determine_alpha_status
fi
fetch_regular_version
determine_regular_status
get_system_info
generate_output
log "INFO" "Script completed successfully."
}
# Start the script
main "$@"
For my setup, I have a Key Sequences / Typed Words trigger in BTT such that whenever I type /BTTD
, it triggers a Run Shell Script action (which executes $HOME/projects/BTT/get_BTT_info.sh
) followed by a Send Keyboard Shortcut action (specifically, ⌘ V) so that the /BTTD
text gets replaced by the following:
- Device: MacBook Pro (14-inch, 2021)
- macOS: 15.3.1 (24D70)
- BTT Version: 5.252 (2025030501)
- Alpha: 0 update(s) behind latest Alpha version 5.252 (2025030501).
- Regular: Installed version is ahead of latest Regular version 5.140.
How the script works: It reads your installed BTT version from the application bundle, then fetches the latest available alpha and regular versions from the BTT websites. It compares these versions to determine if you're behind on updates. This approach bypasses the UI entirely, which makes it more reliable than trying to navigate menus.
You could use the same script for your needs. For you, you could setup a Keyboard Shortcut trigger combined with a Run Shell Script action to display a notification with information about if there are any updates. For example:
alpha_updates="$($HOME/projects/BTT/get_BTT_info.sh -o json | jq -r '.Alpha')"
osascript -e "display notification \"$alpha_updates\" with title \"get_BTT_info.sh\""
Feel free to customize the notification text or modify the script to show only what you need. For example, you could change it to only show notifications when updates are actually available, or format the message differently.
I just tested my script and it works. Here's a full BTT configuration JSON. Whenever you press fn F9, you'll see a notification saying, for example, "0 update(s) behind latest Alpha version 5.252 (2025030501)."
[
{
"BTTActionCategory" : 0,
"BTTLastUpdatedAt" : 1741312247.5872312,
"BTTTriggerType" : 0,
"BTTTriggerClass" : "BTTTriggerTypeKeyboardShortcut",
"BTTUUID" : "0847DA89-AAEE-437F-A30B-6CA0C8860072",
"BTTPredefinedActionType" : 366,
"BTTPredefinedActionName" : "Empty Placeholder",
"BTTAdditionalConfiguration" : "8388608",
"BTTKeyboardShortcutKeyboardType" : 2302,
"BTTTriggerOnDown" : 1,
"BTTLayoutIndependentChar" : "F9",
"BTTEnabled" : 1,
"BTTEnabled2" : 1,
"BTTShortcutKeyCode" : 101,
"BTTShortcutModifierKeys" : 8388608,
"BTTOrder" : 1,
"BTTAutoAdaptToKeyboardLayout" : 0,
"BTTAdditionalActions" : [
{
"BTTActionCategory" : 0,
"BTTLastUpdatedAt" : 1741312514.4169679,
"BTTTriggerParentUUID" : "0847DA89-AAEE-437F-A30B-6CA0C8860072",
"BTTIsPureAction" : true,
"BTTTriggerClass" : "BTTTriggerTypeKeyboardShortcut",
"BTTUUID" : "5C62B509-C161-48C2-A9EF-3FDFE18E897F",
"BTTPredefinedActionType" : 206,
"BTTPredefinedActionName" : "Execute Shell Script or Task",
"BTTShellTaskActionScript" : "alpha_updates=\"$($HOME\/projects\/BTT\/get_BTT_info.sh -o json | jq -r '.Alpha')\"\nosascript -e \"display notification \\\"$alpha_updates\\\" with title \\\"get_BTT_info.sh\\\"\"\n",
"BTTShellTaskActionConfig" : "\/bin\/bash:::-c:::-:::",
"BTTKeyboardShortcutKeyboardType" : 0,
"BTTEnabled" : 1,
"BTTEnabled2" : 1,
"BTTShortcutKeyCode" : -1,
"BTTOrder" : 729,
"BTTAutoAdaptToKeyboardLayout" : 0
}
]
}
]
I hope this makes sense! Let me know if you have any trouble.