Let’s make a Telegram Bot with PHP

I spend a few days creating a Telegram bot for my SaaS. It serves as a convenient way for my users to enter data. The bot isn’t smart at all, but at its core is a regex pattern matcher which allows entering various health-related data points.

While the Telegram docs try to give you an easy start, I found them lacking. With this article, I want to fill some gaps.

How does the Telegram Bot work?

Make sure to read the official Bot introduction for developers by Telegram. This explains how to register your bot with Telegram. Once you registered your bot, the bot won’t do anything, because a Telegram bot is basically only an account with a few extra settings, such as an URL for the Webhook callback.

For the rest of the article, I will call the thing you configure via BotFather your Bot Account.

Your bot lives on your server and processes all messages there. I will call the stuff that runs on your server the Telegram Bot.

The Telegram Bot could be tightly integrated with your web app or it could also be hosted on an extra server and talks to your web app via an API. This is up to you.

I drew an image to illustrate this:

Telegram Bot Communication

How does the Bot API work?

This is where I found the docs quite confusing. Your bot needs three things:

  1. It must be able to receive messages.
  2. This behavior must be configured.
  3. The bot should be able to send messages to the Telegram user.

I. Receiving Messages

Currently, there are two options: Long polling and Webhooks. I decided to use Webhooks, since long polling is kind of outdated and Webhooks are the way to go.

Your webhook is actually quite simple and doesn’t require the Telegram Bot API:

$response = file_get_contents('php://input');
$update = json_decode($response, true);

See, that’s all there is to receive messages from Telegram.

II. Configuring the Webhook

The Telegram server needs to know where to send messages (called Updates).

The confusing part for me was the setWebhook part. You really need to call this only once to setup your Bot Account.

Telegram’s extremely simple (and confusing, and undocumented, and not-very-clean) HelloBot has this section:

if (php_sapi_name() == 'cli') {
  // if run from console, set or delete webhook
  apiRequest('setWebhook', array('url' => isset($argv[1]) && $argv[1] == 'delete' ? '' : WEBHOOK_URL));

Which means that you need to need to run this weird script from your command line once to configure the Webhook. There’s are two much better ways. One is to use existing Telegram API clients (I’ll get back to this in a minute). The second is using cURL from the command line:

curl -H "Content-Type: application/json" -X POST -d '{"url":"https://www.example.com/my-secret-webhook.php"}' https://api.telegram.org/botYOUR_BOT_TOKEN/setWebhook

This sends a JSON request to Telegram’s API and configures the Bot Account to use my-secret-webhook.php on your server.

Once your Webhook URL is set, you don’t have to touch it again and that’s it for the configuration part.

Wait, what about bot commands? All of them live in your actual Telegram Bot on your own server. There is no need to tell the Telegram service which commands your bot understands.

III. Sending Messages

This is where it gets interesting and a bit more complex. I recommend using an inofficial Telegram API client to make this work (because there is no official API client). I tried these four projects and the last one is the best:

kolar/telegram-poll-bot. Simple, but too intermingled with the Poll Bot functionality and kind of hard to separate. Also, it’s outdated and probably doesn’t support everything of the new Bot API 2.0.

TelegramBot/Api. I tried to use this project, but found it confusing and it lacks proper documentation. If I’d try again now with some experience, I might find it useful. At least, it is up to date.

akalongman/php-telegram-bot. This was the first project I got to work. It seems to be actively developed. However, I had the impression that this project is a bit too opinionated about a few things and comes with too much boilerplate. Check out the examples to get an idea of what this project can do.

irazasyed/telegram-bot-sdk. This project is what I finally settled on. It is well documented, comes with optional Laravel integration, but can also be used as a standalone project. I even threw out the entire Command stuff and wrote my own, i. e. you don’t have to use all features. Only downside: Has more Composer dependencies.

Sending messages to a Telegram user

Most of the time, you want to send a message as a reaction when Telegram notifies your bot about an Update (a message). The Update datastructure gives you access to the chat_id, which is another word for Telegram user id. The chat_id for one user is always the same, even if he deletes your bot, the entire chat history and then restarts the bot. It’s also the same accross different devices.

Sending a message to a Telegram user doesn’t have to be a reaction to an Update as long as you know the chat_id (and as long as the user didn’t block the bot). You could even use this to send Push notifications to your users with a cronjob.

The question now is: How do you associate your web app user accounts with the Telegram userids?

The official documentation has a good example for that under section Deep Linking.

I do it slightly different, because not every bot is added via a deep link. A user could simply search for the bot within Telegram:

  1. User adds the bot. Bot replies with the help text and suggest to use the command /connect
  2. User types /connect, Bot generates a random token and stores it along with chat_id and an expiration date in the database. It replies with a message: To connect your account, please visit https://example.com/connect/$RANDOM_TOKEN
  3. User clicks on the link and must log in to the web app (in the browser). Please keep the possibility of CSRF attacks in mind. If I have a connect link and give it to you and you confirm it, I can access your account via the bot, because your web app userid is now associated with my chat_id.
  4. Once the user is logged in, the web app retrieves the token, checks its expiration date and the associated chat_id and stores it in the user properties in another database table.
  5. Now, if we want to send a message to this user, we can look up his chat_id and send a message via the Telegram Bot API.
  6. Furthermore, if bot commands require to retrieve or change data of this user, the Bot needs to access the user data based on the chat_id.

And that’s it for now.

As a quick summary:

  • setWebhook must be called only once
  • retrieving updates is basically processing php://input
  • sending messages via irazasyed/telegram-bot-sdk is easy