Botkit Studio is a hosted development tool that enhances and expands the capabilities of Botkit. While developers may use Botkit without Studio, a Studio account will substantially ease the development and deployment of a Bot, help to avoid common coding pitfalls, a valuable management interface for the bot's dialog content and configuration. Botkit Studio is a product of Howdy.ai, the creators of Botkit.

This document covers the Botkit Studio SDK details only. Start here if you want to learn about to develop with Botkit.

For bot developers who have existing apps but would like to benefit from features like bot-specific content management without using Botkit, you can access the Botkit Studio SDK here

Table of Contents

The goal of Botkit Studio is to make building bots with Botkit easier than ever before. The tools and this SDK are based on feedback from dozens of Botkit developers and more than a year of building and operating our flagship bot, Howdy. However, our intent is not to cover every base needed for building a successful bot. Rather, we focus on several key problem areas:

Botkit Studio authoring tool

The screenshot above shows the script authoring tool, where bot developers can write dialog, manage conversation threads, define variables to capture user input, and set triggers that will automatically activate the bot.

Before you get started, it is important to note that Botkit Studio is an extra set of features built on top of all of the other existing Botkit features and plugins, and all of the documentation and tutorials for those features applies to bots built with Studio as well.

If you already have a Botkit bot, you may want to start here.

The instructions below cover setting up a bot on a Slack team. However, you may also use any of the other Botkit connectors to operate a bot on Facebook, Twilio, or any other supported platform. Follow the instructions for configuring Botkit on those dedicated pages, then pick up below with the additional Studio configuration steps.

1) Register for a developer account at Botkit Studio

2) Download the Botkit Studio Starter Kit for Slack or for Facebook Messenger. This code repository contains a fully functioning bot configured to work with Botkit Studio, and code examples demonstrating all of the key features. It also contains supporting material for the tutorial included here.

3) Make a bot integration inside of your Slack channel. Go here:

https://my.slack.com/services/new/bot

Enter a name for your bot. Make it something fun and friendly, but avoid a single task specific name. Bots can do lots! Let's not pigeonhole them.

When you click "Add Bot Integration", you are taken to a page where you can add additional details about your bot, like an avatar, as well as customize its name & description.

Copy the API token that Slack gives you. You'll need it.

4) Inside Botkit Studio, locate the "API Keys" tab for your bot account, and copy the token. Now you've got everything you need!

4) With both tokens in hand, run the starter kit bot application from your command line:

token=REPLACE_THIS_WITH_YOUR_TOKEN studio_token=REPLACE_WITH_YOUR_BOTKIT_TOKEN node .

5) Your bot should be online! Within Slack, send it a quick direct message to say hello. It should say hello back!

6) Create new scripts, triggers and behaviors within your Botkit Studio developer account, while connecting it to the application logic present in your bot code by using the features described in the function index.

If you've already got a bot built with Botkit, you can get started with new Studio features with only a few extra lines of code.

After you've registered for a Botkit Studio developer account, you will receive an API token that grants your bot access to the content and features managed by the Studio cloud service. You can add this to your existing Botkit app by passing in the Studio token to the Botkit constructor using the studio_token field:

// Create the Botkit controller that has access to Botkit Studio
var controller = Botkit.slackbot({
    debug: false,
    studio_token: 'MY_BOTKIT_STUDIO_TOKEN'
});

In order to add the Botkit Studio "catch all" handler that will activate the cloud script triggering service into your bot, add the following code to your application below all other Botkit hears() events. This will pass any un-handled direct message or direct mention through Botkit Studio's trigger service, and, should a matching trigger be found, execute the script.

controller.on('direct_message,direct_mention,mention', function(bot, message) {
    controller.studio.runTrigger(bot, message.text, message.user, message.channel).catch(function(err) {
        bot.reply(message, 'I experienced an error with a request to Botkit Studio: ' + err);
    });
});

How do the Botkit tools handle your messages? Where do messages come from and go to?

  1. The Botkit-powered application you build (and host yourself) receives messages directly from a messaging platform such as Slack or Facebook.

  2. Botkit will evaluate this message locally (within your application) to match it to internally defined triggers (using hears() or on() handlers).

  3. If and only if a message matches the conditions for being analyzed remotely -- by default, only messages sent directly to the bot that are not already part of an ongoing interaction -- the message is sent over an encrypted SSL channel to the Botkit Studio trigger system.

    The trigger system (using studio.runTrigger) will evaluate the input message against all configured triggers, and, should one be matched, return the full content of the script to your application.

  4. Botkit will compile the script received from the API into a conversation object and conduct the resulting conversation. During the course of the conversation, no messages are sent to the Botkit Studio APIs. Said another way, while a bot is conducting a scripted conversation, all messages are sent and received directly between the application and the messaging platform. This projects your user's messages, reduces network traffic, and ensures that your bot will not share information unnecessarily.

Our recommended best practices for the security and performance of your bot and the privacy of your users is to send as few messages to be interpreted by the trigger APIs as possible. As described above, a normal Botkit Studio bot should only send messages to the API that can reasonably be expected to contain trigger words.

The bottom line is, Botkit Studio does not put itself between your users and your application. All messages are delivered first and directly to your application, and only sent to our APIs once specific criteria are met. This is notably different than some other bot building services that actually receive and process messages on your behalf.

Argument Description
bot A bot instance
input_text The name of a script defined in Botkit Studio
user the user id of the user having the conversation
channel the channel id where the conversation is occurring

controller.studio.run() will load a script defined in the Botkit Studio authoring tool, convert it into a Botkit conversation, and perform the conversation to it's completion.

Developers may tap into the conversation as it is conducted using the before, after, and validate hooks. It is also possible to bind to the normal convo.on('end') event because this function also returns the resulting conversation object via a promise:

controller.studio.run(bot, 'hello', message.user, message.channel).then(function(convo) {
    convo.on('end', function(convo) {
        if (convo.status=='completed') {
            // handle successful conversation
        } else {
            // handle failed conversation
        }
    });
});

Argument Description
bot A bot instance
input_text The name of a script defined in Botkit Studio
user the user id of the user having the conversation
channel the channel id where the conversation is occurring

controller.studio.get() is nearly identical to controller.studio.run(), except that instead of automatically and immediately starting the conversation, the function returns it in a dormant state.

While developers may still tap into the conversation as it is conducted using the before, after, and validate hooks, it must first be activated using convo.activate() in the results of the promise returned by the function.

This enables developers to add template variables to the conversation object before it sends its first message. Read about using variables in messages

controller.studio.get(bot, 'hello', message.user, message.channel).then(function(convo) {
    convo.setVar('date', new Date()); // available in message text as {{vars.date}}
    convo.setVar('news', 'This is a news item!'); // ailable as {{vars.news}}

    // crucial! call convo.activate to set it in motion
    convo.activate();
});

Argument Description
bot A bot instance
input_text The name of a script defined in Botkit Studio
user the user id of the user having the conversation
channel the channel id where the conversation is occurring

In addition to storing the content and structure of conversation threads, developers can also use Botkit Studio to define and maintain the trigger phrases and patterns that cause the bot to take actions. controller.studio.runTrigger() takes arbitrary input text and evaluates it against all existing triggers configured in a bot's account. If a trigger is matched, the script data is returned, compiled, and executed by the bot.

This is different than studio.run() and studio.get() in that the input text may include additional text other than an the exact name of a script. In most cases, runTrigger() will be configured to receive all messages addressed to the bot that were not otherwise handled, allowing Botkit Studio to be catch-all. See below:

// set up a catch-all handler that will send all messages directed to the bot
// through Botkit Studio's trigger evaluation system
controller.on('direct_message,direct_mention,mention', function(bot, message) {
    controller.studio.runTrigger(bot, message.text, message.user, message.channel).catch(function(err) {
        bot.reply(message, 'I experienced an error with a request to Botkit Studio: ' + err);
    });
});

In order to customize the behavior of scripts triggered using runTrigger(), developers define before, after and validate hooks for each script. See docs for these functions below.

Another potential scenario for using runTrigger() would be to trigger a script that includes additional parameters that would normally be provided by a user, but are being provided instead by the bot. For example, it is sometimes useful to trigger another script to start when another script has ended.

controller.hears(['checkin'], 'direct_message', function(bot, message) {

    // when a user says checkin, we're going to pass in a more complex command to be evaluated

    // send off a string to be evaluated for triggers
    // assuming a trigger is setup to respond to `run`, this will work!
    controller.studio.runTrigger(bot, 'run checkin with ' + message.user, message.user, message.channel);
});

While Botkit does not currently provide any built-in mechanisms for extracting entities or parameters from the input text, that input text remains available in the before and after hooks, and can be used by developers to process this information.

The original user input text is available in the field convo.source_message.text. An example of its use can be see in the Botkit Studio Starter bot, which extracts a parameter from the help command.

controller.studio.before('help', function(convo, next) {

    // is there a parameter on the help command?
    // if so, change topic.
    if (matches = convo.source_message.text.match(/^help (.*)/i)) {
        if (convo.hasThread(matches[1])) {
            convo.gotoThread(matches[1]);
        }
    }

    next();

});

Argument Description
bot A bot instance (required only when Botkit Studio token is associated with bot, not controller)
tag (optional) require that all scripts returned have specified tag

This function will return a list of all Botkit Studio scripts available to the bot. It returns a promise that will resolve with an array of scripts. These scripts will contain the name, description, and a list of associated triggers. The name field can be used along with controller.studio.get() and controller.studio.run() to load the actual content of the script.

controller.studio.getScripts().then(function(list) {

  // do something with this array of scripts.
  // list an array in the form:
  // [
  //   {
  //     name: 'script',
  //     description: 'a script',
  //     triggers: [
  //       type: 'string',
  //       pattern: 'trigger',
  //     ]
  //   }
  // ]

});

Argument Description
script_name The name of a script defined in Botkit Studio
hook_function A function that accepts (convo, next) as parameters

Define before hooks to add data or modify the behavior of a Botkit Studio script before it is activated. Multiple before hooks can be defined for any script - they will be executed in the order they are defined.

Note: hook functions must call next() before ending, or the script will stop executing and the bot will be confused!

// Before the "tacos" script runs, set some extra template tokens like "special" and "extras"
controller.studio.before('tacos', function(convo, next) {

    convo.setVar('special', 'Taco Boats');
    convo.setVar('extras', [{'name': 'Cheese'}, {'name': 'Lettuce'}]);

    next();

});

Argument Description
script_name The name of a script defined in Botkit Studio
hook_function A function that accepts (convo, next) as parameters

Define after hooks capture the results, or take action after a Botkit Studio script has finished executing. Multiple after hooks can be defined for any script - they will be executed in the order they are defined.

Note: hook functions must call next() before ending, or the script will stop executing and the bot will be confused!

// After the "tacos" command is finished, collect the order data
controller.studio.after('tacos', function(convo, next) {
    if (convo.status == 'completed') {
        var responses = convo.extractResponses();
        // store in a database
    }
    next();
});

Argument Description
script_name The name of a script defined in Botkit Studio
variable_name The name of a variable defined in Botkit Studio
hook_function A function that accepts (convo, next) as parameters

validate hooks are called whenever a Botkit Studio script sets or changes the value of a variable that has been defined as part of the script.

Note: hook functions must call next() before ending, or the script will stop executing and the bot will be confused!

// Validate a "sauce" variable in the "taco" script
// this will run whenever the sauce variable is set and can be used to
// alter the course of the conversation
controller.studio.validate('tacos', 'sauce', function(convo, next) {

    var sauce = convo.extractResponse('sauce');
    sauce = sauce.toLowerCase();

    if (sauce == 'red' || sauce == 'green' || sauce == 'cheese') {
        convo.setVar('valid_sauce', true);
        next();
    } else {
        convo.gotoThread('wrong_sauce');
        next();
    }

});

Argument Description
script_name The name of a script defined in Botkit Studio
thread_name The name of a thread defined in Botkit Studio
hook_function A function that accepts (convo, next) as parameters

beforeThread hooks are called whenever a Botkit Studio script changes from one thread to another.

This works just like convo.beforeThread(), but operates on the automagically compiled scripts managed by Botkit Studio's IDE.

Note: hook functions must call next() before ending, or the script will stop executing and the bot will be confused! Allows developers to specify one or more functions that will be called before the thread referenced in thread_name is activated.

handler_function will receive the conversation object and a next() function. Developers must call the next() function when their asynchronous operations are completed, or the conversation may not continue as expected.

Note that if gotoThread() is called inside the handler function, it is recommended that next() be passed with an error parameter to stop processing of any additional thread handler functions that may be defined - that is, call next('stop');

// This example demonstrates how to use beforeThread to capture user input, do an asynchronous operation, then display the results in a new thread
// Imagine a conversation called `search` in which the first action is to collect search terms
// the conversation then transitions to the `results` thread, before which we do the actual search!
controller.studio.beforeThread('search', 'results', function(convo, next) {

  var query = convo.extractResponse('query');
  mySearchQuery(query).then(function(results) {

    convo.setVar('results', results);
    next();

  }).catch(function(err) {

    convo.setVar('error', err);
    convo.gotoThread('error');
    next(err);

  });

});

Is something missing or out of date?

This file is managed on Github. click here to view the source, and send us a pull request with your improvements!