Telegram Bots - Coding the back end

Starting with the code of Telegram Bot to enable it to communicate with its users

Now, that you have set up your code in server, let's begin with coding. Here, we will look at how the interaction with bot happens (what actually goes behind the scenes) and how you bot needs to react for a proper workflow.

This article assumes that you are using Web Hooks as a method to receive updates. And if you are using Long Polling, all you will need to do is fetch the updates and go through them in a loop. Anyway, if you can't make sense out of this right now, you should go with the flow to understand the basics.

Setting Webhook for your bot

So, first of all, let's tell Telegram where our bot will be hosted, so that it can receive updates. To do so, you will need the token received from BotFather (Here on referred to as BOT_TOKEN).

<?php
//define BOT_TOKEN
define('BOT_TOKEN', '12345678:replace-me-with-real-token');
//define the base API URL where all requests for this bot will be made
define('API_URL', 'https://api.telegram.org/bot'.BOT_TOKEN.'/');

We'll use the setWebhook method to set the web hook for our bot. Let's say that your path for the bot is https://www.example.com/secret-path/bot.php where you hosted the bot code, then you need to open a web browser with the following link (for now, you can add this to your code to do this automatically for you):

https://api.telegram.org/bot12345678:replace-me-with-real-token/setWebhook?url=https%3A%2F%2Fwww.example.com%2Fsecret-path%2Fbot.php

When you open this in your URL you shall get a response of True.

So, now that the webhook is set for your bot, you can begin getting updates on your bot right away. (For more details on setting a web hooks, see this guide.)

Receiving updates

Telegram bots communicate with the Telegram servers (and to users) via your server. Now, when you receive a message on your bot, Telegram Servers send an update JSON object to your bot's web hook that you just set which looks like this:

{"update_id":123456789,
  "message":{
    "message_id":1,
    "from":{
      "id":1234567890, "is_bot":false, "first_name":"First", "last_name":"Last", "username":"user", "language_code":"en"
    },"chat":{
      "id":1234567890, "first_name":"First", "last_name":"Last", "username":"user", "type":"private"
    },"date":1579261000,
    "text":"/start",
    "entities":[
      {"offset":0,"length":6,"type":"bot_command"}
    ]
  }
}

As, I told you that the first message your bot will ever receive from any chat will be /start as shown in the above update.

The update basically consists of an update_id and the details about what the update is as an optional part.

Here, you can see that we have received a message update. This update has a message_id, a from (basically user who sent the message to your bot), a chat object (this is what you need to store in database), date, text and optionally entities. Let's talk about each of them individually:

from of type User

This object will contain the details about the user who sent the message. It will have a unique identifier id that you can store in your database to identify this user later on. Then you will have the is_bot boolean value, which will always be false (as bots are not allowed to talk with other bots). Then you will have the first_name of the user and a last_name (optionally), if user has one set.

Then the object will have a username, which might be absent, if the user has not set it. Also, this can change at any time, when the user needs to change it. So, don't rely on this to be unique (as one may leave and other user may take it).

chat object

This object will contain the latest chat object that the user has active with the bot. You will be using the id of this chat object to send messages to the user. So, in your database you should associate this chat with the user. It will contain an id, type (will be "private" for user chats) and a few other self-explanatory details.

text

This field will contain the text you have received from the user. And as the first message is always /start, this is what you have received for your bot.

entities

Optionally, you will get this field for text messages, which contain special entities like usernames, URLs, bot commands etc.

Now, we have received our first update from the Telegram servers, so we need to act on it.

//get the update from incoming stream
$content = file_get_contents("php://input");
$update = json_decode($content, true);

//if we received a wrong update, simply ignore it
if (!$update) exit;

if (isset($update["message"])) {
  //process the message here
  processMessage($update["message"]);
}

If we look at the above code, we first get the input stream for our bot and validate if the received message is infact an update and then proceed for processing the update.

The update can contain either a message, edited_message, channel_post, edited_channel_post, inline_query, chosen_inline_result, callback_uery, poll, poll_answer and so on. But, right now we will focus only on the messages that it receives.

function processMessage($message) {
  // process incoming message
  $message_id = $message['message_id'];
  $chat_id = $message['chat']['id'];
  if (isset($message['text'])) {
    // incoming text message
    $text = $message['text'];

    if (strpos($text, "/start") === 0) {
      //send greeting message
    } else if ($text === "Hello" || $text === "Hi") {
      //if user sends Hello or Hi, respond in specific way
    } else if (strpos($text, "/stop") === 0) {
      // stop now, and also mark database that user has stopped the chat
    } else {
      //received something other than /start, Hello and Hi
    }
  } else {
    //the received update is not a text message, it may be an image, video, audio or something else
  }
}

So, basically in above function, we first try to check if the message we received is a text message or not, and then act accordingly.

To send message as reply to this update, we have three options (two for getUpdates method):

  1. Send the reply in response to this request (available only in web hook version of getting updates).
  2. Create a request to Telegram API to reply to this update in form of HTTP GET.
  3. Create a request to Telegram API to reply to this update in form of JSON payload.

The first method is available only if we received the update on our web hook, and we can't know if sending the reply was successful or not (a minor caveat). The other two methods on other hand are just two available options, with no difference but in their methodology of sending the reply.

//method 1: sending reply in response body
function apiRequestWebhook($method, $parameters) {
  if (!is_string($method)) {
    error_log("Method name must be a string\n");
    return false;
  }

  if (!$parameters) {
    $parameters = array();
  } else if (!is_array($parameters)) {
    error_log("Parameters must be an array\n");
    return false;
  }

  $parameters["method"] = $method;

  $payload = json_encode($parameters);
  header('Content-Type: application/json');
  header('Content-Length: '.strlen($payload));
  echo $payload;

  return true;
}
//method 2: creating a fresh request to API using GET parameters
function apiRequest($method, $parameters) {
  if (!is_string($method)) {
    error_log("Method name must be a string\n");
    return false;
  }

  if (!$parameters) {
    $parameters = array();
  } else if (!is_array($parameters)) {
    error_log("Parameters must be an array\n");
    return false;
  }

  foreach ($parameters as $key => &$val) {
    // encoding to JSON array parameters, for example reply_markup
    if (!is_numeric($val) && !is_string($val)) {
      $val = json_encode($val);
    }
  }
  $url = API_URL.$method.'?'.http_build_query($parameters);

  $handle = curl_init($url);
  curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
  curl_setopt($handle, CURLOPT_TIMEOUT, 60);

  return exec_curl_request($handle);
}
//method 3: creating a fresh request to API using JSON object
function apiRequestJson($method, $parameters) {
  if (!is_string($method)) {
    error_log("Method name must be a string\n");
    return false;
  }

  if (!$parameters) {
    $parameters = array();
  } else if (!is_array($parameters)) {
    error_log("Parameters must be an array\n");
    return false;
  }

  $parameters["method"] = $method;

  $handle = curl_init(API_URL);
  curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
  curl_setopt($handle, CURLOPT_TIMEOUT, 60);
  curl_setopt($handle, CURLOPT_POST, true);
  curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($parameters));
  curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));

  return exec_curl_request($handle);
}

Now, you can see that these methods simply differ in approach but all result in the same output. They all accept two parameters: $method and $parameters. The method for this example will be sendMessage and parameters are described as:

Parameter Description
chat_id The unique identifier that we received in the update object
text The text that is to be sent to user
parse_mode Optionally, we can specify how to interpret the text we just sent. It can be MarkdownV2, HTML, Markdown and empty for text. More details here.
reply_to_message_id This will contain the id of message to which reply we are giving. This will show the message as a reply to the message having this id.
reply_markup Optionally, send additional data to enable custom keyboards, inline replies etc. More on this later

The first two parameters are required and others are optional. The parse_mode allows you to specify how to treat the text as - Markdown or HTML or plain text. These modes might require a lot of testing as intially you will fail with working out exactly how these modes work.

For example, the parameters can be:

array('chat_id' => $chat_id, "text" => 'I understand only text messages');
array('chat_id' => $chat_id, "reply_to_message_id" => $message_id, "text" => 'Cool');
array('chat_id' => $chat_id, "text" => 'Hello',
 'reply_markup' => array(
  'keyboard' => array(array('Hello', 'Hi')),
  'one_time_keyboard' => true,
  'resize_keyboard' => true)
);

The first two example are fairly easy to understand. The third example tells Telegram to send a text Hello and show a custom keyboard with two keys: Hello and Hi.

This article majorly took code from the hellobot sample of Telegram bot written in PHP. You are free to look at the code and even implement it on your own bot to start the testing.

Now, that you have understood the basic working of bots, it is time to send a few other type of mesages to the user.

These articles are a part of Telegram bot series and will be using PHP as their coding language. But if you read through the code, you can easily adapt the code to be used in any language.