Чат-боты — это программное обеспечение, которое имитирует общение с пользователями. Сейчас их используют для совершенно любых целей: это может быть простой справочник или сложный сервис с интеграцией в CRM и платежные системы.
Ботов создают для разных мессенджеров. У каждой платформы — свои правила и возможности: где-то нет оплаты, где-то нет гибких клавиатур. В статье говорим о Telegram: он удобен, у него простой API и активная аудитория.
В этой статье мы расскажем:
aiogram
— популярной библиотеки для разработки чат-ботов на Python.Сейчас всё более интенсивно набирают популярность конструкторы — это сервисы, где бота собирают через понятный интерфейс «drag-and-drop». Для работы с ними совсем не нужно знать языки программирования. Можно просто выстраивать логику блоками, как в детской игре.
Существуют недостатки подобных решений:
Ограниченная функциональность. Большинство конструкторов предлагает пользователям лишь часть функционала Telegram API. Например, интеграция со сторонними сервисами через HTTP-запросы есть не у всех. А те, кто имеет такой функционал, предлагают завышенные тарифы.
Одинаковые сценарии. Минимальная гибкость конструкторов приводит к тому, что все боты на конструкторе выглядят и работают одинаково.
Зависимость от сервиса. Если платформа «ляжет» или внезапно подорожает, придется переносить бота.
Конструкторы весьма удобны для прототипов и простых сценариев — приветственное сообщение, ответ на пару вопросов, сбор контактных данных. Более сложные алгоритмы требуют знания переменных, логики обработки данных и функционала Telegram API. Даже в конструкторе нужно понимать, как обратиться к пользователю по имени, как работают inline-клавиатуры и обработка состояний.
Зачастую бесплатные версии таких сервисов имеют ограничения: добавляют свои рекламные сообщения, запрещают интеграцию с нужными API или накладывают лимиты на количество пользователей. Это снижает лояльность аудитории, а сам чат-бот может остаться неиспользуемым. Платные версии в перспективе обходятся дороже, чем разработка бота с нуля и его размещение на сервере.
Если чат-бот должен решать реальные бизнес-задачи, автоматизировать процессы или работать с базами данных, конструктора зачастую будет недостаточно. В таком случае проще обратиться к разработчику: он сразу заложит гибкую архитектуру, подберет оптимальные технологии и избавит от технических ограничений, которые могут стать проблемой при масштабировании проекта. В качестве технического задания для разработчика можно использовать алгоритм из конструктора, если он уже есть.
Теперь обсудим создание чат-бота в Telegram на языке программирования Python. Для работы понадобятся базовые знания о переменных, условных конструкциях, циклах и функциях в Python.
Для разработки чат-ботов чаще всего используют фреймворк — это набор инструментов, библиотек и готовых решений, которые помогают разрабатывать программное обеспечение. Можно использовать чистый Telegram API и реализовывать функционал на HTTP-запросах. Но всё это — тысячи строк кода даже для простых задач.
В статье будем использовать фреймворк aiogram
— один из самых популярных фреймворков для разработки чат-ботов в Telegram на Python.
Использовать виртуальное окружение в любом проекте на Python считается хорошим тоном. К тому же, чат-боты зачастую устанавливаются на облачных серверах, где потребуется установка зависимостей. Удобно экспортировать список зависимостей, принадлежащих конкретному проекту, поможет виртуальное окружение.
О создании виртуального окружения мы рассказывали в нашей инструкции.
Установите саму библиотеку фреймворка через pip
:
pip install aiogram
Добавьте библиотеку для работы с переменными окружения. Мы рекомендуем использовать этот способ передачи токенов в любом проекте, даже если вы не планируете выкладывать его в открытый доступ. Это снижает риск случайной публикации конфиденциальных данных.
pip install python-dotenv
Далее можете установить другие зависимости по необходимости.
Несложный этап, но зачастую именно он вызывает затруднения. Нам самим нужно обратиться к чат-боту, который создаст и пришлет токен нашего проекта.
Перейдите в Телеграм в диалог с @BotFather и нажмите кнопку «Начать».
Бот выдаст приветственное сообщение. Отправьте следующую команду:
/newbot
Бот спросит название будущего бота, которое будет отображаться в списке чатов пользователя.
Далее нужно ввести имя пользователя (username) бота. Оно должно быть уникальным, чтобы любой пользователь смог найти его в поиске Телеграма. Обратите внимание, что имя пользователя должно оканчиваться на «bot».
Всё готово: BotFather создал чат-бота, присвоил ему имя пользователя и токен.
Внимание! Всегда храните токен в секрете. По нему любой человек сможет писать сообщения от лица вашего чат-бота. Если токен мог быть скомпрометирован, немедленно замените его в BotFather.
Перейдите в чат с только что созданным ботом и нажмите на кнопку «Изм.» (1), чтобы поменять фотографию (2), приветствие (3), описание (4) и предложение команд по умолчанию (5).
Далее создайте файл окружение с названием .env
(файл не имеет имени, а название содержит только расширение). Добавьте в файл строку в формате:
BOT_TOKEN = полученный токен
В Linux и MacOS для быстрой записи можно использовать команду:
echo "BOT_TOKEN = полученный токен" > .env
apps
В рабочей директории создайте файл main.py
— тут будет вестись основная работа.
Импортируйте в файл тестовый код, который отправит пользователю приветственное сообщение при получении команды /start
.
import asyncio # Библиотека для работы с асинхронным кодом
import os # Модуль для работы с переменными окружения
from dotenv import load_dotenv # Функция для загрузки переменных окружения из файла .env
from aiogram import Bot, Dispatcher, Router # Импорт необходимых классов из библиотеки aiogram
from aiogram.types import Message # Импорт класса Message для обработки входящих сообщений
from aiogram.filters import CommandStart # Импорт фильтра для обработки команды /start
# Создаем роутер, который будет содержать обработчики сообщений
router = Router()
# Загружаем переменные окружения из файла .env
load_dotenv()
# Обработчик команды /start
@router.message(CommandStart()) # Фильтр, который проверяет, является ли сообщение командой /start
async def cmd_start(message: Message) -> None:
# Получаем имя пользователя (first_name) и фамилию (last_name), если она есть
first_name = message.from_user.first_name
last_name = message.from_user.last_name or "" # Если фамилии нет, подставляем пустую строку
# Отправляем пользователю приветственное сообщение
await message.answer(f"Привет, {first_name} {last_name}!")
# Главная асинхронная функция, которая запускает бота
async def main():
# Создаем объект бота, используя токен из переменных окружения
bot = Bot(token=os.getenv("BOT_TOKEN"))
# Создаем диспетчер для обработки сообщений
dp = Dispatcher()
# Подключаем роутер с обработчиками команд
dp.include_router(router)
# Запускаем бота в режиме опроса (polling)
await dp.start_polling(bot)
# Если этот скрипт запускается напрямую (а не импортируется как модуль),
# то запускаем асинхронную функцию main()
if __name__ == "__main__":
asyncio.run(main())
В примере основные компоненты закомментированы — изучите эти краткие пояснения, если не желаете вникать более глубоко. Можно продолжить узнавать функционал библиотеки aiogram
, приняв использование Dispatcher
, Router
как данность. Далее в статье разберем функционал этих компонентов.
Для начала любого проекта можно использовать эту готовую структуру: по ходу разработки будут добавляться новые обработчики, клавиатуры, состояния.
Запустите созданный скрипт:
python main.py
Теперь можно проверить открыть диалог с ботом и начать его:
Для создания чат-бота в Телеграм необходимое знание немногих компонентов и функций.
В этом разделе будет использоваться версия aiogram v3.x, выпущенная 1 сентября 2023 года. Подойдет любая версия, начинающаяся с тройки. Проекты с версиями ниже встречаются в работе, но aiogram 2.x считается устаревшим.
Bot
— это ваш интерфейс к Telegram API. Через него вы отправляете сообщения, картинки и другие данные пользователям.
bot = Bot(token=os.getenv("TOKEN"))
Токен, разумеется, можно передать напрямую в аргументы класса Bot
, без использования переменных окружения. Но мы рекомендуем сразу привыкать к именно такому способу передачи токена бота, во избежание случайного попадания токена в открытый доступ.
Процесс получения токена описан в третьем шаге предыдущего раздела.
Dispatcher
— «сердце» фреймворка. Он принимает апдейты (входящие сообщения и события) и передает их нужным обработчикам. В v3 используется новая схема с Router
(см. ниже), но Dispatcher
остается для инициализации и запуска.
dp = Dispatcher()
В aiogram v3 обработчики группируют в Router
. Это отдельная сущность, которая хранит логику — хендлеры команд, сообщений, колбэков и т.д.
from aiogram import Router
router = Router()
Потом разработчики регистрируют хендлеры внутри роутера и добавляют router
в экземпляр Dispatcher
:
dp.include_router(router)
Знание следующих инструментов и умение их использовать позволит разработчику реализовать большинство алгоритмов чат-ботов.
Самый популярный сценарий — реагировать на команды вроде /start
или /help
.
from aiogram import F
from aiogram.types import Message
@router.message(F.text == "/start")
async def cmd_start(message: Message):
await message.answer("Привет! Я бот на aiogram.")
F.text == "/start"
— это новый способ фильтрации сообщений через объект F
.
message.answer(...)
отправляет ответ пользователю.
Для реакции на любое сообщение уберите фильтр или сделайте другой:
@router.message()
async def echo_all(message: Message):
await message.answer(f"Вы написали: {message.text}")
В этом примере бот возвращает текст, который прислал пользователь.
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
inline_kb = InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="Нажми!", callback_data="press_button")]
]
)
@router.message(F.text == "/buttons")
async def show_buttons(message: Message):
await message.answer("Мои кнопки:", reply_markup=inline_kb)
Когда пользователь нажмет кнопку, бот получит callback_data="press_button"
— обработать это можно отдельно:
from aiogram.types import CallbackQuery
@router.callback_query(F.data == "press_button")
async def handle_press_button(callback: CallbackQuery):
await callback.message.answer("Вы нажали кнопку!")
await callback.answer() # нужно, чтобы убрать "часики" в верхней части диалога
Визуально в чате inline-кнопки выглядят так:
Обычные кнопки отличаются от inline-кнопок тем, что появляются вместо клавиатуры. Пользователь видит сразу список готовых вариантов для ответа. Отслеживать такие кнопки надо через текст сообщения, а не через callback_data
.
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardRemove
reply_kb = ReplyKeyboardMarkup(
keyboard=[
[
KeyboardButton(text="Посмотреть меню"),
KeyboardButton(text="Оформить заказ")
]
],
resize_keyboard=True # Кнопки будут автоматически подгоняться по размеру
)
@router.message(F.text == "/start")
async def start_cmd(message: Message):
await message.answer(
"Добро пожаловать! Выберите действие:",
reply_markup=reply_kb
)
@router.message(F.text == "Посмотреть меню")
async def show_menu(message: Message):
await message.answer("У нас есть пицца и напитки.")
@router.message(F.text == "Оформить заказ")
async def make_order(message: Message):
await message.answer("Что будем заказывать?")
@router.message(F.text == "/hide")
async def hide_keyboard(message: Message):
await message.answer("Убираем клавиатуру", reply_markup=ReplyKeyboardRemove())
Filters (фильтры) помогают гибко определять, какие сообщения ловить. Например, можно составлять кастомные фильтры.
from aiogram.filters import Filter
class IsAdmin(Filter):
def __init__(self, admin_id: int):
self.admin_id = admin_id
async def __call__(self, message: Message) -> bool:
return message.from_user.id == self.admin_id
@router.message(IsAdmin(admin_id=12345678), F.text == "/admin")
async def admin_cmd(message: Message):
await message.answer("Привет, админ! У тебя есть особые права.")
Middlewares (промежуточные слои) вставляются между входящим запросом и хендлером. Можно перехватывать сообщения, редактировать их, проверять права доступа и т.д.
class LoggingMiddleware(BaseMiddleware):
async def __call__(self, handler, event, data):
if isinstance(event, Message):
logging.info(f"[Message] from {event.from_user.id}: {event.text}")
elif isinstance(event, CallbackQuery):
logging.info(f"[CallbackQuery] from {event.from_user.id}: {event.data}")
# Передаём обработку дальше
return await handler(event, data)
async def main():
load_dotenv()
logging.basicConfig(level=logging.INFO)
bot = Bot(token=os.getenv("BOT_TOKEN"))
dp = Dispatcher()
dp.update.middleware(LoggingMiddleware())
dp.include_router(router)
await dp.start_polling(bot)
Aiogram 3 поддерживает машину состояний (Finite State Machine). Это удобно, когда нужно пошагово собирать данные (например, регистрацию пользователя). FSM — это основной инструмент реализации ветвлений.
Представим, что перед нами стоит задача разработать чат-бот для заказа пиццы. Нам нужно узнать у пользователя размер пиццы и адрес доставки. Сделать это нужно последовательно: нельзя сразу перепрыгнуть на второй шаг без первого. Причем нужно сохранять данные, которые отправляет пользователь, на всех этапах работы с чат-ботом.
Объявите состояния:
from aiogram.fsm.state import State, StatesGroup
class OrderPizza(StatesGroup):
waiting_for_size = State()
waiting_for_address = State()
Переключайтесь между состояниями:
from aiogram.fsm.context import FSMContext
@router.message(F.text == "/order")
async def cmd_order(message: Message, state: FSMContext):
# Создаём inline-кнопки для выбора размера
size_keyboard = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Большая", callback_data="size_big"),
InlineKeyboardButton(text="Средняя", callback_data="size_medium"),
InlineKeyboardButton(text="Маленькая", callback_data="size_small")
]
]
)
await message.answer(
"Какую пиццу хотите? Нажмите на одну из кнопок:",
reply_markup=size_keyboard
)
# Устанавливаем состояние, при котором мы ждём, что пользователь выберет размер
await state.set_state(OrderPizza.waiting_for_size)
# Шаг 2. Обрабатываем нажатие на кнопку с выбором размера
@router.callback_query(OrderPizza.waiting_for_size, F.data.startswith("size_"))
async def choose_size_callback(callback: CallbackQuery, state: FSMContext):
# Callback data может быть size_big / size_medium / size_small
size_data = callback.data.split("_")[1] # например, "big", "medium" или "small"
# Сохраняем выбранный размер пиццы во временное хранилище состояния
await state.update_data(pizza_size=size_data)
# Подтверждаем, что кнопку нажали (убираем "часики" в интерфейсе Telegram)
await callback.answer()
await callback.message.answer("Укажите адрес доставки:")
await state.set_state(OrderPizza.waiting_for_address)
# Шаг 2а. Если пользователь напишет сообщение вместо нажатия кнопки (в состоянии waiting_for_size),
# можно отреагировать отдельно. Например, попросим всё же нажать кнопку.
@router.message(OrderPizza.waiting_for_size)
async def handle_text_during_waiting_for_size(message: Message, state: FSMContext):
await message.answer(
"Пожалуйста, выберите размер пиццы, используя кнопки выше. "
"Без этого мы не можем продолжить."
)
# Шаг 3. Пользователь присылает адрес
@router.message(OrderPizza.waiting_for_address)
async def set_address(message: Message, state: FSMContext):
address = message.text
user_data = await state.get_data()
pizza_size = user_data["pizza_size"]
size_text = {
"big": "большую",
"medium": "среднюю",
"small": "маленькую"
}.get(pizza_size, "неопределённую")
await message.answer(f"Вы заказали {size_text} пиццу по адресу: {address}")
# Очищаем состояние -- сценарий завершён
await state.clear()
Обратите внимание, как хранится информация в промежуточных состояниях — используется внутреннее хранилище, принадлежащее конкретному пользователю, без использования базы данных.
Так пользователь идет по цепочке вопросов, а в конце можно отправить информацию о заказе на внутренний API.
Мы подготовили таблицу для универсального подбора конфигурации в зависимости от задач чат-бота. Поля в первой колонке кликабельны — там ссылки на создание сервера выбранной конфигурации в панели управления Timeweb Cloud.
Конфигурация |
Стоимость облачного сервера + публичный IP |
Стоимость размещения через Dockerfile в Timeweb Cloud Apps |
Задачи |
Вывод |
300 ₽/мес |
188 ₽/мес |
Простые текстовые боты, легкие aiogram-боты без БД, inline-кнопки, парсеры по расписанию |
Только для самых базовых ботов без лишней нагрузки |
|
550 ₽/мес |
355 ₽/мес |
Боты с небольшой базой (SQLite, PostgreSQL), API-интеграции, простые аудиоботы, парсеры |
Стандартный сервер для небольшого коммерческого бота |
|
850 ₽/мес |
555 ₽/мес |
Боты с активной БД (PostgreSQL, MySQL), чат-боты с анализом сообщений, легкие торговые боты |
Подходит для средних проектов с активной базой данных |
|
1000 ₽/мес |
655 ₽/мес |
Боты с несколькими модулями, обработка изображений (QR-коды, ресайз), API-серверы с ботами, OpenAI API |
Универсальный сервер для большинства ботов |
|
1650 ₽/мес |
1100 ₽/мес |
Многопользовательские боты (до 10 000+), работа с Telegram API, распознавание аудио, обработка изображений и видео |
Оптимально для ботов с высокой нагрузкой |
|
2800 ₽/мес |
1833 ₽/мес |
Боты с AI-функциями, мультиязычные боты, торговые боты для криптобирж |
Для сложных задач, но без GPU |
|
4200 ₽/мес |
2777 ₽/мес |
AI-боты с ML, мультитрединг с Redis и MongoDB, боты с видеообработкой в реальном времени |
Максимальные возможности, но для AI лучше использовать GPU-сервер |
Разберем два основных способа деплоя (развертывания проекта на сервере)
Этот способ не требует никаких знаний системного администрирования, весь деплой производится автоматически. К тому же, этот способ позволяет сэкономить (стоимость минимальной конфигурации — 188 рублей в месяц). Следуйте этим шагам:
requirements.txt
. Здесь пригождается использование виртуального окружения, чтобы не тянуть библиотеки со всего компьютера. Выполните в Терминале в директории проекта:pip freeze > requirements.txt
Добавьте в директорию проекта новый файл для деплоя — Dockerfile. Расширения у файла нет, только имя. Вставьте следующее содержимое:
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 9999
CMD ["python", "main.py"]
Создайте Git-репозиторий и выложите его на GitHub. Для этого можно воспользоваться минимальным набором команд git из нашей статьи, выполнив эти команды последовательно.
Не забудьте добавить файл переменных окружения .env
в .gitignore
— он не должен попасть в общий доступ.
Осталось немного: перейдите в панель управления Timeweb Cloud, выберите таб «Apps», нажмите кнопку «Добавить» или «Создать». Во вкладке «Тип» перейдите на таб «Dockerfile».
Привяжите свой аккаунт GitHub или подключите Git-репозиторий по URL.
Выберите репозиторий из списка после того, как привязали свой аккаунт с GitHub.
Выберите конфигурацию. В Timeweb Cloud Apps доступна конфигурация CPU 1 x 3.3ГГц, RAM 1Гб, NVMe 15Гб стоимостью 188 рублей в месяц. Она отлично подходит для простых текстовых ботов, проектов с небольшими inline-клавиатурами, базовой FSM-логикой, не требовательными API-запросами, работы с SQLite или легковесными JSON-файлами. Такая конфигурация может обрабатывать до 50-100 пользователей в минуту.
Добавьте токен в переменные окружения. Во вкладке «5. Настройка приложения» нажмите на кнопку «+ Добавить». Ключ — BOT_TOKEN
, а в значение вставьте токен, полученный из BotFather.
Запустите деплой и дождитесь его окончания. После этого бот будет запущен.
Выгрузите все зависимости проекта в файл requirements.txt
. Выполните следующую команду в Терминале, находясь в директории проекта:
pip freeze > requirements.txt
Создайте облачный сервер в панели Timeweb Cloud с нужной конфигурацией и ОС Ubuntu.
Перенесите файлы проекта в директорию на удаленном сервере. Проще всего это сделать с помощью утилиты rsync
, если используете Ubuntu / MacOS:
rsync -av --exclude="venv" --exclude=".idea" --exclude=".git" ./ root@176.53.160.13:/root/project
Не забудьте вставить IP сервера и исправить конечную директорию.
Для пользователей на Windows — используйте отправку файлов через FileZilla. Об этом рассказано в нашей документации.
Подключитесь к серверу по SSH. Если возникают сложности, воспользуйтесь информацией из нашей документации по UNIX-системам.
Установите пакет для виртуального окружения:
sudo apt install python3.10-venv
python -m venv venv
ource venv/bin/activate
pip install -r requirements.txt
Проверьте работоспособность бота — запустите его с помощью:
python main.py
Если всё работает, то переходите к следующему шагу.
Создайте юнит-файл /etc/systemd/system/telegram-bot.service
:
sudo nano /etc/systemd/system/telegram-bot.service
Добавьте в файл следующее содержимое содержимое:
[Unit]
Description=Telegram Bot Service
After=network.target
[Service]
User=root
WorkingDirectory=/root/project
ExecStart=/root/proj/venv/bin/python /root/proj/main.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Измените значения следующих переменных:
WorkingDirectory
— директория проектаExecStart
— команда для запуска чат-бота в формате <интерпретатор> <полный путь до файла>
При использовании виртуального окружения интерпретатор находится по тому же пути, как в примере. Если работаете без venv, то используйте /usr/local/bin/python3
.
Перезапустите systemd
и включите службу:
sudo systemctl daemon-reload
sudo systemctl enable telegram-bot.service
sudo systemctl start telegram-bot.service
Проверьте статус службы и просмотрите логи, при необходимости:
sudo systemctl status telegram-bot.service
При нормальном запуске в поле «Active» находится значение «active (running)».
Просмотр логов бота:
sudo journalctl -u telegram-bot.service -f
При необходимости, управляйте службой следующими командами:
Перезапуск бота
sudo systemctl restart telegram-bot.service
Остановка бота
sudo systemctl stop telegram-bot.service
Удаление службы (если нужно)
sudo systemctl disable telegram-bot.service
sudo rm /etc/systemd/system/telegram-bot.service
sudo systemctl daemon-reload
Надежное облако для ваших проектов
Создание чат-бота в Telegram на Python — задача, которую можно решить даже без опыта в программировании, используя конструкторы.
Если вам нужна гибкость и более широкие возможности, лучше освоить фреймворк aiogram и развернуть собственный проект. Вы сразу получите контроль над кодом, сможете легко дорабатывать функционал, управлять интеграциями и не зависеть от ограничений платных тарифов. Для запуска бота на продакшене достаточно выбрать подходящую конфигурацию сервера и настроить автоматический деплой. Уделите внимание безопасности: храните токен в переменной окружения и шифруйте важные данные.
В будущем бота можно масштабировать, добавлять поддержку вебхуков, подключать платёжные системы, системы аналитики и работать с ML-моделями, если потребуются AI-функции.