Serverless Telegram bot with Kotlin, Firebase and Google Cloud Functions – Telegram Group

Building serverless Telegram bot with Kotlin, Google Cloud Functions and Firebase Realtime Database

Introduction

Firebase Realtime Database is a NoSQL cloud storage with built-in ability to perform CRUD operations using REST API. It is easy to setup and has a free pricing tier which makes it a good choice for small projects. Google Cloud Functions is a FaaS (Function-as-a-Service) solution that allows you to deploy snippets of code in the cloud and run them in response to various events, without having to provision and configure servers by yourself. In this tutorial we are going to use Realtime Database together with Google Cloud Function written in Kotlin to build a simple chat bot that can be used to track user daily expenses. We will also use Google Secret Manager to store the sensitive configuration data and expose it to the Cloud Function instance.

Prerequisites:

  1. Google Cloud account with active billing account (All cloud resources used in this tutorial have free pricing tiers, however some of them, like Cloud Functions cannot be used without enabling billing)
  2. Google Cloud SDK — gcloud command line tool is used to create and manage cloud resources
  3. Firebase CLI — Command line tool to manage Firebase resources
  4. Kotlin 1.5.21
  5. Gradle — Used to build and run function locally

Creating Telegram bot

We’ll start from creating a new Telegram bot using BotFather. Just follow the bot instructions, select a name and username for the bot and receive the API token at the end. Save the token locally in bot-token.txt file.

Initializing cloud resources

We use Google Cloud SDK command line tool to create and configure necessary cloud resources. (The same results can be achieved using Google Cloud console in browser, however this tutorial focuses on the usage of command line).

First, login to you Google account:

Then create new Google Cloud project. Projects serve as directories to group other resources together.

The command above will ask you to confirm the generated project ID (in my case it was expense-tracker-330011). It is a unique identifier of your project that can’t be changed after a project was created. If you don’t want the ID to be generated for you, you can specify it in the command like this:

After successfully creating project, save project ID in the environment variable for future use:

Now we also need to link a billing account to new project since some of the services we’re using (Secret Manager and Cloud Functions) cannot be enabled for a project without a billing account. With the following command you can see the list of your billing accounts.

If you don’t have any billing account set up, follow this tutorial. Copy ACCOUNT_ID field of one of the accounts and use the following command to link it with your Google Cloud project:

With linked billing account we can enable Google Cloud services that will be used by the application:

Creating secret

Next, let’s create a new secret that will hold Telegram bot token. Secrets are resources designed to store sensitive data like passwords and various API keys.

bot-token.txt is the name of the file where Telegram API key was saved.

Creating Service Account

Next step is to create service account for Google Cloud Functions instance. Service accounts are resources that are used by applications to perform authorized API calls to other cloud resources. In our case service account used by cloud function instance needs permissions to access and manage data stored in Firebase Realtime Database as well as read secrets content. The following command will create a new service account named expense-tracker-sa:

Service accounts are uniquely identified by email address with the following format: [service-account-name]@[project-id].iam.gserviceaccount.com

Save service account email in the variable for later use:

After that all you need to do is to give this service account necessary permissions by assigning IAM roles. It is important to use service accounts with the least possible amount of permissions required to perform the necessary tasks.

roles/firebasedatabase.admin role allows service account to perform both read and write operations on the database, while roles/secretmanager.secretAccessor grants read access to secret values

Initializing Firebase

In the next step we will enable Firebase for our Google Cloud project and initialize Realtime Database instance. This can be done via Firebase CLI. Run the following commands in the shell (assuming you have Firebase CLI installed):

When running firebase init database command, select the option Use an existing project , then select your project ID from the list. After that confirm that you want to initialize Realtime Database in your project and choose the region for the database instance.

To verify that all steps were successful, you can run the following command: firebase database:instances:list --project $GCLOUD_PROJECT_ID. The output should show that you have one Realtime Database instance (see example below).

Each Realtime Database instance has it’s unique reference URL which is used to communicate with the database. The URL may have one of 2 possible formats:

(in case you selected us-central1 region), or

(for all other regions), where [database-name] is the name that is returned by firebase database:instances:list command (in my case it’s expense-tracker-330011-default-rtdb). Save database reference URL in another environment variable:

Implementing Cloud Function

Now it’s finally time to write some code! In this tutorial we organize the code in a Gradle project, however this is not a strict requirement for using Cloud Functions. The complete codebase can be found here. Let’s have a look at build.gradle file which contains dependencies and build scripts for the project:

runFunction task can be used to launch an instance of cloud function locally. This is useful to test the logic without deploying the function to the cloud.

FirebaseRepository class is responsible for database operations. URL of Realtime Database instance is taken from FIREBASE_URL environment variable. During class initialization, GoogleCredentials object is created which is then used obtain the value for access_token query param that should be passed with every request to the database.

The actual function logic is implemented in ExpenseTrackerFunction class. Since the function we’re building will be triggered by HTTP requests, it extends com.google.cloud.function.HttpFunction The entrypoint for incoming requests is service method that together with request and response arguments looks similar to java HttpServlet. Our bot will support 2 commands: /addExpense that adds some amount to the user’s expenses and /getSummary that shows the current total amount. The logic of this class is simple enough — it reads the bot command from request body, updates the data stored in the database using repository class and sends the response back to the user using Telegram API (the cloud function itself does not write anything to the response)

The last class is TelegramService that incapsulates some logic related to communicating with Telegram API. It helps to parse Telegram Update object from incoming request and to send the response message back to the user. Note that Telegram bot token is fetched from environment variable.

Running function locally

As mentioned before, build.gradle file contains runFunction task that can be used to launch local server hosting our cloud function. Before executing the command we create a dummy environment variable with Telegram bot token (you can set the real token here if you want your local function instance to be able to send messages to Telegram API):

When you’ll see the following line in terminal output: INFO: URL: http://localhost:8080/ it means that the function is ready to process requests. We can launch another shell session and use curl to imitate incoming Telegram update request:

This request contains a command to add 100$ expense to user with ID 23432456 . We can check if data was saved to the database making another request directly to the database (note the we’re using another gcloud command to generate database access token, without it request will not be permitted):

The response should look like this: {“amountInCents”:10000}

Deploying function

In the final step of the tutorial we will deploy cloud function and wire it to the Telegram bot. The first can be done with gcloud beta functions deploy command. We’re using beta version of the command since exposing secret to a cloud function is a relatively new feature so it cannot be done with the normal version of the command. The following should be executed from the Gradle project root directory:

Here we specify some configuration for the function instance like amount of memory and java runtime, also setting FIREBASE_URL environment variable that is pointing to our Realtime Database instance and exposing Telegram bot token secret as BOT_TOKEN environment variable. The full explanation of the command arguments can be found here. After executing the command, source code will be submitted to Google Cloud Build which will in turn build the code, run tests and then deploy it as a new Cloud Function instance.
After a few minutes you should see lines similar to that in the output:

The url here is used to trigger the function execution. If you want to redeploy the function after adding some changes to the code, you can omit most of the parameters and use the following command:

Creating webhook

Now we need to configure Telegram bot to send incoming chat messages to new function. This can be done using webhooks feature. Do create a webhook, simply perform the following GET request (through curl or your browser), replacing placeholders:

  • bot-token is the token you got from BotFather
  • cloud-function-url is trigger URL you received after deploying the function in the previous step

You should receive the following response:

After that your bot should be ready to process incoming requests. You can start the conversation with the bot in Telegram client and check it out.

You can also go to Firebase console and view/edit database content directly from your browser.

Ten articles before and after

TRX1 Dev Blog #11 (November 2021) – Telegram Group

Telegram Bot 跟我想的不一樣. 第一天進公司時,Slack就被邀進同事們訂便當的群組,在上面會宣布今天訂那一家便… – Telegram Group

How to Create a Telegram Chatbot in 2022 – Telegram Group

Google Sheets Formula & Telegram Message ✈️ – Telegram Group

Monitor Server with Telegram Bot and Python – Telegram Group

How To: Deploy Java Telegram Bot to Heroku – Telegram Group

How To: Create a Java Telegram Bot – Telegram Group

How I Use A Telegram Bot To Track My Expenses – Telegram Group

CryptoChat: Un chatbot hecho con rasa sobre criptomonedas – Telegram Group

data-rh=”true”>Share your Ref link to your friends:
t.me/metamusk_airdrop_bot?start=21FF1F54D851B44DBAB310ABA260150F
? Metamusk is a native token issued to support Elon Musk and his great projects on the way to… – Loiscwai – Medium – Telegram Group