Skip to content
Monomux edited this page May 11, 2025 · 11 revisions

The structure of event system is experimental and may be changed in the future.

Table of content

Usage

Basic operation

Desktop mode

  1. Click Record button to start recording

  2. Do anything like clicking mouse or tapping keyboard, which will be recorded

  3. Click Finish button to stop recording

  4. Click Launch button to reproduce the operation recorded in step 2

Command line mode

usage: KeymouseGo.py [-h] [-rt RUNTIMES]
                     sctipts [sctipts ...]

positional arguments:
  sctipts               Path for the scripts

optional arguments:
  -h, --help            show this help message and exit
  -rt RUNTIMES, --runtimes RUNTIMES
                        Run times for the script

Run specific script

> ./KeymouseGo scripts/0314_1452.txt

Run specific script for 3 times

> ./KeymouseGo scripts/0314_1452.txt -rt 3
> ./KeymouseGo scripts/0314_1452.txt --runtimes 3

Grammar of script

Assume that the resolution of screen is 1920 * 1080

{
  scripts: [
    // Press mouse right button at the relative coordinates `(0.05208, 0.1852)`(i.e. absolute coordinates `(100,200)`) after 3000ms
    // Note that relative coordinate and absolute coordinate are supported and the recorder uses relative coordinate by default
    {type: "event", event_type: "EM", delay: 3000, action_type: "mouse right down", action: ["0.05208%", "0.1852%"]},
    // Release mouse right button at the coordinates after 50ms
    // The mouse event will execute on the position that the cursor is currently in when the coordinate is set to [-1, -1]
    {type: "event", event_type: "EM", delay: 50, action_type: "mouse right up", action: [-1, -1]},
    // Press key 'f' after 1000ms
    {type: "event", event_type: "EK", delay: 1000, action_type: "key down", action: [70, 'F', 0]},
    // Release key 'f' after 50ms
    {type: "event", event_type: "EK", delay: 50, action_type: "key up", action: [70, 'F', 0]},
    // Press mouse left button at the relative coordinates `(0.2604, 0.4630)`(i.e. absolute coordinates `(500,500)`) after 100ms
    {type: "event", event_type: "EM", delay: 100, action_type: "mouse left down", action: ["0.2604%", "0.4630%"]},
    // Move mouse to the relative coordinates `(0.2604, 0.4630)`(i.e. absolute coordinates `(500,500)`) after 100ms
    {type: "event", event_type: "EM", delay: 100, action_type: "mouse move", action: ["0.2604%", "0.5556%"]},
    // Release mouse left button at the relative coordinates `(0.3125, 0.5556)`(i.e. absolute coordinates `(600,600)`) after 100ms
    {type: "event", event_type: "EM", delay: 100, action_type: "mouse left up", action: ["0.3125%", "0.5556%"]},
    // Input 'Hello world' at current coordinate after 100ms
    {type: "event", event_type: "EX", delay: 100, action_type: "input", action: "Hello world"}
  ]
}

The script uses json5 format, with each innermost JSON object representing an event:

Key/Mouse Event

Basic format

{
   // jsonobject
   type: "event",
   // The duration between this event and the previous event, measured in milliseconds.
   delay: /*<Int>*/ ,v
   // Mouse action or keyboard action: `EM` stands for mouse, `EK` stands for keyboard, `EX` stands for other extended action.
   event_type: /*<str>*/,
   /* Types of Actions:
    `mouse left down`: mouse left button pressed, `mouse left up`: mouse left button released,
    `mouse right down`: mouse right button pressed, `mouse right up`: mouse right button released,
    `mouse middle down`: mouse middle button pressed, `mouse middle up`: mouse middle button released,
    `mouse wheel up`: mouse wheel scrolled up, `mouse wheel down`: mouse wheel scrolled down,
    `key down`: keyboard key pressed, `key up`: keyboard key released,
    `mouse move` mouse moved to position, `input`: typing text.*/
   action_type: /*<str>*/,
   /* Specific action parameters
    Mouse action: consists of two sub-elements namely the horizontal and vertical coordinates [x, y] of the mouse's position on the screen. 
    When the coordinates are [-1, -1], it indicates that the operation is performed at the current location of the mouse. 
    Keyboard action: consists of three sub-elements: [key number, key name, extended marker]. 
    Input text action: the content of the text to be entered.*/
   action: /*<List>/<str>*/,
   // (Optional) Fill in the label of function to be called before execution in the list, multiple functions can be specified, and the corresponding plugins need to be registered in advance.
   call: [/* <str>... */],
   // (Optional) Label for goto; other types of jsonobjects can also have labels. Please ensure that different jsonobjects are given different labels.
   label: /*<str>*/,
   // (Optional) Other key-value parameters, can be read by plugin manager during registration.
   variable: value
}

Sequence Event

If you want to apply same action over a series of keyboard and mouse events, this type of event can be used to wrap events. The basic format is:

{
   // jsonobject
   type: "sequence",
   // Key/Mouse event list
   events: [/* ... */] ,
   // (Optional)The label of function to be called before each execution of event in the list. 
   // This can simplify the writing for a series of keyboard and mouse events that require adding the same calling function.
   attach: [/* <str>... */],
   // (Optional) Label for goto; other types of jsonobjects can also have labels. Please ensure that different jsonobjects are given different labels.
   label: <str>
}

Process Event

if

Basic format:

{
   // jsonobject
   type: "if",
   // The program first calls the function specified by `judge`. If it returns True, program will execute the events inside do, and execute events inside else if else.
   judge: "registered_function_name",
   do: [/* ... */],
   else: [/* ... */],
   // (Optional) Label for goto; other types of jsonobjects can also have labels. Please ensure that different jsonobjects are given different labels.
   label: <str>
}

goto

Basic format:

{
   // jsonobject
   type: "goto",
   // the label of jsonobject you wish the process to jump at
   tolabel: "label",
   // (Optional) Label for goto; other types of jsonobjects can also have labels. Please ensure that different jsonobjects are given different labels.
   label: <str>
}

Subprocess Event

It allows the program to run another script during execution. The basic format:

{
   // jsonobject
   type: "subroutine",
   // the other scripts to run sequentially
   path: ["subscript_path"],
   // (Optional) Label for goto; other types of jsonobjects can also have labels. Please ensure that different jsonobjects are given different labels.
   label: <str>
}

Custom Event

Simply run the registered function. The basic format:

{
   // jsonobject
   type: "custom",
   // the registered functions to run sequentially
   call: [/* <str>... */],
   // (Optional) Label for goto; other types of jsonobjects can also have labels. Please ensure that different jsonobjects are given different labels.
   label: <str>
}
  • Please strictly follow the format when making modifications, otherwise it may cause the script to fail to run. It is recommended to back up before making changes.
  • The program does not have detection protection for the execution process, please ensure that the execution flow of the script is reasonable.

Register a function

You can register custom functions by writing plugins. To write a plugin, you need to create a new 'plugins' folder in the program's working directory, and the directory structure of the plugin is like:

/plugins/Myplugin/

- manifest.json5

- .py files

- ...

format of manifest.json5

// manifest.json5
{
   "entry": "Example.py", // the file that implement the plugin interface
   "plugin_class": "MyExample", // the class that implement the plugin interface
   // (optional) Basic information
   "name": "Example",
   "description": "This is an example plugin",
   "author": "",
   "version": "1.0"
}

The plugin interface is defined as:

class PluginInterface:
    def __init__(self, manifest: Dict):
        self.meta = PluginMeta(manifest)

    # register function to be called during execution
    @abstractmethod
    def register_functions(self) -> Dict[str, Callable]:
        pass

register_functions needs to return a dictionary, where the key corresponds to the function's label. The values is the function to be registered itself. All registered functions will receive a parameter of type JsonObject, and by accessing the content property of that object, the properties of the currently executing object can be obtained. In the PluginInterface, the content of manifest.json5 is stored in self.meta as a JsonObject, and custom parameters added in the definition file can be accessed through this variable.

Example1:

The following content achieves the feature of modifying the delay of mouse and keyboard events. Before executing each mouse and keyboard event, the program will call the registered function labeled 'rd' and modify the passed delay parameter, thereby randomly providing an execution delay within a specified range.

// test.json5
{
    scripts: [
        {
            type: "sequence",
            events: [
                {
                    type: "event",
                    delay: "2-8",
                    event_type: "EM",
                    action_type: "mouse move",
                    action: ["0.39427083333333335%","0.45740740740740743%"]
                },
                {
                    type: "event",
                    delay: "1-514",
                    event_type: "EM",
                    action_type: "mouse move",
                    action: ["0.49270833333333336%","0.4398148148148148%"]
                }
            ],
            attach: ["rd"]
        },
    ]
}
// manifest.json5
{
   // 基本信息
   "name": "Random Delay",
   "description": "Make the format of delay becomes [0-9]+-[0-9]+,i.e. X-Y,where X is the minimal delay and Y is the maximum delay,X,Y are integer mesured in milliseconds",
   "author": "Monomux",
   "version": "1.0",
   "entry": "Random_Delay.py",
   "plugin_class": "RandomDelay",
   "enabled": true
}

Random_Delay.py

from typing import Dict, Callable, List
from Util.Parser import JsonObject
from Plugin.Interface import PluginInterface
from loguru import logger
import re
import random


class RandomDelay(PluginInterface):
    def __init__(self, manifest: Dict):
        super().__init__(manifest)

    def register_functions(self) -> Dict[str, Callable]:
        funcs: Dict[str, Callable] = {}

        def random_delay(json_object: JsonObject):
            delay: str = json_object.content['delay']
            # Prevent redundant modification
            if type(delay) == str:
                delays = re.match('([0-9]+)\-([0-9]+)', delay).groups()
                json_object.content['delay'] = random.randint(int(delays[0]), int(delays[1]))

        funcs['rd'] = random_delay
        return funcs

Example 2

Version 5.2 removed the speed modification feature, which can now be implemented through sequence events and function calls. The following content implements the feature of modifying the delay of mouse and keyboard events. Before executing each mouse and keyboard event, the program will call the registered function labeled 'rd' and modify the incoming delay parameter.

// test.json5
{
    scripts: [
        {
            type: "sequence",
            events: [
                {
                    type: "event",
                    delay: 50,
                    event_type: "EM",
                    action_type: "mouse move",
                    action: ["0.39427083333333335%","0.45740740740740743%"]
                },
                {
                    type: "event",
                    delay: 50,
                    event_type: "EM",
                    action_type: "mouse move",
                    action: ["0.49270833333333336%","0.4398148148148148%"]
                }
            ],
            attach: ["cs"]
        },
    ]
}

manifest.json5

{
   // 基本信息
   "name": "Configure Speed",
   "description": "Change the program execution speed, the parameter speed is an integer in the range (0, ∞). Note that the program itself also has execution delay, and the rate will become ineffective beyond a certain point.",
   "author": "Monomux",
   "version": "1.0",
   "entry": "Configure_Speed.py",
   "plugin_class": "ConfigureSpeed",
   "speed": 0.8 // Speed multiplier, increasing speed when greater than 1.
}

ConfigureSpeed.py

from typing import Dict, Callable, Any
from Util.Parser import JsonObject
from Plugin.Interface import PluginInterface
from loguru import logger


class ConfigureSpeed(PluginInterface):
    def __init__(self, manifest: Dict):
        super().__init__(manifest)

    def register_functions(self) -> Dict[str, Callable]:
        funcs: Dict[str, Callable] = {}

        def change_speed(jsonObject: JsonObject):
            delay: int = jsonObject.content['delay']
            factor = self.meta.speed
            jsonObject.content['delay'] = int(delay / factor)

        funcs['cs'] = change_speed

        return funcs
Clone this wiki locally