README.md

September 25, 2026 ยท View on GitHub

LOGO

MaaFramework Go Binding


English | ็ฎ€ไฝ“ไธญๆ–‡

Go binding for MaaFramework, a cross-platform automation testing framework based on image recognition.

๐Ÿš€ No Cgo Required! Pure Go implementation using purego.

โœจ Features

  • Cross-platform Controllers - ADB, Win32, Linux, macOS, PlayCover, and Android Native
  • Recording and Replay - Capture controller operations to JSONL, then replay them for debugging and regression testing
  • Virtual Gamepad Controller (Windows only) - Gamepad automation via ViGEm
  • Toolkit Utilities - Find ADB devices and desktop windows; manage macOS automation permissions
  • Image Recognition - Template matching, OCR, and feature detection
  • Custom Extensions - Custom recognitions, actions, and controllers in pure Go
  • Agent Support - Run custom recognition and action logic from an external process
  • Async Jobs and Events - Poll job status and task details, or subscribe to resource, controller, and tasker events
  • Pipeline and Runtime APIs - Declarative JSON task flows; run tasks, recognitions, and actions from a Context at runtime

๐Ÿ“ฆ Installation

Requires Go 1.24 or later.

1. Install Go Package

go get github.com/MaaXYZ/maa-framework-go/v4

2. Download MaaFramework

Download the MaaFramework Release for your platform and extract it.

PlatformArchitectureDownload
Windowsamd64MAA-win-x86_64-*.zip
Windowsarm64MAA-win-aarch64-*.zip
Linuxamd64MAA-linux-x86_64-*.zip
Linuxarm64MAA-linux-aarch64-*.zip
macOSamd64MAA-macos-x86_64-*.zip
macOSarm64MAA-macos-aarch64-*.zip
Androidamd64MAA-android-x86_64-*.zip
Androidarm64MAA-android-aarch64-*.zip

โš™๏ธ Runtime Requirements

Programs built with maa-framework-go require MaaFramework dynamic libraries at runtime. Provide them in one of these ways:

  1. Via Init() Option - Specify library path programmatically:

    maa.Init(maa.WithLibDir("path/to/MaaFramework/bin"))
    
  2. Working Directory - Place MaaFramework libraries in your program's working directory

  3. Environment Variables - Add library path to PATH (Windows), LD_LIBRARY_PATH (Linux), or DYLD_LIBRARY_PATH (macOS)

  4. System Library Path - Install libraries to system library directories

MaaFramework Compatibility

This binding tracks the latest MaaFramework release, including prereleases; compatibility with older releases is not guaranteed. A missing library or symbol generally means the installed MaaFramework is older than the release this binding targets.

Init requires all four libraries from one compatible release: MaaFramework, MaaToolkit, MaaAgentServer, and MaaAgentClient. Missing libraries or symbols make Init fail with a diagnostic error instead of leaving partial state behind.

Release unloads the libraries only after all native objects are destroyed and the Agent Server is shut down; otherwise it returns ErrLibraryInUse.

After AgentServerDetach, Release remains blocked for the rest of the process: the native API cannot confirm that the detached service thread has exited, even after AgentServerJoin or AgentServerShutDown. Keep the service thread attached if you need to release the libraries.

Calls to Init and Release are serialized, but other MAA operations must not run concurrently with either. If unloading fails, IsInited becomes false; retry Release to finish cleanup before calling Init again.

๐Ÿš€ Quick Start

package main

import (
	"fmt"
	"os"

	"github.com/MaaXYZ/maa-framework-go/v4"
)

func main() {
	if err := maa.Init(); err != nil {
		fmt.Println("Failed to init MAA:", err)
		os.Exit(1)
	}
	if err := maa.ConfigInitOption("./", "{}"); err != nil {
		fmt.Println("Failed to init config:", err)
		os.Exit(1)
	}
	tasker, err := maa.NewTasker()
	if err != nil {
		fmt.Println("Failed to create tasker")
		os.Exit(1)
	}

	devices, err := maa.FindAdbDevices()
	if err != nil {
		fmt.Println("Failed to find adb devices:", err)
		os.Exit(1)
	}
	device := devices[0]
	ctrl, err := maa.NewAdbController(
		device.AdbPath,
		device.Address,
		device.ScreencapMethod,
		device.InputMethod,
		device.Config,
		"path/to/MaaAgentBinary",
	)
	if err != nil {
		fmt.Println("Failed to create ADB controller")
		os.Exit(1)
	}
	defer ctrl.Destroy()
	ctrl.PostConnect().Wait()
	tasker.BindController(ctrl)

	res, err := maa.NewResource()
	if err != nil {
		fmt.Println("Failed to create resource")
		os.Exit(1)
	}
	defer res.Destroy()
	res.PostBundle("./resource").Wait()
	tasker.BindResource(res)
	defer tasker.Destroy()
	if !tasker.Initialized() {
		fmt.Println("Failed to init MAA.")
		os.Exit(1)
	}

	detail, err := tasker.PostTask("Startup").Wait().GetDetail()
	if err != nil {
		fmt.Println("Failed to get task detail:", err)
		os.Exit(1)
	}
	fmt.Println(detail)
}

Native object lifetime

NewTasker, NewResource, and controller constructors return objects that own their native handles. GetResource, GetController, Context.GetTasker, and event callbacks return borrowed views; calling Destroy on one returns ErrBorrowed. Repeated successful Destroy calls on an owner are safe. Destroy returns ErrInUse if a call or asynchronous job is active, even if the returned Job was discarded; retry after it finishes. Call Wait before destroying the owner if you need the job's outcome. After closing, methods that return an error report ErrClosed, and jobs expose it through Error().

Keep every resource and controller bound to a tasker alive until the tasker is destroyed, including earlier bindings after rebinding. Closing one before then returns ErrBound. Rebinding a running tasker returns ErrTaskerRunning. An AgentClient also keeps its bound resource and registered event sources alive until the client is destroyed. Destroying an owner from its callback returns ErrInCallback. A callback Context, including a clone, expires when the callback returns.

๐Ÿ“– Examples

For more examples, see the examples directory:

๐Ÿ“š Documentation

๐Ÿค Contributing

Bug reports, feature suggestions, and pull requests are welcome.

๐Ÿ“„ License

This project is licensed under the LGPL-3.0 License.

๐Ÿ’ฌ Community