saMacros Icon
saMacros
Paid Feature v2.0.0 User Manual API Docs

Scripting Guide

Paid

Extend 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.

GraalVM JS Engine Event-Driven API Sandboxed & Secure Native Access Control

💳 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:

Windows: %USERPROFILE%\AppData\saMacros\scripts\
macOS / Linux: ~/.local/share/saMacros/scripts/

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_nameYesShown in Script Manager UI.
register_nameYesMust be unique across all installed scripts. Used as the dependency identifier.
authorYesUsed for author-level native access whitelisting.
versionYesSemantic version string.
available_versionNoSupports wildcard (*) and range (~) syntax. Use "*" for any version.
hard_dependenciesNoArray of register_name strings. Script will not load if any hard dep is missing.
soft_dependenciesNoOptional; script still loads if these are missing.
requireNativeAccessNoIf true, script is disabled by default until the user manually approves it.
requireNativeAccessDescriptionNoDisplayed 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
1Mouse button press
2Mouse button release
3Mouse wheel scroll
10Key press
11Key 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

  1. 1saMacros detects requireNativeAccess = true in the script file.
  2. 2The script file is renamed to .js.disabled and marked as disabled.
  3. 3The Script Manager shows the script with a warning icon and your requireNativeAccessDescription.
  4. 4The user reviews the description and manually enables the script in Settings → Script Manager.
  5. 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 toggleEnable or disable a script. Disabled scripts are not loaded and their listeners are removed.
Issues columnDisplays warnings for missing dependencies, version mismatches, or pending native access approval.
Native Access buttonGrant native access to a script or its author. A security warning is shown before approval.
WhitelistApproved 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?
OnAppLaunchedEventApplication startup completesNo
BeforeRecordStartEventRecording is about to startYes
AfterRecordStopEventRecording has stoppedNo
BeforePlaybackStartEventPlayback is about to beginYes
BeforeStepExecuteEventBefore each individual action during playbackYes
AfterStepExecuteEventAfter each individual action completesNo
OnLoopCompleteEventAfter all actions in one loop iterationNo
OnInputCapturedEventA mouse/keyboard input is captured during recordingYes
OnActionAddedEventAn action is added to the recording listYes
OnMacroLoadEventA macro file has been loaded (content modifiable)Yes
OnMacroSaveEventA macro is about to be saved (content modifiable)Yes
OnConfigChangedEventA settings value has changedNo
OnMenuInitEventSystem tray menu is being builtNo
OnTooltipDisplayEventA tooltip is about to be shownNo

🔧 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.