На эту тему создания ботов полноценных гайдов и материала не так много. Причина в том, что для опытного разработчика создание бота это нечто простое и само собой разумеющееся. А зря, ведь для начинающих любой пример может оказаться полезным потому что при создании первых проектов обычно в голове нет каких-либо четких инструкции и точных файловых структур.

Этой статьей я хотел сделать более доступной информацию которая явно бы пригодилась мне в далеком прошлом. Ну а для бывалых коллег это будет просто одна из реализаций на заметку. Статья не перевернет вас с ног наголову или что-то в этом роде. Я просто хочу чтобы после прочтения вы сказали "О, а можно ведь и так сделать".

Сегодня речь пойдет о соединения вашего серверного приложения на Django с Telegram ботом реализованным библиотекой pyTelegramBotApi на языке Python поэтому если вы уже знакомы с созданием ботов и знаете фреймворк то смело пропускайте начало.


Введем вас в курс дела

Эта статья больше ориентирована на новичков нежели на опытных разработчиков, поэтому небольшой экскурс и пара моментов после прочтения которого вы точно поймете дальнейшее содержание статьи.

  • pyTelegramBotApi - это лишь обертка вокруг Telegram API на языке Python, поэтому если перед вами стоит задача написать бота с использованием любой другой библиотеки, то вообще не беспокойтесь. Они все одинаковые... В прямом смысле, ведь их функционал не может выйти за рамки Telegram API.

  • API - это программный интерфейс предоставляющий доступ к данным или функционалу (вдруг кто не знает). В нашем случае это будут HTTP запросы в библиотеках Telegram ботов.

  • Python - язык программирования на котором написан Django.

  • Название нашей библиотеки может быть и pyTelegramBotApi, но в коде импортируется как import telebot. Не путайте ее с другой аналогичной библиотекой telebot которая тоже не плохая, но просто в этой статье речь пойдет не о ней.

Файловая структура проектов Django

Стандартная структура проекта Django в проводнике Visual Studio Code
Стандартная структура проекта Django в проводнике Visual Studio Code

В папке project, обычно одноименной с названием проекта, хранятся настройки и пути на уровне всего проекта. В приложениях, то-есть в папке application, прописываются модели, представления, маршруты, тесты, настройки административного сайта и вся остальная логика приложения. Этот список дополняется и изменяется в зависимости от предназначения приложения, формата получаемых и возвращаемых данных.

Структура ботов

Здесь уже нет какой-то конкретной структуры, а просто следуют общим методологиям, архитектурам и шаблонам проектирования. Функционал ботов составляют обработчики событий, команд, сообщений и типов контента. Сам код запускает внутренний сервер который принимает запросы, и взаимодействуют с сервером с помощью специального API токена и запросов.

А теперь когда всем всё ясно можно начинать!


Использование BaseCommand в Django

В данной статье мы рассмотрим взаимодействие с телеграмм ботов в проекте через команды в Django. Да, это значит что данный подход можно использовать и в других фреймворках написанных на других языках программирования аналогичным способом. Например через scripts в фреймворках на Node Js в файле package.json.

А вот как в Django приложения встраивают команды:

В папке приложения необходимо создать модуль под названием management, то-есть создать папку и в ней создать файл __init__.py. Внутри папки management нужно таким же образом создать модуль commands и уже внутри commands создать файл для будущей команды. Команду вы будете вызывать передавая название файла в файл точки входа в приложение поэтому название файла должно описывать действие команды.

Пример использования команды: python manage.py bot

В файле команды, в нашем случае я назвал его это - bot.py, нужно создать класс который наследует от BaseCommand и обязательно классу дать название Command как показано ниже:

from django.core.management.base import BaseCommand
from django.conf import settings

from telebot import TeleBot


# Объявление переменной бота
bot = TeleBot(settings.TELEGRAM_BOT_API_KEY, threaded=False)


# Название класса обязательно - "Command"
class Command(BaseCommand):
  	# Используется как описание команды обычно
    help = 'Just a command for launching a Telegram bot.'

    def handle(self, *args, **kwargs):
        bot.enable_save_next_step_handlers(delay=2) # Сохранение обработчиков
        bot.load_next_step_handlers()				# Загрузка обработчиков
        bot.infinity_polling()						# Бесконечный цикл бота

К слову не забываем что токен от бота это секретная информация, и ее стоит хранить в специальных файлах конфигурации вроде .env или settings.ini которые как правило скрыты от систем контроля версий. Объявить переменную можно в файле конфигурации проекта - settings.py, а чтобы достать токен можно воспользоваться импортом django.conf.settings. Дальше вы можете творить что угодно на свое усмотрение.

Что просто замечательно в таком подходе так это то, что вам не нужно подключаться к базе данных из вне или как-либо усложнять себе жизнь для соединения базы данных бота и вашего серверного приложения. Все данные вам будут доступны через импортирование моделей самого Django.

Конечно в данном подходе есть и свои минусы ведь всегда надежнее построить микросервисную архитектуру, хранить данные и давать к ним доступ через запросы REST API на том же Flask или FastAPI. Однако, в кодовой базе таких фреймворков как Django уже так много уже написанного функционала что иногда, если уместно, можно воспользоваться и таким подходом.

А теперь хотелось бы поблагодарить вас за прочтение этой статьи. Я надеюсь, что она добавит вам еще одну карту в колоду решений.

Это моя первая статья поэтому если есть какие-либо пожелания или вопросы, то оставляйте их в комментариях. На вопросы постараюсь ответить, а предложения рассмотреть в следующих статьях.

Комментарии (17)


  1. buriy
    08.09.2023 15:51
    +3

    Так и что с этим дальше делать? Как отправлять данные через бот? Как принимать данные и куда они попадут? Как асинхронщину включать?


    1. nurekeshka Автор
      08.09.2023 15:51

      С базой данных можно взаимодействовать через импортирование моделей Django. Там же можно создать модель Telegram пользователя и хранить их информацию. В одном из первых стартапов, я как-то писал декоратор который по ID Telegram пользователя в сообщении авторизовывал его связывая Telegram ID с его учетной записью в общей базе данных.


      1. buriy
        08.09.2023 15:51
        +1

        Так, что-то вы не ответили на вопросы, давайте по-порядку.
        Бот нужен, чтобы:
        1) передавать данные в телеграм.
        Как вы это будете делать из вьюшек django, если у вас бот -- в команде, работающей отдельным потоком? Никак, значит, вы облажались.
        Есть один обходной путь: вам придётся создавать на каждую отправку сообщения пользователю по записи в БД, и читать её из процесса с ботом. Тогда 5000 сообщений в секунду -- ваш предел, БД целиком забьётся. Но и 1000 сообщений в секунду уже будет много. И ещё будет поллинг сообщений из БД, а это задержки отправки и постоянная небольшая нагрузка на БД.
        2) получать данные из телеграма.
        Тут ок, работает.
        Ну сделаем мы django.setup() из питон файла вместо того, чтобы сложить его в django management commands. Что же поменялось? А ничего не поменялось, спокойно можно использовать его, только надо помнить, что вызовы Django ORM -- синхронные, и надо ставить асинхронный драйвер БД и поменьше вызывать бизнес-логику из models.py / managers.py / где там она у вас лежит. А иначе больше 100 одновременных пользователей не вывезете в этом одном процессе. Ну, наверное, на ваших проектах больше и не нужно никогда, и все счастливы.


        1. nurekeshka Автор
          08.09.2023 15:51

          Хорошо, давайте попробую ответить на ваши вопросы по порядку)

          1. Передавать данные в Telegram:
          Само содержимое файлов Django приложения никак не обязывает разработчика использовать представления фреймворка в целях обработки сообщений и вызова команд. Напротив, в том же файле представлений можно прописать обработчики сообщений используя классы и функции библиотек которые будут возвращать результат в виде отправки сообщения или редактирования уже существующего (например для меню).

          Во вторых, далеко не обязательно сохранять историю сообщений с пользователем ведь часто в этом нет необходимости, да и это противоречит главной политике самих чат ботов. По их изначальной задумке они должны вести частную переписку с пользователем и лишь обрабатывать запросы, а не хранить их. От того большой необходимости в постоянном редактировании базы данных нет.

          Разве, что хранение пользователей и информации о них, но чтобы оптимизировать эту операцию можно применить хэш таблицы где формат ключ - значение идеально подойдет под Telegram ID пользователя и его запись в базе данных (для которой кстати Telegram ID можно использовать в роли первичного ключа).

          2. Получать данные из Telegram:
          Здесь отчасти соглашусь, Django не предоставляет полноценной поддержки асинхронного программирования из-за чего приходится прибегать к использованию сторонних библиотек.

          С другой стороны Telegram боты не всегда имеют настолько большие нагрузки, от того в статье и было указано, что данный подход удобен лишь при разработке на скорость и относительно небольшой кодовой базе Telegram бота. Если ваша функциональность выходит за рамки предельной производительности данного подхода, то смело пишите отдельный проект и связывайте их любым известным вам способом.

          Если у вас возникнут еще вопросы по поводу статьи или данные выше ответы не полностью покрывают ваш интерес, то пишите в комментарии, я обязательно отвечу)


          1. buriy
            08.09.2023 15:51

            Что-то вижу какой-то ответ в стиле чатгпт.

            Вот у вас во views возникло какое-то действие пользователя, которое должно отправить сообщение в чат-бот. Что делать будете? Приложите код или псевдокод. То, что запуск бота можно положить в команду django для запуска через manage.py -- не решает никаких архитектурных проблем само по себе.


            1. nurekeshka Автор
              08.09.2023 15:51

              Возвращать ответ как и в обычном Telegram боте. ¯\_(ツ)_/¯
              Никаких архитектурных проблем и не было. Просто вам не нужно будет лишний раз заморачиваться с вынесением функционала при написании простого приложения, как и говорилось в статье.


          1. buriy
            08.09.2023 15:51

            Второй вопрос вынесу отдельно, раз уж ваш chatgpt его тоже проигнорировал.
            Что именно вы выигрываете, положив чат-бота именно в команду django?
            Чем хуже использовать django.setup() ? Вы ведь про него в курсе, я надеюсь?

            Пока что ваш пост напоминает мне "а знаете ли вы, что вы можете сложить любой фоновый процесс в команду django и потом запустить его через django manage.py mycommand ?".


            1. nurekeshka Автор
              08.09.2023 15:51

              Интересный вы человек однако. В статье так и сказано, что статья не должна перевернуть вас с ног наголову или решить какую-то проблему, а просто напомнить лишний раз что так тоже можно иногда делать.


  1. blablatdinov
    08.09.2023 15:51

    Круто! Мне нравится использовать django для телеграм ботов, как-то делал шаблон для связки django с pytelegrambotapi. Его можно найти на github: blablatidnov/django-telegram-bot-template


    1. nurekeshka Автор
      08.09.2023 15:51

      Да, мне тоже нравится, ведь данный подход удобен своей простотой и скоростью разработки. Однако, как и говорилось в статье, иногда можно выносить самого бота в отдельный проект, доступный лишь локально на сервере, в целях безопасности, чтобы не перегружать кодовую базу.


    1. buriy
      08.09.2023 15:51

      Эм, https://github.com/blablatidnov/django-telegram-bot-template выдаёт мне 404.
      Вот правильная ссылка: https://github.com/blablatdinov/django-telegram-bot-template
      Так, вы используете синхронный АПИ для бота, у вас библиотека телебот -- наоборот, работает только на отправку.
      Вы сохраняете все сообщения от бота и к боту в БД, как я и писал выше (но по другой причине).
      А для получения входящих сообщений вы подключаетесь через веб-хук.
      А как вы читаете пропущенные сообщения за то время, пока бот был выключен? Как вы избегаете двойного подключения к веб-хуку в разных потоках? И как вы к веб-хуку подключаетесь из приватной сети?


  1. proDream
    08.09.2023 15:51
    +1

    Это выглядит как "мы впихнём тебе монитор в монитор, что бы ты смотрел в монитор пока смотришь в монитор".

    Куда интереснее было бы почитать про передачу данных из Django в отдельно работающего, скажем на aiogram, бота.

    P.S. Насколько я знаю, тг не любит, когда токен бота используется в двух местах одновременно, тогда получается, что эти действия бессмысленны, если есть рабочий бот и надо что-то отправлять из Django?


    1. nurekeshka Автор
      08.09.2023 15:51

      Да, API Токены это опасная информация которая определенно не должна фигурировать лишний раз. Однако, если выносить Telegram бота в отдельный проект, на языке Python можно реализовать хранение через какую-нибудь ORM вроде SQLAlchemy, а получать информацию через HTTP запросы в формате JSON который будет обрабатывать написанная вами API на более простом фреймворке вроде Flask или FastAPI. В таком случае хранение токена в нескольких проектах не будет необходимостью.


  1. das_tier
    08.09.2023 15:51

    Автор путает пакеты с модулями.


    1. nurekeshka Автор
      08.09.2023 15:51

      Да, в действительности. Что-то я их перепутал когда писал статью. Объединенные модули называются пакетами.


  1. Maniac82
    08.09.2023 15:51

    Ну и? А как собрались запускать сервис джанго и бота одновременно? Или ваш вариант только для или/или?


    1. nurekeshka Автор
      08.09.2023 15:51

      Сервис Django обычно работает через настроенный WSGI на Gunicorn. А бот можно запускать вручную через консоль сервера или я вам советую более продвинутый метод, это написать админку для вашего бота и управлять его состоянием через подпроцессы реализованные через стандартную библиотеку subprocess