Features | Technologies | Getting started | Usage | Chat extensions | CLI | API Doc |
new BotServer({
verifyToken: "hasta-la-vista-baby",
accessToken: "open-sesame",
appSecret: "too-secret-to-say"
})
.hear("order", async (chat: Chat) => {
await chat.say("Well, let's order some Botcoins. I'll just ask you a few details.");
botcoinMachine.placeOrder({
amount: await chat.ask("How many Botcoins you want to buy?"),
wallet: await chat.ask("What's the address of your Botcoin wallet?"),
email: await chat.ask("And finally, tell me your email where I should send instructions.")
});
chat.say("Thank you for your order!");
})
.start();
- Express.js based, event-driven bot server handling both Webhook and Chat Extension requests.
- Subscribing to incoming text messages using regular expresions or to events emitted by the webhook middleware.
- Support for synchronous conversations between the bot and users.
- Utilities for calling Messenger Platform API functions from code outside chatbot flow.
- Standalone CLI for instant access to backing functions of the Messenger API.
- Complete type definitions so the package is ready to be used in both JavaScript and TypeScript projects.
Node is an open-source, cross-platform JavaScript run-time environment for executing JavaScript code server-side. Fot its event-driven architecture capable non-blocking I/O it is perfectly fitting platform for building chatbots.
A syntactical superset of JavaScript adding static typing, implementing object-oriented principles and adopting latest ES features like generics, decorators or reflection. For Node projects the TypeScript especially brings a higher level of maintainability.
Express is a helpful framework built around Node.js for performing actions as a web server. The package uses the Express for handling webhook requests (incoming chat messages) as well as providing access to Chat Extensions.
Axios provides fully Promise based HTTP client functionality, so it was a clear choice for implementation of calling Facebook Graph APIs.
EJS is a very simple templating language that helps to create HTML for the pages to be shown in a programmatic way with injecting values. The package uses the EJS for rendering Chat Extensions.
Jest is yet another unit testing framework by Facebook. It covers all needed unit testing areas: tests definition, assertion and code coverage measuring and reporting. In addition the Jest supports TypeScript and Promises. It's interface is just right balanced between descriptiveness and verbosity, so its using is very intuitive..
As a task runner the Grunt helps to organize building, releasing and maintaining the package.
- Node installed
- TypeScript installed
- Facebook developers account
- «optional» ngrok (or any other tunnel tool) for developing and debuging locally
npm install @aiteq/messenger-bot --save
Create and setup a Facebook application using Quick Start Guide and find out access token and app secret.
Create index.ts
and let's go to start coding:
import { BotServer, Chat } from "@aiteq/messenger-bot";
Create an instance of BotServer:
let bot: BotServer = new BotServer({
name: "MyBot",
port: process.env.PORT || 8080,
verifyToken: "hasta-la-vista-baby",
accessToken: "open-sesame",
appSecret: "too-secret-to-say"
});
Subscribe for some text:
bot.hear("hello", (chat: Chat) => {
chat.say("Hello! I'm Emil, the Bot. How are you?");
});
Start the server:
bot.start();
Add some scripts to package.json
:
"scripts": {
"compile": "tsc -p .",
"start": "node ./dist/index.js"
},
Create tsconfig.json
:
{
"compilerOptions":
{
"module": "commonjs",
"target": "es6",
"rootDir": "src",
"outDir": "bin"
},
"include": [ "src/**/*" ]
}
Transpile the source:
npm run compile
Now the bot is ready and you can bring it to live:
npm run start
Start ngrok:
ngrok http 8080
and copy the provided https:// URL for the following step.
Follow the guide, paste the previously copied URL to Callback URL and add /webhook
. So, if the URL provided by ngrok is e.g. https://54d4f722.ngrok.io
, the Callback URL will be https://54d4f722.ngrok.io/webhook
.
Set hasta-la-vista-baby
(see creating the bot above) as Verify Token and click Verify and Save.
Now the bot is listening for messages sent to your page. Try to send message "hello".
You can subscribe to specific content of incoming text messages in two ways: exact commands and regular expressions. The subscribing is done using the BotServer.hear() method.
Hooking exact words or phrases can be useful when your bot is supposed to listen for commands like a CLI. Commands are specified as strings or arrays of strings and are considered to be case-insensitive.
bot.hear("wait", (chat: Chat) => {
chat.say("I'm waiting ...");
})
.hear(["sleep", "go sleep", "go to sleep"], (chat: Chat) => {
chat.say("Well, good night");
});
Subscribing to specific content using regular expressions is much more flexible than listing exact words. Like commands, regular expressions can be specified as an array or single. If the regular expression contains capturing groups they are passed to the callback as third argument.
bot.hear(/^good (night|morning)$/i, (chat: Chat, text: string, captured: string[]) => {
chat.say(`Hi, good ${captured[0]}!`);
});
In addition, you can mix commands and regular expressions in the same array.
Note: The regular expressions are used exactly as given, so if you want to test the text in case-insensitive manner you must explictily indicate it (the i
flag).
Besides searching for specific text you can also subscribe to a number of events emitted while the bot is receiving messages through webhook. The subscribing is done using the BotServer.on() method.
Check the Webhook.Event enum for complete set of available events.
When subscribing to Postback based events
you have two options:
- subscribe to the type of the event and receive all events of this type (e.g. Persistent Menu item selected):
bot.on(Webhook.Event.PERSISTENT_MENU, (chat: Chat) => {
chat.say("You've selected some menu item, but I really don't know what you want to do...");
});
- subscribe to the specific ID in addition to the type, what is much more useful way:
bot.on(Webhook.Event.PERSISTENT_MENU, "menu-item-about", (chat: Chat) => {
chat.say("What can I say about myself... I'm a bot.");
});
Conversation is synchronous message exchange between users and the bot by setting a flow of questions and answers. It's a useful way to get conventional UI tasks, like form filling, closer to interpersonal communication.
In order to ensure execution of steps of the conversation synchronously, all methods of the Chat class return Promises. So you can call next step after resolving the current. And when you add a little bit of syntactic sugar using the async
/await
concept, the conversation flow will look much more readable, almost like a real dialog.
There are two methods for interaction with the user:
- ask() - asking with plain text question
- askWithMessage() - asking with structured message
Note: No events are emitted and no hear handlers called when the bot receives an answer to the question asked.
bot.on(Webhook.Event.PERSISTENT_MENU, "menu-item-song", async (chat: Chat) => {
profile.favSong = await chat.ask("What's your favourite song?");
});
Or more complex flow:
bot.on(Webhook.Event.PERSISTENT_MENU, "menu-item-order", async (chat: Chat) => {
await chat.say("Well, let's order some Botcoins. I'll just ask you a few details.");
order.amount = await chat.ask("How many Botcoins you want to buy?");
order.wallet = await chat.ask("What's the address of your Botcoin wallet?");
order.email = await chat.ask("And finally, tell me your email where I should send instructions for payment.");
chat.say("Thank you for your order!");
});
As with classic forms, even in the case of a conversation, we need to validate user inputs. Therefore, the interface offers the ability to call a validation function wich you can pass when calling the ask() method. As a validator you can conveniently use functions from validator.js package:
import * as validator from "validator";
bot.on(Webhook.Event.PERSISTENT_MENU, "menu-item-form", async (chat: Chat) => {
//...
let email: string = await chat.ask("Give me your email address, please", validator.isEmail)
//...
});
The bot will automatically repeat the question until the user enters a valid email address.
The questions asked using the ask() or askWithMessage() method may remain unanswered by the user. In order to avoid pending Promises these questions are set to automatically expire. The expiration period is set to 5 minutes by default but you can override it using BotConfig.askTimeout parameter. The unanswered question is rejected after its expiration. If you want to react to this situation you may catch the rejection:
bot.on(Webhook.Event.PERSISTENT_MENU, "menu-item-name", async (chat: Chat) => {
try {
let name = await chat.ask("What's your name?");
chat.say(`Hello, ${name}. My name is Emil.`);
} catch (error) {
chat.say("I'm so sorry you have no name.");
}
});
If you won't catch the expiration the bot will swallow it without consequences. Don't worry about it.
When you're about to send a message with a media attached you can indicate wheather the media should be reused. The bot stores all reusable attachment ID's. When you try to send the same attachment (with the same URL and reusable
set to true
) twice or more times the bot replace media's URL with stored attachment ID.
The Facebook Messenger Platform API contains not only interactive functions for message exchange between the bot and users. There are a lot of services in the API backing the communication like activating Get Started button, installing Persistent Menu or generating Messenger Code.
Sometimes, we also want to send a push message - a message sent to the user proactively, not just as a response to some incoming message.
The above cases are not quite bot-aware functions. Thus, in order to keep BotServer's interface clean, these services are made available through the BotUtils class.
An instance of the BotUtils is initialized passing the the accessToken
.
let utils: BotUtils = new BotUtils("open, sesame");
utils.sendText("123450987643", "RATE ALERT: Botcoin price has reached $1,000");
utils.setGetStartedButton();
See BotUtils.setGetStartedButton()
utils.generateMessengerCode("my-m-code.png");
See BotUtils.generateMessengerCode()
The bot server supports responding for ping requests. By default, the ping service is attached to /ping
path and may be overrided by BotConfig.pingPath configuration parameter. The ping request must use the GET method. If all goes well the "OK"
string is returned with 200 HTTP code.
The ping feature is useful with conjunction with up-time monitoring services like Uptime Robot.
The package supports Embedded JavaScript Templates (EJS) for rendering Chat Extension views. For creating a new extension follow these steps.
1. Implement ChatExtension interface
import { ChatExtension } from "@aiteq/messenger-bot";
export class MyExtension implements ChatExtension {
constructor() {
// name your extension - the name will be a part of extension's URL
super("my");
}
// implement abstract method getModel()
public getModel(): any {
return {
name: "Captain Nemo"
};
}
}
The chat extension class must implement abstract method getModel()
that provides data to be used in the view.
The getModel
is called every time an extension is requested.
In your project root create views
folder (default for placing views in Express application) and my.ejs
file within it.
<!DOCTYPE html>
<html>
<head>
<title>My Extension</title>
</head>
<body>
<div>Greetings, <%= name %>!</div>
</body>
</html>
At last you have to register the extension to the bot server using the addChatExtension method.
bot.addChatExtension(new MyExtension());
Now the extension is ready to use and you can test it pointing to <your-bot-url>/ext/my
.
Note that the default path for extensions is /ext
and you can chanage it by setting the extensionPath
property of BotConfig.
The BotUtils class is useful if you need non-interactive functions of the Messenger API to be called within your application. More often, however, you will need to use these features one-time, operatively, or as a part of such automated workflow like shell script. There is a Command Line Interface ready for these cases.
mbutil <group> [command] [options]
A group represents a specific part of the Messenger API. Available groups are:
Group | Functions |
---|---|
send |
Send text or attachment message |
getstarted |
Manage Get Started button |
greeting |
Manage page's localized greetings |
menu |
Manage Persistent Menu |
domains |
Manage Domain Whitelist |
audience |
Manage Target Audience settings |
accountlinking |
Manage Account Linking settings |
chatext |
Manage Chat Extensions settings |
code |
Generate Messenger Code |
For each group, you can view help by:
mbutil <group> --help
Global options:
Option | Function |
---|---|
--config <path> |
path to the config JSON file; must contain the accessToken property |
--accessToken <token> |
access token (one of --config or --accessToken must be specified) |
--help |
display help for the group |
Send plain text or attachment push message.
Usage:
mbutil send "<text>" --recipient <id> [options]
mbutil send image|audio|video|file --url <url> --recipient <id> [options]
Options:
Option | Function |
---|---|
--recipient <id> |
ID of the recipient |
--url <url> |
URL of the file to be attached |
Manage Get Started button.
Display current setting:
mbutil getstarted get [options]
Activate the button with optional data:
mbutil getstarted set [--data "<data>"] [options]
Remove the button:
mbutil getstarted delete [options]
Options:
Option | Function |
---|---|
--data "<data>" |
text or JSON to be send when the user tapped the button |
Manage page's localized Greeting.
Display current setting:
mbutil greeting get [options]
Add localized greeting text:
mbutil greeting add "<text>" [--locale <locale>] [options]
Remove greeting text:
mbutil greeting delete [options]
Options:
Option | Function |
---|---|
--locale <locale> |
greeting's locale (supported locales); if omitted the text will be set as default |
Manage Persistent Menu.
Display current setting:
mbutil menu get [options]
Set Persistent Menu according to definition in a JSON file:
mbutil menu set --file <path> [--locale <locale>] [options]
Remove Persistent Menu:
mbutil menu delete [options]
Options:
Option | Function |
---|---|
--file <path> |
path to menu definition JSON file |
Required structure of the JSON menu definition file is clear from the following example (object contains two variants of the menu for "default"
and "cs_CZ"
locales):
{
"default": {
"composerInputDisabled": false,
"items": [
{
"title": "Show exchange rate",
"id": "menu-rate"
},
{
"title": "Buy Botcoins",
"id": "menu-buy"
},
{
"title": "Aiteq International, Ltd.",
"url": "http://www.aiteq.international"
}
]
},
"cs_CZ": {
"composerInputDisabled": false,
"items": [
{
"title": "Aktuální kurz",
"id": "menu-rate"
},
{
"title": "Koupit Botcoiny",
"id": "menu-buy"
},
{
"title": "Aiteq Reloaded, s.r.o.",
"url": "http://www.aiteq.com"
}
]
}
}
Manage Domain Whitelist.
Display current whitelisted domains:
mbutil domains get [options]
Add one or more domains (space separated list) to the whitelist:
mbutil domains add <domain> [domains] [options]
Delete the domain whitelist:
mbutil domains delete [options]
Manage Target Audience settings. Countries are identified by ISO 3166 Alpha-2 codes.
Display current setting:
mbutil audience get [options]
Open Target Audience for all countries:
mbutil audience open [options]
Close Target Audience for all countries:
mbutil audience close [options]
Add one or more countries (space separated list) to the whitelist:
mbutil audience whitelist <country> [countries] [options]
Add one or more countries (space separated list) to the blacklist:
mbutil audience blacklist <country> [countries] [options]
Remove all Target Audience settings:
mbutil audience delete [options]
Manage Account Linking URL.
Display currently set Account Linking URL:
mbutil accountlinking get [options]
Set Account Linking URL:
mbutil accountlinking set <url> [options]
Delete currently set Account Linking URL:
mbutil accountlinking delete [options]
Manage Chat Extension URL.
Display currently set Chat Extension URL and settings:
mbutil chatext get [options]
Set Chat Extension URL:
mbutil chatext set <url> [options]
Delete currently set Chat Extension URL:
mbutil chatext delete [options]
Options:
Option | Value | Function |
---|---|---|
--inTest |
controls whether public users can see the Chat Extension | |
--shareButton |
controls whether the share button in the webview is enabled |
Generate Messenger Code.
Set Chat Extension URL:
mbutil code generate [options]
Options:
Option | Value | Function |
---|---|---|
--out |
path | output file's path and name (default: ./code.png ) |
--size |
number between 100 - 2000 |
size of generated image, in pixels (default: 1000 ) |
--ref |
text | data to be received when user scans the code (optional) |
Package's reference API documentation is located in doc folder.
MIT