Creating a Facebook Messenger BOT with Laravel 5

Keeping in constant communication with your customers is a necessity for most businesses now days. Most companies do this via email and SMS messaging, but keeping in contact via Facebook Messenger can also be a great tool in increasing sales and customer satisfaction.

Even better, having a Facebook Messaging BOT to automate your customer’s initial incoming messages can cut down on costs, and help your company be more productive and efficient.

Over the last few months I was tasked with integrating Facebook’s Messenger API into a popular SaaS platform, so this tutorial will get your feet wet with the Messenger API in general, and then show you how to get started integrating the Messenger API with your company’s platform using Laravel 5.


** NOTE: For this tutorial, you will need a Facebook Developer account


First create a Facebook developer account (if you don’t already have one), then create an app, a page, and then add the messenger product.

This Quick Start Guide will guide you through the Facebook app procedure quickly.


Alright… Now let’s get started!


First we need to create our routes..
Create a route group in your routing file and add the following two routes.

app/Http/routes.php


Route::group(['prefix' => 'facebook'], function () {
    Route::get('message', 'FacebookController@verify');
    Route::post('message', 'FacebookController@message');
});

Next up, just like any Laravel route, we need to create our Facebook Controller with the following two methods that the above routes are referencing.

After our dependencies are injected in our constructor, we have our facebookVerify method. This method is used in order for our Facebook Messenger app’s webhook to verify that our route is alive and returning
a 200 response.


** NOTE: To understand why this method is needed, refer to the Facebook Messenger API docs.


Our second method, facebookMessage is the entry point for all incoming messages from our Facebook User.

First, we get our Request, process the object’s JSON and then pass our payload off to our message handler. Our message handler is located in in our Facebook Manager service.

Let’s take a look.

app/Http/Facebook/FacebookController.php


namespace App\Http\Controllers\Facebook;

use App\Http\Controllers\Controller;
use App\Services\Facebook\FacebookManagerIncomingMessageService;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\ResponseFactory;

class FacebookController extends Controller
{
    private $facebookManagerIncomingMessageService;
    private $responseFactory;

    public function __construct(
        FacebookManagerIncomingMessageService 
            $facebookManagerIncomingMessageService,
        ResponseFactory $responseFactory)
    {
      $this->facebookManagerIncomingMessageService = 
          $facebookManagerIncomingMessageService;
      $this->responseFactory = $responseFactory;
    }

    /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function facebookVerify(Request $request)
    {
        $verified = $this
            ->facebookManagerIncomingMessageService
            ->incomingMessageVerify(
                $request->hub_challenge, 
                  $request->hub_verify_token);

        if (!empty($verified)) {
            echo $verified;
        } else {
            return $this
                ->responseFactory
                ->json(Response::HTTP_FORBIDDEN);
        }
    }

    /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function facebookMessage(Request $request)
    {
        $message = $request->json()->all();

        try {
            $this
              ->facebookManagerIncomingMessageService
              ->incomingMessageHandler($message);
        } catch (\Exception $e) {
            return $e;
        }

        // we want to always send a 200 back 
        // to the messenger api - even with 
        // an issue on our side or messages 
        // start backing up and everything 
        // comes to a halt really quick
        return $this
            ->responseFactory
            ->json(Response::HTTP_OK);
    }
}

Now let’s take a look at the FacebookManagerIncomingMessageService.

This Service is used to process our incoming messages FROM the Facebook Messenger API, along with any handling (i.e. saving the message, creating a customer, etc).

Our entry point to our Facebook Manager service is the method incomingMessageHandler. This method contains a switch statement that is used to parse our JSON that is passed to it from the controller for the sender ID, the recipient ID, the actual message, and any other metadata that is needed for our application. For this tutorial all we care about is the sender and recipient IDS and the message. For more information on all the available fields reference the Facebook Messenger API docs.


Our next method setConversationParticipants is pretty self explanatory. It is used to set the IDs of both the sender and the recipient.


** NOTE: The sender ID and recipient ID reverse when we are parsing an ECHO message. To learn more about echo messages, and their purpose reference the Messenger API.


The next methods are our setFacebookPageByPageId and setFacebookUserBySenderId. These methods are used to set the recipient ID (non echo message), and sender ID (non echo message) in our class to use when needed. This let’s us know which message came from which party.


Code tracing through the code brings us to the createOrUpdateCustomer and getFacebookUserBasicInfo methods. These methods are used to check our database if our customer is already in our DB, and if so retrieve that model, if not we save this Facebook User as a new customer. Our Facebook User is referenced by the Facebook Sender ID that we parse out in the incoming JSON.

If this Facebook User is new and not a customer in our database, we then use our basic info method together with our Facebook User’s Sender ID to retrieve the user’s first name, last name, and avatar image from the Facebook Graph API.

Next is our setFacebookPageByPageId method. This method is how we keep track and reference the Facebook Page (your company’s page) that the Facebook User is talking to. In our Customer’s table we save the Facebook User’s senderID, and in our Page table, we save the Facebook Page’s page ID (usually the recipient ID if not an echo message).


OK… Enough talking… Let’s take a look at the actual code..

This is our FacebookManagerIncomingMessageService.

app/Services/Facebook/FacebookManagerIncomingMessageService.php


namespace App\Services\Facebook;

use App\Services\Facebook\FacebookManagerOutgoingMessageService;

use App\Contracts\Repositories\FacebookRepositoryInterface;
use App\Contracts\Repositories\CustomerRepositoryInterface;

use Illuminate\Contracts\Logging\Log;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class FacebookManagerIncomingMessageService
{
    private $facebookManagerOutgoingMessageService;

    private $facebookPageRepository;
    private $customerRepository;
    
    private $facebookPage;
    private $facebookPageByPageId;

    private $customer;
    private $facebookUserBySenderId;
   
    private $message;

    private $log;

    public function __construct(
        FacebookManagerOutgoingMessageService $facebookManagerOutgoingMessageService,
        FacebookRepositoryInterface $facebookRepository,
        CustomerRepositoryInterface $customerRepository,
        Log $log
    ) {
        $this->facebookManagerOutgoingMessageService = $facebookManagerOutgoingMessageService;
        $this->facebookRepository = $facebookRepository;
        $this->customerRepository = $customerRepository;
        $this->log = $log;
    }

    /**
     * Verify the Facebook challenge.
     *
     * @param $challenge
     * @param $verifyToken
     * @return null
     */
    public function incomingMessageVerify($challenge, $verifyToken)
    {
        if ($verifyToken === $this
                ->facebookManagerOutgoingMessageService
                ->getVerificationToken()) {
            return $challenge;
        } else {
            return null;
        }
    }

    /**
     * Entry point for handling any incoming messages
     * from the Facebook Messenger Webhook
     *
     * @param array $message
     * @throws FacebookManagerIncomingMessageException
     */
    public function incomingMessageHandler(array $message)
    {
        $this->setMessage($message);

        if (!empty($message['object']) && 
          $message['object'] === 'page') {

          foreach ($message['entry'] as $pageEntry) {

            // iterate through each message and process
            foreach ($pageEntry['messaging'] as $message) {

             // set the correct sender & recipient
             $this->setConversationParticipants($message);

             switch ($message) {
                       
               // incoming facebook user message
               case (
                 !empty($message['message']) &&
                 empty($message['message']['is_echo']))):
                   $this->processFacebookUserMessage($message);
                   break;

               // incoming facebook echo message
               // echo messages are sent 
               // FROM 
               // the API when messages are sent 
               // TO
               // the API. Echo messages contain 
               // the message that was
               // (along with other metadata) 
               // to the API in order "echo" 
               // what was sent so you can 
               // process it (i.e. store the
               // message in your database)
               case (
                 !empty($message['message']) && 
                 !empty($message['message']['is_echo']):
                   $this->processFacebookPageMessage($message);
                   break;
                        
               default:
                   break;
            }
          }
        }
        return true;
    } else {
      throw new FacebookManagerIncomingMessageException(
        'Problem processing the incoming facebook message.');
    }
  }

   /**
    * Set our Facebook User's incoming message.
    * ** NOTE: Echo messages have the  
    * sender & recipient IDs reversed.
    */
    private function setMessage(
        $message)
    {
        $this->message = $message;
    }

    private function setConversationParticipants(
        $message)
    {
        // incoming facebook user visitor message
        if (empty($message['message']['is_echo'])) {
            $this->isEcho = false;
            $this->setFacebookPageByPageId($message['recipient']['id']);
            $this->setFacebookUserBySenderId($message['sender']['id']);
        } else { // this is an echo message - swap sender and recipient
            $this->isEcho = true;
            $this->setFacebookPageByPageId($message['sender']['id']);
            $this->setFacebookUserBySenderId($message['recipient']['id']);
        }
    }

    /**
     * Process the Facebook User's incoming message
     *
     * @param $message
     */
    private function processFacebookUserMessage($message)
    {
        return $this->saveMessageFromFacebookUser($message['message']);
    }
    
    /**
     * Check to see if there is a Facebook Page by this
     * Facebook Page ID. 
     * ** NOTE:This is the ID that is
     * associated with the Facebook Page. The Facebook
     * Page is the page that the user is sending messages to.
     *
     * @param $facebookPageId
     * @return mixed
     * @throws ModelNotFoundException
     */
    private function setFacebookPageByPageId($facebookPageId)
    {
  
        try {
            $facebookPage = $this->facebookPageRepository
                ->findByFacebookPageIdOrFail($facebookPageId);
        } catch (
            ModelNotFoundException $e) {
            throw $e;
        }

        return $facebookPage;
    }

    /**
     *  Check to see if there is an already existing
     *  customer for this Facebook customer
     *
     * @param $facebookSenderId
     * @return $this|null
     */
    private function setFacebookUserBySenderId(
        $facebookUserSenderId)
    {
        // check to see if we have a record of this customer already
        $this->customer = $this->customerRepository
            ->getFirstBy('facebook_sender_id', $facebookUserSenderId);

        // This Facebook user is not a customer yet
        // Create a new customer
        if (empty($this->customer)) {
            $this->customer = $this->createOrUpdateCustomer();
        }
    }

    /**
     * Create a new customer based on this Facebook User
     *
     * @return \App\Models\Customer
     */
    private function createOrUpdateCustomer()
    {
      $customerData = $this
        ->getFacebookUserBasicInfo();
       
      $customerData['first_name'] = 
        (!empty($customerFacebookData['first_name']))
          ? $customerFacebookData['first_name']
          : '';
      $customerData['last_name'] = 
        (!empty($customerFacebookData['last_name']))
          ? $customerFacebookData['last_name']
          : '';
      $customerData['avatar'] = 
        (!empty($customerFacebookData['profile_pic']))
          ? $customerFacebookData['profile_pic']
          : '';
      $customerData['facebook_sender_id'] = 
        (!empty($this->customerByFacebookSenderId))
          ? $this->customerByFacebookSenderId
          : '';

      if (empty($this->customer)) {
        $this->customer = $this
          ->customerRepository'
          ->create($customerData);
      } else {
        $this->customer->first_name = $customerData['first_name'];
        $this->customer->last_name = $customerData['last_name'];
        $this->customer->avatar = $customerData['avatar'];
      }

      $this->customer->save();

      return $this->customer;
  }

    /**
    * Use our Facebook Manager Outgoing Message
    * Service to send a message to the Facebook
    * Messenger API to query basic user info for
    * our Facebook User (first name, last name,
    * avatar, etc) and return this info so we
    * can use it to create our new customer.
    */
    private function getFacebookUserBasicInfo()
    {
        try {
            $response = $this
                ->facebookManagerOutgoingMessageService
                ->getFacebookUserBasicInfo(
                    $this->facebookUserSenderId, 
                    $this->facebookPage
                      ->facebook_page_access_token
                );

            $responseBody = $response->getBody();
            $facebookUserBasicInfo = json_decode($responseBody, true);

        } catch (\Exception $e) {
            $facebookUserBasicInfo = [];
        }

        return $facebookUserBasicInfo;
    }

   /**
    * Save or process the Facebook User's message
    */
    private function saveFacebookUserMessage($message) 
    {
      // ...
      // ...
      // Save or do something cool with your Facebook User's message.
      // ...
      // ...
    }

   /**
    * Save or process the Facebook Page's message
    */
    private function saveFacebookPageMessage($message) 
    {
      // ...
      // ...
      // Save or do something cool with the message that your Employee sent
      // FROM your Facebook Page to the 
      // Facebook User.
      // ...
      // ...
    }



** Note: This tutorial already assumes you have a Customer and FacebookPage Model created and that your database is in sync with them.



Now let’s take a look at our model retrieval method inside our Facebook Repository. This method is used to lookup our FacebookPage information (so the Facebook User (customer) is communicating with the correct Facebook Page (your employee)).

app/Repositories/EloquentFacebookRepository.php


namespace App\Repositories;

use App\Models\FacebookPage;

class EloquentFacebookRepository extends AbstractEloquentRepository
{
    // ...
    // ...

    public function findByFacebookPageIdOrFail($facebookPageId)
    {
        $facebookPage = $this
            ->model
            ->where(
                'facebook_page_id', $facebookPageId)
            ->first();
        if (is_null($facebookPage)) {
            $e = new ModelNotFoundException;
            $e->setModel(FacebookPage::class);
            throw $e;
        }

        return $location;
    }

    // ...
    // ...
}


Now that we know how to process incoming messages from our Facebook User (customer), let’s see how we can use our FacebookManagerOutgoingMessageService to send a message to the Facebook Messenger API from our Facebook Page (employee) and ultimately to our Facebook User (customer).

For HTTP requests we will be using Guzzle.


The first method getFacebookUserBasicInfo is how we query the Facebook Messenger API for basic Facebook User information. Reference the API docs to see all of the available information that you can retrieve with the Facebook User’s Sender ID.

The next method, our sendMessageToFacebookUser is used to send the actual message FROM our Facebook Page (employee) TO our Facebook User (customer). First, we retrieve our Page API key that is stored in our config file, our Page token (stored also in our FacebookPage table), and our Facebook User’s Sender ID, and then concatenate them along with the URI to send a message to our Facebook User.


Let’s take a look at the code…

app/Services/Facebook/FacebookManagerOutgoingMessageService.php


namespace App\Services\Facebook;

use App\Models\Customer;
use App\Models\;
use GuzzleHttp\Client as GuzzleClient;

class FacebookManagerOutgoingMessageService
{
 
    /** @var array facebook configuration */
    private $facebookConfig;
    private $guzzleClient;

    public function __construct(
        array $facebookConfig,
        GuzzleClient $guzzleClient)
    {
        $this->facebookConfig = $facebookConfig;
        $this->guzzleClient = $guzzleClient;
    }

    /**
     * Get the Facebook verification 
     * token from our config
     *
     * @return mixed
     */
    public function getVerificationToken()
    {
        return $this->facebookConfig['app_verification_token'];
    }

   /**
     * Get the Facebook User basic profile info
     *
     * @param $visitorByFacebookSenderId
     * @param $locationByFacebookPageAccessToken
     * @return mixed|\Psr\Http\Message\ResponseInterface
     * @throws Exception
     */
    public function getFacebookUserBasicInfo(
      $facebookUserBySenderId, $facebookPageAccessToken)
    {
        $apiUrl =
            $this->facebookConfig['graph_user_profile_api_uri'].
            $facebookUserBySenderId. '?access_token='.
            $facebookPageAccessToken;

        try {
            $response = $this
                ->guzzleClient
                ->request('GET', $apiUrl);
        } catch (\Exception $e) {
            throw $e;
        }

        return $response;
    }

    /**
     * Send the message to our the Facebook messenger API
     * and ultimately the Facebook User we're communicating
     * with.
     *
     * @param FacebookPage $facebookPage
     * @param array $requestArguments
     * @return mixed|\Psr\Http\Message\ResponseInterface|void
     * @throws Exception
     */
    private function sendMessageToFacebookUser(
        FacebookPage $facebookPage, array $requestArguments)
    {
        $apiUrl =
            $this->facebookConfig['graph_messenger_api_uri'].
            $facebookPage->facebook_page_access_token;

        try {
            return $this->guzzleClient->request('POST', $apiUrl, $requestArguments);
        } catch (\Exception $e) {
            throw $e;
        }
    }


And finally a quick look at your Facebook Configuration. Add the following (update with your values) the config tokens in your .env file.


// ...
// ...
// ...
FACEBOOK_APP_VERIFICATION_TOKEN = my_voice_is_my_password_verify_me
FACEBOOK_GRAPH_MESSENGER_API_URI = https://graph.facebook.com/v2.8/me/messages?access_token=
// ...
// ...
// ...


Now the only value that will be pulled from your database will be the Page Access Token, the Page ID, for your Facebook Page, and the Facebook User’s Sender ID for your Customer.


And that should be it! This was a long tutorial, so if you see any mistakes or have any questions on something that’s not clear and confusing… Just ping me! I’ll be glad to help.. 😀


Have fun coding! 😀

7 thoughts on “Creating a Facebook Messenger BOT with Laravel 5

  1. I have error. Can you have githab or more details for examle this
    use App\Repositories\CustomerRepositoryInterface;

  2. Thanks for the feedback.
    In class FacebookManagerIncomingMessageService, function incomingMessageVerify we call
    $this->facebookManagerOutgoingMessageService ->getVerificationToken()

    Where this instance of the class must be initialized?

    • Sorry about that, looks like that was left out. It’s injected into the FacebookManagerIncomingMessageService constructor.
      I updated the FacebookManagerIncomingMessageService constructor above to reflect the changes.

      In this situation Laravel is implicitly ( magically 😀 ) doing the dependency injection for you. If you need to explicitly inject
      arguments into a service that Laravel can’t determine on the fly, you define what Laravel should inject in a provider.

      Let me know if you have any other questions.

  3. Sorry if this is a simple question.

    BindingResolutionException in Container.php line 873: Target [App\Contracts\Repositories\FacebookRepositoryInterface] is not instantiable while building [App\Http\Controllers\FacebookController, App\Services\Facebook\FacebookManagerIncomingMessageService].

    My interface:
    /*——————————————————————————–*/
    app->bind(
    ‘App\Contracts\Repositories\CustomerRepositoryInterface’,
    ‘App\Contracts\Repositories\FacebookRepositoryInterface’
    );
    }

    c) in composer.json
    “autoload”: {
    “classmap”: [
    “database”
    ],
    “psr-4”: {
    “App\\”: “app/”,
    “App\\Models\\”: “app/Models/”,
    “App\\Contracts\\”: “app/Contracts/”,
    “App\\Contracts\\Repositories\\”: “app/Contracts/Repositories/”,
    “App\\Repositories\\”: “app/Repositories/”

    }
    },

    and this)) php artisan clear-compiled
    What’s wrong?

  4. Sorry if this is a simple question.

    BindingResolutionException in Container.php line 873: Target [App\Contracts\Repositories\FacebookRepositoryInterface] is not instantiable while building [App\Http\Controllers\FacebookController, App\Services\Facebook\FacebookManagerIncomingMessageService].

    My interface:
    /*——————————————————————————–*/

    namespace App\Contracts\Repositories;

    use App\Models\FacebookPage;

    interface FacebookRepositoryInterface
    {

    }
    /*————————————————————-*/

    I tried these options to solve the error:

    a) in FacebookManagerOutgoingMessageService.php
    use App\Contracts\Repositories\FacebookRepositoryInterface as FacebookRepositoryInterface;
    use App\Contracts\Repositories\CustomerRepositoryInterface as CustomerRepositoryInterface;

    b) in ServiceProvider.php (namespace App\Providers)

    public function register()
    {
    $this->app->bind(
    ‘App\Contracts\Repositories\CustomerRepositoryInterface’,
    ‘App\Contracts\Repositories\FacebookRepositoryInterface’
    );
    }

    c) in composer.json
    “autoload”: {
    “classmap”: [
    “database”
    ],
    “psr-4”: {
    “App\\”: “app/”,
    “App\\Models\\”: “app/Models/”,
    “App\\Contracts\\”: “app/Contracts/”,
    “App\\Contracts\\Repositories\\”: “app/Contracts/Repositories/”,
    “App\\Repositories\\”: “app/Repositories/”

    }
    },

    and this)) php artisan clear-compiled
    What’s wrong?

    • It’s kinda hard to tell what’s fully wrong from just that snippet, but what stands out is when you explicitly define what is going to be injected into the class (instead of letting Laravel do it for you) you need to tell your service provider what class it’s binding the dependencies to. Also, when you explicitly define your injections, you have to define all of the dependencies and all of them in the correct order in which they’re being injected.

      For example,
      If you want to explicitly inject the dependencies into the FacebookManagerIncomingMessageService (assuming my example is what your FacebookManagerIncomingMessageService looks like), you would do

      
      $this->app->bind(FacebookManagerIncomingMessageService::class, function($app) {
          return new FacebookManagerIncomingMessageService(
            $app[FacebookManagerOutgoingMessageService::class],
            $app[FacebookRepositoryInterface::class],
            $app[CustomerRepositoryInterface::class]
          );
      });
      
      

      However, for my example you shouldn’t have to explicitly define the injections for the FacebookManagerIncomingMessageService, so you shouldn’t have to define anything any in your service provider. Laravel should be able to determine what to inject dynamically.

Leave a Reply

Your email address will not be published. Required fields are marked *