How to develop an IM bot for Facebook Messenger

Maxim Vyazemsky
9 min readApr 18, 2016

--

Currently, there is really booming chat messengers. One after another, the platform for instant messaging to announce the launch of a platform for the development of bots.

Not was an exception and Facebook. April 12 at F8 conference Facebook introduced a platform for developing bots for messenger.
In this article I want to share experience in developing an IM bot for Facebook on PHP.

Introduce

Chat bots on Facebook are based on personal communications with a public page.
Therefore, to create the bot itself will need to create an application for access to the API, and a public page where users will communicate.

Page creation

Simply create a public page, we call it the way we want to see in our messenger bot, and load the icon.

Register and configure the application

Go to the registration of your application in the developer’s account.

Go to the link http://developers.facebook.com/apps

Click to add a new application, choose a different setting manually:

Next, fill in the form below:

After creating the application, in the left menu, select the Messenger tab and click on it.

Click “Start”.

First of all, select the page created for the bot, and copy the token. Save it somewhere, it is useful to us later.

Next, we will configure webhook for processing incoming messages.

In this step, you need to upload the following script on the server, where the bot will be placed:

$verify_token = ""; 
// Verify token
if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] == 'subscribe' && $_REQUEST['hub_verify_token'] == $verify_token) {
echo $_REQUEST['hub_challenge'];
}

The variable $verify_token need to add some text.

The script is upload to the server. For example, our script is available at the following address: https://domain.com/fbbot

Go back to the Messenger tab in the FB app settings.

We are looking for unit «Webhooks» and button «Setup Webhooks». Click on it.

In the “Reverse URL-address” indicates the address of our bot — https://domain.com/fbbot

SSL — certificate is mandatory. A self-signed certificate will not work.

In the “Verify marker” indicate the text that is specified in the variable $verify_token in the script.

In the “Fields subscription” field we choose what we want to receive a notice in our webhook:

  • message_deliveries — sending delivery notifications
  • messages — messages writted to bot from user
  • messaging_optins — callback when a message is received via a button on the website (Send-to-Messenger Plugin)
  • messaging_postbacks — transitions the buttons from the previous bot posts (will be clear later)

Select needed and click “Confirm and save.”

Assign an application and page

Type in the console:

curl -ik -X POST "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=-token-"

-token- replaced with a token of your page.

Message types in the FB Messenger

Messages can be either a text or Structured Text, which in turn can be:

  • button — Button
  • generic — elements
  • receipt — invoice for payment

Buttons (button)

This type is used to send messages, which require user response.

They look something like this:

The buttons can be of two types:

  1. Sends a reply to the bot
  2. Rollovers to the address on the Internet

An important point: in one such message can be a maximum of 3 buttons, when you try to send a message to a large number of buttons — it just will not reach the recipient

Elements (generic)

This type of card for sending goods or other elements having similar structure.

Each element can have a title, subtitle, description, image and buttons.

In one message can contain up to 10 elements. If there is more than one item, there is a horizontal scrolling.

An important point: in one such message can be a maximum of 3 buttons, when you try to send a message to a large number of buttons — it just will not reach the recipient.

Receipt for payment (receipt)

Purpose is clear from the title.

Facebook decided to make their messenger a full store.

Bill may contain information about products, cost, payment, delivery address, discounts.

An important point: the account number must be unique.

Writing the code

At the time of writing the bot on GitHub has not been API implementation in PHP, so I had to write your own PHP SDK.

Install PHP SDK to work with FB Messenger API using the composer:

composer require "pimax/fb-messenger-php" "dev-master"

Create a index.php file:

$verify_token = ""; // Verify token
$token = ""; // Page token
if (file_exists(__DIR__.'/config.php')) {
$config = include __DIR__.'/config.php';
$verify_token = $config['verify_token'];
$token = $config['token'];
}
require_once(dirname(__FILE__) . '/vendor/autoload.php');use pimaxFbBotApp;
use pimaxMessagesMessage;
use pimaxMessagesMessageButton;
use pimaxMessagesStructuredMessage;
use pimaxMessagesMessageElement;
use pimaxMessagesMessageReceiptElement;
use pimaxMessagesAddress;
use pimaxMessagesSummary;
use pimaxMessagesAdjustment;
$bot = new FbBotApp($token);if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] == 'subscribe' && $_REQUEST['hub_verify_token'] == $verify_token)
{
// Webhook setup request
echo $_REQUEST['hub_challenge'];
} else {
$data = json_decode(file_get_contents("php://input"), true);
if (!empty($data['entry'][0]['messaging']))
{
foreach ($data['entry'][0]['messaging'] as $message)
{
// Receive message
// Main code will be here
// ...
}
}
}

We try to send a message in response to the user upon receipt of any communication from him.

For this purpose, the unit receiving the message, adding:

$bot->send(new Message($message['sender']['id'], ‘Hi there!'));

Check. We find in our messenger bot and try to send him any message.

In response, we need to get away from it «Hi there!».

Important: As long as the application is not passed moderation bot will work only for the application of the author.

If everything works as it should, we go further.

The unit receiving the message adds:

// Skip delivery items
if (!empty($message['delivery'])) {
continue;
}
$command = "";
// Receive message from user
if (!empty($message['message'])) {
$command = $message['message']['text'];
// OR receive button click
} else if (!empty($message['postback'])) {
$command = $message['postback']['payload'];
}
// Handle the command
switch ($command) {
// When bot receive "text"
case 'text':
$bot->send(new Message($message['sender']['id'], 'This is a simple text message.'));
break;
// When bot receive "button"
case 'button':
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_BUTTON,
[
'text' => 'Choose category',
'buttons' => [
new MessageButton(MessageButton::TYPE_POSTBACK, 'First button'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Second button'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Third button')
]
]
));
break;
// When bot receive "generic"
case 'generic':
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_GENERIC,
[
'elements' => [
new MessageElement("First item", "Item description", "", [
new MessageButton(MessageButton::TYPE_POSTBACK, 'First button'),
new MessageButton(MessageButton::TYPE_WEB, 'Web link', 'http://facebook.com')
]),
new MessageElement("Second item", "Item description", "", [
new MessageButton(MessageButton::TYPE_POSTBACK, 'First button'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Second button')
]),
new MessageElement("Third item", "Item description", "", [
new MessageButton(MessageButton::TYPE_POSTBACK, 'First button'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Second button')
])
]
]
));
break; // When bot receive "receipt"
case 'receipt':
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_RECEIPT,
[
'recipient_name' => 'Fox Brown',
'order_number' => rand(10000, 99999),
'currency' => 'USD',
'payment_method' => 'VISA',
'order_url' => 'http://facebook.com',
'timestamp' => time(),
'elements' => [
new MessageReceiptElement("First item", "Item description", "", 1, 300, "USD"),
new MessageReceiptElement("Second item", "Item description", "", 2, 200, "USD"),
new MessageReceiptElement("Third item", "Item description", "", 3, 1800, "USD"),
],
'address' => new Address([
'country' => 'US',
'state' => 'CA',
'postal_code' => 94025,
'city' => 'Menlo Park',
'street_1' => '1 Hacker Way',
'street_2' => ''
]),
'summary' => new Summary([
'subtotal' => 2300,
'shipping_cost' => 150,
'total_tax' => 50,
'total_cost' => 2500,
]),
'adjustments' => [
new Adjustment([
'name' => 'New Customer Discount',
'amount' => 20
]),
new Adjustment([
'name' => '$10 Off Coupon',
'amount' => 10
])
]
]
));
break; // Other message received
default:
$bot->send(new Message($message['sender']['id'], 'Sorry. I don’t understand you.'));
}

We try to send the bot posts:

  • text
  • button
  • generic
  • receipt

If everything is done according to the instructions, you should get a message to the messenger of all types.

A real example Bot for freelance project Job4Joy

So, our goal is to implement a bot that we will issue a request for new projects in the corresponding category.

These will receive RSS, using picoFeed — https://github.com/fguillot/picoFeed

composer require fguillot/picofeed @stable
composer require "pimax/fb-messenger-php" "dev-master"

Create index.php:

$verify_token = ""; // Verify token
$token = ""; // Page token
$config = []; // config
if (file_exists(__DIR__.'/config.php')) {
$config = include __DIR__.'/config.php';
$verify_token = $config['verify_token'];
$token = $config['token'];
}
require_once(dirname(__FILE__) . '/vendor/autoload.php');use PicoFeedReaderReader;
use pimaxFbBotApp;
use pimaxMessagesMessage;
use pimaxMessagesMessageButton;
use pimaxMessagesStructuredMessage;
use pimaxMessagesMessageElement;
$bot = new FbBotApp($token);if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] == 'subscribe' && $_REQUEST['hub_verify_token'] == $verify_token)
{
// Webhook setup request
echo $_REQUEST['hub_challenge'];
} else {
$data = json_decode(file_get_contents("php://input"), true);
if (!empty($data['entry'][0]['messaging']))
{
foreach ($data['entry'][0]['messaging'] as $message)
{
if (!empty($data['entry'][0])) {
if (!empty($data['entry'][0]['messaging']))
{
foreach ($data['entry'][0]['messaging'] as $message)
{
if (!empty($message['delivery'])) {
continue;
}
$command = ""; if (!empty($message['message'])) {
$command = $message['message']['text'];
} else if (!empty($message['postback'])) {
$command = $message['postback']['payload'];
}
if (!empty($config['feeds'][$command]))
{
getFeed($config['feeds'][$command], $bot, $message);
} else {
sendHelpMessage($bot, $message);
}
}
}
}
}
}
}
/**
* Send Help Message
*
* @param $bot Bot instance
* @param array $message Received message
* @return bool
*/
function sendHelpMessage($bot, $message)
{
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_BUTTON,
[
'text' => 'Choose category',
'buttons' => [
new MessageButton(MessageButton::TYPE_POSTBACK, 'All jobs'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Web Development'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Software Development & IT')
]
]
));
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_BUTTON,
[
'text' => ' ',
'buttons' => [
new MessageButton(MessageButton::TYPE_POSTBACK, 'Design & Multimedia'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Mobile Application'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Host & Server Management')
]
]
));
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_BUTTON,
[
'text' => ' ',
'buttons' => [
new MessageButton(MessageButton::TYPE_POSTBACK, 'Writing'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Mobile Application'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Marketing')
]
]
));
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_BUTTON,
[
'text' => ' ',
'buttons' => [
new MessageButton(MessageButton::TYPE_POSTBACK, 'Business Services'),
new MessageButton(MessageButton::TYPE_POSTBACK, 'Translation & Languages')
]
]
));
return true;
}
/**
* Get Feed Data
*
* @param $url Feed url
* @param $bot Bot instance
* @param $message Received message
* @return bool
*/
function getFeed($url, $bot, $message)
{
try {
$reader = new Reader;
$resource = $reader->download($url);
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
$feed = $parser->execute();
$items = array_reverse($feed->getItems());
if (count($items)) {
foreach ($items as $itm)
{
$url = $itm->getUrl();
$message_text = substr(strip_tags($itm->getContent()), 0, 80);
$bot->send(new StructuredMessage($message['sender']['id'],
StructuredMessage::TYPE_GENERIC,
[
'elements' => [
new MessageElement($itm->getTitle(), $message_text, '', [
new MessageButton(MessageButton::TYPE_WEB, 'Read more', $url)
]),
]
]
));
}
} else {
$bot->send(new Message($message['sender']['id'], 'Not found a new projects in this section.'));
}
}
catch (Exception $e) {
writeToLog($e->getMessage(), 'Exception');
}
return true;
}
/**
* Log
*
* @param mixed $data Data
* @param string $title Title
* @return bool
*/
function writeToLog($data, $title = '')
{
$log = "
------------------------
";
$log .= date("Y.m.d G:i:s") . "
";
$log .= (strlen($title) > 0 ? $title : 'DEBUG') . "
";
$log .= print_r($data, 1);
$log .= "
------------------------
";
file_put_contents(__DIR__ . '/imbot.log', $log, FILE_APPEND); return true;
}

And config.php:

return [
'token' => '', // Токен страницы
'verify_token' => '', // Проверочный токен
'feeds' => [
'All jobs' => 'https://job4joy.com/marketplace/rss/',
'Web Development' => 'https://job4joy.com/marketplace/rss/?id=3',
'Software Development & IT' => 'https://job4joy.com/marketplace/rss/?id=5',
'Design & Multimedia' => 'https://job4joy.com/marketplace/rss/?id=2',
'Mobile Application' => 'https://job4joy.com/marketplace/rss/?id=7',
'Host & Server Management' => 'https://job4joy.com/marketplace/rss/?id=6',
'Writing' => 'https://job4joy.com/marketplace/rss/?id=8',
'Customer Service' => 'https://job4joy.com/marketplace/rss/?id=10',
'Marketing' => 'https://job4joy.com/marketplace/rss/?id=11',
'Business Services' => 'https://job4joy.com/marketplace/rss/?id=12',
'Translation & Languages' => 'https://job4joy.com/marketplace/rss/?id=14',
]
];

Publication in the catalog for all

Now out bot is only available to the account holder. To the boat was available to all, it is necessary on the page App Review — publish the application:

Then you need to ask for moderation messenger. To do this, go to the tab — Messenger.

press button «Request Permissions» in «App Review for Messenger» block.

In the resulting window, select «pages_messaging» and click «Add items».

Now it remains only to wait for moderation.

At the time of this writing, the approval of our first bot is not complete, although it’s been more than two business days from the date of filing.

Conclusion

In this article we examined the basic aspects of a chat bot for Facebook.

Links

  1. Getting Started with FB Chatbots — developers.facebook.com/docs/messenger-platform/quickstart
  2. Web hook Reference — developers.facebook.com/docs/messenger-platform/webhook-reference
  3. FB Messenger PHP API — github.com/pimax/fb-messenger-php
  4. Examples PHP API — github.com/pimax/fb-messenger-php-example
  5. Job4Joy FB Bot — github.com/pimax/job4joy_fb
  6. Job4Joy Freelance website — https://job4joy.com

--

--