Plugins
Plugins are the best way to provide API integrations and business logic to your chatbot. Plugins is a serverside code with customisable representation in a wingbot.ai designer.
Plugin representation in designer
Each plugin can be used as any other conversation element like message.
Configuring the designer
[{"id": "exampleBlock","name": "example block name","description": "example block description text","inputs": [{"type": "text","name": "text","label": "Text","validations": [{"type": "regexp","value": "^(\\s*).{0,2}$","message": "Should not be shorter than 3 letters!"}]},{"type": "select","name": "sel","label": "Select","options": [{"value": "val","label": "lab"}]}],"items": [{"id": "responseBlockName","description": "description of an interaction block"}]}]
You can specify:
inputs - configuration of each plugin
There are two types:
text
,select
,array
andpostback
. Input data are accessible inparams
as a 5th parameter of plugin and inreq.params
.items - the response blocks
Blocks of conversation elements to respond with. How to pass the data to messages? You can use
res.setData({ myVar: 'foo' })
and use the Handlebars template in message like{{myVar}}
Plugin as a function
You can register your plugin simply.
const { Plugins } = require('wingbot');const plugins = new Plugins();plugins.register('exampleBlock', async (req, res, postBack) => {// load the plugin configurationconst { text, sel } = req.params;// set data to make them accessible in handlebars templatesres.setData({ myVar: text' });// run an interaction blockawait res.run('responseBlockName');});module.exports = plugins;
Understanding return values
How to use directives Router.NEXT
, Router.END
, Router.CONTINUE
and Router.BREAK
Plugin as a nested Router
For more sophisticated use cases you can use the router as a plugin. But don't forget to register it as a factory!
const { Plugins, Router } = require('wingbot');const plugins = new Plugins();function exampleBlockFactory (params) {// load the plugin configurationconst { text } = params;// there you're able to validate the paramsconst exampleBlock = new Router();// an incomming routeexampleBlock.use('/', (req, res, postBack) => {// send the text as a responseres.text(text);// make some async jobpostBack('after-timeout', async () => {await new Promise(r => setTimeout(r, 1000));return { myVar: 'foo' };});});// following actionexampleBlock.use('after-timeout', async (req, res) => {const { myVar } = req.actionData();await res.run('responseBlockName');});return exampleBlock;}plugins.registerFactory('exampleBlock', exampleBlockFactory);module.exports = plugins;
Testing the plugin
You can use the Tester util.
const { Tester } = require('wingbot');const plugins = require('../../bot/plugins');describe('exampleBlock plugin', function () {it('should work', async function () {const t = new Tester(plugins.getWrappedPlugin('exampleBlock'));await t.postBack('/'); // run the initial actionawait new Promise(r => setTimeout(r, 1500));// assertst.respondedWithBlock('responseBlockName');t.passedAction('after-timeout');});});
There is a complex example.
- it passes params to plugins
- implements some conversation items, which can be trigged with
res.run()
method - allows to test behavior of plugin in attached in a bot
const { Tester, Router } = require('wingbot');const plugins = require('../../bot/plugins');describe('complex plugin', function () {it('should work', async function () {const bot = new Router();const items = {responseBlockName: (req, res) => { res.text('done'); }};bot.use('there-is-plugin', plugins.getWrappedPlugin('exampleBlock', { text: 'the text' }, items));bot.use((req, res) => {res.text('fallback');});const t = new Tester(bot);await t.postBack('there-is-plugin'); // run the initial actiont.any().contains('the text');await new Promise(r => setTimeout(r, 1500));t.any().contains('after-timeout');});});
Nested forms in plugin configuration
if you want to gather array of objects for your plugin's configuration, you can use an array
input type.
keyPropertyName: string
- required identifying property of each objectcolumn: { name: string, label: string }[]
- to make the table more readable, theres ability to show additional label columns
{"type": "array","name": "products","label": "List of products","keyPropertyName": "ean","columns": [ // optional{"name": "product_name","label": "Product name"}],"inputs": [{"type": "text","name": "ean","label": "Unique identification of product"},{"type": "text","name": "product_name","label": "Product name"}]}
This input will produce a params object like this:
{products: [{ean: '123132321',product_name: 'Foo'},{ean: '8989898',product_name: 'Bar'}]}