Creating a Cryptocurrency Telegram Bot API with Laravel

20,373

Telegram is a non-profit cloud-based instant messaging service. It is similar to many other messaging applications like WhatsApp and Messenger but offers great security. Telegram apps are available for multiple operating systems.

Telegram released their bot platform in 2015. We can use bot API to create bots that will respond to our specific commands. You can checkout Bots API on the official documentation.

You can also see how to send notifications to Telegram with Laravel.

In this tutorial, we will create a Telegram cryptocurrency bot that will give us the market prices and global crypto market data. Let’s build this crypto bot.

Bootstrapping the application

We will start by creating a new Laravel application named CryptoBot. I am using laravel 5.6.5 for this tutorial.

To create a new project, run the following command.

laravel new CryptoBot

To communicate with Telegram API, we will use Telegram Bot API PHP SDK wrapper. It supports laravel out of the box. It is unofficial Telegram Bot SDK.

Install this package by requiring it through composer.

composer require irazasyed/telegram-bot-sdk

It will successfully require this package. If you are using previous versions of laravel, you need to add service provider and facade for this package in config/app.php file. Add the following service provider in providers array.

Telegram\Bot\Laravel\TelegramServiceProvider::class,

Add the following entry in aliases array to setup facade.

'Telegram' => Telegram\Bot\Laravel\Facades\Telegram::class,

You do not need to add service provider and facade if you are using laravel version 5.5 or above.

Now, publish configuration file for the above package by running the command below.

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

The command above will publish a telegram.php configuration file in config directory.

To get Cryptocurrency prices, we will be using coinmarketcap.com API. There is a PHP wrapper available that we will be using in our application. This package is available on dzarezenko/coinmarketcap-api.

To require this package, run the following command:

composer require dzarezenko/coinmarketcap-api:dev-master

Creating a Telegram Bot

Let’s start by creating a Telegram Bot. You need to have telegram app installed. You need to talk to BotFather for the creation of Telegram bot. For this, you need to send the message to @BotFather. Visit this link through mobile and click Send Message to start your talk with BotFather.

Now click start at the bottom. Your communication will start with botfather by sending /start. To create a new bot, send /newbot to botfather. It will ask for your bot name. I will call mine crypto prices. BotFather will further ask for bot username. Username must end with the word bot. I will name mine cryptoTFWbot. You can call it whatever you want but remember to follow the tutorial with your chosen username.

After bot is created successfully, BotFather will send a message with a token to access HTTP API.

Creating a telegram bot

Copy the bot token and insert it into .env file under the key TELEGRAM_BOT_TOKEN. An entry will be added like this in .env file. Replace the bot token with your bot token in the line below.

TELEGRAM_BOT_TOKEN=512242175:AAGmWciWYfF2PMNCWcrF8TF_KvHnvAtV3Ps

You can change other options such as name, description, profile pic and other options by sending commands to the botfather but we will not be doing that here. You can find all of these options in the official documentation.

Testing SDK

We can test if the SDK is configured correctly by calling getMe() method. To do this, let’s create a controller first. We will be using model and a migration later in our application but we will create them along with the controller. Run the following command to create model, migration, and controller.

php artisan make:model Telegram -mc

We also need to set up a route for this endpoint. So, add the following entry in routes/web.php file.

Route::get('get-me', 'TelegramController@getMe');

Now add getMe method in app/Http/Controllers/TelegramController.php file.

use Telegram\Bot\Api;

class TelegramController extends Controller
{
    protected $telegram;

    public function __construct()
    {
        $this->telegram = new Api(env('TELEGRAM_BOT_TOKEN'));
    }

    public function getMe()
    {
        $response = $this->telegram->getMe();
        return $response;
    }
}

In the above code, we are creating new API class instance by passing telegram bot token. telegram is added as a property because we will be using it in other methods as well.

SDK is working fine if you get a response like:

{"id":512242175,"is_bot":true,"first_name":"crypto prices","username":"cryptoTFWbot"}

Setting Up WebHook

Now that we have set up SDK, next step is to set up webhook. Webhook only works with HTTPS website. If you are testing it on localhost, you can create and deploy your application for free to Heroku and test it. You can check out this article to find more about heroku and laravel.

First we have to create a route and a method to setup the webhook url that will be used by telegram.

Add the following route in your route/web.php file:

Route::get('set-hook', 'TelegramController@setWebHook');

Now in TelegramController.php file add a setWebHook method.

public function setWebHook()
{
    $url = 'https://applicationdomain.com/' . env('TELEGRAM_BOT_TOKEN') . '/webhook';
    $response = $this->telegram->setWebhook(['url' => $url]);

    return $response == true ? redirect()->back() : dd($response);
}

Remember to replace applicationdomain.com with your domain name. If you are using Heroku, use its app domain address. In the setWebHook method, we are creating a URL where Telegram will send a request once a new command is entered in our bot. Finally, we are redirecting if the response is true, otherwise we are dumping the response.

Now we need to create another post route with the url that we hooked in the url variable in the setWebHook method. Add the following entry to your routes file.

Route::post(env('TELEGRAM_BOT_TOKEN') . '/webhook', 'TelegramController@handleRequest');

We are taking token from our .env file. Laravel includes CSRF protection which will cause exception when using third party webhooks. We need to disable csrf protection for this route. Go to app\Http\Middleware\VerifyCsrfToken.php and add the following entry in the $except array.

protected $except = [
    '512242175:AAGmWciWYfF2PMNCWcrF8TF_KvHnvAtV3Ps/webhook',
];

Replace the token in the key above with your bot token.

Finally to setup webhook, hit the set-hook endpoint from your domain. It will be like https://applicationdomain.com/set-hook.

getUpdates() method will not work when webhook is setup. You can use removeWebHook() method to remove the webhook.

CryptoCurrency Bot

Now let’s start adding methods so that our bot can respond to our requests. We will be adding a method to find all of the available commands. There will be another method to get crypto market global data. Another method to get prices of top 10 cryptocurrencies. Last one to get data for a specific cryptocurrency.

We will also see how we can handle inputs. Handling inputs require a database connection. So, go to your .env file and configure your database credentials. If you are using Heroku, you can install Postgres addon. You can find more about it in the documentation. Now go to your create_telegrams_table.php in database/migrations and replace the up method with the code below.

public function up()
{
    Schema::create('telegrams', function (Blueprint $table) {
        $table->increments('id');
        $table->string('username');
        $table->string('command');
        $table->timestamps();
    });
}

Here we are adding username and command to our database table. Now, go to app/Telegram.php file and add a protected property named fillable to get rid of mass assignment exception.

protected $fillable = ['username', 'command'];

Now, let’s setup our logic in the controller. I will first paste all of the code for our controller and then we will discuss it below. So, copy the code below and replace it with your code in TelegramController.php file. Change applicationdomain.com with your domain name in the setWebHook method.

<?php

namespace App\Http\Controllers;

use App\Telegram;
use Carbon\Carbon;
use coinmarketcap\api\CoinMarketCap;
use Exception;
use Illuminate\Http\Request;
use Telegram\Bot\Api;

class TelegramController extends Controller
{
    protected $telegram;
    protected $chat_id;
    protected $username;
    protected $text;

    public function __construct()
    {
        $this->telegram = new Api(env('TELEGRAM_BOT_TOKEN'));
    }

    public function getMe()
    {
        $response = $this->telegram->getMe();
        return $response;
    }

    public function setWebHook()
    {
        $url = 'https://applicationdomain.com/' . env('TELEGRAM_BOT_TOKEN') . '/webhook';
        $response = $this->telegram->setWebhook(['url' => $url]);

        return $response == true ? redirect()->back() : dd($response);
    }

    public function handleRequest(Request $request)
    {
        $this->chat_id = $request['message']['chat']['id'];
        $this->username = $request['message']['from']['username'];
        $this->text = $request['message']['text'];

        switch ($this->text) {
            case '/start':
            case '/menu':
                $this->showMenu();
                break;
            case '/getGlobal':
                $this->showGlobal();
                break;
            case '/getTicker':
                $this->getTicker();
                break;
            case '/getCurrencyTicker':
                $this->getCurrencyTicker();
                break;
            default:
                $this->checkDatabase();
        }
    }

    public function showMenu($info = null)
    {
        $message = '';
        if ($info) {
            $message .= $info . chr(10);
        }
        $message .= '/menu' . chr(10);
        $message .= '/getGlobal' . chr(10);
        $message .= '/getTicker' . chr(10);
        $message .= '/getCurrencyTicker' . chr(10);

        $this->sendMessage($message);
    }

    public function showGlobal()
    {
        $data = CoinMarketCap::getGlobalData();

        $this->sendMessage($this->formatArray($data), true);
    }

    public function getTicker()
    {
        $data = CoinMarketCap::getTicker();
        $formatted_data = "";

        foreach ($data as $datum) {
            $formatted_data .= $this->formatArray($datum);
            $formatted_data .= "-----------\n";
        }

        $this->sendMessage($formatted_data, true);
    }

    public function getCurrencyTicker()
    {
        $message = "Please enter the name of the Cryptocurrency";

        Telegram::create([
            'username' => $this->username,
            'command' => __FUNCTION__
        ]);

        $this->sendMessage($message);
    }

    public function checkDatabase()
    {
        try {
            $telegram = Telegram::where('username', $this->username)->latest()->firstOrFail();

            if ($telegram->command == 'getCurrencyTicker') {
                $response = CoinMarketCap::getCurrencyTicker($this->text);

                if (isset($response['error'])) {
                    $message = 'Sorry no such cryptocurrency found';
                } else {
                    $message = $this->formatArray($response[0]);
                }

                Telegram::where('username', $this->username)->delete();

                $this->sendMessage($message, true);
            }
        } catch (Exception $exception) {
            $error = "Sorry, no such cryptocurrency found.\n";
            $error .= "Please select one of the following options";
            $this->showMenu($error);
        }
    }

    protected function formatArray($data)
    {
        $formatted_data = "";
        foreach ($data as $item => $value) {
            $item = str_replace("_", " ", $item);
            if ($item == 'last updated') {
                $value = Carbon::createFromTimestampUTC($value)->diffForHumans();
            }
            $formatted_data .= "<b>{$item}</b>\n";
            $formatted_data .= "\t{$value}\n";
        }
        return $formatted_data;
    }

    protected function sendMessage($message, $parse_html = false)
    {
        $data = [
            'chat_id' => $this->chat_id,
            'text' => $message,
        ];

        if ($parse_html) $data['parse_mode'] = 'HTML';

        $this->telegram->sendMessage($data);
    }
}

handleRequest()

First, we have the handleRequest method that telegram webhook request will be sent. In this method, we are getting chat_id, username, and text from the $request array. Next, we are calling the appropriate method based on the user command.

showMenu()

Next, we have the showMenu method, where we show the available commands. We are first creating message and then calling sendMessage method. sendMessage calls sendMessage method on telegram API.

showGlobal()

In the showGlobal method, we are simply getting data from CoinMarketCap and sending it to the user after formatting. We will look at the formatArray method later. This method returns cryptocurrency global data.

getTicker()

getTicker() method gets the data for the top 10 cryptocurrencies. We can change the limit. Further we loop through all of the data and format it in the form of HTML from JSON. Finally we send it to the sendMessage method.

formatArray()

We will look getCurrencyTicker() method later. In this method, we loop through the array passed as argument. Coinmarketcap returns keys separated by _. We replaced _ with spaces. Furhter last updated property is a key present in the coinmarketcap response which shows when the data was last updated. We are using carbon to format it in a better way. Finally we add the array key in bold and then array value. Then, this method returns that formatted string.

sendMessage()

sendMessage method sends chat_id and text to the telegram sendMessage API method. If we are using HTML, we have to add 'parse_mode' => 'HTML' to the array we are sending. We will do this based on the boolean which defaults to false.

Handling Input

In the getCurrencyTicker and checkDatabase method, we ask the user about the name of the cryptocurrency. Then, we return the details for that currency.

getCurrencyTicker()

In getCurrencyTicker method we are simply sending message to the telergram. We are also saving a record to the databse. We are saving username of the user who sent the message. We are also saving command, which will be the name of the method from where it is called. It will help us tracking what to do when we have multiple input handlings.

checkDatabase()

When user sends the name of the currency, it goes through the default case in handleRequest method and sent to checkDatabase() method. Here it gets slightly compilcated. We first get the latest available command for the username in our telegrams table. We are using firstOrFail(), so if there is no element it will throw an exception. We catch the exception and say Sorry, no such cryptocurrency found. Then, we call showMenu() method with the message. It will show user all available options with the message as well.

Then, we check whether the command is getCurrencyTicker. In our case it will definitely be getCurrencyTicker. But if we have multiple methods, it will be different so just a quick check. Then we are getting the data of that cryptocurrency though coinmarketcap API. If the name of currency is wrong, coinmarketcap sends the response where there is an error message on they key error. So, we are checking whether error key exist or not. If that exist we set message to Sorry no such cryptocurrency found. Otherwise we format the data. Then we delete all of the commands by that username in the database. It is required because otherwise, it will cause errors. Finally, we send the response back to the user.

Viewing Crypto Bot

You can find this bot and test it out by visiting @cryptoTFWbot

Well, that’s all required to make a telegram bot. You can also create separate commands and register them in config/telegram.php file. But, we will not be doing that here because you can check that on your own.

Here’s how it looks.

Telegram crypto bot

You can find the entire source code for this tutorial at GitHub

If you have any problems regarding this tutorial, do not hesitate to ask in the comments.

Read how to send notifications to Telegram messenger with laravel.

You might also like
Comments