На эту тему создания ботов полноценных гайдов и материала не так много. Причина в том, что для опытного разработчика создание бота это нечто простое и само собой разумеющееся. А зря, ведь для начинающих любой пример может оказаться полезным потому что при создании первых проектов обычно в голове нет каких-либо четких инструкции и точных файловых структур.
Этой статьей я хотел сделать более доступной информацию которая явно бы пригодилась мне в далеком прошлом. Ну а для бывалых коллег это будет просто одна из реализаций на заметку. Статья не перевернет вас с ног наголову или что-то в этом роде. Я просто хочу чтобы после прочтения вы сказали "О, а можно ведь и так сделать".
Сегодня речь пойдет о соединения вашего серверного приложения на Django с Telegram ботом реализованным библиотекой pyTelegramBotApi на языке Python поэтому если вы уже знакомы с созданием ботов и знаете фреймворк то смело пропускайте начало.
Введем вас в курс дела
Эта статья больше ориентирована на новичков нежели на опытных разработчиков, поэтому небольшой экскурс и пара моментов после прочтения которого вы точно поймете дальнейшее содержание статьи.
pyTelegramBotApi - это лишь обертка вокруг Telegram API на языке Python, поэтому если перед вами стоит задача написать бота с использованием любой другой библиотеки, то вообще не беспокойтесь. Они все одинаковые... В прямом смысле, ведь их функционал не может выйти за рамки Telegram API.
API - это программный интерфейс предоставляющий доступ к данным или функционалу (вдруг кто не знает). В нашем случае это будут HTTP запросы в библиотеках Telegram ботов.
Python - язык программирования на котором написан Django.
Название нашей библиотеки может быть и pyTelegramBotApi, но в коде импортируется как
import telebot
. Не путайте ее с другой аналогичной библиотекой telebot которая тоже не плохая, но просто в этой статье речь пойдет не о ней.
Файловая структура проектов Django
В папке 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)
blablatdinov
08.09.2023 15:51Круто! Мне нравится использовать django для телеграм ботов, как-то делал шаблон для связки django с pytelegrambotapi. Его можно найти на github: blablatidnov/django-telegram-bot-template
nurekeshka Автор
08.09.2023 15:51Да, мне тоже нравится, ведь данный подход удобен своей простотой и скоростью разработки. Однако, как и говорилось в статье, иногда можно выносить самого бота в отдельный проект, доступный лишь локально на сервере, в целях безопасности, чтобы не перегружать кодовую базу.
buriy
08.09.2023 15:51Эм, https://github.com/blablatidnov/django-telegram-bot-template выдаёт мне 404.
Вот правильная ссылка: https://github.com/blablatdinov/django-telegram-bot-template
Так, вы используете синхронный АПИ для бота, у вас библиотека телебот -- наоборот, работает только на отправку.
Вы сохраняете все сообщения от бота и к боту в БД, как я и писал выше (но по другой причине).
А для получения входящих сообщений вы подключаетесь через веб-хук.
А как вы читаете пропущенные сообщения за то время, пока бот был выключен? Как вы избегаете двойного подключения к веб-хуку в разных потоках? И как вы к веб-хуку подключаетесь из приватной сети?
proDream
08.09.2023 15:51+1Это выглядит как "мы впихнём тебе монитор в монитор, что бы ты смотрел в монитор пока смотришь в монитор".
Куда интереснее было бы почитать про передачу данных из Django в отдельно работающего, скажем на aiogram, бота.
P.S. Насколько я знаю, тг не любит, когда токен бота используется в двух местах одновременно, тогда получается, что эти действия бессмысленны, если есть рабочий бот и надо что-то отправлять из Django?
nurekeshka Автор
08.09.2023 15:51Да, API Токены это опасная информация которая определенно не должна фигурировать лишний раз. Однако, если выносить Telegram бота в отдельный проект, на языке Python можно реализовать хранение через какую-нибудь ORM вроде SQLAlchemy, а получать информацию через HTTP запросы в формате JSON который будет обрабатывать написанная вами API на более простом фреймворке вроде Flask или FastAPI. В таком случае хранение токена в нескольких проектах не будет необходимостью.
das_tier
08.09.2023 15:51Автор путает пакеты с модулями.
nurekeshka Автор
08.09.2023 15:51Да, в действительности. Что-то я их перепутал когда писал статью. Объединенные модули называются пакетами.
Maniac82
08.09.2023 15:51Ну и? А как собрались запускать сервис джанго и бота одновременно? Или ваш вариант только для или/или?
nurekeshka Автор
08.09.2023 15:51Сервис Django обычно работает через настроенный WSGI на Gunicorn. А бот можно запускать вручную через консоль сервера или я вам советую более продвинутый метод, это написать админку для вашего бота и управлять его состоянием через подпроцессы реализованные через стандартную библиотеку
subprocess
buriy
Так и что с этим дальше делать? Как отправлять данные через бот? Как принимать данные и куда они попадут? Как асинхронщину включать?
nurekeshka Автор
С базой данных можно взаимодействовать через импортирование моделей Django. Там же можно создать модель Telegram пользователя и хранить их информацию. В одном из первых стартапов, я как-то писал декоратор который по ID Telegram пользователя в сообщении авторизовывал его связывая Telegram ID с его учетной записью в общей базе данных.
buriy
Так, что-то вы не ответили на вопросы, давайте по-порядку.
Бот нужен, чтобы:
1) передавать данные в телеграм.
Как вы это будете делать из вьюшек django, если у вас бот -- в команде, работающей отдельным потоком? Никак, значит, вы облажались.
Есть один обходной путь: вам придётся создавать на каждую отправку сообщения пользователю по записи в БД, и читать её из процесса с ботом. Тогда 5000 сообщений в секунду -- ваш предел, БД целиком забьётся. Но и 1000 сообщений в секунду уже будет много. И ещё будет поллинг сообщений из БД, а это задержки отправки и постоянная небольшая нагрузка на БД.
2) получать данные из телеграма.
Тут ок, работает.
Ну сделаем мы django.setup() из питон файла вместо того, чтобы сложить его в django management commands. Что же поменялось? А ничего не поменялось, спокойно можно использовать его, только надо помнить, что вызовы Django ORM -- синхронные, и надо ставить асинхронный драйвер БД и поменьше вызывать бизнес-логику из models.py / managers.py / где там она у вас лежит. А иначе больше 100 одновременных пользователей не вывезете в этом одном процессе. Ну, наверное, на ваших проектах больше и не нужно никогда, и все счастливы.
nurekeshka Автор
Хорошо, давайте попробую ответить на ваши вопросы по порядку)
1. Передавать данные в Telegram:
Само содержимое файлов Django приложения никак не обязывает разработчика использовать представления фреймворка в целях обработки сообщений и вызова команд. Напротив, в том же файле представлений можно прописать обработчики сообщений используя классы и функции библиотек которые будут возвращать результат в виде отправки сообщения или редактирования уже существующего (например для меню).
Во вторых, далеко не обязательно сохранять историю сообщений с пользователем ведь часто в этом нет необходимости, да и это противоречит главной политике самих чат ботов. По их изначальной задумке они должны вести частную переписку с пользователем и лишь обрабатывать запросы, а не хранить их. От того большой необходимости в постоянном редактировании базы данных нет.
Разве, что хранение пользователей и информации о них, но чтобы оптимизировать эту операцию можно применить хэш таблицы где формат ключ - значение идеально подойдет под Telegram ID пользователя и его запись в базе данных (для которой кстати Telegram ID можно использовать в роли первичного ключа).
2. Получать данные из Telegram:
Здесь отчасти соглашусь, Django не предоставляет полноценной поддержки асинхронного программирования из-за чего приходится прибегать к использованию сторонних библиотек.
С другой стороны Telegram боты не всегда имеют настолько большие нагрузки, от того в статье и было указано, что данный подход удобен лишь при разработке на скорость и относительно небольшой кодовой базе Telegram бота. Если ваша функциональность выходит за рамки предельной производительности данного подхода, то смело пишите отдельный проект и связывайте их любым известным вам способом.
Если у вас возникнут еще вопросы по поводу статьи или данные выше ответы не полностью покрывают ваш интерес, то пишите в комментарии, я обязательно отвечу)
buriy
Что-то вижу какой-то ответ в стиле чатгпт.
Вот у вас во views возникло какое-то действие пользователя, которое должно отправить сообщение в чат-бот. Что делать будете? Приложите код или псевдокод. То, что запуск бота можно положить в команду django для запуска через manage.py -- не решает никаких архитектурных проблем само по себе.
nurekeshka Автор
Возвращать ответ как и в обычном Telegram боте. ¯\_(ツ)_/¯
Никаких архитектурных проблем и не было. Просто вам не нужно будет лишний раз заморачиваться с вынесением функционала при написании простого приложения, как и говорилось в статье.
buriy
Второй вопрос вынесу отдельно, раз уж ваш chatgpt его тоже проигнорировал.
Что именно вы выигрываете, положив чат-бота именно в команду django?
Чем хуже использовать django.setup() ? Вы ведь про него в курсе, я надеюсь?
Пока что ваш пост напоминает мне "а знаете ли вы, что вы можете сложить любой фоновый процесс в команду django и потом запустить его через django manage.py mycommand ?".
nurekeshka Автор
Интересный вы человек однако. В статье так и сказано, что статья не должна перевернуть вас с ног наголову или решить какую-то проблему, а просто напомнить лишний раз что так тоже можно иногда делать.