Bot Template Framework
, (*1)
Allows to simplify chatbot development using Botman.
With one scenario file you can make your bot working., (*2)
You can find downloadable chatbot example here., (*3)
E.g. Simple Telegram Hello World chatbot:, (*4)
{
"name": "Hello World Chatbot",
"fallback": {
"name": "Hello Block",
"type": "block"
},
"blocks": [
{
"name": "Hello Block",
"type": "text",
"content": "Hello World!",
"template": "Hi;Hello;What's up;Good day;/start"
}
],
"drivers": [
{
"name": "Telegram",
"token": "590000000:AAHp5XAGrpIyZynnIjcLKJSwUpPu0b1FXEY"
}
]
}
Usage
-
Install package with composer:, (*5)
composer require adellantado/bot-template-framework
-
Add your chatbot scenario template.json file, for example, to storage/app, (*6)
-
Add drivers to config:, (*7)
$this->app->singleton('botman', function ($app) {
$config = TemplateEngine::getConfig(file_get_contents(storage_path('app/template.json')));
return BotManFactory::create($config);
});
-
Call listen() to template engine before Botman:, (*8)
$botman = resolve('botman');
$templateEngine = new TemplateEngine(file_get_contents(storage_path('app/template.json')), $botman);
$templateEngine->listen();
// Start listening
$botman->listen();
Scenario file structure
// template.json
{
"name": "Chatbot Name",
"fallback": "This is fallback message",
"blocks": [
{
"name": "Simple Text Block",
"type": "text",
"content": "Hi, this is simple text message"
},
{
...
}
],
"drivers": [
{
"name": "Telegram",
"token": "<your-telegram-token>"
},
{
...
}
]
}
With blocks you can describe action and data to send, like send text or image, request location from user or
ask to answer question, (*9)
Within drivers you set platforms which you want your chatbot working on, like Facebook or Telegram, (*10)
Blocks
There are 21 types of block, (*11)
text, image, menu, audio, video, file, location, attachment, carousel, list, request, ask, intent, if, method, extend, idle, save, random, payload, validate
Every block extends abstract block, which has next properties:, (*12)
{
"name": "Block Name",
"type": "idle",
"template": "Image;Show image;Want to see an image",
"typing": "1s",
"drivers": "any;!telegram",
"locale": "en",
"options": {"someProperty": "someValue"}
"next": "Next Block Name"
},
name
- (required) name of the block, uses to identify blocks;
type
- (required) type of the block (e.g. image, text ..);
template
- (optional) identify key phrases which chatbot react to (see $botman->hear());
typing
- (optional) shows typing effect in chat before running the block;
drivers
- (optional) exclude/include block from execution for some drivers (e.g. 'any' or '*' - runs for all drivers,
'facebook;telegram' - runs for telegram and facebook, 'any;!telegram' - runs for any driver, except telegram);
locale
- (optional) assigns block to particular locale, works like namespaces. e.g. you describe blocks with
locale 'en' and then copy them translated with locale 'ge';
options
- (optional) sets some driver specific properties;
next
- (optional) name of the next block to execute in chain, (*13)
Text Block
Send simple text response, (*14)
{
"name": "Greetings",
"type": "text",
"content": "Hi! Nice to meet you {{user.firstName}};Hi there, {{user.firstName}}",
"template": "Hello;Hi;Good day",
"typing": "1s"
}
note: learn about variables, (*15)
Image Block
Draw image with description and buttons or without them, (*16)
{
"name": "Logo",
"type": "image",
"content": {
"text": "This is the logo;Our logo is following",
"url": "https://logo.com/logo.jpg",
"buttons": {
"Callback": "Learn More"
}
},
"template": "Show me the logo"
}
url
- (required) image url;
text
- (optional) image description;
buttons
- (optional) adds buttons under image;, (*17)
note: learn about menu block, (*18)
Menu Block
1.Show buttons, (*19)
{
"name": "Menu Block",
"type": "menu",
"content": {
"text": "This is a simple menu; This is a menu",
"buttons": [
{"Callback": "Learn More"},
{
"https://website.com/": "Visit Website",
"Ask Support": "Ask Support"
}
]
}
}
note: buttons may vary dramatically from driver to driver (learn more about in official docs of the platform), (*20)
E.g. Telegram:, (*21)
Format #1
"buttons": [
{ This is a simple menu
"https://website.com/": "Visit Website", ==> ------------------------------------
"Ask Support": "Ask Support" | Visit Website | Ask Support |
} ------------------------------------
]
Format #2
"buttons": [
{ This is a simple menu
"https://website.com/": "Visit Website" ==> ---------------------
},{ | Visit Website |
"Ask Support": "Ask Support" ---------------------
} | Ask Support |
] ---------------------
E.g. Facebook - has 3 only buttons in one menu., (*22)
2.Show quick buttons, (*23)
{
"name": "Quick Menu Block",
"type": "menu",
"mode": "quick",
"content": {
"text": "This is a quick menu",
"buttons": [
{
"https://website.com/": "Visit Website",
"Ask Support": "Ask Support"
}
]
}
}
Audio, Video and File Blocks
Drop video, audio and file directly to the chat, (*24)
{
"name": "File Block",
"type": "file",
"content": {
"text": "Download the file",
"url": "https://sample.com/doc.pdf"
}
}
url
- (required) file, video or audio link;
text
- (optional) description;, (*25)
Location Block
Request location from the user, (*26)
{
"name": "Location Test",
"type": "location",
"content": "Please, share your location by clicking button below;Send your location",
"template": "share location",
"result": {
"save": "{{location}}"
}
}
content
- (required) description;
result.save
- (required) save data in json {latitude:.., longitude: ..} to variable;, (*27)
note: learn about variables, (*28)
Attachment Block
Request image, video, audio or file from the user, (*29)
{
"name": "Attachment Test",
"type": "attachment",
"mode": "image",
"content": "Please, make a photo to verify your identity",
"result": {
"save": "{{photo}}"
}
}
mode
- (optional) could be image, video, file, audio, if mode not provided - file by default;, (*30)
Carousel and List Blocks
Draw carousel or list of components, (*31)
{
"name": "List Test",
"type": "list",
"content": [
{
"url": "https://image.com/img1.jpg",
"title": "Component #1",
"description": "This is component #3"
},
{
"url": "https://image.com/img2.jpg",
"title": "Component #2",
"description": "This is component #3"
},
{
"url": "https://image.com/img3.jpg",
"title": "Component #3",
"description": "This is component #3",
"buttons": {
"example btn": "Example Button"
}
}
],
}
note: some platforms doesn't support list or carousel components natively, (*32)
Request Block
Make a custom GET/POST request, (*33)
{
"name": "Tell a joke",
"type": "request",
"method": "GET",
"url": "http://api.icndb.com/jokes/random",
"result": {
"field": "value.joke",
"save": "{{joke}}"
},
"template": "Tell a joke;Joke;Do you know some jokes?"
}
result.field
- (optional) read the data from the result;
result.save
- (optional) save result to variable;, (*34)
note: learn about variables, (*35)
Ask Block
Ask a question and wait for user answer, (*36)
{
"name": "Ask Phone",
"type": "ask",
"content": "Can you left us your phone to contant you only in case of urgency?",
"validate": "number|min:10",
"errorMsg": "This is custom error message occurs if validation hasn't been passed",
"skip": "pause;skip",
"stop": "stop;off",
"result": {
"prompt": "yes;no"
},
"next": {
"yes": "Type Phone Block",
"no": "Ask Email Block",
"fallback": "Ask Email Block"
}
}
validate
- (optional) validate user input, doesn't save variable and repeats question when validation isn't passed.
Possible values: number
(validates integer), email
(sends quick button for Facebook and validates email),
url
, phone
(sends quick button for Telegram and Facebook), image
, file
, video
, audio
,
location
(sends quick button for Telegram and Facebook), confirm
(requires two times input),
size:<number>
, min:<number>
, max:<number>
(exactly/minimum/maximum letters),
numeric
(validates float values), digits
, non-free-input
and free-input
(for values inputed with text input bar),
/^[0-9]*$/
(any regexp, similar to this one);
errorMsg
- (optional) validation error message;
skip
,stop
- (optional) pause/stop conversation key phrases;
result.prompt
- (optional) shows quick buttons;
next.<user answer>
- (optional) depends on user answer, run next block
('fallback' - reserved for any answer which are not in the list)., (*37)
note: Rules could be combined with "|" symbol (e.g. numeric|min:10|max:12)
note: Learn more about results
note: You need to set up persistent cache (like Redis), learn more on Botman website, (*38)
Intent Block
{
"name": "AlexaTest",
"provider": "alexa",
"type": "intent",
"template": "BeverageIntent",
"content": "well done; cool",
"result": {
"field": "beverage",
"save": "{{user_beverage}}"
},
"next": {
"coffee": "Coffee Card Block",
"tea": "Tea Card Block",
"default": "Repeat Question Block"
}
}
provider
- (required) could be 'alexa', 'wit' or 'dialogflow';
template
- (required) intent name for alexa and wit, action name for dialogflow;
content
- (required (alexa) | optional (dialogflow) | optional (wit)) answer into the chat;
result.field
- (optional) entity or slot name;
result.save
- (optional) saves entity or slot value;
next.<entity_value>
- (optional) triggers next block by entity or slot value;, (*39)
note: you should use amazon alexa console, wit or dialogflow console to have
this block running;
, (*40)
note: for dialogflow v2, official php library required - google/cloud-dialogflow;
, (*41)
note: to call the block after dialogflow executes, add next payload in dialogflow console - {"next": "MyNextBlock"}, (*42)
If Block
{
"name": "Comparison Test",
"type": "if",
"next": [
["{{var}}", "==", "1", "Block 1"],
["{{var}}", "<", "1", "Block 2"],
["{{var}}", ">", "1", "Block 3"],
]
}
E.g. Simply calls "Block 1" when {{var}} == 1
, calls "Block 2" when {{var}} < 1
and "Block 3" when {{var}} > 1
., (*43)
Supported operators: ==
, !=
, >
, >=
, <
, <=
, (*44)
Method Block
Simply call method from your own strategy, (*45)
{
"name": "Test method",
"type": "method",
"method": "myMethod"
}
method
- (required) method name, (*46)
note: For each driver you should have strategy class in App\Strategies
folder with 'myMethod' function, (*47)
namespace App\Strategies;
use BotTemplateFramework\Strategies\Strategy;
class Telegram extends Strategy {
function myMethod() {
$this->bot->reply('This is my method replies');
}
}
Extend Block
Simply overrides properties of the parent block. Could be very helpful when you need to make very similar block with small changes., (*48)
{
"name": "Oysters Extended",
"type": "extend",
"base": "Oysters Block",
"content": {
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Oysters_p1040741.jpg/330px-Oysters_p1040741.jpg"
}
},
{
"name": "Oysters Block",
"type": "image",
"template": "Oysters",
"content": {
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Crassostrea_gigas_p1040848.jpg/450px-Crassostrea_gigas_p1040848.jpg"
}
}
base
- (required) Name of the parent block
, (*49)
E.g. Extends "Oysters Block" overriding content
field, (*50)
note: do not extend blocks - intent
and ask
;
note: extend only fields of 1st level of nesting (like type
, content
, next
, but impossible to extend only url
(2nd level of nesting) field from image content);, (*51)
Idle Block
Simply does nothing, but can be used to call next block, (*52)
{
"name": "Does Nothing",
"type": "idle",
"template": "call idle",
"typing": "1s",
"next": "Next Block"
}
Save Block
Simply saves some value (including existing variable value) to some variable, (*53)
{
"name": "Saves to Variable",
"type": "save",
"value": "123",
"variable": "{{someVar}}"
}
Random Block
{
"name": "Random Block",
"type": "random",
"next": [
["20%", "Block 1"],
["30%": "Block 2"],
["40%": "Block 3"]
]
}
Validate Block
Validates variable and executes block with 'true' key in case of success, (*54)
{
"name": "Validate Block",
"type": "validate",
"validate": "number|size:5",
"variable": "{{myVar}}",
"next": {
"true": "Block 1",
"false": "Block 2"
}
}
variable
- (required) name of variable;
validate
- (required) Possible values: number
(validates integer), email
(sends quick button for Facebook and validates email),
url
, phone
(sends quick button for Telegram and Facebook), image
, file
, video
, audio
,
location
(sends quick button for Telegram and Facebook), size:<number>
, min:<number>
, max:<number>
(exactly/minimum/maximum letters),
numeric
(validates float values), digits
, non-free-input
and free-input
(for values inputed with text input bar),
/^[0-9]*$/
(any regexp, similar to this one);, (*55)
Payload Block
Send a payload to a messenger, (*56)
{
"name": "Test payload",
"type": "payload",
"payload": {
"text": "This is an inline keyboard example",
"reply_markup": {
"inline_keyboard": [
[
{
"text": "Button1",
"callback_data": "callback1"
},
{
"text": "Button2",
"callback_data": "callback2"
}
]
]
}
},
"drivers": "telegram"
}
payload
- (required) payload to send;
drivers
- (required) it's essential to set exact driver, because a payload is unique for each of them, (*57)
note: study appropriate messenger's API documentation, (*58)
Drivers
Before using driver in here, first you need to install proper driver for Botman.
Available drivers are next:, (*59)
Facebook, Telegram, Skype, Dialogflow, Alexa, Viber, Web, Chatbase, Wit
note: Because BotMan doesn't ship with Viber driver, you need to run, (*60)
composer require adellantado/botman-viber-driver,
note: Because BotMan doesn't ship with Chatbase, you need to run, (*61)
composer require bhavyanshu/chatbase-php
Example:, (*62)
"drivers": [
{
"name": "Dialogflow",
"token": "b71dd842a2eb43434f4fg5eee55"
},
{
"name": "Web",
"token": "web"
},
{
"name": "Chatbase",
"token": "b71dd-842a-2eb4-3434-fw32e3233"
},
{
"name": "Facebook",
"app_secret": "FACEBOOK_APP_SECRET",
"token": "FACEBOOK_TOKEN",
"verification": "FACEBOOK_VERIFICATION",
"config": "true",
"events": {
"delivery": "BlockExecOnDeliveryEvent",
"read": "BlockExecOnReadEvent"
}
}
]
name
- (required) Name of the driver
token
- (require|optional) token for telegram, viber, dialogflow, chatbase, web(optional, default token is 'web').
Fields: verification, token, app_secret - for facebook;
app_id, app_key - for skype;
project_id, key_path, version=2 - for dialogflow v2 api.
config
- (optional) shows that fields should be read from env()
events
- (optional) blocks triggers by event.
E.g. on "delivery" event in facebook, trigger block with
name "BlockExecOnDeliveryEvent". See events in Botman., (*63)
Variables
Variables're saving to Botman userStorage(), so be sure to pass proper
storage to Botman instance., (*64)
BotManFactory::create($config, null, null, new FileStorage(__DIR__));
Using variables
Use variables with figure brackets, (*65)
{{my_variable}}
Save variables with 'result.save' field with request, ask, intent blocks.
And by using special save block., (*66)
Predefined variables
- {{user.id}}
- {{user.firstName}}
- {{user.lastName}}
- {{bot.name}}
- {{bot.driver}}
- {{message}} (this is the last user's sent message)
Results
There are 5 blocks which returns result: location
, attachment
, request
, ask
, intent
, (*67)
For each you can apply, (*68)
"result": {
"save": "{{my_variable}}"
},
Three of them (request
, ask
, intent
) could have next
field impacted depends on result value:, (*69)
E.g. In this example triggers block Type Phone Block
then result is yes
string value,
triggers Ask Email Block
- when result is no
and when neither yes
nor no
:, (*70)
"next": {
"yes": "Type Phone Block",
"no": "Ask Email Block",
"fallback": "Ask Email Block"
}
note: for the intent
block result is an entity value (dialogflow) or a slot value (alexa), which name set with field
field, (*71)
Ask Result
Use prompt
field to add quick buttons and simplify reply for user, like in the example below:, (*72)
{
"name": "Ask Phone",
"type": "ask",
"content": "Can you left us your phone to contant you only in case of urgency?",
"result": {
"prompt": "yes;no"
},
"next": {
"yes": "Type Phone Block",
"no": "Ask Email Block",
"fallback": "Ask Email Block"
}
}
Request Result
Use field
field to quickly pull data from json response, like here:, (*73)
{
"name": "Tell a joke",
"type": "request",
"method": "GET",
"url": "http://api.icndb.com/jokes/random",
"result": {
"field": "value.joke",
"save": "{{joke}}"
},
"template": "Tell a joke;Joke;Do you know some jokes?"
}
Response looks like this:, (*74)
{
"type": "success",
"value": {
"id": 495,
"joke": "Chuck Norris doesn't needs try-catch, exceptions are too afraid to raise.",
"categories": ["nerdy"]
}
}
Fallback
Fallback might be set in 3 different formats:, (*75)
1.text, (*76)
"fallback": "This is default reply messsage"
2.dialogflow, (*77)
"fallback": {
"type": "dialogflow",
"default": "This is default reply message when no answer from Dialogflow"
}
3.block, (*78)
"fallback": {
"name": "Hello Block",
"type": "block"
}
Builder
Using builder is a straight-forward, below is an example of simple chatbot:, (*79)
$template = (new Template('Beedevs Chatbot'))
->addDrivers([
new TelegramDriver('123123wefwef:wefonwewerwerwerw')
])
->addFallbackMessage('This is default message')
->addBlocks([
(new TextBlock())
->text('Hi! Welcome to beedevs chatbot')
->template([
'Hello',
'Hi',
'What\'s up',
'Good day'
])->typing(1)
->next(
$about = (new ImageBlock())
->url('https://pbs.twimg.com/profile_images/799239637684355072/SGIDpffc_400x400.jpg')
->buttons([
(new Button('Visit'))->url("https://beedevs.com")
])
->text('Beedevs is a chatbot development studio. Want to know more? Visit our website!')
->template([
'About'
])
),
$about,
(new RequestBlock())
->url('http://api.icndb.com/jokes/random')
->method('GET')
->result((new RequestResult())
->field(['value', 'joke'])
->save('{{joke}}')
)->template([
'Tell a joke',
'Joke',
'Do you know some jokes?'
])
->next(
$joke = (new TextBlock())
->text('{{joke}}')
->typing(1)
),
$joke,
(new MenuBlock())
->text('Menu')
->buttons([
(new Button('Website'))->url("https://beedevs.com"),
(new Button('About'))->callback('About')
])
->template([
'Show menu',
'Menu',
'Main menu'
]),
(new ListBlock())->items([
(new ListItem('ListItem1', 'https://static.addtoany.com/images/dracaena-cinnabari.jpg'))
->buttons([
(new Button('About'))->callback('About')
])
->description('National Park'),
(new ListItem('ListItem2', 'https://cloud.google.com/blog/big-data/2016/12/images/148114735559140/image-classification-1.png'))
->buttons([
(new Button('About'))->callback('About')
])
->description('Sunflower fields at summer time')
])->template([
'List',
'Show list'
])
]);
And then just insert your $template
into engine, like that:, (*80)
$templateEngine = new TemplateEngine($template, $botman);
$templateEngine->listen();
// Start listening
$botman->listen();
Inside, engine, just converts it to an array similar to json you already used to, but with builder your
abilities leveling up with convenience and speed of development., (*81)