Telegram Bot with Apps Script

Google Apps Script is a cloud scripting service based in JavaScript and makes it easy to do cool things with most of Google's products. Lately, I've been a big fan of its flexibility and versatility, for example with just a few lines of code you can interact with the Telegram Bot API to control your bot, you won't have to worry about having a server, SSL certificates or configure ports.

What We Are Going to Do

Our goal is to create a simple Telegram bot that replies to a command with a random quote from Quotes on Design.

You can test it just by starting a conversation with DesignQuotesBot and sending the command /quote.

Now let's get started with the step by step instructions:

🤖 Meet BotFather

The first step is to talk to BotFather to create and setup our bot, he will also give us our authorization token.

Sign in to Telegram from your favorite device and search for the account BotFather and start a conversation.

Search for the user BotFather

Click the Start button.

Start a conversation with BotFather

He will reply with the list of commands supported by BotFather.

List of commands supported by BotFather

Create a new bot by sending the command /newbot. The BotFather will ask you for a name, we'll call our bot Quotes on Design.

The name of your bot will be displayed in contact details and elsewhere.

Give your bot a name

Now, BotFather will ask you for a username, let it be DesignQuotesBot.

The username is a short name, to be used in mentions and telegram.me links. Usernames are 5-32 characters long and are case insensitive, but may only include Latin characters, numbers, and underscores. Your bot's username must end in ‘bot’, e.g. ‘tetris_bot’ or ‘TetrisBot’.

Give your bot a username

BotFather will congratulate us, and give us an unique authorization token, it is a string along the lines of 2970976:AAFbL7yMJqVaGP4, this will be required to send requests to the Bot API.

Authorization Token

Ok, It's time to code!

Let's jump to Google Apps Script, grab your browser and visit script.google.com/intro (You'll need to be signed in to your Google account.) You'll see the Script Editor with a Blank project, delete any code in the Code.gs file.

Google Apps Script Blank Project

Telegram Bot API currently supports two (mutually exclusive) ways of receiving updates. We can either use long polling or Webhooks, for this example we're going to use Webhooks, this means every time there is an update in for the bot (like when someone sends a command to our bot or add him to a group), they will send an HTTP POST request to a specified URL containing a JSON-serialized Update.

So, we need to create an application to be able to respond to POST requests sent from the Bot API, the good news are that Google Apps Script would let us deploy our script as a Web App that would let us interpret those request.

When an user or a program (in this case de Bot API) sends to our script an HTTP Post request, Apps Script will run the special callback function doPost(e) so all we need to do is define that function in our script. The e argument represents an event parameter that contains the information of the request. As we mentioned above, the Bot API will send the POST request containing a JSON-serialized, this means we're gonna recieve an object converted into String, so we need to parse the text content of the POST body e.postBody.contents with the method JSON.parse().

function doPost(e) {
  var update = JSON.parse(e.postData.contents);
}

When our script receives an update, in this case the command /quote the JSON-object should look similar to this example:

{
  "update_id": 926604562,
  "message": {
    "message_id": 357,
    "from": {
      "id": 10610041,
      "first_name": "Jane Doe",
      "username": "janedoe"
    },
    "chat": {
      "id": 10610041,
      "first_name": "Jane Doe",
      "username": "janedoe",
      "type": "private"
    },
    "date": 1473910635,
    "text": "/quote",
    "entities": [
      {
        "type": "bot_command",
        "offset": 0,
        "length": 6
      }
    ]
  }
}

An incoming update can be of many types, for this example we're going to focus on bot commands, so first we need to make sure the update is type message, in JavaScript we can use the method hasOwnProperty() to determine whether an object has the specified property.

Let's define two variables, the first one will be chatId here we're going to store the update's unique identifier for this chat, this will become handy when we want to reply with the quote to the correct conversation. The second will be the whole message property, we're going to name it msg.

if (update.hasOwnProperty("message")) {
  var msg = update.message;
  var chatId = msg.chat.id;
}

Once we are sure the update is a message, we need to confirm the user sent a command, we can determine this by the field type in the message entity. When a user sends a command to our bot this field will have the String bot_command.

The field entities contains an Array, remember that JavaScript Arrays are zero-indexed, so the first element is at the index 0, so to access the element type we need to use msg.entities[0].type.

Now we just need to confirm that command we received is /quote, by evaluating if msg.text is equal to /quote.

if (msg.hasOwnProperty("entities") && msg.entities[0].type == "bot_command") {
  // If the user sends the /quote command
  if (msg.text == "/quote") {
  }
}

At this point our doPost() function should look like this:

function doPost(e) {
  var update = JSON.parse(e.postData.contents);
 
  // Make sure this is update is a type message
  if (update.hasOwnProperty("message")) {
    var msg = update.message;
    var chatId = msg.chat.id;
 
    // Make sure the update is a command.
    if (
      msg.hasOwnProperty("entities") &&
      msg.entities[0].type == "bot_command"
    ) {
      // If the user sends the /quote command
      if (msg.text == "/quote") {
      }
    }
  }
}

We're going to retrieve a random quote from quotesondesign.com so we can send it to the user. Fortunatelly the website has a JSON REST API, to get a random quote we can use the URL: https://quotesondesign.com/wp-json/wp/v2/posts/?orderby=rand.

Google Apps Script can interact with APIs from all over the web, we can use the built-in URL Fetch Service with the method fetch() to make a request to the URL mentioned above.

The same as the Bot API, we need to parse the response to work with a JSON Object.

var url = "https://quotesondesign.com/wp-json/wp/v2/posts/?orderby=rand";
var data = UrlFetchApp.fetch(url);
var posts = JSON.parse(data);

The returned value would look like an array of this:

[
  {
    id: 2415,
    title: { rendered: "Alan Cooper" },
    content: {
      rendered:
        "<p>No matter how cool your interface is, it would be better if there were less of it.</p>\n",
      protected: false,
    },
  },
];

The returned value is an Array, so we can use the JavaScript method shift() it will remove the first element from an Array and returns that element.

var post = posts.shift();

The Bot API supports basic formatting for messages, let's take advantage of this and format the quote, we're goinf to add quotations marks, an em dash and the author in bold.

"No matter how cool your interface is, it would be better if there were less of it." — Alan Cooper

Notice the returned value contains HTML p tags, since Bot API does not support those tags, we need to remove them, we can achieve this with the JavaScript replace() method and the Regular expression /<(?:.|\n)*?>/gm (explanation). Let's also get rid of the last line break \n with the regular expression /\n/gm.

// Delete the html tags and \n (newline)
var cleanContent = post.content.rendered
  .replace(/<(?:.|\n)\*?>/gm, "")
  .replace(/\n/gm, "");
 
// Format the quote
var quote =
  '"' + cleanContent + '"\n — <strong>' + post.title.rendered + "</strong>";

The next step is to send the Bot API our request, for this we're also going to use the URL Fetch Service and the Bot API method sendMessage.

We need to define the POST body for the request containing the requiered parameters:

  1. The method we're going to use, as we mentioned above sendMessage.
  2. The chat_id, this is the unique identifier of the conversation to reply, we stored it in the chatId variable.
  3. The text we want to send, that is in our quote variable.
  4. The parse_mode with the value HTML because we're sending our text formatted with HTML.
var payload = {
  method: "sendMessage",
  chat_id: String(chatId),
  text: quote,
  parse_mode: "HTML",
};

Because payload is a JavaScript object, it will be interpreted as an HTML form. (We do not need to specify contentType; it will automatically default to either 'application/x-www-form-urlencoded' or 'multipart/form-data')

We also need to specify the HTTP method for the request, in this case Post.

var data = {
  method: "post",
  payload: payload,
};

To make the request we're going to use the method fetch(url, params). All queries to Telegram Bot API must be served over HTTPS to the endpoint: https://api.telegram.org/bot<token>. Here is where we're going to use the token we received from the BotFather.

var API_TOKEN = "2970976:AAFbL7yMJqVaGP4";
UrlFetchApp.fetch("https://api.telegram.org/bot" + API_TOKEN + "/", data);

The Full Code

function doPost(e) {
  var update = JSON.parse(e.postData.contents);
  // Make sure this is update is a type message
  if (update.hasOwnProperty("message")) {
    var msg = update.message;
    var chatId = msg.chat.id;
 
    // Make sure the update is a command.
    if (
      msg.hasOwnProperty("entities") &&
      msg.entities[0].type == "bot_command"
    ) {
      // If the user sends the /quote command
      if (msg.text == "/quote") {
        var url =
          "https://quotesondesign.com/wp-json/wp/v2/posts/?orderby=rand";
        var data = UrlFetchApp.fetch(url);
        var posts = JSON.parse(data);
        var post = posts.shift();
 
        // Delete the html tags and \n (newline)
        var cleanContent = post.content.rendered
          .replace(/<(?:.|\n)*?>/gm, "")
          .replace(/\n/gm, "");
 
        // Format the quote
        var quote =
          '"' +
          cleanContent +
          '"\n — <strong>' +
          post.title.rendered +
          "</strong>";
 
        var payload = {
          method: "sendMessage",
          chat_id: String(chatId),
          text: quote,
          parse_mode: "HTML",
        };
 
        var data = {
          method: "post",
          payload: payload,
        };
 
        // Replace with your token
        var API_TOKEN = "2970976:AAFbL7yMJqVaGP4";
        UrlFetchApp.fetch(
          "https://api.telegram.org/bot" + API_TOKEN + "/",
          data
        );
      }
    }
  }
}

Dude, Just Deploy It Already

To publish our script as a web app, we need to follow these steps:

  1. Save our code with File > Save
  2. In the Script Editor, select Publish > Deploy as web app.
  3. Under Project version, select New, optionally set a description.
  4. Under Execute the app as, select Me.
  5. Under Who has access to the app, select Anyone, even anonymous.
  6. Click Deploy.
    Deploy as Web App

Once we click Deploy, we'll see one of the authorization dialogs, we need to authorize our script to Connect to an external services because we're using the URL Fetch Service, click Review Permissions and then Allow.

Authorization required
Request for permission

We'll see a new dialog indicating that your project has been successfully deployed as a web app. The dialog provides two URLs for your app, the first labeled Current web app URL and ends in /exec we're going to need this to set our Webhook.

The final step is to share the Bot API the URL they need to send the update requests, the easiest way is to open your browser and visit the following url:

https://api.telegram.org/bot{API_TOKEN}/setWebHook?url={CURRENT_WEB_APP_URL}

Replacing with {API_TOKEN} and {CURRENT_WEB_APP_URL} with our respective token and URL.

If everything turns out as planned, after visiting the url we'll see the response:

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

Let's Test It!

We just need to open Telegram on any device, search for our bot and send the command /quote.

Testing out bot

We’re done!

I hope this post gave you a better understanding of what we can achieve with Google Apps Script and a little more on how to create a Telegram Bot.

Further Reading and Related Links