Skip to content

Child Scripts

Although child scripts behave similarly to the main script, there are some key differences. First of all, child scripts are optional, whereas the main script is mandatory. Additionally, there are some differences in the signature of a child script class, which are outlined below.

Example Use Cases

Some use cases where a child script can be helpful: * When you want to monitor price drops and, once the price is below a certain amount, purchase the product with multiple accounts simultaneously. The price drop monitoring can be part of the main script, while the purchasing process can be handled by the child script. This setup results in a single task monitoring the price drop and multiple concurrent tasks performing the checkout. * When you first need to retrieve available products and, after retrieving the list, monitor each product for changes in stock. Retrieving the list of available products can be part of the main script, while a child script can be created to monitor individual products.

Creating a Child Script

An example implementation of a child script looks like this:

@ChildScript(
    id = "CHILD_SCRIPT_ID",
    name = "My child script",
)
class MyChildScript(private val parameters: ScriptParameters) : Script<MyChildConfiguration> {
    override suspend fun onStart(
        configuration: MyChildConfiguration,
        provider: ComponentProvider,
    ): Boolean {
        // TODO
        return true
    }

    override suspend fun execute(
        configuration: MyChildConfiguration,
        provider: ComponentProvider,
        statusUpdate: suspend (message: String) -> Unit,
    ): ExecutionResult {
        // TODO

        return ExecutionResult.Success("Success")
    }

    override suspend fun onFinish(
        configuration: MyChildConfiguration,
        provider: ComponentProvider,
    ) {
        // TODO
    }
}

The MyChildConfiguration interface is defined the same way as usual, as outlined on the script configuration page.

@ChildScript(

All child scripts require this annotation to be detected as a child script.

id = "CHILD_SCRIPT_ID",

This is the unique ID for your child script. When creating multiple child scripts, make sure this ID is unique within your package. You can create this ID yourself, but it must remain consistent across releases of your script to ensure that the state is correctly stored and restored by Cereal.

name = "My child script",

A human-readable name used as the display name for your script. It should, therefore, describe the purpose of the child script.

class MyChildScript(private val parameters: ScriptParameters) : Script<MyChildConfiguration> {

The script class optionally accepts ScriptParameters, which are provided when the child script is started by one of your other scripts. Since the child script implements the same Script interface as your main script, the methods you need to implement are identical.

Starting a Child Script

You can start a child script from within any other script using the ScriptLauncherComponent available in the ComponentProvider. For more information on component providers, see this page. The ScriptLauncherComponent contains a start method that accepts the child script class type and, optionally, any parameters you'd like to pass to the child script. A handle is returned, allowing you to poll the execution status of the child scripts that are started.

val scriptLauncher = provider.scriptLauncher()
val handle = scriptLauncher.start(MyChildScript::class.java)

The start method throws StartScriptException if the child script cannot be started.

Passing Data with ScriptParameters

ScriptParameters is a typed key-value bag for sending data from a parent script to a child script.

val params = ScriptParameters()
params.putString("productId", "ABC-123")
params.putInteger("quantity", 2)
params.putLong("startTime", System.currentTimeMillis())
params.putDouble("maxPrice", 49.99)
params.putFloat("discount", 0.1f)

val handle = scriptLauncher.start(MyChildScript::class.java, params)

Inside the child script, read the parameters from the constructor argument:

@ChildScript(id = "CHILD_SCRIPT_ID", name = "My child script")
class MyChildScript(private val parameters: ScriptParameters) : Script<MyChildConfiguration> {

    override suspend fun onStart(
        configuration: MyChildConfiguration,
        provider: ComponentProvider,
    ): Boolean {
        val productId = parameters.getString("productId")   // returns null if key not set
        val quantity = parameters.getInteger("quantity") ?: 1
        return true
    }
    // ...
}

Supported ScriptParameters types

Put method Get method Type
putString getString String
putInteger getInteger Int
putLong getLong Long
putDouble getDouble Double
putFloat getFloat Float
putByte getByte Byte
putShort getShort Short
putInstant getInstant Instant

All getters return null if the key has not been set. ScriptParameters is not thread-safe — confine access to a single coroutine.