How I Built The LaravelQuiz Chatbot With BotMan and Laravel Ever wondered how well you know your favorite PHP framework Laravel? Give the LaravelQuiz Chatbot a try, and you will find out. This article is a step by step guide on how I created this chatbot with BotMan and Laravel.
Intro Hey everyone. In this article, I want to show you how I built the LaravelQuiz Chatbot. If you haven't tried it yet, it is a good idea to check it out before reading this article. This will help to understand what I am talking about. Right now it only supports Telegram. The chatbot provides a quiz with all kinds of questions q uestions about the Laravel framework. Every question comes with up to three possible answers. You need to pick the right one to collect points and make it to the highscore. But besides the ranks, the quiz is about having a good time. Enjoy the questions and see what you know about Laravel. Laravel. Have fun! Note: All
the code can be found in the public repository .
Setup Let's start this project by installing BotMan Studio. The easiest way to install BotMan Studio is via the installer. (Skip this, if you got it already installed)
composer global require "botman/installer"
Then we can crate a new BotMan Studio instance. botman new laravelquiz-chatbot
Since we want to provide this bot on Telegram, we need to install the
TelegramDriver as
well.
php artisan botman:install-driver telegram
This will install the Telegram driver and add the Telegram config file /config/botman/telegram.php . In there, you will see that it loads the Telegram bot token from the environment file. You get this token when you create a Telegram bot. bot . Add it to the .env file like: TELEGRAM_TOKEN=your-telegram-token-here
Now we can connect the BotMan application to the Telegram bot. Create a secured public URL with Laravel Valet. valet share
There is an excellent artisan command, that lets us set our BotMan URL via the command line. Make sure to add "/botman" to your application's URL. Just follow the instructions. php artisan botman:telegram:registe botman:telegram:register r
You should get a success success message. If not, please check that your provided URL is correct. correct. To make sure the bot is connected to our application, type hi.
Note: If
you don't use Laravel Valet, try a service like ngrok.
Storing Questions and Answers
Next, we want to think about our questions. The bot will ask the user about 20 questions. It will also provide two or three possible answers. I want to store questions and answers in a database. Each answer is connected to one question. So we need a table for the questions and one for the answers. We start with the questions.
Questions php artisan make:migration make:migration create_questions_table create_questions_table --create=questions --create=questions php artisan make:model Question
The model we will use later. Now we define our table structure for the questions. public function up up() () { Schema::create('questions' Schema::create('questions', , function function (Blueprin (Blueprint t $table) { $table->increments('id' $table->increments( 'id'); ); $table->string('text' $table->string('text'); ); $table->integer( 'points' 'points')->unsigned(); )->unsigned(); $table->timestamps(); }); }
Every question has
text and points . The user will get
these points if he answers correctly.
Answers Next, we create a migration for the answers. php artisan make:migration make:migration create_answers_table create_answers_table --create=answers php artisan make:model Answer
Since an answer is connected to a question, we use a question_id field. Additionally, we also add a text field and a flag to define if this answer is the correct one. public function up up() () { Schema::create('answers' Schema::create('answers', , function function (Bluepri (Blueprint nt $table) { $table->increments('id' $table->increments( 'id'); ); $table->integer( 'question_id' 'question_id'); ); $table->string('text' $table->string('text'); ); $table->boolean( 'correct_one' 'correct_one'); ); $table->timestamps(); }); }
Last step here is to connect the models. We can do this by adding a relationship to the Question model.
class Question extends Model { public function answers answers() () { return $this $this->hasMany(Answer::class); ->hasMany(Answer::class); } }
Create and Fill The Tables Before we can create these tables, we need to define our database credentials in the .env file. I use Laravel Valet with this settings. Of course, course, they will be different, different, depending on your local local setup. DB_DATABASE=laravelquiz-chatbot DB_USERNAME=root DB_PASSWORD=
Now, we can create the tables. php artisan migrate
Nice! Our tables are ready to get filled. I decided to use a Laravel Seeder class to do so. This way I can keep all questions and answers inside the repository and use an artisan command to fill the database. datab ase. php artisan make:seeder make:seeder QuestionAnswerSeeder QuestionAnswerSeeder
You could use two different different seeders as well, but I am okay okay with one here. In the run method of the seeder, I am truncating the tables and then store each question and its answers. public function run run() () { Question::truncate(); Answer::truncate(); $questionAndAnswers $questionAndAnswers = $this $this->getData(); ->getData(); $questionAndAnswers->each(function ($question) $questionAndAnswers->each(function ($question) { $createdQuestion $createdQuestion = Question::create([ 'text' => 'text' => $question['question' $question[ 'question' ], 'points' => $question['points' $question['points'], ], ]); collect($question[ 'answers' 'answers'])->each( ])->each( function function ($answer) ($answer) use use ($createdQuestion) ($createdQuestion) { Answer::create([ 'question_id' => $createdQuestion->id, $createdQuestion->id, 'text' => $answer['text' $answer[ 'text'], ], 'correct_one' => $answer['correct_one' $answer[ 'correct_one' ], ]); }); }); }
For the actual data, I use a separate getData method to keep it cleaner. In this method, I return a big collection with all the questions and answers. For better reading, I only show two of them here. private function getData getData() ()
{ return collect([ [ 'question' => 'question' => 'Who created Laravel?', Laravel?', 'points' => '5' , 'answers' => 'answers' => [ [ 'text' => 'Christoph Rumpel', Rumpel', 'correct_one' 'correct_one' => => false false], ], [ 'text' => 'Jeffrey Way', Way' , 'correct_one' => false false], ], [ 'text' => 'Taylor Otwell', Otwell', 'correct_one' 'correct_one' => => true true], ], ], ], [ 'question' => 'question' => 'Which of the following is a Laravel product?', product?', 'points' => '10' '10', , 'answers' => 'answers' => [ [ 'text' => 'Horizon' 'Horizon', , 'correct_one' => true true], ], [ 'text' => 'Sunset' , 'correct_one' => false false], ], [ 'text' => 'Nightfall' , 'correct_one' => true true], ], ], ], ]); }
As you can see, the the code is quite straightforward. It is just just a big array with all the data for questions and and answers. Don't forget to import the namespaces for the Answer and Question class. Inside database/seeds/DatabaseSeeder.php I add this new seeder to the run method. public function run run() () { $this->call(QuestionAnswerSeeder::class); $this ->call(QuestionAnswerSeeder::class); }
As a result, we can now seed seed the tables with an artisan command. php artisan db:seed
Voilà, we have our our questions and answers in in the database!
QuizConversation Now that everything is setup, we can go on and implement the chatbot logic for showing the questions to the user. I already know that I want to use a conversation class just for this purpose. Create a app/Conversation/QuizConversation.php file and fill it with this base code.
/**
* Start the conversati conversation. on. * * @return mixed */ public function run run() () { } }
Let's recap what we want to achieve - The questions are stored in the database. We want to grab g rab them and ask the user each of them. If you have followed other chatbot tutorials of mine, you've probably recognized that I usually write a method for every question. This would lead to more than 20 methods and a lot of code here. So what else can we do? All the questions are very similar to each other. The only significant difference is that some have two and some three possible replies. But that's not a big deal. The goal is to use one single method to ask these questions.
Show Info But before dealing with the questions, we tell the user what this conversation is about. /** @var integer */ protected $questionCount protected $questionCount = 0; public function run run() () { $this->showInfo(); $this ->showInfo(); } private function showInfo showInfo() () { $this->say( $this ->say( 'You will be shown '. '. $this $this->questionCount. ->questionCount. ' questions about Laravel. Every correct answer will reward you with a certain amount of points. Please keep it fair, and don\'t use any help. All the best! ' ); }
To try this out, we need to add a listener to the routes/botman.php file. hears('Hi' $botman->hears('Hi', , function function (BotMan (BotMan $bot) { $bot->reply('Hello!' $bot->reply('Hello!' ); }); $botman->hears('start' $botman->hears('start', , function function (BotMan (BotMan $bot) { $bot->startConversation( new QuizConversation() QuizConversation()); ); });
Typing start will kick off this conversation. Try it out in the Telegram chat.
Looping Questions To make looping the questions work, we need a few things. First, we define some more class properties. We will require them to show the user how many questions are left and to count count the user's points. /** @var Question */ protected $quizQuestions; protected $quizQuestions; /** @var integer */ protected $userPoints protected $userPoints = 0; /** @var integer */ protected $userCorrectAnswers protected $userCorrectAnswers = 0; /** @var integer */ protected $questionCount protected $questionCount = 0; // we already had this one /** @var integer */ protected $currentQuestion protected $currentQuestion = 1;
Then in the run method, we load our questions and fill some properties. We also shuffle the questions and switch the collection's key to the id. This will make it easier for us to remove items from the collection. public function run run() () { $this ->quizQuestions $this->quizQues tions = Question::all()->shuffl Question::all()->shuffle(); e(); $this->question $this ->questionCount Count = $this->quizQuestions->count(); $this ->quizQuestions->count(); $this->quizQues $this ->quizQuestions tions = $this $this->quizQuestions->keyBy( ->quizQuestions->keyBy('id' 'id'); ); $this->showInfo(); $this ->showInfo(); }
Then, before showing a question, we check if there are any left. After every question we will remove it. When there are are no more questions left, left, we show the user the quiz quiz result.
private function showInfo showInfo() () { $this->say( $this ->say( 'You will be shown '. '. $this $this->questionCount. ->questionCount. ' questions about Laravel. Every correct answer will reward you with a certain amount of points. Please keep it fair and don\'t use any help. All the best! ' ); $this->checkForNextQuestion(); $this ->checkForNextQuestion(); } private function checkForNextQuestion checkForNextQuestion() () { if ($this ( $this->quizQues ->quizQuestions->count() tions->count()) ) { return $this $this->askQuestion( ->askQuestion( $this $this->quizQuestions->first()); ->quizQuestions->first()); } $this->showResult(); $this ->showResult(); }
And here is the askQuestion method. It always gets the first item of the t he questions collections and uses the BotMan ask method to show the text to the user, as well as the answers. We loop over the question's answers, to add a button for each of them to the template. private function askQuestion askQuestion(Question (Question $question) { $questionTemplate $questionTemplate = BotManQuestion::create BotManQuestion::create($question->text) ($question->text); ; foreach ($question->answers->sh ($question->answers->shuffle() uffle() as as $answer) $answer) { $questionTemplate->addBu $questionTemplate->addButton(Button tton(Button::create($a ::create($answer->text)->val nswer->text)->value($answer->id ue($answer->id)); )); } $this ->ask($questionTemplate, function $this->ask($questionTemplate, function (BotManAnswer (BotManAnswer $answer) use use ($question) ($question) { $this->quizQuestions->forget($question->id); $this ->quizQuestions->forget($question->id); $this->checkForNextQuestion(); $this ->checkForNextQuestion(); }); } private function showResult showResult() () { $this->say( $this ->say( 'Finished ' ); }
Also, we add a showResult method, which is called when there are no more questions left. When you start the conversation again, you should be able to see all the questions. Each click on an answer will result in the next question, and after the last one, you y ou should get the finish message. Try it out yourself.
Note: Since
conversations get stored in the cache, you sometimes need to clear it with "php artisan cache:clear" to see changes. Adding Details This is already great. We can use this code to show as many questions as we want. At any time we can add a question to the database and the code will still work. I love it! Now I want to work on the details. There are a bunch of things to add:
show current question show how many questions are left show the users' choice tell if the reply was correct show the result
I am not able to go into each of them in depth. This is why I will show the changed methods and explain important steps. private function askQuestion askQuestion(Question (Question $question) { $this->ask( $this ->ask( $this $this->createQu ->createQuestionTempl estionTemplate($questi ate($question), on), function function (BotManAnswer (BotManAnswer $answer) use ($question) { $quizAnswer = Answer::find($answer->get Answer::find($answer->getValue()); Value()); if (! $quizAnswer) {
$this ->say( 'Sorry, I did not get that. Please use the buttons.'); $this->say( return $this $this->checkForNextQuestion(); ->checkForNextQuestion(); } $this->quizQuestions->forget($question->id); $this ->quizQuestions->forget($question->id); if ($quizAnswer->corre ($quizAnswer->correct_one) ct_one) { $this->userPoi $this ->userPoints nts += $question->points; $this->userCorrectAnswers++; $this ->userCorrectAnswers++; $answerResult = '✅' ; } else else { { $correctAnswer $correctAnswer = $question->answers()->wh $question->answers()->where( ere('correct_one' 'correct_one' , true true)->first())->first()>text; $answerResult = "❌ (Correct: {$correctAnswer})" {$correctAnswer})"; ; } $this->currentQuestion++; $this ->currentQuestion++; $this ->say( "Your answer: {$quizAnswer->text} {$answerResult}"); $this->say( {$answerResult}"); $this->checkForNextQuestion(); $this ->checkForNextQuestion(); }); } private function createQuestionTemplate createQuestionTemplate(Question (Question $question) { $questionText $questionText = '➡ Question: '. '. $this $this->currentQuestion. ->currentQuestion. ' / '. ' . $this $this->questionCount. ->questionCount. ' : ' .$question->text; $questionTemplate $questionTemplate = BotManQuestion::create BotManQuestion::create($questionT ($questionText); ext); $answers = $question->answers->shuff $question->answers->shuffle(); le(); foreach ($answers as $answer) { foreach ($answers $questionTemplate->addBu $questionTemplate->addButton(Button tton(Button::create($a ::create($answer->text)->val nswer->text)->value($answer->id ue($answer->id)); )); } return $questionTemplate; } private function showResult showResult() () { $this->say( $this ->say( 'Finished ' ); $this->say( $this ->say( "You made it through all the questions. You reached {$this->userPoints} points! Correct answers: {$this->userCorrectAnswers} {$this->userCorrectAnswers} / {$this->ques {$this->questionCount}" tionCount}"); }
Note that there is a new separate createQuestionTemplate method to make the code cleaner. Also interesting is that I check if the user's reply is from a button click. If not, I will repeat this question. This is possible because the buttons have the answer's id as the value. We use it to find the answer. $quizAnswer = Answer::find($answer->getV Answer::find($answer->getValue()); alue()); if (! if (! $quizAnswer) { $this->say( $this ->say( 'Sorry, I did not get that. Please use the buttons.'); buttons.'); return $this $this->checkForNextQuestion(); ->checkForNextQuestion(); }
HighscoreConversation Create a Table and Model To make the quiz more fun, we need a highscore. We start by adding a migration and the model. php artisan make:migration make:migration create_highscore_table create_highscore_table --create=highscore --create=highscore php artisan make:mode Highscore
In there, we save the user's chat ID, the name, points, count of correct answers and how often he tried. Since we work with points, it might be possible that someone answers less question correctly, but still leads the highscore table. The Highscore model will include a few things. First, we define the fillable property to whitelist what values we can store later. later. Also, the table name needs needs to be defined here because because it differs from the traditional names. protected $fillable = ['chat_id' protected $fillable ['chat_id', , 'name' , 'points' , 'correct_answers' , 'tries' 'tries']; ]; protected $table protected $table = 'highscore' 'highscore'; ;
Then we add a method for storing highscore entries. I use the updateOrCreate method to prevent duplicate entries. Every user should only have one entry. With every new try, the highscore will get updated. The unique field for these entries is the t he chat_id and not, like normally, the id field. The updateOrCreate method gets this info as the first parameter. Also note how I increase the tries field if the entry is not a new one. For this, the wasRecentlyCreated method comes in handy. public static function saveUser saveUser(UserInterf (UserInterface ace $botUser, int $userPoints, int $userCorrectAnswers) $userCorrectAnswers) { $user = static ::updateOrCreate([ 'chat_id' => $botUser->getId()], [ 'chat_id' => 'chat_id' => $botUser->getId(), 'name' => 'name' => $botUser->getFirstName().' $botUser->getFirstName().' ' .$botUser->getLastName(), 'points' => 'points' => $userPoints, 'correct_answers' => 'correct_answers' => $userCorrectAnswers, $userCorrectAnswers, ]); $user->increment('tries' $user->increment('tries'); ); $user->save(); return $user; return $user; }
At the end of the quiz, we show show the user his rank. This This is what the next method method is for. We count how many users have more points than the current one. We are only interested in the unique values because users with the same count will share a rank. public function getRank getRank() () { return static static::query()->where( ::query()->where( 'points' , '>' , $this $this->points)->pluck( ->points)->pluck('points' 'points')->unique())->unique()>count() >count() + 1 ; }
For the highscore, only the top 10 users will be shown. public static function topUsers topUsers($size ($size = 10) { return static ::query()->orderByDesc( 'points' )->take($size)->get(); }
We fetch the users users with the most points and add a rank field field to every entry.
Show The Highscore For displaying the highscore we create a new conversation c onversation class app/Conversations/HighscoreConversation.php . showHighscore(); $this ->showHighscore(); } private function showHighscore showHighscore() () { $topUsers = Highscore::topUsers() Highscore::topUsers(); ; if (! $topUsers->count() $topUsers->count()) ) { return $this $this->say( ->say( 'The highscore is still empty. Be the first one! ' ); } $topUsers->transform(function ($user) $topUsers->transform(function ($user) { return "{$user->ran "{$user->rank} k} - {$user->name {$user->name} } {$user->poi {$user->points} nts} points"; points"; }); $this ->say( 'Here is the current highscore. Do you think you can do better? Start the quiz: $this->say( /startquiz.'); /startquiz.' ); $this->say( $this ->say( ' HIGHSCORE ' ); $this->say($topUsers->implode( $this ->say($topUsers->implode("\n" "\n")); )); } }
We use the topUsers data to build a little lit tle highscore table, as well as adding some other text replies. Also, emojis always help!
To trigger this conversation conversation,, we need another listener. Besides the add /highscore which will become a Telegram command later.
highscore keyword
I also
$botman->hears('/highscore|highscore' $botman->hears('/highscore|highscore' , function function (BotMan (BotMan $bot) { $bot->startConversation( new HighscoreConversation HighscoreConversation()); ()); })->stopsConversation();
Type one of the keywords to the bot to see what happens. It will tell you that the highscore is still empty.
For testing purposes, we can manually add a few highscore entries to the table. I will use Sequel Pro to do that. Now we should get some highscore entries back. Notice the users with the same points will share a rank.
Ask User About Highscore After a quiz, I want to ask the user, if he wants to get added to the highscore. This is a good idea because we need to store personal personal data like his chat id and name to do so. so. I don't want to save him without consent. After showing the user user his result, we ask him about the the highscore. If he agrees, agrees, we will store him to the highscore table. We use the saveUser method we created earlier within the Highscore model. Additionally, we start the highscore highscore conversation to show show the other results. If the user does not want to get stored, we just reply with text messages. private function showResult showResult() () { $this->say( $this ->say( 'Finished ' ); $this->say( $this ->say( "You made it through all the questions. You reached {$this->userPoints} points! Correct answers: {$this->userCorrectAnswers} {$this->userCorrectAnswers} / {$this->ques {$this->questionCount}" tionCount}"); $this->askAboutHighscore(); $this ->askAboutHighscore(); // this is new in this method } private function askAboutHighscore askAboutHighscore() () { $question = BotManQuestion::create BotManQuestion::create( ('Do you want to get added to the highscore list? Only your latest result will be saved. To achieve that, we need to store your name and chat id.') ->addButtons([ Button::create('Yes Button::create('Yes please')->value( please' )->value( 'yes' 'yes'), ), Button::create('No' Button::create('No')->value( )->value('no' 'no'), ), ]); $this->ask($question, $this ->ask($question, function function (BotManAns (BotManAnswer wer $answer) {
switch ($answer->getVa switch ($answer->getValue()) lue()) { case 'yes' 'yes': : $user = Highscore::saveUser( Highscore::saveUser($this $this->bot->getUser(), ->bot->getUser(), $this $this>userPoints, $this $this->userCorrectAnswers); ->userCorrectAnswers); $this->say( $this ->say("Done. "Done. Your rank is {$user->rank}."); {$user->rank}."); return $this $this->bot->startConversation( ->bot->startConversation( new HighscoreConversation( HighscoreConversation()); )); case 'no' 'no': : return $this $this->say( ->say('Not 'Not problem. You were not added to the highscore. Still you can tell your friends about it ' ); default: default : return $this $this->repeat( ->repeat('Sorry, 'Sorry, I did not get that. Please use the buttons.'); buttons.' ); } }); }
When you give the quiz another try, you will will see the question about the highscore highscore at the end.
Nice! We have already come a long way, and the bot looks excellent. We can play the quiz and the highscore works as well.
Extras Welcome
To give the user a better experience, we are going to create an app/Conversations/WelcomeConversation.php class. It will take care of onboarding the user. First, we welcome him and tell some info about the purpose of this bot. Then we ask if he is ready ready for the quiz. If he is, we start the quiz conversation.
BotMan \ BotMan \ Messages \ Incoming Incoming\ \ Answer ; BotMan \ BotMan \ Messages \ Outgoing Outgoing\ \ Question Question; ; BotMan \ BotMan \ Messages \ Outgoing Outgoing\ \ Actions Actions\ \ Button Button; ; BotMan \ BotMan \ Messages \ Conversations Conversations\ \ Conversation ;
class WelcomeConversation extends Conversation { /** * Start the conversati conversation. on. * * @return mixed */ public function run run() () { $this->welcomeUser(); $this ->welcomeUser(); } private function welcomeUser welcomeUser() () { $this->say( $this ->say('Hey 'Hey '. ' . $this $this->bot->getUser()->getFirstName(). ->bot->getUser()->getFirstName(). ' ' ); $this->askIfReady(); $this ->askIfReady(); } private function askIfReady askIfReady() () { $question = Question::create( Question::create('Welcome 'Welcome to the *LaravelQuiz Chatbot*! How well do you know your favourite PHP framework? Are you ready for the quiz?') ->addButtons([ Button::create('Sure' Button::create('Sure' )->value('yes' )->value('yes'), ), Button::create('Not Button::create('Not now')->value( now' )->value('no' 'no'), ), ]); $this->ask($question, $this ->ask($question, function function (Answer (Answer $answer) $answer) { if ($answer->getValue() === 'yes' 'yes') ) { $this->say( $this ->say( 'Perfect!' ); return $this $this->bot->startConversation( ->bot->startConversation( new QuizConversation()) QuizConversation()); ; } $this ->say( '' ); $this->say( $this->say( $this ->say( 'If you change your opinion, you can start the quiz at any time using the start command or by typing "start".'); "start".'); }); } }
To trigger this new conversation, we add another listener. The keyword when a Telegram user user joins the bot for the first time. time.
/start is
also what gets called
$botman->hears('/start' $botman->hears('/start' , function function (BotMan (BotMan $bot) { $bot->startConversation( new WelcomeConversation() WelcomeConversation()); ); })->stopsConversation();
This time the listener gets extended through the stopsConversation method. It means that when this command is used within a conversation, it will get stopped to start a new quiz. I use this for all Telegram commands so that the user can use them at any time.
Delete User Data Since the GDPR it is essential to give the user the possibility to delete his stored data. I want to handle that in a new app/Conversations/PrivacyConversation.php class. First we make sure we have stored this current user. If so, we ask him if he really wants to delete his highscore entry.
App \ Highscore Highscore; ; BotMan \ BotMan \ Messages \ Incoming Incoming\ \ Answer ; BotMan \ BotMan \ Messages \ Outgoing Outgoing\ \ Question Question; ; BotMan \ BotMan \ Messages \ Outgoing Outgoing\ \ Actions Actions\ \ Button Button; ; BotMan \ BotMan \ Messages \ Conversations Conversations\ \ Conversation ;
class PrivacyConversation extends Conversation { /** * Start the conversati conversation. on.
* * @return mixed */ public function run run() () { $this->askAboutDataDeletion(); $this ->askAboutDataDeletion(); } private function askAboutDataDeletion askAboutDataDeletion() () { $user = Highscore::where( Highscore::where('chat_id' 'chat_id', , $this $this->bot->getUser()->getId())->first(); ->bot->getUser()->getId())->first(); if (! $user) { return $this $this->say( ->say( 'We have not stored any data of you.' you.'); ); } $this ->say( 'We have stored your name and chat ID for showing you in the highscore.'); $this->say( $question = Question::create( Question::create('Do 'Do you want to get deleted?')->addButtons([ deleted?')->addButtons([ Button::create('Yes Button::create('Yes please')->value( please' )->value('yes' 'yes'), ), Button::create('Not Button::create('Not now')->value( now' )->value('no' 'no' ), ]); $this->ask($question, $this ->ask($question, function function (Answer (Answer $answer) $answer) { switch ($answer->getVa switch ($answer->getValue()) lue()) { case 'yes' 'yes': : Highscore::deleteUser($this Highscore::deleteUser( $this->bot->getUser()->getId()); ->bot->getUser()->getId()); return $this $this->say( ->say('Done! 'Done! Your data has been deleted.'); deleted.'); case 'no' 'no': : return $this $this->say( ->say('Great 'Great to keep you ' ); default: default : return $this $this->repeat( ->repeat('Sorry, 'Sorry, I did not get that. Please use the buttons.'); buttons.' ); } }); } }
The deleteUser is a new Highscore model method you have to add. public static function deleteUser deleteUser(string (string $chatId) { Highscore::where('chat_id' Highscore::where('chat_id' , $chatId)->delete(); } Note: Find
more info about this topic in my article Make Your Chatbots GDPR Compliant. Compliant .
About If someone wants to know more about this bot, I will give him the possibility by adding some about info. Since I only return one little text message, I will handle it inside the listener. $botman->hears('/about|about' $botman->hears('/about|about' , function function (BotMan (BotMan $bot) { $bot->reply('LaravelQ $bot->reply('LaravelQuiz uiz is a project by Christoph Rumpel. Find out more about it on https://christoph-rumpel.com'); })->stopsConversation();
Telegram Commands This is an excellent time to add the Telegram commands to the bot. You need to use Telegram's BotFather to achieve that. Follow his instructions to edit the bot and add the commands. You always have to fill all the commands at once.
When you start typing typing / you will see all the commands. Now it is effortless for the user to trigger the main features.
Typing Indicator With chatbots, it is common to show show that they are typing while processing the message. This feels more natural because we are used to these indicators when we talk to friends. In BotMan it is straightforward to use it inside conversations, but I want to add it to a middleware. midd leware. This way I can activate them for all outgoing messages. php artisan botman:make:middleware botman:make:middleware TypingMiddleware
This command will generate the class for use. Delete the dummy code and replace it with the following. We only need the sending method, which will be active when BotMan sends a message back to the messenger. typesAndWaits(1); return $next($payload); } }
Also, the middleware must must be added to the main BotMan instance instance in the BotMan route file. $typingMiddleware $typingMiddleware = new new TypingMiddleware(); TypingMiddleware(); $botman->middleware->sendi $botman->middleware->sending($typingM ng($typingMiddleware) iddleware); ;
Testing For this article, I left out handling tests. Of course, this doesn't mean they are not important. But the focus was on different parts parts of this app. If you like to know more more about how to test conversations, conversations, check my article Build a newsletter chatbot in PHP - Part 3. 3.
Conclusion And again, this article got much longer than expected. expected. But I hope all the details can help you to build similar chatbots yourself. For me, especially the part about looping over stored questions was fascinating. This gives you a lot of new possibilities to build conversations dynamically . I always love to put out real projects because they help people more than fictive ones. Another real chatbot project is included in my book Build Chatbots with PHP. PHP . Subscribe to the email list to get a free sample chapter and to receive more info about this new project of mine.