Make a Telegram Bot for free with MongoDB Atlas. Part 1

In my first post, I want to share how I implemented a simple Telegram Bot with the following features:

  • Responds to messages directed to it, with random phrases
  • Sends a good morning message every day at 8 AM
  • Without a single coin spent :)

Pre-requisites for this post:

  • Basic Javascript and NodeJS programming skills (very basic, actually you could also accomplish the tasks in this post without any programming skills)
  • Basic knowledge of basic concepts concerning http, like endpoint, http method (GET, POST, etc.) and tools like Postman or Curl if you are a linux user.
  • Basic use of browser and UIs :)

Bot Creation

To implement a new bot, the first thing to do is to create a new bot on Telegram. You can create a new bot talking with the BotFather on Telegram. I’m not joking, you have to search for BotFather on Telegram and us his commands to create a new bot.

Pay attention that you are talking with the real BotFather and not with clones or fake channels. The real one has a blue checkmark, like the famous twitter checkmark, and the username is @BotFather.

BotFather profile picture

If you post the /start command, the bot will show you a little guide about the commands you can send him.

Help returned after the start command

It’s pretty obvious that what we are looking for, is the /newbot command.

Running the /newbot command, botFather asks you some questions like the name you want to assign to your bot and the username. If the username already exists botFather will ask you to choose anoter one, until you choose a unique username. If you want to cancel the entire process, digit /cancel and the bot creation process will be aborted. In the next picture there is a successful bot creation.

Successful bot creation

I obscured the token, for security reason. You will have to keep that token secret, because it is the secret code that allows you (or someone else that already knows it) to control your bot. You can change the authorization code at any time, using the commands that botFather lists in the help seen before.

You can also set a profile picture for your bot, using the /setuserpic command and uploading a picture to botFather. He will set the profile picture you send for your bot.

Set the profile picture

Now that you created your bot and you have your token you have to add it to a Telegram group. You can create a new one or use a channel you already have.

Testing the new Bot

After you added the bot to your channel, searching it by username, you can test if he can send messages to the group chat.

To send messages and receive messages you can use the Telegram Bots API. The methods you will need are:

  • sendMessage: to send messages to a chat
    • It accept a json where the mandatory fields are:
      • chat_id: the id of the chat where the bot belongs and will send the message
      • text: the text of the message to send
  • setWebhook: Telegram allows to get updates from the chats in two ways, the getUpdates and the webhook. The getUpdates expects that you have a running process polling for new events, the webhook, on the other hand, sends all the events to a receiving endpoint. The way you will use to implement the bot behavior on MongoDB Atlas, is the webhook. A webhook is a registered endpoint that Telegram will call when there are updates on a chat. The required parameter is the url of the receiving service. We will talk about it in a while.

You can test if the bot is able to send messages to the group chat using Postman or Curl. Before doing it, you need the chat id.

To obtain the chat id you can add the @RawDataBot to your group, after adding it, it will write a message containing a json with the raw characteristics of you channel. The part of the json that you need is the one containing the chat_id:

{
    "update_id": 826285355,
    "message": {
        "message_id": 1913755,
        "from": {
            "id": "*********",
            "is_bot": false,
            "first_name": "Tito",
            "username": "coluccelli",
            "language_code": "en"
        },
        "chat": {
            "id": -857338537, //THE CHAT ID THAT YOU NEED
            "title": "Atlas tutorial bot test",
            "type": "group",
            "all_members_are_administrators": true
        }
        //...
}

In my case, the chat_id is -857338537

So, the curl to command your bot to send a message to the chat, is:

 curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{"chat_id": "-857338537", "text": "This is a test from curl", "disable_notification": true}' \
     https://api.telegram.org/bot<INSERT HERE YOUR BOT TOKEN>/sendMessage

As you can see from the example above, you have to send a POST request to the https://api.telegram.org/bot/sendMessage endpoint. In the json you have to specify your chat_id (I used the one retrieved before), and you have to put your token right after the https://api.telegram.org/bot

You can also do it with javascript, using NodeJS, with the following code.

const chatId = //Insert here your chat id ;
const botToken = //Insert here your bot token ;

const Client = require('node-rest-client').Client;
const readline = require('readline');

let client = new Client();

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', (line) => {
    let args = { 
        parameters: {
            chat_id: chatId, 
            text: line
        }
    };
    let url = 'https://api.telegram.org/bot' + botToken + '/sendMessage';
    client.post(url, args, function (data, response) {
        console.log("answer() - ok: ", data.ok);
        console.log("answer() - error code: ", data.error_code);
        console.log("answer() - description: ", data.description);
    });
});

rl.once('close', () => {
     // end of input
});

This script reads from the standard input (your keyboard), and send a message for each new line you insert after the script starts. To run the script, insert your chat id and your bot token in the first two lines and save it into a js file. You can run it with node and type your messages that will be sent to the chat you specified the id.

Lines from 22 to 28 contains the code that send the message using the Telegram API. You will need them later in the tutorial. As you can see, at line 4 we import node-rest-client, a little module that allows to send http requests.

Configuring the MongoDB Atlas endpoint

Before digging into implementation of our bot’s behavior, Let’s clarify what do we need. As we said before, When something happens on the chat where the bot has been added, Telegram generates objects called updates. Details about this objects are reported in the Telegram API. Telegram allows to receive these updates in two mutually exclusive ways (if you use one you can’t use the other):

  • getUpdates method: You can use this method configuring a polling process that calls this method to obtain all the updates
  • setWebHook method: You configure an endpoint where Telegram will send all the updates

For our purposes we will use the setWebHook method, so we need to configure an http endpoint to receive the updates. Later in this post, I will explain how I used the setWebHook method, at the moment let’s focus on the bot implementation.

I searched around to find an easy and unexpensive way to configure an http endpoint, basically a REST service, where implement my bot’s logic. I searched and I almost got lost in the “clouds” while I found the MongoDB Atlas cloud.

Atlas Cloud has a lot of interesting features, that go far beyond the implementation of simple RESTful APIs. It allows to use the MongoDB features, and implement . MongoDB is one of the most popular non-relational databases, and it offers a lot of very interesting features. But let’s stay focused on our goals, maybe I will have fun with MongoDB’s features in a future post. Anyway, On Atlas is possible to define http endpoints and implement a behavior behind the endpoint with a very complete javascript runtime.

To start you have to register a new account, you can access with your google or github account, or registering a new account from the Sign Up link. After the registration, and the login, choose the Free Tier, the provider and the region (I use aws Ireland, but feel free to choose the best for you) and set a Cluster name. Clicking on Create you will access you Dashboard, where you see something like the following image:

Click on App Services and select Build your own App from the template selection. Click next, set your app name, and then click on “Create you App Service”.

After you created your App, you see a dashboard like the following:

Click on HTTP Endpoints, and click on Add an Endpoint, and set your Route. You can set, for example /atlas_tutorial_bot, feel free to set anything you prefer. I prefer to use the / at the beginning, so the endpoint is more clear.

Set the following values:

  • Http method: POST
  • Respond with result: disabled
  • Return type: JSON
  • Function: Select New Function

Selecting New Function a text area like the following will show:

Set the function name, as you prefer, and copy paste the following code in the text area:

// This function is the endpoint's request hanlder.
exports = function({ query, headers, body}, response) {
    const botId = '';//Insert yours :)
    let jBody = JSON.parse(body.text()); //Let's parse the body
    let messageType;
    let chatId;
    let messageText;
    
    if (jBody.message){ //There is a message field in the Telegram Update
      if (jBody.message.entities && jBody.message.entities[0]) { //There is at least one elememt in entities array in message 
        console.log("Message type: ", jBody.message.entities[0].type);
        messageType = jBody.message.entities[0].type; //Let's initialize messageType
        messageText = jBody.message.text; //Let's initialize messageText
      } else if (jBody.message.caption_entities && jBody.message.caption_entities[0]) { //Otherwise, if the message has captions (like in image messages)
        console.log("Message type (caption): ", jBody.message.caption_entities[0].type);
        messageType = jBody.message.caption_entities[0].type;
        messageText = jBody.message.caption;
      }
      
      if ((messageType === "mention" || messageType === "text_mention") &&
            messageText.includes('@atlas_tutorial_bot')) { //If the message mentions the Bot
        console.log("Request body:", JSON.stringify(jBody));
        console.log("Message: ", jBody.message.text);
        console.log("Caption: ", jBody.message.caption);
        if (jBody.message.chat) {
          console.log("chat id: ", jBody.message.chat.id);
          answer(botId, jBody.message.chat.id); //Answer to the message
        }
      }
    }
}

//This function implements the random response to Telegram messages sent to the Bot
//Accepts the botId, the chatId and an optional message
//If the message is not passed to this function, then the response will be random among a list of predefined responses
function answer(botId, chatId, message) {
  let Client = require('node-rest-client').Client;
  let client = new Client();
  if (!message){ 
    //prhases defines the possible responses
      let phrases = [
              "I'm occupied right now, try later",
              "Do you have a good reason for waking me up?",
              "Hi"
          ];
      //Let's compute a random index
      let index = Math.floor(Math.random() * phrases.length);
      //Let's select the random message
      message = phrases[index];
  }
  
  //Here we build and send the reponse to the Telegram chat
  let args = { 
      parameters: {
          chat_id: chatId, 
          text: message
      }
  };
  let url = 'https://api.telegram.org/' + botId + '/sendMessage';
  //console.log("answer() - URL: ", url);
  client.post(url, args, function (data, response) {
    if (!data.ok) {
      console.log("answer() - ok: ", data.ok);
      console.log("answer() - error code: ", data.error_code);
      console.log("answer() - description: ", data.description);
      //console.log("answer() - response: ", response);
    }
  });
    
}

The code is pretty straightforward, read to the comments to understand what it does. Briefly, it receives the Update from Telegram, extracts the message, understands if it is directed to the Bot and answer with a random message, picked among the content of the phrases array. You can add how many phrases you want to the array, it will work. Click on the Add Dependency button and add the node-rest-client dependency.

Scroll at the end of the page and click Save Draft. You can deploy your solution by clicking on REVIEW DRAFT & DEPLOY.

You have a last step that is fundamental to make your endpoint work smoothly. You have to go in Functions, then click on the … button near your function and click edit.

From the Settings tab you can set the Authentication for the function. To simplify we will set System, that will bypass all the security rules. I know this is the less secure option, but consider that we are not accessing to any other service of the Atlas Platform from our function, we just receive a message and send another one to the Telegram API. Anyway, if you want a more secure solution you can use User Id to execute the function as a specific user, but you have to figure out by yourself how to configure users in the Atlas platform, because it is beyond the scope of this post.

After you set the Authentication for your function you have all that you need to make your bot chatting.

Set the Telegram WebHook

My compliments! You arrived here and you have your Bot implementation. A little effort to attach this behavior to the Telegram Bot and you are ready to go.

As we’ve seen before, Telegram exposes the setWebHook method in his api. How can we use this method to bind the endpoint we created in Atlas?
The method is exposed by Telegram as a http endpoint too. Like what we have done to write messages in the chat, we can use a tool to call the setWebHook endpoint and set the Atlas endpoint in Telegram.

If you scroll up to the Add Endpoint interface in this post, you can see that Atlas provides you the endpoint url, in the Endpoint Settings.

You will provide that endpoint to Telegram in the setWebHook call parameters. To call the Telegram API you can use curl, Postman or the tool you prefer to make http requests. I am going to use curl, like this:

curl --location --request GET 'https://api.telegram.org/bot<PUT YOUR BOT TOKEN HERE>/setWebhook?url=https://eu-west-1.aws.data.mongodb-api.com/app/telegrambot-htdfx/endpoint/atlas_tutorial_bot' -v

As you have seen in the previous script about the sendMessage method, telegram uses the url: https://api.telegram.org/bot as base for the API. You have to append your bot token, provided by BotFather, like: https://api.telegram.org/bot123445:ABCDEFG292D_HEKLfdfsdmfna (the token is invented, just to explain).

You can specify your endpoint url to the url queryparameter, like in my example, where I specified my url. If everything goes fine, you should see something like this, at the end of the curl output.

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

This is the response provided by the setWebHook telling us that everything went fine and the Webhook was set.

Chat with your Bot :)

Now that everyting is set, you can test your bot in your channel by sending messages to your bot, and checking if it answers and everything works fine. Remember to remove Telegram Bot Raw from your channel otherwise you will see every message you send in the chat. I can be useful for debugging but it can also become annoying.

To make your bot receiving messages you have to enable it as channel administrator from your Telegram mobile app. Click on the channel name, and you will see the users belonging to the group. Long touch on the bot and you should see “Make it administrator”. Remember to properly set the permissions for the bot, I removed all the permissions like “Delete users” and other managing permissions for the channel. Refer to the Telegram documentation to understand how to set the user parameters.

VoilĂ , the Bot is responding with the sentences we configured in the script. If you experience problems, you can see the logs from the Logs menu voice.

Expanding the log entries you can see the output of the console.log calls in the js code we used to implement the bot. If there are errors you can also expand and see what the error is about. In my case I did’t set the Authentication params for the function.

Ok, I hope you enjoyed this tutorial and that you have fun with Telegram Bots implementation. See you on th…

> Wait a minute, you talked about a good morning message at the beginning of this post!

Uh! Yeah… Right, we will see how to set Scheduled Actions in the part 2 of this post. In the meantime… Have fun! :)

If you enjoyed this post, and you want to share it to your contacts on the social media, you can do it, by clicking on the social icons at the beginning of this post, otherwise you can copy the url and share it wherever you like.