Scripting Guide
PaidExtend saMacros with custom JavaScript powered by GraalVM
The saMacros scripting system gives you a full-featured GraalVM JavaScript engine to automate complex workflows beyond simple recording. Listen to application events, simulate actions programmatically, read pixel colors, and integrate with external processes.
💳 Paid Feature
JavaScript scripting is a premium feature. Please ensure your license includes scripting access before following this guide.
📁 Installing Scripts
Place your .js script files inside the scripts directory in your saMacros data folder:
Scripts are loaded automatically at application startup. To reload scripts without restarting, use the Script Manager in Settings.
💡 Tip
The scripts/ directory is created automatically the first time saMacros runs. If it does not exist, launch the app once and the folder will appear.
🏷 Script Metadata
Every script must declare metadata variables at the top of the file, inside a // ==UserScript== block.
// ==UserScript==
var display_name = "My Awesome Script"; // Human-readable name shown in Script Manager
var register_name = "my_awesome_script"; // Unique ID — lowercase, snake_case
var author = "YourName"; // Single author name
var version = "1.0.0"; // Semantic version
var description = "What this script does."; // Short description
var available_version = "2.0.0~2.1.*"; // Compatible saMacros version range
var hard_dependencies = []; // Script IDs that MUST be enabled
var soft_dependencies = []; // Optional script IDs
var requireNativeAccess = false; // Set true for file I/O, network, etc.
var requireNativeAccessDescription = ""; // Explain why native access is needed
// ==/UserScript==
| Variable | Required | Notes |
|---|---|---|
| display_name | Yes | Shown in Script Manager UI. |
| register_name | Yes | Must be unique across all installed scripts. Used as the dependency identifier. |
| author | Yes | Used for author-level native access whitelisting. |
| version | Yes | Semantic version string. |
| available_version | No | Supports wildcard (*) and range (~) syntax. Use "*" for any version. |
| hard_dependencies | No | Array of register_name strings. Script will not load if any hard dep is missing. |
| soft_dependencies | No | Optional; script still loads if these are missing. |
| requireNativeAccess | No | If true, script is disabled by default until the user manually approves it. |
| requireNativeAccessDescription | No | Displayed in the security approval dialog. Explain clearly why native access is needed. |
📝 Basic Usage
The global mm object is your entry point to the saMacros API.
Logging
Write messages to the application log panel:
mm.log("Hello from my script!");
Listening to Events
Use mm.on(eventClassName, callback) to react to application events. The first argument is the full Java class name of the event:
// Log a message when the app launches
mm.on('io.github.samera2022.samacros.api.event.events.OnAppLaunchedEvent',
function(event) {
mm.log("App started! Version: " + event.getAppVersion());
}
);
// Log the number of actions when recording stops
mm.on('io.github.samera2022.samacros.api.event.events.AfterRecordStopEvent',
function(event) {
mm.log("Recording stopped. Actions captured: " + event.getActionCount());
}
);
Cancelling Events
Some events are cancellable — calling event.setCancelled(true) prevents the default action from occurring:
// Prevent recording from starting when a condition is not met
mm.on('io.github.samera2022.samacros.api.event.events.BeforeRecordStartEvent',
function(event) {
if (!isReadyToRecord()) {
event.setCancelled(true);
mm.log("Recording blocked: system not ready.");
}
}
);
Showing Notifications
var ctx = mm.getContext();
// Show a system tray notification
ctx.showToast("Task Complete", "Your macro finished successfully!");
🖱 Advanced Actions
Use mm.getContext() to access the ScriptContext for programmatic action simulation and screen reading.
Simulating Mouse Clicks
Pass an action object with the same fields used in recorded macros:
var ctx = mm.getContext();
// Left mouse button press at (960, 540)
ctx.simulate({ x: 960, y: 540, type: 1, button: 1, delay: 0 });
// Left mouse button release at same position
ctx.simulate({ x: 960, y: 540, type: 2, button: 1, delay: 80 });
// Right click
ctx.simulate({ x: 200, y: 300, type: 1, button: 2, delay: 100 });
ctx.simulate({ x: 200, y: 300, type: 2, button: 2, delay: 60 });
| type value | Meaning |
|---|---|
| 1 | Mouse button press |
| 2 | Mouse button release |
| 3 | Mouse wheel scroll |
| 10 | Key press |
| 11 | Key release |
Reading Pixel Colors
Use getPixelColor(x, y) to read a screen pixel and make conditional decisions:
var ctx = mm.getContext();
var color = ctx.getPixelColor(500, 300);
mm.log("R=" + color.getRed() + " G=" + color.getGreen() + " B=" + color.getBlue());
// Example: only click if the button is green
if (color.getGreen() > 200 && color.getRed() < 50) {
ctx.simulate({ x: 500, y: 300, type: 1, button: 1, delay: 0 });
ctx.simulate({ x: 500, y: 300, type: 2, button: 1, delay: 50 });
}
Reading App Configuration
var ctx = mm.getContext();
var config = ctx.getAppConfig();
var lang = config.getString("lang");
var darkMode = config.getBoolean("enableDarkMode");
mm.log("Language: " + lang + ", Dark mode: " + darkMode);
🔐 Native Access
Scripts requiring file I/O, network access, or external process execution must request Native Access explicitly.
Requesting Native Access
Set these variables in your script metadata:
var requireNativeAccess = true;
var requireNativeAccessDescription = "This script reads log files from the system to generate a report.";
What Happens at First Load
- 1saMacros detects
requireNativeAccess = truein the script file. - 2The script file is renamed to
.js.disabledand marked as disabled. - 3The Script Manager shows the script with a warning icon and your
requireNativeAccessDescription. - 4The user reviews the description and manually enables the script in Settings → Script Manager.
- 5The script is added to
white_list.json(by script name or author) and loads on next startup.
⚠ Best Practice
Only request native access when absolutely necessary. Write a clear, honest requireNativeAccessDescription — users see this in the security dialog and it directly affects their trust in your script.
🗂 Script Manager
Manage all installed scripts from Settings → Script Manager.
| Column / Action | Description |
|---|---|
| Enabled toggle | Enable or disable a script. Disabled scripts are not loaded and their listeners are removed. |
| Issues column | Displays warnings for missing dependencies, version mismatches, or pending native access approval. |
| Native Access button | Grant native access to a script or its author. A security warning is shown before approval. |
| Whitelist | Approved scripts/authors are stored in white_list.json and granted native access automatically on future startups. |
💡 Reload without restart
After placing a new script file in the scripts/ directory, use the Script Manager's reload button (or restart the app) to pick it up.
🔗 Dependencies
Scripts can declare dependencies on other scripts to ensure load order and availability.
Hard Dependencies
Declared in hard_dependencies. The dependent script will not load if any hard dependency is missing or disabled.
var hard_dependencies = ["utils_library", "network_helper"];
Soft Dependencies
Declared in soft_dependencies. The script still loads if these are absent, but enhanced features may be unavailable.
var soft_dependencies = ["optional_plugin"];
⚠ Circular Dependencies
If Script A lists Script B as a hard dependency, and Script B lists Script A, both will fail to load. Break the cycle by converting one link to a soft dependency or extracting shared code into a third script.
📖 mm — ScriptAPI Object
| Method | Parameters | Description |
|---|---|---|
| on() | eventClassName, callback | Register a listener for the named event. The first argument is the full Java class name of the event (see Event List). |
| log() | message | Print a message to the application's log panel. |
| getContext() | — | Returns the ScriptContext object for advanced interactions (see ScriptContext). |
| cleanup() | — | Unregister all event listeners registered by this script. Called automatically when the script is disabled. |
📖 mm.getContext() — ScriptContext
| Method | Returns | Description |
|---|---|---|
| simulate(action) | void | Execute a mouse/keyboard action via Java Robot. The action object must include x, y, type, button, delay fields. |
| getPixelColor(x, y) | Color | Read the pixel color at the given screen coordinates. Returns a Java Color with getRed(), getGreen(), getBlue() methods. |
| showToast(title, msg) | void | Show a system tray notification with the given title and message. Falls back to console output if system tray is unavailable. |
| getAppConfig() | IConfig | Returns the application configuration with getBoolean(key), getInt(key), getString(key) accessors. |
📋 Event List
All event classes live under the base package io.github.samera2022.samacros.api.event.events. Pass the full class name to mm.on().
| Event Class | Trigger | Cancellable? |
|---|---|---|
| OnAppLaunchedEvent | Application startup completes | No |
| BeforeRecordStartEvent | Recording is about to start | Yes |
| AfterRecordStopEvent | Recording has stopped | No |
| BeforePlaybackStartEvent | Playback is about to begin | Yes |
| BeforeStepExecuteEvent | Before each individual action during playback | Yes |
| AfterStepExecuteEvent | After each individual action completes | No |
| OnLoopCompleteEvent | After all actions in one loop iteration | No |
| OnInputCapturedEvent | A mouse/keyboard input is captured during recording | Yes |
| OnActionAddedEvent | An action is added to the recording list | Yes |
| OnMacroLoadEvent | A macro file has been loaded (content modifiable) | Yes |
| OnMacroSaveEvent | A macro is about to be saved (content modifiable) | Yes |
| OnConfigChangedEvent | A settings value has changed | No |
| OnMenuInitEvent | System tray menu is being built | No |
| OnTooltipDisplayEvent | A tooltip is about to be shown | No |
🔧 Troubleshooting
Common scripting issues and how to resolve them.
?
Script is not loading / stays disabled
Reason 1 — Native access required: The script has requireNativeAccess = true but has not been approved. Go to Settings → Script Manager and grant access.
Reason 2 — Renamed to .js.disabled: saMacros renames unapproved native-access scripts. Re-enable from the Script Manager.
Reason 3 — Missing hard dependency: A hard_dependencies entry is not installed or is disabled. Check the Issues column in Script Manager.
Reason 4 — Circular dependency: Two scripts each declare the other as a dependency. Break the cycle by converting one link to a soft dependency.
Reason 5 — Version mismatch: The available_version range does not include the running saMacros version. Update the metadata.
!
"ScriptAPI Error: Event class not found" in the log
Cause: You passed an incorrect or misspelled event class name to mm.on(). Event class names are case-sensitive and must include the full package path.
Example of a correct class name:
io.github.samera2022.samacros.api.event.events.OnAppLaunchedEvent
Refer to the Event List section for all valid names.
!
"Error executing script callback for event" in the log
Cause: An unhandled exception was thrown inside your event callback. The full stack trace is printed to the console.
- Wrap your callback body in a
try { ... } catch(e) { mm.log(e); }block to surface errors clearly. - Check that any variables you reference inside the callback are defined and in scope.
?
Macro playback is blocked by my script
Cause: Your script is listening to BeforePlaybackStartEvent or BeforeStepExecuteEvent and calling event.setCancelled(true) unintentionally.
- Add a conditional check before cancelling — ensure the event is only cancelled when you actually intend to block it.
- Temporarily disable your script in the Script Manager to verify it is the cause.