Telegram is one of the simplest platforms to build chatbots. Telegram does not have lengthy approval processes or strict quality guidelines. In this tutorial, You will learn how to build a telegram bot using Python and Django.

This is the first part of a three-part tutorial. Here are the links to other parts of this tutorial.

  1. Part 1(You are here)
  2. Part 2
  3. Part 3

Creating A Bot

Managing a telegram app different from how you typically manage bots with other platforms such as Facebook. With telegram, creating and managing bots are done by sending messages to a special account called the Bot Father.

You will need a telegram account to complete this step. Install the Telegram application on your device before following the below steps.

  1. Find the BotFather user in telegram using the search option or visit this link from a telegram installed device. You will be managing your bot by sending messages to Bot Father
  2. Send /newbot to BotFather. BotFather will ask for a name and a username for your bot. You can name your bot anything you choose but the username should end with bot and it should be unique. You will not be able to choose a username that is already taken by another bot.
  3. Once Bot Father is happy with your choices, he will create a bot for you and give you the credentials. Take a note of the token Bot Father sends you. It will start with a number followed by a colon(:) and some random characters.

Setting Up Django Project

In this section, we will be setting up a brand new Django project for our bot. Feel free to skip this section if you already have a project set up.

I will be using pipenv to manage my virtual environment. Install pipenv using pip install pipenv if you don’t have it installed already. (On Linux and MacOS you might have to use sudo pip3 install pipenv.)

Create a directory for your chatbot project and open a terminal from that directory. Run the following command to create a new pipenv environment. Substitute 3.10 with your python version if you are using a different version.

pipenv --python 3.10

Start a pipenv shell next.

pipenv shell

Now, we need to install the packages needed for this project.

pipenv install django djangorestframework requests

The requests library is for making HTTP calls to the telegram API and djangorestframework is for creating a clean-looking API for telegram to call. Next, we need to create a new django project.

django-admin startproject your-project-name .

Open the settings file django created for you and add the following line to the end of it.

TELEGRAM_API_TOKEN = "your-token"

Don’t forget to replace your-token with the token you got from Bot Father. (Ideally, you should not hard-code sensitive information in your settings files. You should be reading them from environment variables. We omit that step here to keep this tutorial simple.)

You should also add rest_framework to the installed apps section.

INSTALLED_APPS = [
    # rest of you apps
    "rest_framework",
]

Creating A Webhook for Telegram

Whenever someone messages our bot, we want telegram to notify our django application. Then our application can take actions such as sending a reply. A webhook is a way for telegram to call us when someone is interacting with our bot.

A webhook is just a regular django view. It has a URL and it will receive data through the HTTP post method.

We will create a new app for our webhook views. Make sure you have pipenv activated and you are in the correct directory before running these commands.

python manage.py startapp webhooks

Then open the settings file and add webhooks to the installed apps.

Now open the views.py file inside the new webhooks app and replace its contents with the following.

from rest_framework.response import Response
from rest_framework.views import APIView


class TelegramWebhook(APIView):
    def post(self, request):
        print(request.data)
        return Response({"success": True})

Here we have a plain rest framework view. When a request comes in, we are printing the post data and then return a JSON indicating a successful call.

Next, create a urls.py inside webhooks and add the following to it.

from django.urls import path

from . import views

app_name = "webhooks"

urlpatterns = [
    path("telegram/", views.TelegramWebhook.as_view(), name="telegram_webhook")
]

Then open your project’s main urls.py file and add this to the existing URL patterns.

urlpatterns = [
    # Your existing paths
    path("webhooks/", include("webhooks.urls")),
]

Don’t forget to update the import statements

from django.urls import include, path

Now, start django development server by running python manage.py runserver and visit http://127.0.0.1:8000/webhooks/telegram/ from your browser. You should see the django rest framework view in your browser.

Enter some json in the content field and hit post. You should see the success response. If you open the terminal where django server is running, you should see the json data printed.

Setting Up Ngrock

When we develop our application, we need telegram to call our API but telegram cannot access the django server running on our computer. For this we will be using a tool called ngrock. You don’t have to do this if your API is deployed on a server.

The first step for this to go to ngrock website and follow the installation instructions for your operating system. Next, create an ngrock account from the website. You will get an API key once registration is complete.

Run the following command to set up your API key. You need to do this only once.

ngrok config add-authtoken <your-key-here>

Next run ngrock tunnel using the following command.

ngrok http 8000

You will see two web addresses ending with .ngrock.io generated for you. Copy the HTTPS address and open it in your browser. You should see the django 404 error page. Next, add the telegram webhook URL to the end of this address and you should see rest framework API page. Try sending some json data and make sure it works.

Make sure you have the django development server running before visiting the ngrock url in your browser. You should have two terminals by now - one running ngrock and another running django server.

Keep the ngrock running on a terminal. Note that if you kill the ngrock command and rerun it, you will get a different address.

Adding Webhook to Telegram Bot

The next thing we have to do is to let telegram know about the webhook we built. Then telegram will call our webhook every time our bot receives a message. For this, we will need the API token Bot Father gave us.

Visit the following URL from your browser to set the webhook. Make sure you have replaced API token and webhook URL with your values.

https://api.telegram.org/bot<your-bot-token>/setWebhook?url=<your-ngrock-webhook-url>

Do not remove the bot part before the bot token. For some reason, telegram requires all tokens to be prefixed with bot. Also, for URL, you have to use the full URL to the webhook, not just the main ngrock domain.

If everything goes well, you should see a success message in your browser.

To test this, we need to send a message to our bot. Open your bot in telegram and send a message to it. You should see the json data from telegram printed in your terminal running django.

(if you don’t know how to find your bot in telegram, open the message the bot father sent you after creating the bot. There will be a URL to find your bot. It should be something like t.me/<your_bot_username>. Click on that link and telegram will open the bot chat for you.)

Management Command for Setting Webhook

You may have already noticed a problem with our approach. Every time the ngrock URL changes, we have to construct a long telegram API URL and then open it in a browser. In this step, we are going to make it easier by adding a management command for this process.

Create a new directory, management inside the webhooks app. Inside the management directory, create another directory, commands. Add an empty __init__.py file to each of these new directories. Make sure you use the exact directory names. Otherwise, the commands won’t work.

Next, create a set_telegram_webhook.py file inside commands directory and add the following code to it.

import requests

from django.conf import settings
from django.core.management import BaseCommand
from django.urls import reverse


class Command(BaseCommand):
    help = "Set new telegram webhook"

    def add_arguments(self, parser) -> None:
        parser.add_argument("root_url", type=str)

    def handle(self, *args, **options):
        root_url = options["root_url"]
        webhook_path = reverse("webhooks:telegram_webhook")
        webhook_url = f"{root_url}{webhook_path}"

        set_webhook_api = f"https://api.telegram.org/bot{settings.TELEGRAM_API_TOKEN}/setWebhook"

        self.stdout.write(f"Setting webhook url {webhook_url}\n")

        requests.post(set_webhook_api, data={"url": webhook_url})

The code should be self-explanatory but here is a short overview of what is happening.

  1. Our command takes the root URL of the server as its argument. For our development purposes, this is the URL ngrock gave us.
  2. We then add the path to our webhook view to this root URL to generate the full path to the webhook. Note that we are using the reverse function instead of hard-coding /webhooks/telegram/
  3. We have added the telegram API token to our settings file in a previous step. Here we take that value from settings and use it to construct the URL to send webhook information.
  4. Then we use the requests library to make a call to the telegram API and send the webhook URL. Note that we are using http post here. When we were doing this through the browser, the method has get. Telegram supports both get and post methods.

To test this, run the following command from your django project directory. Make sure you have pipenv activated before running this.

python manage.py set_telegram_webhook <your-ngrock-url>

Note that you have to use the url ngrock base url, note the full url to your webhook.

Securing Webhook

Our webhook is not very secure now. Anybody who knows the URL can call our webhook and our view has no way to tell if the call is from telegram or from someone else. We will fix that in this section.

We will be adding a few random characters to the end of our webhook url. Open our settings file and add the following variable to it.

TELEGRAM_WEBHOOK_TOKEN = "some-random-string"

Make sure you have a long random set of characters in there. Use your password manager or some similar way to generate this.

Next, we need to update the view to check for this token. Open the views.py file inside the webhooks app and replace it with this code.

from django.conf import settings

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView


class TelegramWebhook(APIView):
    def post(self, request, token):
        if token != settings.TELEGRAM_WEBHOOK_TOKEN:
            return Response(
                {"error": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED
            )

        print(request.data)
        return Response({"success": True})

Our view has an extra parameter, token. Whenever a new request comes in, we compare the token with the token in the settings. If they do not match, we will return an unauthorized error. If they match, we do our usual stuff.

We have to update our URL patterns since the view needs an extra parameter now. Open the urls.py file in the webhooks app and update the URL pattern for telegram webhook as follows.

path("telegram/<str:token>/", views.TelegramWebhook.as_view(), name="telegram_webhook")

To test this, open your webhook (<ngrock-url>/webhooks/telegram/<webhook-token>) and send some data to it. Now, change the token and see what happens.

Next, we should update our management command to include this token in the webhook URL we send to telegram. Open the management command file(set_telegram_webhook.py) and update the webhook_path variable.

webhook_path = reverse("webhooks:telegram_webhook", kwargs={"token": settings.TELEGRAM_WEBHOOK_TOKEN})

We are updating the reverse call to include the token from settings. The rest of the code should be the same.

Now call the management command with ngrock URL. Make sure ngrock is running before you do this.

python manage.py set_telegram_webhook <your-ngrock-url>

Verify everything works by sending a message to your bot. You should see the data from telegram on the terminal running django server.

Sending A Reply

When someone messages our bot, they would expect a reply. In this section, you will learn how to send a reply back to the user.

We will start another django app to process the messages. Run the following command to create a chat app.

python manage.py starapp chat

Don’t forget to add chat to your installed apps in settings.

Now, create a new utils.py file inside the chat app. Add the following code to it.

import requests

from django.conf import settings


def process_telegram_message(message):
    name = message["message"]["from"]["first_name"]
    text = message["message"]["text"]
    chat_id = message["message"]["chat"]["id"]

    reply = f"Hi {name}! Got your message: {text}"

    reply_url = f"https://api.telegram.org/bot{settings.TELEGRAM_API_TOKEN}/sendMessage"
    
    data = {"chat_id": chat_id, "text": reply}

    requests.post(reply_url, data=data)

You might have noticed the structure of telegram responses when we printed them into the terminal. Here we are extracting the user’s name, their message and the id of the chat. Then we construct a reply message with the name and the text. We use the requests library to send this to the telegram API.

Next, we need to update the view to call our new process_telegram_message_function. Open the views.py file in webhooks app and update it with the following code.

from django.conf import settings

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from chat.utils import process_telegram_message


class TelegramWebhook(APIView):
    def post(self, request, token):
        if token != settings.TELEGRAM_WEBHOOK_TOKEN:
            return Response(
                {"error": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED
            )

        process_telegram_message(request.data)

        return Response({"success": True})

We replaced the print line with a call to our new function.

To test this, make sure both ngrock and django are running. If the ngrock url has changed, use the management command to update it. Then send a message to our bot in telegram. You should see the reply.

Congratulations! You have made a telegram bot. In the next parts of this tutorial, we will see how we can improve our bot.

Go to the part 2 of this tutorial


Last updated on September 15, 2022
Tags: Python Django Telegram Chatbot