After struggling with outdated tutorials and lackluster documentation for a while, I was finally able to build the Telegram bot that I had in mind for a group of friends. Something really simple that took me half a day. As I don’t want to repeat this waste of time, I decided to write down an up-to-date guide on how to build and deploy a Telegram bot using a wrapper built in Python, and AWS as serverless architecture.

Using serverless will make our bot scalable, and it is an easy and secure way of deploying our bot.

1. Create your AWS account and credentials

As we are going to use AWS as serverless provider, we will need to create an account. AWS is a world on its own, so I won’t go into much detail of what we are going to use. Just know that we will need S3 for storage, Lambda to run our code, IAM for authentication, CloudFormation to manage our stack and API Gateway as… API Gateway. It sounds like a lot, doesn’t it? But don’t worry, we will be using a framework that will make all the work for us.

Select the free tier and create the account, as it has more than enough features for what we need. Once you have created your account, go into My Security Credentials-> Users-> Add user

Give it a comprehensive name such as ‘serverless-admin’ and check the Programmatic access checkbox.

On the next screen click on Attach exising policies directly and select AdministratorAccess.

Click on next and finish the creation of the user that will be used by the serverless framework. Note down the ‘Access key ID’ and the ‘Secret’ as you will need them in the next step.

2. Install the serverless framework

Now that we have our user we will install the serverless framework, which will help us to deploy our bot to AWS seamlessly. Installing it is as simple as running:

npm install -g serverless

Once installed, we need to setup our AWS credentials so that the framework can do all the heavy lifting for us. This can be done with the following command:

serverless config credentials --provider aws --key YOUR_KEY --secret YOUR_SECRET

Replace YOUR_KEY and YOUR_SECRET with the values that you noted down in the previous step.

Finally, install a plugin needed for Python dependencies:

sls plugin install -n serverless-python-requirements

If you need a more detailed explanation about steps 1 and 2, here is a good tutorial(although it uses nodejs instead of Python).

3. Talk with the Botfather

Before actually coding the bot, we need a bot token, and these are given by the Botfather. You can start a conversation with him here. Use the /newbot command and follow the instructions, it is really simple.

botfather

Note down your token!

4. Code your bot!

We can now start coding our bot, finally!

We will create the project by using the serverless framework we installed previously. Open a terminal window and type serverless. Type yes, then choose AWS Python. Finally, choose a cool name for your project, for this demo I chose telegram-bot-demo(I know, very original 😃). Choose No for the last two commands.

The framework has now created two files, handler.py(our bot) and serverless.yml(the serverless framework configuration file). We need to create another file called serverless.env.yml, where we will place our Telegram token so that the serverless framework has access to it:

TELEGRAM_TOKEN: XXXXXXXX:YYYYYYYYYY

Finally, create a file called requirements.txt where we will tell to the framework which dependencies it has to install before running. In this case, it’s just one: python-telegram-bot.

Now, let’s replace the placeholder code in handler.py by the real code of our bot. We will implement an echo bot, which repeats the messages that it receives.

First we write the necessary imports ans HTML response messages:

import json
import logging
import os

from telegram import Update, Bot
from telegram.ext import CommandHandler, Dispatcher, MessageHandler, Filters

# Enable logging
logger = logging.getLogger()
if logger.handlers:
    for handler in logger.handlers:
        logger.removeHandler(handler)
logging.basicConfig(level=logging.INFO)

# Define responses
OK_RESPONSE = {
    'statusCode': 200,
    'headers': {'Content-Type': 'application/json'},
    'body': json.dumps('ok')
}
ERROR_RESPONSE = {
    'statusCode': 400,
    'body': json.dumps('Oops, something went wrong!')
}

Next, we define the function that initialises the bot instance:

def configure_telegram():
    """
    Configures the bot with a Telegram Token.
    Returns a bot instance.
    """

    TELEGRAM_TOKEN = os.environ.get('TELEGRAM_TOKEN')
    if not TELEGRAM_TOKEN:
        logger.error('The TELEGRAM_TOKEN must be set')
        raise NotImplementedError

    return Bot(TELEGRAM_TOKEN)

The following snippet defines the functions that will be called after the bot receives a message or a command. For example, it the bot receives the comman /start it will reply saying Hi!:

def start(update, context):
    """Send a message when the command /start is issued."""
    update.message.reply_text('Hi!')


def help_command(update, context):
    """Send a message when the command /help is issued."""
    update.message.reply_text('Help!')


def echo(update, context):
    """Echo the user message."""
    update.message.reply_text(update.message.text)

Then, we initialise the bot and tell the Dispatcher how to react when it receives a message:

# Initialize bot and dispatcher to register handlers
bot = configure_telegram()
dp = Dispatcher(bot, None, use_context=True)
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, echo))
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help_command))

Finally, we define the method that will be called by everytime that an interaction with the occurs. This is the method that we will link with our AWS backend:

def process_update(event, context):
    """
    Processes the received update and sends it to the dispatcher.
    """
    logger.info(f'Event: {event}')

    try:
        dp.process_update(Update.de_json(json.loads(event.get("body")), bot))
    except Exception as e:
        logger.error(e)
        return ERROR_RESPONSE

    return OK_RESPONSE

The Python code is ready, now go ahead and replace the contents of serverless.yml by this:

service: telegram-bot-demo

provider:
  name: aws
  runtime: python3.8
  region: eu-west-1
  environment:
    TELEGRAM_TOKEN: ${file(./serverless.env.yml):TELEGRAM_TOKEN, ''}

functions:
  bot:
    handler: handler.process_update
    events:
      - http: POST /

plugins:
  - serverless-python-requirements

Here we are telling the framework the name of our service, that we want to use AWS services on the EU server(you should change this to the region where you are), and we are passing our Telegram token as environment variable.

More importantly, we are defining a function which will be called every time the bot receives a message. In this case, we are defining a function called bot which will call the method handler.process_update(the method in our Python code) every time a POST / request is made.

4. Deploy to AWS

Now that both the bot code and the serverless configuration files are ready, we can deploy our bot for the first time! This process could not be simpler, just open the console on the project root directory and type serverless deploy.

After the deploy is complete, note down the URL provided by AWS, it will be something like https://xxxxxxxxx.execute-api.eu-west-1.amazonaws.com/dev/.

If you’re curious, you can go into de AWS Management Console and check how the serverless framework has created everything that we need.

As a final step, we need to connect out bot with the AWS backend. This can be done by using webhooks.

This process is as simple as visiting this link and replacing the needed values by your bot token and the AWS url that you just noted down in the previous step: http://api.telegram.org/bot{telegram_token}/setWebhook?url={bot_url}

6. The end

That’s it! Your bot is alive, scalable and free. Open a chat with it and see how it replies back.

Now you can extend the bot functionality by using the Python Telegram Bot API.

In addition, you can read about the serverless framework if you are curious about what the framework is doing behind the scenes.

I hope this tutorial was useful for you! You can take a look at the full source code here.

Cheers.