Over a million developers have joined DZone.

How to Develop an IM Bot for Facebook Messenger

With bots gaining traction across multiple applications and devices, it's a good idea to start learning how to build them. In this article, I would like to share my experience of developing an IM bot for Facebook in PHP.

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Chat messengers are really taking the Internet by storm. One after another, instant messaging platforms announce the launch of a platform for building bots.

Facebook is no exception. During F8 conference on April 12th, Facebook introduced its own Messenger Platform for building bots.

In this article, I would like to share my experience of developing an IM bot for Facebook in PHP.

Introduction

Facebook chatbots are based on personal communications with a specific public page.
Therefore, in order to create a bot, we need to create an application for API access and a public page where users will communicate.

Page Creation

You have to simply create a public page and give it the name you want to see in the bot messenger, and upload the icon.

Registering and Configuring Your App

Now you are going to register your application in the developer's account.

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

Click “Add a new app”, then choose basic setup for a different platform:


Next, fill in the form below:


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

Click "Start".

First of all, select the page you created for the bot, and copy the token. You need to save it somewhere, because you will need it later.

Next, let’s 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']; 
} 

You have to add some text to the $verify_token variable.

The script is uploaded to the server. For instance, let’s assume that your script is available at the following address: https://domain.com/fbbot.

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

You need to look for the Webhooks block and Setup Webhooks button. When you find the button, click it.


Next, you need to enter the bot address https://domain.com/fbbot in the "Reverse URL-address" field.

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

You will need to enter the text indicated in the script of the $verify_token variable in the "Verify marker" field.

Pick the notifications you want to receive in your webhook using the "Fields subscription" option:

  • "message_deliveries" means message delivery notifications
  • "messages" notification means messages written by a user to the bot 
  • "messaging_optins"notification is designed for the callback whenever a message is received via the button on a website (via Send-to-Messenger Plugin)
  • “messaging_postbacks” means notifications when somebody visits your page by clicking the buttons in previous bot posts (I will explain it later).

Select the necessary notifications and then click "Confirm and save." 

Connecting the App and the Page

Type the following in the console:

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

Replace the -token- with a token of your page.

Message Types in the FB Messenger

Messages may be of two main types, containing either a text or Structured Text, which in turn can be of the following types:

  • buttons

  • generic messages (elements)

  • receipts (e.g. invoice for payment)

Buttons

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

They look like this:

The buttons can be of two types:

  1. Type 1 buttons send a reply to the bot

  2. Type 2 buttons send a user to a specific URL

An important point: one message can contain no more than 3 buttons, otherwise it won’t reach the recipient. 

Elements (Generic)

This type of messages is used to send product cards or other elements with a similar structure.

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

One message can contain up to 10 elements. If there is more than one item, a horizontal scroll bar appears.

Invoice for Payment (Receipt)

The purpose of this message is clear from the title.

Facebook decided to make a fully functional store out of their messenger.

The invoice can contain information about products, their cost, payment, delivery address, and any discounts.

An important point: the account number must be unique.

Writing the code

At the time of building this bot, the GitHub didn't have API implementation in PHP, so I had to write my own PHP SDK.

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

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

Create 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
// ...
            }
   }
}

Let's try to send a message in response to the user's message.

In order to do this, you have to add the following into the message receipt block:

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

Time to check the result. Let's find our bot in the messenger and send a message to it.

We are expecting to get "Hi there!" in response.

Important: Until your app passes moderation, the bot will work only for its author.

If everything works as it should, we go further.

You need to add the following in the message receipt block:

// 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.'));
}

Let's try to send the following posts through the bot:

  • text

  • button

  • generic

  • receipt

If everything is done according to the instructions, you should receive all types of above mentioned messages in the messenger.

A Real Example of a Bot for Job4Joy Freelance Portal

Our goal is to create a bot that we will issue new projects in the corresponding category.

We will receive data through 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 Public Catalog

So far our bot has been accessible only for the account holder. To make the boat available for everybody, you need to publish the app on the App Review page:


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

Press the "Request Permissions" button in "App Review for Messenger" block.

Next, select "pages_messaging" in the window and click "Add items".

Now you only have to wait for moderation.

At the time of writing this article, the moderation process for our first bot was not complete, though it's been more than two business days from the date our application was sent.

Conclusion

In this article, we have covered the basic aspects of creating a chatbot for Facebook.

Useful 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 of 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


Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
messenger ,dev ,bots ,facebook ,php

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}