Разработка Telegram-ботов: от основ до продвинутых техник

Раздел: Веб-разработка -> Telegram боты

Основы создания ботов на aiogram

Основной и наиболее эффективный способ разработки Telegram-бота на Python - использование библиотеки aiogram (версия 3.x). Она предоставляет мощные инструменты для обработки команд, сообщений, callback-запросов, конечных автоматов (FSM) и middleware. Ниже приведена базовая структура проекта.


# bot.py
import asyncio
from aiogram import Bot, Dispatcher, Router
from aiogram.filters import Command
from aiogram.types import Message

API_TOKEN = 'ваш_токен'

bot = Bot(token=API_TOKEN)
dp = Dispatcher()
router = Router()

@router.message(Command('start'))
async def start_handler(message: Message):
    await message.answer('Привет! Я бот на aiogram.')

async def main():
    dp.include_router(router)
    await dp.start_polling(bot)

if __name__ == '__main__':
    asyncio.run(main())

библиотека aiogram python (библиотека aiogram для telegram ботов)

Пояснение: создаются объекты Bot и Dispatcher. Через Router группируются обработчики. Команда /start фильтруется через Command('start'). Запуск происходит через start_polling.

Цель базового решения - быстрый старт с минимальным кодом. Используется для простых ботов-приветствий или эхо-функций.

Как реализовать диалог с пользователем с запоминанием состояния?

Если требуется последовательность вопросов (например, анкета), применяется FSM (Finite State Machine). Библиотека aiogram предоставляет класс State и StatesGroup.


from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext

class Form(StatesGroup):
    name = State()
    age = State()

@router.message(Command('form'))
async def form_start(message: Message, state: FSMContext):
    await state.set_state(Form.name)
    await message.answer('Как вас зовут?')

@router.message(Form.name)
async def process_name(message: Message, state: FSMContext):
    await state.update_data(name=message.text)
    await state.set_state(Form.age)
    await message.answer('Сколько вам лет?')

@router.message(Form.age)
async def process_age(message: Message, state: FSMContext):
    data = await state.get_data()
    await message.answer(f'Спасибо, {data["name"]}! Вам {message.text} лет.')
    await state.clear()

Python telegram bot (создание telegram бота на python)

Проблема: если пользователь вводит некорректные данные (возраст буквами), бот упадет. Решение - добавить валидацию и обработку исключений через try/except.

Как создать интерактивные кнопки и обрабатывать нажатия?

Для инлайн-клавиатур используется InlineKeyboardBuilder. Каждая кнопка имеет callback_data.


from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.filters import CallbackQuery

@router.message(Command('inline'))
async def inline_example(message: Message):
    builder = InlineKeyboardBuilder()
    builder.button(text='Кнопка 1', callback_data='btn1')
    builder.button(text='Кнопка 2', callback_data='btn2')
    await message.answer('Выберите:', reply_markup=builder.as_markup())

@router.callback_query(lambda c: c.data in ['btn1', 'btn2'])
async def process_callback(callback: CallbackQuery):
    if callback.data == 'btn1':
        await callback.message.answer('Нажата кнопка 1')
    else:
        await callback.message.answer('Нажата кнопка 2')
    await callback.answer()

Py telegram python (telegram боты на python (pytelegrambotapi))

Ошибка: забыть вызвать callback.answer() - кнопка будет висеть в состоянии загрузки.

Как добавить автоматическое логирование всех сообщений?

Для мониторинга действий пользователей подключают middleware. Создается класс, наследуемый от BaseMiddleware.


from aiogram import BaseMiddleware
from typing import Callable, Dict, Any, Awaitable
from aiogram.types import TelegramObject

class LoggingMiddleware(BaseMiddleware):
    async def __call__(
        self,
        handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
        event: TelegramObject,
        data: Dict[str, Any]
    ) -> Any:
        print(f'Получено событие: {type(event).__name__}')
        return await handler(event, data)

# Включение:
dp.message.middleware(LoggingMiddleware())

Aiogram python боты (боты на aiogram python)

Цель - отладка и сбор статистики без изменения каждого обработчика. Проблема: если middleware очень медленный, он затормозит обработку всех сообщений.

Типичные ошибки и их решения

  • Не установлен aiogram: используйте
    pip install aiogram
  • Пропущен await: все обращения к методам бота должны быть асинхронными. Например, message.answer() без await приведет к предупреждению.
  • Неправильный тип фильтра: для команд используйте Command(‘start’), а не CommandStart() (устаревший синтаксис).
  • Отсутствие регистрации роутера: не забудьте dp.include_router(router).
  • Блокирующий код: не используйте time.sleep() - только asyncio.sleep().

Расширенные примеры кода

Ниже представлены три практических сценария с полным кодом и результатом выполнения.

Пример 1: Бот-опрос (FSM с валидацией)

Код:

Пример

from aiogram import Bot, Dispatcher, Router
from aiogram.filters import Command
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext
from aiogram.types import Message
import asyncio

API_TOKEN = 'ваш_токен'
bot = Bot(token=API_TOKEN)
dp = Dispatcher()
router = Router()

class Survey(StatesGroup):
    name = State()
    age = State()

@router.message(Command('survey'))
async def start_survey(message: Message, state: FSMContext):
    await state.set_state(Survey.name)
    await message.answer('Введите ваше имя:')

@router.message(Survey.name)
async def get_name(message: Message, state: FSMContext):
    if len(message.text.strip()) < 2:
        await message.answer('Имя должно содержать минимум 2 символа. Попробуйте снова:')
        return
    await state.update_data(name=message.text)
    await state.set_state(Survey.age)
    await message.answer('Введите ваш возраст (число):')

@router.message(Survey.age)
async def get_age(message: Message, state: FSMContext):
    if not message.text.isdigit():
        await message.answer('Возраст должен быть числом. Попробуйте снова:')
        return
    data = await state.get_data()
    name = data['name']
    age = int(message.text)
    await message.answer(f'{name}, вам {age} лет. Спасибо за участие!')
    await state.clear()

async def main():
    dp.include_router(router)
    await dp.start_polling(bot)

if __name__ == '__main__':
    asyncio.run(main())

Результат работы (логи пользователя):

Пользователь: /survey
Бот: Введите ваше имя:
Пользователь: Александр
Бот: Введите ваш возраст (число):
Пользователь: abc
Бот: Возраст должен быть числом. Попробуйте снова:
Пользователь: 30
Бот: Александр, вам 30 лет. Спасибо за участие!

В примере добавлена валидация ввода, предотвращающая ошибки.

Пример 2: Инлайн-режим (поиск по запросу)

Код:

Пример

from aiogram import Bot, Dispatcher, Router
from aiogram.types import InlineQuery, InlineQueryResultArticle, InputTextMessageContent
import hashlib

API_TOKEN = 'ваш_токен'
bot = Bot(token=API_TOKEN)
dp = Dispatcher()
router = Router()

@router.inline_query()
async def inline_search(query: InlineQuery):
    results = []
    if query.query:
        text = f'Вы искали: {query.query}'
        results.append(InlineQueryResultArticle(
            id=hashlib.md5(query.query.encode()).hexdigest(),
            title='Показать результат',
            input_message_content=InputTextMessageContent(message_text=text)
        ))
    else:
        results.append(InlineQueryResultArticle(
            id='default',
            title='Начните вводить запрос',
            input_message_content=InputTextMessageContent(message_text='Введите текст для поиска')
        ))
    await query.answer(results, cache_time=10)

async def main():
    dp.include_router(router)
    await dp.start_polling(bot)

if __name__ == '__main__':
    asyncio.run(main())

Результат (в любом чате через @имя_бота):

Пользователь вводит @bot_name привет
Появляется всплывающий список с одной кнопкой "Показать результат"
При нажатии отправляется сообщение: "Вы искали: привет"

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

Пример 3: Отправка фото по команде

Код:

Пример

from aiogram import Bot, Dispatcher, Router
from aiogram.filters import Command
from aiogram.types import FSInputFile, Message
import asyncio

API_TOKEN = 'ваш_токен'
bot = Bot(token=API_TOKEN)
dp = Dispatcher()
router = Router()

@router.message(Command('photo'))
async def send_photo(message: Message):
    photo = FSInputFile('example.jpg')  # файл должен быть в текущей папке
    await bot.send_photo(chat_id=message.chat.id, photo=photo, caption='Пример изображения')

async def main():
    dp.include_router(router)
    await dp.start_polling(bot)

if __name__ == '__main__':
    asyncio.run(main())

Результат:

Пользователь: /photo
Бот: отправляет файл example.jpg с подписью "Пример изображения"

Важно: файл должен быть доступен локально. Для отправки по URL используйте InputMediaPhoto(media='url').

Дополнительно можно обрабатывать ошибки, если файл не найден:

Пример

try:
    # код отправки
except FileNotFoundError:
    await message.answer('Файл не найден на сервере.')

Боты на aiogram Python - comments

En
Aiogram python боты (python)