Telegram Django Bot in a couple of lines

Hi all! My name is Alexander Aleskin, and I have participated in the creation of dozens of Telegram bots. Quite often, I encountered the same problems during development due to a lack of tools: there are no web forms in Telegram -it is impossible to request several attributes from the user at the same time; localization support; taking into account the state of the user to display information; similar actions occur on different data types, but you need to write separate handlers for each type, etc.

In fact, all these problems are quite easily solved one by one. However, when expanding the functionality of the bot, this inevitably leads to duplication of code to one degree or another, as well as confusion among the functions.

This article will talk about tools that allow you to standardize the development of bots. Most of them are widely used in web development and are present in Django (or Django Rest Framework). These tools are customized for use with Telegram bots in the library Telegram Django Botwhich will be discussed below.

Telegram-Django-Bot library

Telegram-Django-Bot is based on the use of the following two libraries:

  1. python-telegram-bot – a library for interacting with the Telegram API (22k ​​stars on github).

  2. Django – a framework for developing web services (in the TOP-10 frameworks in the world).

In fact, Telegram-Django-Bot tightly ties these libraries together and provides a similar Django Rest Framework data management tool.

Viewsets and Forms

The key entity of Telegram-Django-Bot is TelegramViewSet – an abstract class for managing database models (tables) described through Django ORM. This class allows you to create, display, delete and display entity elements. As in Django Rest Framework Viewsets, you can add your own methods for processing data if necessary. However, unlike the web counterpart, this class generates a response not in the form of json or xml, but in natural language in the form of text and buttons for further display to the user in the Telegram. That is, it combines data processing and the formation of an interface for users.

This approach templates data processing and communication between the bot and the user. As a result, this allows you to significantly reduce the amount of code.

The easiest way to demonstrate this approach is with an example telegram_django_bot_template. This repository was created as a template for creating bots and contains small examples of how to use it.

For a more detailed and real-life example, consider a live bot @Disk_Drive_Bot (bot source code). The main purpose of the bot is to store files in Telegram, just like Yandex disk or Google drive does.

So, for example, in the template code, management of some BotMenuElem entity (a table in the database) is set in 40 lines of code:

class BotMenuElemViewSet(TelegramViewSet):
    viewset_name="BotMenuElem"    # Название Вьюсета для отображения
    model_form = BotMenuElemForm   # Форма, где указываются поля для забора от клиента и их валидация. Дочерний класс от Django.forms.ModelForm
    queryset = BotMenuElem.objects.all()  # указываем, что за модель базы данных
    foreign_filter_amount = 1  # количество переменных окружения (дополнительный атрибут, детали в документации)

    prechoice_fields_values = {  # Часто используемые варианты значений атрибутов или просто формат отображения некоторых значений атрибутов
        'is_visable': (  # варианты для переменной is_visable в формате (значение, текст)
            (True, '👁 Visable'),
            (False, '🚫 Disabled'),
        )
    }

    def get_queryset(self):  # переопределение вспомогательной функции для учета бизнес логики
        queryset = super().get_queryset()
        if self.foreign_filters[0]:  # хотим, чтобы если указана переменная окружения, то отображали только те элементы, в которых поле команда начинались со значения переменной окружения
            queryset = queryset.filter(command__contains=self.foreign_filters[0])
        return queryset

    def create(self, field=None, value=None, initial_data=None):  # переопределение функции создания элемента
        initial_data = {  # просто хотим указать стартовые значения для некоторых атрибутов
            'is_visable': True,
            'callbacks_db': '[]',
            'buttons_db': '[]',
        }
        return super().create(field, value, initial_data)

    def show_list(self, page=0, per_page=10, columns=1):  # переопределение функции отображения списка элементов
        reply_action, (mess, buttons) = super().show_list(page, per_page, columns)
        buttons += [   # к дефолтному отображению элементов в виде текста и кнопок добавляем кнопки «создание» и «назад»
            [InlineKeyboardButtonDJ(
                text=_('➕ Add'),
                callback_data=self.gm_callback_data('create')
            )],
            [InlineKeyboardButtonDJ(
                text=_('🔙 Back'),
                callback_data=settings.TELEGRAM_BOT_MAIN_MENU_CALLBACK
            )],
        ]
        return reply_action, (mess, buttons)  # для ответа пользователю возвращаем формат ответа и атрибуты ответа (в данном случае текст сообщения и кнопки)

And these 40 lines allow you to create, modify, delete and view the list of elements BotMenuElem in the bot. As a result of this description BotMenuElemViewSet we get the necessary business logic and user interface in the bot:

The magic lies in the fact that the basic logic of interaction and display of elements is described in the parent class TelegramViewSet. It contains the following standard features:

Method

Description

create

Model creation

change

Attribute changes

delete

Deleting a Model

show_elem

Element Display

show_list

Displaying a List of Items

For the convenience of customizing business logic, these functions call helper methods inside. So, for example, in BotMenuElemViewSet only one helper function is redefined get_queryset according to the specifics of the context.

Among the features TelegramViewSet are of particular interest create And update, because they do a lot of work with forms (a structure of several attributes that users specify). Since Telegram does not know how to work with forms, a consistent approach is used here (as well as ConversationHandler: first users specify one field, then the second, etc.:

To do this, as in Django, a data form is created, which is indicated in the BotMenuElemViewSet:

# base/forms.py
from telegram_django_bot import forms as td_forms

class BotMenuElemForm(td_forms.TelegramModelForm):
    class Meta:
        model = BotMenuElem
        fields = ['command', "is_visable", "callbacks_db", "message", "buttons_db"]


# base/views.py
class BotMenuElemViewSet(TelegramViewSet): 
    model_form = BotMenuElemForm
    …

Routing

The structure of the code is one of the important components for maintaining the project. To do this, Django suggests splitting the project into applications (apps), each of which is responsible for its own task (for example, payment or integration with some other service).

Telegram-Django-Bot makes it possible to use this mechanism when developing bots through a universal handler RouterCallbackMessageCommandHandler. This class must be added to the list of Python-Telegram-Bot web server handlers:

from telegram_django_bot.tg_dj_bot import TG_DJ_Bot
from telegram_django_bot.routing import RouterCallbackMessageCommandHandler

# look in the template: https://github.com/alexanderaleskin/telergam_django_bot_template/blob/main/run_bot.py#L19
#  in 13.x version of Python-Telegram-Bot:
updater = Updater(bot=TG_DJ_Bot(settings.TELEGRAM_TOKEN))
updater.dispatcher.add_handler(RouterCallbackMessageCommandHandler())

#  or in 20.x version :
bot = TG_DJ_Bot(settings.TELEGRAM_TOKEN)
application = ApplicationBuilder().bot(bot).build()
application.add_handler(RouterCallbackMessageCommandHandler())

After that, you can write paths to specific handlers (endpoints) and viewsets in the standard Django notation:

# файл bot_conf/utrls.py (основной)
urlpatterns = [
    re_path('', include(('base.utrls', 'base'), namespace="base")),  # подключаем пути из модуля base
]


# файл base/utrls.py
from django.urls import re_path, include
from .views import start, BotMenuElemViewSet, UserViewSet  # импорт veiwset и обычной функции  


urlpatterns = [  # добавляем пути
    re_path('start', start, name="start"),
    re_path('main_menu', start, name="start"),

    re_path('sb/', BotMenuElemViewSet, name="BotMenuElemViewSet"),
    re_path('us/', UserViewSet, name="UserViewSet"),
]
# Теперь по нажатию на кнопку с callback_data="sb/<suffix>" будут вызываться методы BotMenuElemViewSet

As in Django, you must specify the main request routing file in the project settings (in settings.py fall):

TELEGRAM_ROOT_UTRLCONF = 'bot_conf.utrls'

As a result of such manipulations, the application can be divided into several folders, each of which stores its own independent business logic (and if you write more than one bot, but several, then this approach makes it easy to connect individual modules).

Additional Library Tools

The library also has a number of tools that are often needed in production bots:

  1. Built-in models in the database:

    1. actionlog – stores user actions (clicking on buttons, clicking on commands, etc.). This model allows you to analyze the activity and actions of users;

    2. TeleDeepLink – stores information about where the users came from (if the user went to the bot via deeplink). This information helps to build a sales funnel and track the effectiveness of advertising;

    3. BotMenuElem – quite often there are blocks in the bot where static information is indicated (for example, a message about support contacts, a knowledge base or a start message). This model makes it possible to create such menu blocks in the bot, while specifying links to other elements or functions/viewsets through buttons. This model is used in the template example (standard creation is assumed through the admin panel).

    4. trigger – when certain events occur (looks in the ActionLog), sends certain messages to users (BotMenuElem). So, for example, you can send messages to users who started the bot and did not continue interaction, or ask users who use more than 7 days, for example, to give feedback on interaction with the bot;

  2. Additional functions:

    1. telegram_django_bot.utils.handler_decor – a wrapper for its handlers (Usage example; viewsets are automatically wrapped in a decorator). Allows you to log user actions, catch and log all errors, and process user creation;

    2. telegram_django_bot.utils.CalendarPagination– a class for generating a calendar in the bot menu in the form of active buttons (you can go to other sections by clicking);

    3. Telegram_django_bot.tg_dj_bot.TG_DJ_Bot – a child class telegram.Bot, which adds a number of functions, including task_send_message_handler for safely sending messages to a group of users (handles errors when sending messages).

  3. Localization – Django’s localization tools are used.

Conclusion

Telegram-Django-Bot speeds up the creation of Telegram bots by standardizing actions. An important tool of the library is TelegramViewSet class, the use of which allows you to describe in a few lines how to process and display data to the user.

You can see how it works, and you can also run it yourself using examples telegram_django_bot_template And Disk_Drive_Bot.

Your feedback is very interesting, as well as suggestions for improvement. And requests to the repository are especially welcome!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *