Разработка 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('Файл не найден на сервере.')