Setup Telegram Bot SDK with webhook in Laravel

I use Telegram Bot SDK from Syed with Laravel. This SDK is very good to use but I also see many users that have issues with getting the webhook working in a proper way. I see custom TelegramControllers, insecure webhook URI’s and, exposed public folders and no working response to the basic /help command. The issue log in GitHub contains many questions about getting it to work and the documentation is not updated for the latest version ^3.1. Work needs to be done for sure but until then I decided to write an article how to setup a working webhook installation for the most basic command /help. In the end you should get response from your bot like this:

If you are looking how to debug your webhook setup please go to this post.

My DEV environment is in my local network and not accessible from the outside world. But I can still use the webhook to send messages to my local DEV. I use NGROK for that. Check the NGROK website for more information about creating a tunnel to your localhost. If you have a public DNS that is pointing to your DEV environment then that is OK as well. The most important part is that your webhook URL is accessible at least through a secure connection with minimal TLS1.2. In other words: Use HTTPS with a valid certificate like Let’s Encrypt. When you use a temporary Ngrok tunnel you are already set because Ngrok is also accessible thru HTTPS.

Now let’s dive in to setting up a new Laravel test site on your development environment. For more information about installing Laravel and DEV environments like Valet see the Laravel website.

Setting up Laravel test site

Go to your Laravel Development folder where you will create a new Laravel site ‘test’. Execute the following command inside the Laravel folder:

composer create-project --prefer-dist laravel/laravel test

This will create a new test folder inside /Laravel folder. Currently it has installed Laravel 7.6.0 (april 2020). Make sure you can access this site in your preferred browser and confirm that the site works!

Install Telegram-Bot-SDK

Following the documentation at readme.io we can install or add the Telegram-Bot-SDK to Laravel with composer via the following command:

cd test
composer require irazasyed/telegram-bot-sdk ^3.1

We are installing version ^3.1 and not 2.0.

Publish Telegram configuration file

Because we need to edit some configuration settings for the bot we need to publish the telegram.php config file from the vendor map by executing this command from your project ‘test’ folder:

php artisan vendor:publish --provider="Telegram\Bot\Laravel\TelegramServiceProvider"

Open your favorite IDE and open the Laravel test site. The telegram.php config file should be in the config folder:

Configure your bot

Before we dive into creating a bot we need to understand the components of the bot ecosystem. A bot consists of two parts:

  1. A created and configured bot on the Telegram servers with a name and a bot token;
  2. A piece of software that is the heart of the bot. This is your Laravel Telegram Bot SDK 🙂

To create a new bot, talk to @botfather in your Telegram Client. Grab your bot token from @botfather and keep it private to yourself! Also write down the Bot username. We need both to be added to the Laravel configuration. Example of a bot token: 123456789:ABC_a0bcDefGhijklmno_0pqrSt-uvWXYZA

Open the .env file of your project and past the following lines at the bottom of your .env:

TELEGRAM_BOT_TOKEN=
TELEGRAM_BOT_NAME=
TELEGRAM_CERTIFICATE_PATH=
TELEGRAM_WEBHOOK_URL=
TELEGRAM_ASYNC_REQUESTS=

And now paste in your bot token you got from @botfather and the bot name. Set the Async_Requests to false. Leave Certificate Path and Webhook URL empty. It should look like this:

TELEGRAM_BOT_TOKEN=123456789:ABC_a0bcDefGhijklmno_0pqrSt-uvWXYZA
TELEGRAM_BOT_NAME=My_Precious_bot
TELEGRAM_CERTIFICATE_PATH=
TELEGRAM_WEBHOOK_URL=
TELEGRAM_ASYNC_REQUESTS=false

Save the .env file.

Open the config/telegram.php file. In the first part of the file you see the configuration of mybot. This is also the default bot. Token, certificate_path and webhook_url are pointing to the .env file. Username is not! To make sure the username is also read from the .env file we change this line to => env(‘TELEGRAM_BOT_NAME’, ‘YOUR_BOT_NAME’). It should look like this:

'bots'                         => [
        'mybot' => [
            'username'            => env('TELEGRAM_BOT_NAME', 'YOUR_BOT_NAME'),
            'token'               => env('TELEGRAM_BOT_TOKEN', 'YOUR-BOT-TOKEN'),
            'certificate_path'    => env('TELEGRAM_CERTIFICATE_PATH', 'YOUR-CERTIFICATE-PATH'),
            'webhook_url'         => env('TELEGRAM_WEBHOOK_URL', 'YOUR-BOT-WEBHOOK-URL'),
            'commands'            => [
                //Acme\Project\Commands\MyTelegramBot\BotCommand::class
            ],
        ],

You now have preconfigured your Telegram Bot in Laravel. But it still does not work. We have to do some more to be able to respond to a /help command.

Disabling the global /help command

Out of the box the Telegram Bot SDK comes with a predefined HelpCommand.php and it is defined already globally. You can check this file in the vendor map for it’s structure:

Open config\telegram.php again and scroll half way the file where you will find ‘commands’. These are the global commands where you can add commands that are used by all your bots. You see that there is already 1 command in place and this is the default HelpCommand. Add a // in front of this row to disable loading this command. Looks like this:

'commands'                     => [
        //Telegram\Bot\Commands\HelpCommand::class,
    ],

It seems that with Laravel 5 there is an issue where caching the config leaves an error because of this standard HelpCommand.php. More information about this see Github.

Creating the HelpCommand file

Now we have disabled the default HelpCommand file we need our own first command and we will call it HelpCommand. How predictable 🙂

You can decide your own folder structure where you will place all your individual Command files. Since there is already something like the Artisan Console Commands for scheduling tasks I use a structure to identify these commands as Telegram commands. So I add a folder ‘Telegram’ inside my ‘app’ folder and inside ‘Telegram’ I create the ‘Commands’ folder. Like so:

Remember if you create a different structure you need to set the right namespace in the HelpCommand.php file so it reflects the location where the file resides.

Create the HelpCommand.php file and paste the following code:

<?php

namespace App\Telegram\Commands;

use Telegram\Bot\Commands\Command;
use Telegram;

/**
 * Class HelpCommand.
 */
class HelpCommand extends Command
{
    /**
     * @var string Command Name
     */
    protected $name = 'help';

    /**
     * @var array Command Aliases
     */
    protected $aliases = ['listcommands'];

    /**
     * @var string Command Description
     */
    protected $description = 'Help command, Get a list of all commands';

    /**
     * {@inheritdoc}
     */
    public function handle()
    
    {
        $response = $this->getUpdate();
        
        $text = 'Hey stranger, thanks for visiting me.'.chr(10).chr(10);
        $text .= 'I am a bot and working for'.chr(10);
        $text .= env('APP_URL').chr(10).chr(10);
        $text .= 'Please come and visit me there.'.chr(10);
        
        $this->replyWithMessage(compact('text'));

    }
}

Save the file.

Activating the HelpCommand class

Open the config\telegram.php once more. In the upper body where your ‘mybot’ section is you see another ‘commands’ section. There is a disabled BotCommand:class which we will replace with the location of your own custom HelpCommand.php. Like so:

'bots'                         => [
        'mybot' => [
            'username'            => env('TELEGRAM_BOT_NAME', 'YOUR_BOT_NAME'),
            'token'               => env('TELEGRAM_BOT_TOKEN', 'YOUR-BOT-TOKEN'),
            'certificate_path'    => env('TELEGRAM_CERTIFICATE_PATH', 'YOUR-CERTIFICATE-PATH'),
            'webhook_url'         => env('TELEGRAM_WEBHOOK_URL', 'YOUR-BOT-WEBHOOK-URL'),
            'commands'            => [
                App\Telegram\Commands\HelpCommand::class,
            ],
        ],

Save the file.

Exclude CSRF and create a proper route for the webhook

To make use of the internal commandshandler we need a POST route where Telegram servers will drop off messages.

Laravel protects all POST request with CSRF protection. A loaded webform in a browser will contain a CSRF token that is send along with the payload to Laravel. Laravel checks if the token is valid and in this way we can assure that the payload has not been altered by someone. Telegram is not able to send this CSRF token with the POST request. Therefore we need to exclude the webhook route from CSRF middleware. But because this is not save we need to take additional measures to hide the webhook url from the public. The only way to make it nearly impossible to guess this URI, is to make it a very long string of random characters. Example:

https://example.com/42yUojv1YQPOssPEpn5i3q6vjdhh7hl7djVWDIAVhFDRMAwZ1tj0Og2v4PWyj4PZ/webhook

To generate your own random string you could use a random string generator like Browserling.com. Use between 40 and 64 characters.

Golden rules:

  • Stick to the hard to guess webhook url;
  • Do not point your webhook to the root of your domain (like example.com) ;
  • example.com/public is also not done. The root of your website should be in the public map. Check your webserver site configuration.

Let’s add a proper webhook route. Open routes/web.php.

Add the following route to the routes file above any authentication routes. This webhook route cannot have any authorization middleware:

Route::post('/42yUojv1YQPOssPEpn5i3q6vjdhh7hl7djVWDIAVhFDRMAwZ1tj0Og2v4PWyj4PZ/webhook', function () {
    $update = Telegram::commandsHandler(true);
});

Safe the file.

Open app\Http\Middleware\VerifyCsrfToken.php and add the webhook URI to the protected $except array like so:

protected $except = [
        '/42yUojv1YQPOssPEpn5i3q6vjdhh7hl7djVWDIAVhFDRMAwZ1tj0Og2v4PWyj4PZ/webhook/',
    ];

Save the file.

We are done with the Laravel part. We now need to let Telegram know about this new webhook url.

Set the webhook with Postman

There are several ways to set the webhook. You could create a route for it or paste in the query in a browser but I like the tool Postman. In Postman you can save your API call’s for later use and it is easier to troubleshoot. So let’s use Postman.

  1. Open postman and create a new POST request;
  2. Name it ‘setWebhook’ and optionally set a description;
  3. Paste in the URL to set a webhook for your bot like https://api.telegram.org/bot123456789:ABC_a0bcDefGhijklmno_0pqrSt-uvWXYZA/setWebhook. Take close attention to your bot token. It should begin with the word ‘bot’. The endpoint or method is ‘setWebhook’;
  4. Press the Body tab / button;
  5. Select the radio-button form-data;
  6. Create a key ‘url’;
  7. Past in your complete public webhook url;
  8. Double check al the information;
  9. Press the Send button.

The response from Telegram should look like this:

{
    "ok": true,
    "result": true,
    "description": "Webhook was set"
}

Alternative: Set the webhook from your routes file

Oke, oke, one other method to set your webhook. This approach is handy when you use ngrok and you want set your webhook with a URI.

Open routes\web.php.

Paste the following code in your web.php file:

Route::get('/setwebhook', function () {
	$response = Telegram::setWebhook(['url' => 'https://d79e77f5.ngrok.io/42yUojv1YQPOssPEpn5i3q6vjdhh7hl7djVWDIAVhFDRMAwZ1tj0Og2v4PWyj4PZ/webhook']);
	dd($response);
});

Remember to replace https://d79e77f5.ngrok.io with your own public address and replace the webhook uri with your code.

You could also replace the url with env(‘TELEGRAM_WEBHOOK_URL’). In that case you need to set your complete url into the .env file at the TELEGRAM_WEBHOOK_URL property. (Example: $response = Telegram::setWebhook([‘url’ => env(‘TELEGRAM_WEBHOOK_URL’)]);)

Open your browser and browse to your localhost address of your Laravel test site and go to the URI /setwebhook. dd($response) will show you the following:

Moment of truth

From this point you have told Telegram where to deliver any messages that are send to your bot. Telegram will forward them to your webhook url and Laravel will handle the request.

To test your HelpCommand, open Telegram client, search for your bot and send a /help. You now should receive the ‘Hey, stranger…..’ message.

Congratulations! You have a working Telegram Bot build in Laravel. From here on you can start expanding your product. Let me know in the comments if you succeeded to get a response.

Trouble begins….

/help did not do anything? No messages and nothing in the Laravel.log file? You must have made a typo 🙂

There are ways to debug the chain of components. Because there are many parts in this chain we need to check each one of them. Please continue reading the debug article here.

Leave a comment

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

7 thoughts on “Setup Telegram Bot SDK with webhook in Laravel”