Adventures with the Telegram API

I'm working on some home automation things with a Raspberry Pi and wanted it to be able to send me messages on telegram, as well as I ask for stuff and it gives it back to me. So this has lead to me spending a small amount of time playing with the telegram bot API, and it turns out it's actually pretty simple (at least for just sending messages) once figured out. I did find the main documentation a bit vague though in terms of a simple send messages script, and a lot of blog posts about setting up a bot use a framework which just does it all for you and offers too much in overhead/features for what I need.

So for a simple bot you just need to do the following;

Firstly message "BotFather" from your mobile telegram app (It doesn't seem to work well with the browser) . You then type /newbot and go through the prompts. The bot username has to end in bot, but the display name can be what you like. Once setup you will get a token, this comprises of the bot's id, and the auth token. In the format of bot$id:$token, for example bot1234:onDWzmiOTGB2V53qeUhXFxC1tHI0RS

Once the bot is created, you will be able to search for it and message it.

To get the messages you send to the bot, you can either poll the getUpdates endpoint or setup a web endpoint that telegram will POST some JSON to. This is the method I'm using but I will give an example of the updates API.

I'm using curl piped to jq for pretty printing of JSON on the command line. The format of the API URLs is;

https://api.telegram.org/{bot_token}/{endpoint}

For the getUpdates endpoint;

$ curl -s "https://api.telegram.org/bot1234:onDWzmiOTGB2V53qeUhXFxC1tHI0RS/getUpdates" | jq
{
  "ok": true,
  "result": [
    {
      "update_id": 123,
      "message": {
        "message_id": 2,
        "from": {
          "id": 1234567,
          "is_bot": false,
          "first_name": "Craig",
          "username": "exampleusr",
          "language_code": "en"
        },
        "chat": {
          "id": 1234567,
          "first_name": "Craig",
          "username": "exampleusr",
          "type": "private"
        },
        "date": 1613134430,
        "text": "Boop"
      }
    }
  ]
}`

As you can see it returns an array of the message. The chat id is important as you need that to send a message back to the chat, and message text is the content of the message you have sent. If you were only going to send messages to yourself from an application, you could use the sendMessage endpoint (example below) after you have found your chat id from the getUpdates endpoint. As I wanted to be able to request info as well as send messages I went with the webhook method.

To have Telegram send the messages to your webserver you need to set a webhook via the API. Your webserver requires a valid SSL certificate, and not self signed. So use Lets Encrypt.

$ curl -s "https://api.telegram.org/bot1234:onDWzmiOTGB2V53qeUhXFxC1tHI0RS/setWebhook?url=https://serverhostna.me/telegramexample/" | jq
{
  "ok": true,
  "result": true,
  "description": "Webhook was set"
}

You can then check your webhooks info with the API

$ curl -s "https://api.telegram.org/bot1234:onDWzmiOTGB2V53qeUhXFxC1tHI0RS/getWebhookInfo" | jq
{
  "ok": true,
  "result": {
    "url": "https://serverhostna.me/telegramexample/",
    "has_custom_certificate": false,
    "pending_update_count": 0,
    "max_connections": 40,
    "ip_address": "185.119.173.64"
  }
}

Now when you send a message to your bot, some JSON will get posted to your URL.

To send a message you send to the sendMessage endpoint, using the chat id retrieved from the message update/webhook, and the text field for the message should be urlencoded.

$ curl -s "https://api.telegram.org/bot1234:onDWzmiOTGB2V53qeUhXFxC1tHI0RS/sendMessage?chat_id=1234567&text=ping" | jq
{
  "ok": true,
  "result": {
    "message_id": 5,
    "from": {
      "id": 1234,
      "is_bot": true,
      "first_name": "Example",
      "username": "examplebot"
    },
    "chat": {
      "id": 1234567,
      "first_name": "Craig",
      "username": "exampleusr",
      "type": "private"
    },
    "date": 1613141321,
    "text": "ping"
  }
}

An example of my php webhook on my server;

<?php

$telegram_auth = 'bot1234:onDWzmiOTGB2V53qeUhXFxC1tHI0RS';

$input = file_get_contents("php://input");
$input = json_decode($input,true);

if ($input) {
    $user_id = $input['message']['from']['id'];
    $chat_id = $input['message']['chat']['id'];
    $request = strtolower($input['message']['text']);
    if ($user_id != '1234567') {
        exit();
    }
    if ($request == 'temps') {
        $temps = json_decode(file_get_contents("https://serverhostna.me/?temps"),true);
        foreach ($temps as $key => $value) {
            $output .= $key . ' => ' . $value . PHP_EOL;
        }
        $message = urlencode($output);
        file_get_contents("https://api.telegram.org/".$telegram_auth."/sendMessage?chat_id=".$chat_id."&text=".$message);
    }
}

As you can see, I take the input and decode it into an array, which then can be processed. I've got a check for the requesting user, if it doesn't match my user id the script will exit. I also convert the incoming message to lowercase for easier matching, for when the mobile app capitalises the first letter of a word.

My first feature is to return the output of my temperature sensors. The message is urlencoded for the request to the API.

And it in action!