DEV Community

Cover image for Building a booking chatbot using BotMan
Kiran Krishnan
Kiran Krishnan

Posted on • Edited on

Building a booking chatbot using BotMan

BotMan is the most popular chatbot development framework for PHP. You can use BotMan in your existing codebase or with any PHP frameworks.

There are different chatbot development frameworks available for Node Js, Python. BotMan is the only PHP framework that truly helps the developers to build chatbot using PHP.

If you are a Laravel developer, BotMan Studio is the one thing you are looking for. BotMan Studio is a bundled version of BotMan and Laravel.

How to create a chatbot using BotMan?

Let's take a look at BotMan Studio and build a simple Salon booking chatbot for the website.

Install and set up the BotMan Studio

First, make sure you have composer installed on your system. Now download the BotMan installer using Composer.

composer global require "botman/installer"
Enter fullscreen mode Exit fullscreen mode

This will install the BotMan Studio on your system.

Make sure to place the $HOME/.composer/vendor/bin directory (or the equivalent directory for your OS) in your $PATH so the botman executable can be located by your system.

Type "botman" on your command line to see if installation works correctly. If you can see the output similar to the below image then BotMan Studio installation is a success.

Create a new chatbot project

Now let's use the BotMan Studio to create new chatbot. You can use 'botman new' command which will create a fresh BotMan Studio installation.

botman new salon
Enter fullscreen mode Exit fullscreen mode

The above command will create a new directory called "salon" with the BotMan Studio installation with all of BotMan's dependencies already installed.

Test the installation works

Now start the development server using the

cd salon
php artisan serve
Enter fullscreen mode Exit fullscreen mode

That will start the development server at http://127.0.0.1:8000

If the installation is okay, you can see the welcome page from Botman.

Click the link "Tinker", there you can try a very simple chatbot. Say "Hi" to bot and the bot will reply with "Hello!".

BotMan chatbot development

Great job. Now you have everything ready to build your first chatbot.

Install the web driver

BotMan comes with many drivers for the developers. Drivers help us to connect the bot with messaging channels such as Facebook Messenger, Slack, Telegram, Twilio, Web etc.

As we are going to build a web-based chatbot, let's install the web driver.

php artisan botman:install-driver web
Enter fullscreen mode Exit fullscreen mode

We can use WebDriver for adding the chatbot to our website or use the APIs. Web driver comes with APIs for sending and receiving messages. This helps us to build our own chat interfaces by leveraging the WebDriver APIs.

BotMan ships with a web widget that you can use out of the box. We can include the Javascript snippet to our welcome view (welcome.blade.php)

<script src='https://cdn.jsdelivr.net/npm/botman-web-widget@0/build/js/widget.js'></script>
Enter fullscreen mode Exit fullscreen mode

Now, refresh your page and you can see the chat widget at the bottom right corner of the page. Say "Hi" to bot and the bot will reply with "Hello!". That's it.

BotMan chat widget

There are 2 important core concepts you should learn before starting building chatbot using the BotMan Studio - Hearing the Messages and Sending the Messages.

Like any other web application, chatbot receives some request, process it and send the responses back to the channels (Web, Messenger, Slack etc).

Hearing the Messages

$botman = resolve('botman');

$botman->hears('Hi', function ($bot) {
    $bot->reply('Hello!');
});
Enter fullscreen mode Exit fullscreen mode

You can even use a regular expression like below. It listens for either "Hi" or "Hello" anywhere in the incoming message.

$botman = resolve('botman');

$botman->hears('._(Hi|Hello)._', function ($bot) {
    $bot->reply('Hello!');
});
Enter fullscreen mode Exit fullscreen mode

Instead of Closure, you can pass a class and method that will get called if the keyword matches. In the example below, startConversation method will get called if the user says "Hi".

$botman = resolve('botman');

$botman->hears('._(Hi|Hello)._', BotManController::class.'@startConversation');
Enter fullscreen mode Exit fullscreen mode

Sending the Messages

You have already seen how to send a message using BotMan. BotMan can send messages in different ways - text, buttons, attachments, generic templates etc.

Let's see how to send a text message with two buttons.

$botman = resolve('botman');

$question = Question::create('Do you agree with me?')
    ->callbackId('agree')
    ->addButtons([
        Button::create('Yes')->value('yes'),
        Button::create('No')->value('no'),
    ]);

$botman->ask($question, function(Answer $answer) {

});
Enter fullscreen mode Exit fullscreen mode

Chatbot conversation flows

We are going to build a simple salon booking chatbot which helps the website visitors to book the salon services through the chatbot from their website.

Every chatbot must have one or more conversation flows. Here is the expected conversation flow of chatbot we are going to build.

  • Welcome the visitor
  • Ask for the name, email, and mobile
  • Prompt the visitor to choose one of the services
  • Prompt the visitor to choose a date and time slot
  • Show the booking information

Build the chatbot dialogs

Now open routes/botman.php and change it so that when someone says 'Hi' or 'Hello', startConversation method in BotManController will get called.

<?php

use App\Http\Controllers\BotManController;

$botman = resolve('botman');

$botman->hears('._(Hi|Hello)._', BotManController::class.'@startConversation');
Enter fullscreen mode Exit fullscreen mode

Create a new conversation class OnboardingConversation

php artisan botman:make:conversation OnboardingConversation
Enter fullscreen mode Exit fullscreen mode

The above command will create the OnboardingConversation class at app/Http/Conversations/

<?php

namespace App\Http\Conversations;

use BotMan\BotMan\Messages\Conversations\Conversation;

class OnboardingConversation extends Conversation
{
    public function run()
    {
        //
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's take a look at the BotManController class located at app/Http/Controllers/ and update the code so that it can use our new OnboardingConversation class.

<?php

namespace App\Http\Controllers;

use BotMan\BotMan\BotMan;
use Illuminate\Http\Request;
use App\Http\Conversations\OnboardingConversation;

class BotManController extends Controller
{
    public function handle()
    {
        $botman = app('botman');

        $botman->listen();
    }

    public function tinker()
    {
        return view('tinker');
    }

    public function startConversation(BotMan $bot)
    {
        $bot->startConversation(new OnboardingConversation());
    }
}
Enter fullscreen mode Exit fullscreen mode

Now open the conversation class OnboardingConversation. It extends an abstract class Conversation. The run method is the starting point of the conversation and get's executed immediately.

<?php

namespace App\Http\Conversations;

use BotMan\BotMan\Messages\Conversations\Conversation;

class OnboardingConversation extends Conversation
{
    public function run()
    {
        //
    }
}
Enter fullscreen mode Exit fullscreen mode

Ask for the name, email, and mobile

Let's ask the visitor to enter his/her name. This is a very simple thing to do.

<?php

namespace App\Http\Conversations;

use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Conversations\Conversation;

class OnboardingConversation extends Conversation
{
    public function askName()
    {
        $this->ask('What is your name?', function(Answer $answer) {
            $this->say('Nice to meet you '. $answer->getText());
        });
    }

    public function run()
    {
        $this->askName();
    }
}
Enter fullscreen mode Exit fullscreen mode

How does it work?

The run method will call the askName method when the conversation starts. Inside the askName it uses the 'ask' method which will output a question in the chatbot. The user response can be fetched using $answer->getText().

Now the bot wants to ask the 2nd and 3rd questions. So let's create two methods - askEmail()and askMobile().

public function askEmail()
{
    $this->ask('What is your email?', function(Answer $answer) {

        $validator = Validator::make(['email' => $answer->getText()], [
            'email' => 'email',
        ]);

        if ($validator->fails()) {
            return $this->repeat('That doesn\'t look like a valid email. Please enter a valid email.');
        }

        $this->bot->userStorage()->save([
            'email' => $answer->getText(),
        ]);

        $this->askMobile();
    });
}
Enter fullscreen mode Exit fullscreen mode

There are a few interesting things to notice.

First, I have used Laravel validation to validate the email. If the validation fails, the chatbot prompts the users to enter a valid email address. The method 'repeat' will ask the last asked question again.

$this->repeat('That doesn\'t look like a valid email. Please enter a valid email.');
Enter fullscreen mode Exit fullscreen mode

userStorage method as the name suggests saving the current chatbot user information. Hence we will use userStorage method to store the booking information.

$this->bot->userStorage()->save([
    'name' => 'Your name here',
]);
Enter fullscreen mode Exit fullscreen mode

The bot will ask the question one after another. Here is the complete code.

<?php

namespace App\Http\Conversations;

use Validator;
use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Conversations\Conversation;

class OnboardingConversation extends Conversation
{
    public function askName()
    {
        $this->ask('What is your name?', function(Answer $answer) {
            $this->bot->userStorage()->save([
                'name' => $answer->getText(),
            ]);

            $this->say('Nice to meet you '. $answer->getText());
            $this->askEmail();
        });
    }

    public function askEmail()
    {
        $this->ask('What is your email?', function(Answer $answer) {

            $validator = Validator::make(['email' => $answer->getText()], [
                'email' => 'email',
            ]);

            if ($validator->fails()) {
                return $this->repeat('That doesn\'t look like a valid email. Please enter a valid email.');
            }

            $this->bot->userStorage()->save([
                'email' => $answer->getText(),
            ]);

            $this->askMobile();
        });
    }

    public function askMobile()
    {
        $this->ask('Great. What is your mobile?', function(Answer $answer) {
            $this->bot->userStorage()->save([
                'mobile' => $answer->getText(),
            ]);

            $this->say('Great!');
        });
    }

    public function run()
    {
        $this->askName();
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's try our chatbot to see if that works. Go to http://127.0.0.1:8000, refresh the page and start the conversation by sending 'Hello'.

Botman Booking Chatbot

Prompt the visitor to choose one of the services

Now let's move to the next questions. This time the chatbot will ask the users to choose one of the services. Assume the salon offers 3 services - Hair, Spa, Beauty.

Create a new conversation class SelectServiceConversation

php artisan botman:make:conversation SelectServiceConversation
Enter fullscreen mode Exit fullscreen mode

When we build more and more conversations, we want to connect conversations with one another. You can use the method $this->bot->startConversation(new AnotherConversation()) inside any conversation class to connect it with another conversation.

Open the OnboardingConversation conversation class and make the following changes.

public function askMobile()
{
    $this->ask('Great. What is your mobile?', function(Answer $answer) {
        $this->bot->userStorage()->save([
            'mobile' => $answer->getText(),
        ]);

        $this->say('Great!');

        $this->bot->startConversation(new SelectServiceConversation()); // Trigger the next conversation
    });
}
Enter fullscreen mode Exit fullscreen mode

BotMan ships cool method to asking multiple choice questions - Question and Button classes.

<?php

namespace App\Http\Conversations;

use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Conversations\Conversation;

class SelectServiceConversation extends Conversation
{
    public function askService()
    {
        $question = Question::create('What kind of Service you are looking for?')
            ->callbackId('select_service')
            ->addButtons([
                Button::create('Hair')->value('Hair'),
                Button::create('Spa')->value('Spa'),
                Button::create('Beauty')->value('Beauty'),
            ]);

        $this->ask($question, function(Answer $answer) {
            if ($answer->isInteractiveMessageReply()) {
                $this->bot->userStorage()->save([
                    'service' => $answer->getValue(),
                ]);
            }
        });
    }

    public function run()
    {
        $this->askService();
    }
}
Enter fullscreen mode Exit fullscreen mode

isInteractiveMessageReply method detects if the user interacted with the message and clicked on a button or simply entered text.

Let's try our chatbot to see if that works. Go to http://127.0.0.1:8000, refresh the page and start the conversation by sending 'Hello'.

Prompt the visitor to choose a date and time slot

Great!. As what we have done before let's create one more conversation class to ask the users to choose a date and timeslot for the booking.

php artisan botman:make:conversation DateTimeConversation
Enter fullscreen mode Exit fullscreen mode

The below conversation class will first prompt the users to choose a date then a timeslot and save the responses in the current chatbot user storage.

Here is the complete code.

<?php

namespace App\Http\Conversations;

use Carbon\Carbon;
use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Conversations\Conversation;

class DateTimeConversation extends Conversation
{
    public function askDate()
    {
        $availableDates = [
            Carbon::today()->addDays(1),
            Carbon::today()->addDays(2),
            Carbon::today()->addDays(3)         ];

        $question = Question::create('Select the date')
            ->callbackId('select_date')
            ->addButtons([
                Button::create($availableDates[0]->format('M d'))->value($availableDates[0]->format('Y-m-d')),
                Button::create($availableDates[1]->format('M d'))->value($availableDates[1]->format('Y-m-d')),
                Button::create($availableDates[2]->format('M d'))->value($availableDates[2]->format('Y-m-d')),
            ]);

        $this->ask($question, function(Answer $answer) {
            if ($answer->isInteractiveMessageReply()) {
                $this->bot->userStorage()->save([
                    'date' => $answer->getValue(),
                ]);

                $this->askTime();
            }
        });
    }

    public function askTime()
    {
        $question = Question::create('Select a time slot')
            ->callbackId('select_time')
            ->addButtons([
                Button::create('9 AM')->value('9 AM'),
                Button::create('1 PM')->value('1 PM'),
                Button::create('3 PM')->value('3 PM'),
            ]);

        $this->ask($question, function(Answer $answer) {
            if ($answer->isInteractiveMessageReply()) {
                $this->bot->userStorage()->save([
                    'timeSlot' => $answer->getValue(),
                ]);
            }
        });
    }

    public function run()
    {
        $this->askDate();
    }
}
Enter fullscreen mode Exit fullscreen mode

Don't forget to connect the DateTimeConversation conversation with SelectServiceConversation inside the askService method.

$this->bot->startConversation(new DateTimeConversation());
Enter fullscreen mode Exit fullscreen mode

Finally, let's show the booking details back to the user using a new conversation class BookingConversation.

php artisan botman:make:conversation BookingConversation
Enter fullscreen mode Exit fullscreen mode

Here is the BookingConversation class

<?php

namespace App\Http\Conversations;

use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Conversations\Conversation;

class BookingConversation extends Conversation
{
    public function confirmBooking()
    {
        $user = $this->bot->userStorage()->find();

        $message = '-------------------------------------- <br>';
        $message .= 'Name : ' . $user->get('name') . '<br>';
        $message .= 'Email : ' . $user->get('email') . '<br>';
        $message .= 'Mobile : ' . $user->get('mobile') . '<br>';
        $message .= 'Service : ' . $user->get('service') . '<br>';
        $message .= 'Date : ' . $user->get('date') . '<br>';
        $message .= 'Time : ' . $user->get('timeSlot') . '<br>';
        $message .= '---------------------------------------';

        $this->say('Great. Your booking has been confirmed. Here is your booking details. <br><br>' . $message);
    }

    public function run()
    {
        $this->confirmBooking();
    }
}

Enter fullscreen mode Exit fullscreen mode

Conclusion

The source code for this tutorial can be found here on Github.

I hope you found this article insightful.

Let's connect 🌎

Top comments (25)

Collapse
 
jamesnm profile image
James Moore

I get the following error

include(): Opis\Closure\ClosureStream::stream_set_option is not implemented!

When using the following code

$this->ask('What is your name?', function(Answer $answer) {
   $this->say('Nice to meet you '. $answer->getText());

}); 
Enter fullscreen mode Exit fullscreen mode

Has anyone else gotten this

Collapse
 
jamesnm profile image
James Moore

its due to php 7.4 that I have installed locally (arrow functions and all that fun stuff), my vagrant homestead setup is currently on 7.2~3 so I just served it from there and everything works great. Great tutorial thanks !

Collapse
 
devkiran profile image
Kiran Krishnan

Glad you found it useful.

Collapse
 
poojanetsol profile image
Pooja Goyal

Hello Sir

Iam buiding my chatbot with laravel and integrating using carbon library.
Can you please help me how can I show date picker in textbox.I wanted to select dat from date picker and save that value.

I waiting for your reply .Please help me as soon as possible.

Thanks
Pooja

Collapse
 
devkiran profile image
Kiran Krishnan

Hello Pooja,

Botman web widget doesn't support datepicker component by default. But you can customize the web widget according to your needs github.com/botman/web-widget

Collapse
 
lucasm profile image
Lucas Menezes

In the newest version of Botman, when creating a new conversation using the command 'php artisan botman:make:conversation OnboardingConversation' this create file in to the path 'App\Conversations'. I had to replace the "namespace App\Http\Controllers;" to "namespace App\Controllers;" and "use App\Http\Conversations\OnboardingConversation;" to "use App\Conversations\OnboardingConversation;".

Thanks for the great tutorial Kiran! Helped me introduce Botman for the team =)

Collapse
 
devkiran profile image
Kiran Krishnan

Thank you.

Collapse
 
rajput2219 profile image
rajput2219

Hello,
I want to conversation start from botman side.

Currently, firstly we have to enter the message after that Botman reply. But I want to when user open chatbot Botman start conversation.

Please help.

Collapse
 
bpropelled profile image
Propelled

Excellent article Kiran, I had no idea about this package. thanks for sharing

Collapse
 
devkiran profile image
Kiran Krishnan

Thank you!

Collapse
 
kasparlavik profile image
Kaspar Lavik

Hi, I am student of MscIT, basically I like to learn about AI Driven chatbot, How to build a chatbot and importance of chatbot for various industries. I also like to refer technical blogs like this blog. I always curious to learn new things and about new technologies. I gained my knowledge from this content. It was amazing and helpful too. Thank you writer for sharing this knowledge with us. bit.ly/2N5yyRm

Collapse
 
speedygonzales77 profile image
speedygonzales77

This was a great tutorial. The only thing that I saw different in my system is that the Classes were getting installed in /app/ and not in /app/Http/, but was able to edit a bit and made it work.

Thanks for great tutorial!!!

Collapse
 
joaoufms profile image
João Gabriel

I'm using the web driver without BotmanStudio, and when a type Hi i don't get a response. What can i do?

Collapse
 
cykonetic profile image
Nicholai Bush • Edited

Do you have any pointers/tips for using BotMan with Laravel Echo? I am using a custom Web driver
and web widget, just simple modifications of the originals.

Collapse
 
hardiktechtic profile image
hardik-techtic

Hi

How to add custom URL or custom parameter in response.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.