Advanced topics

What causes the error: UnhandledPromiseRejectionWarning: TypeError: Cannot perform 'get' on a proxy that has been revoked

This happens when a call to bot.say, bot.reply, or bot.beginDialog has been used without the await keyword.

Make sure you await all calls to these and similar functions! These functions return promises that have to be resolved properly, otherwise you'll get the above error!

Botkit 4.0 Goals:

These were the goals we set out to achieve in creating the new version of Botkit.

What's different between 0.7 and 4.0?

The changelog has lots of details on the new features.

In addition, here are some notes on the major changes:

How to upgrade from 0.7 to 4.0

Though many things have changed in the latest version of Botkit, and it is not directly backwards compatible with previous versions, many elements of the previous Botkit syntax are still present and the vast majority of features from the previous versions. Experienced Botkit developers will recognize the familiar syntax of features like hears() and ask().

The overall structure of the Botkit application is roughly the same, though the shape of some components have shifted. Before upgrading your bot, use the yeoman generator to create a sample app -- if only to understand its structure. In most cases, the best approach will be to create a new bot using the generator, then port existing "skill" files to the new syntax.

You may need to update your Node version because v4 of Botkit uses modern Javascript syntax. We suggest using the LTS version.

Changes to how Botkit is installed and configured:

FROM:

var Botkit = require('botkit');

var controller = new Botkit.slackbot(options);

TO:

const { Botkit } = require('botkit');
const { SlackAdapter } = require('botbuilder-adapter-slack');

let adapter = new SlackAdapter(options);
let controller = new Botkit({
    adapter: adapter
});

Syntax changes in your bot code:

FROM:

bot.hears('foo', 'message_received', function(bot, message) { 
    bot.reply(message,'bar');
});

TO:

bot.hears('foo', 'message', async(bot, message) => { 
    await bot.reply(message,'bar');
});

Conversation changes:

FROM:

bot.hears('tacos', 'direct_message', function(bot, message) {
    bot.startConversation(function(err, convo) { 

        convo.say('SOMEONE SAID TACOS!');
        convo.ask('Do you want to eat a taco?', [
            {
                pattern: 'yes',
                default: true,
                callback: function(response, convo) {
                    convo.gotoThread('yes_tacos');
                }
            },
            {
                pattern: 'no',
                callback: function(response, convo) {
                    convo.gotoThread('no_tacos');
                }
            }
        ], {key: 'wants_taco'});

        convo.addMessage('Hooray for tacos!', 'yes_tacos');
        convo.addMessage('ERROR: Tacos missing!!', 'no_tacos');

        convo.on('end', function(convo) {
            var responses = convo.extractResponses();
            // responses.wants_tacos
        });
    });
});

TO:

const { BotkitConversation } = require('botkit');

let convo = new BotkitConversation('tacos', controller);
convo.say('SOMEONE SAID TACOS!');
convo.ask('Do you want to eat a taco?', [
    {
        pattern: 'yes',
        default: true,
        handler: async(response, convo, bot) => {
            await convo.gotoThread('yes_tacos');
        }
    },
    {
        pattern: 'no',
        handler: async(response, convo, bot) => {
            await convo.gotoThread('no_tacos');
        }
    }
], 'wants_taco');

convo.addMessage('Hooray for tacos!', 'yes_tacos');
convo.addMessage('ERROR: Tacos missing!!', 'no_tacos');

convo.after(async(results, bot) => {

    // results.wants_taco

})

// add to the controller to make it available for later.
controller.addDialog(convo);

controller.hears('tacos', 'direct_message', async(bot, message) => {
    await bot.beginDialog('tacos');
});

Botkit Studio / Botkit CMS changes:

The functionality previously associated with Botkit Studio and now associated with Botkit CMS has been now been moved out of the core SDK and into a plugin module.

To access dialog content build in Botkit CMS, install botkit-plugin-cms, and adjust calls to the CMS from controller.studio.* to controller.plugins.cms.*:

Read more about using botkit-plugin-cms here

Storage changes

In v4 of Botkit, the storage system is currently only used to store and retrieve the conversation state between turns. Other than this, Botkit will no longer be providing an interface for connecting to or using databases. Developers should build their own database abstractions.

However to reduce the complexity of the upgrade process, existing bots can continue to use storage adapters from previous versions of Botkit using the technique discussed here.

Anatomy of a Botkit App

File structure:

in bot.js:

in features/ folder:

modules in the form:

module.exports = function(controller) {
    // some code here.
}

Flow of activity as a message is processed

When a message is sent:

How to use "Bot Inspector" mode

With Bot Inspector mode enabled, you can use Bot Framework Emulator to connect to your bot while it also sends and receives messages to the live platform of your choice. Once activated, you'll be able to inspect the JSON payloads of incoming and outgoing messages, as well as inspect your bot's state variables.

It is TRULY COOL AND USEFUL, like opening an access panel into your bot's brain and being able to poke around like they did with Data on Star Trek: The Next Generation.

To enable this in a Botkit app:

Typescript

coming

How to build a new adapter

coming

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!

Back to top