Skip to content

Writing Custom Scripts

Erik Bigler edited this page Nov 29, 2017 · 50 revisions

Table Of Contents

Overview

Firebot allows you to fire off custom scripts when a button is pressed. This enables users to extend Firebot's capabilities beyond the base app. As of v4.2, Firebot also supports callbacks that will allow custom scripts to trigger any effect that a button can do. Custom scripts must be written in JavaScript and are treated as a Node module, so at least a basic understanding of JavaScript will be required.

DISCLAIMER: USE THIS AT YOUR OWN RISK

Custom scripts from unknown sources can pose a risk to your system. Use with discretion.

Simple script

There are only two requirements for a valid bare-bones custom script:

  • a function that takes in one argument:
    • a runRequest object which will contain: buttonId, username, parameters, and modules
  • that function is exported under the name run so it's visible to Node (and therefore Firebot).

Example

function run(runRequest) {
    var buttonId = runRequest.button.name;
    var username = runRequest.user.name;

    // Do some stuff
}

// Export 'run' function so it is visible to Node
exports.run = run;

runRequest

Note that the runRequest argument will now contain all the properties you might need. Currently, runRequest will look like this:

runRequest = {
    parameters: object or null,   // this will contain any parameters the script has defined 
    user: {
        name: string,             // The username of the viewer who pressed the button/issued command
        roles: array              // List of mixer roles the user is in (aka mod, sub, etc)    
    },
    triggerType: string,         // how the script was triggered. Options: interactive, command, custom_script, api
    button: {                    // null if triggerType != interactive
        name: string,            // The id of the button that was pressed
        meta: object or null     // any meta data saved in the mixer dev lab
    },
    command: {             // null if triggerType != command
        trigger: string,   // the command trigger
        args: array        // an array of args that user inputted
    }, 
    modules: object        // Contains commonly used Node modules. Options: request, spawn, fs, path
}

Advanced Uses

Custom Scripts can also define parameters that allow users to tweak behavior, or callback to tell Firebot to run any effect a button can.

Parameters

Defining parameters allow you to write one script that can be dynamically changed by each button it's attached to. Input UI elements for parameters are automatically generated in the Edit Button modal (where you select what script a button should run). Parameters are then sent back to the scripts run function whenever the button is pressed.

To define parameters, you must have a function that returns a Promise object that resolves with an object containing parameter objects. That function must be exported to node as getDefaultParameters.

function getDefaultParameters() {
    return new Promise((resolve, reject) => {
        resolve({
            "name": {
                "type": "string",
                "description": "What is your name?"
            },
            "shouldWhisper": {
                "type": "boolean",
                "description": "Whisper button presser",
                "default": false
            }
        });
    });
}
exports.getDefaultParameters = getDefaultParameters;

You can access the parameters in your run function via the runRequest argument.

Assuming your parameters were defined using the example above, you could access them as such:

function run(runRequest) {

    var name = runRequest.parameters.name;
    var shouldWhisper = runRequest.parameters.shouldWhisper;
    
    // Do something
}

Types

Each parameter must define a type. The type is what is used to determine what input element to use when generating the UI controls. Here is the list of supported types and their corresponding UI element:

  • string : text field
  • number : text field limited to numbers only
  • boolean : checkbox
  • enum : dropdown select list
  • filepath : text field with 'choose' button that opens up a file picker

Other things parameters can define

  • description : This will be used as the text above the UI element. If no description is proved, the parameter name will be used instead.
  • default : The default value to use
  • showBottomHr : This will add a horizontal rule below the parameter, giving some extra padding. Good for visually grouping parameters.

Special cases

  • The enum paramater type must also define an options property that is an array of strings that the user can pick from.
  • The string parameter type can optionally include useTextArea: true to use a multiline text area instead of a text field.

Effects

If you want to callback to Firebot to run effects, you must return a Promise object and resolve with a response object. This will allow you to run asynchronous code without stalling Firebot.

Response Example

function run(runRequest) {
  // Return a Promise object
  return new Promise((resolve, reject) => {
    var buttonId = runRequest.buttonId;
    var username = runRequest.username;
    // Do some stuff

    // Create a response 
    var response = {
      success: true,
      errorMessage: "Failed to run the script!", // If 'success' is false, this message is shown in a Firebot popup.
      effects: [ // An array of effect objects to run
          {
              type: EffectType.CHAT,
              message: "Hello chat!",
              chatter: "Streamer"
          },
          {
              type: EffectType.PLAY_SOUND,
              volume: 5,
              file: "C:\some\file\path"
          }
      ] 
    }

    // Resolve Promise with the response object
    resolve(response);
  });
}
// Export 'run' function so it is visible to Node
exports.run = run;

Building Effect Objects

Each effect object requires a type that tells Firebot what kind of effect the object is. We recommend you use our EffectType enum instead of hard coding the type string. You can see the list of all effect types here.

The rest of the effect object depends on the type of effect. The best way to know what an effect object should look like for a particular effect is to create the effect manually on a button and then view the JSON file for your interactive board.

You can view your board's JSON file by: Open Firebot > Settings > Open Root Folder > user-settings > controls Then pick the .json file for the board you want.

In the json, look for your button's effects under firebot/controls/your_button_name/effects

For example, if you were to create a "Show Image" effect, you would find this in your JSON:

Effects are run in order. If you want a delay between effects, include the "Delay" effect just like you would with a button.

Script Manifest

As of v4.5, scripts can now define a manifest. These fields all get displayed in the UI.

exports.getScriptManifest = function() {
	return {
		name: "Random Pictures",
		description: "Selects a random picture from a folder of pictures.",
		author: "ebiggz",
		version: "1.4",
		website: "https://twitter.com/ItsEbiggz"
	}
}

Would show up as:

Other Examples

Mixer API Example

Here is an example of getting information from the Mixer API about a user who pushed the button, and then broadcasting that to chat. In this case we get a user's level, sparks, and experience. Note that I am using the 'request' module. This is accessible via the modules property of the runRequest.

function run(runRequest) {
  var buttonId = runRequest.button.name;
  var username = runRequest.user.name;

  const request = runRequest.modules.request;

  // Return a Promise object
  return new Promise((resolve, reject) => {
    var url = 'https://mixer.com/api/v1/channels/'+username;
      
	request(url, function (error, response, data) {

          var response = {};

	  if (!error) {
            // Got response from Mixer.
            var data = JSON.parse(data);

            // Find the data we want in the Mixer json.
            var level = data['user'].level;
            var sparks = data['user'].sparks;
            var exp = data['user'].experience;

            // Build our message.
            var message = username+" || Level: "+level+" || Sparks: "+sparks+" || Exp: "+exp;

            // Create a success response 
            response = {
                success: true,
                effects:[
                    {
                        type: EffectType.CHAT,
                        message: message,
                        chatter: "Streamer"
                    }
                ]                  
            }
	  } else {
            // We had an error with the mixer request. So, create an error popup in Firebot.

            // Create a failed response 
            response = {
              success: false,
              errorMessage: 'There was an error retrieving data from the Mixer API.'                         
            }
         }
         // Resolve Promise with the response object
         resolve(response);
     })
  });
}

// Export 'run' function so it is visible to Node
exports.run = run;

Scripts by the Community

ThePerry: I'm currently working on making a custom script for 7 Days to Die: https://gist.github.com/ThePerry-2015/ad60287370400cd810441964b79b0289

It's not adjusted for A16 yet since it relies on Valmod Expansion Pack, and Valmod has had some RL stuff to take care of so he hasn't gotten around to update yet. I'll update it for A16 as soon as Valmod Expansion Pack is released.

Clone this wiki locally