Какую роль выполняет FSM в библиотеке aiogram

Ответ

FSM (Finite State Machine, или конечный автомат) в aiogram — это механизм для управления состоянием пользователя в диалоге. Он необходим для создания многошаговых сценариев, таких как регистрация, анкетирование, оформление заказа или любая другая последовательность действий, где боту нужно запоминать предыдущие ответы пользователя.

Без FSM бот не помнит контекст: каждый новый handler обрабатывает сообщение независимо. FSM позволяет "вести" пользователя по заранее определенному сценарию, переключая его из одного состояния в другое.

Пример: Анкетирование пользователя

from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup

# 1. Определяем состояния
class UserPoll(StatesGroup):
    name = State()      # Состояние для ожидания имени
    age = State()       # Состояние для ожидания возраста
    feedback = State()  # Состояние для ожидания отзыва

# ... (инициализация бота и диспетчера)

# 2. Вход в сценарий (установка первого состояния)
@dp.message_handler(commands=['poll'])
async def start_poll(message: types.Message):
    await UserPoll.name.set() # Устанавливаем состояние 'name'
    await message.answer("Как вас зовут?")

# 3. Обработка ответа в состоянии 'name'
@dp.message_handler(state=UserPoll.name)
async def process_name(message: types.Message, state: FSMContext):
    async with state.proxy() as data: # Сохраняем данные в FSMContext
        data['name'] = message.text
    await UserPoll.next() # Переключаемся на следующее состояние ('age')
    await message.answer("Сколько вам лет?")

# 4. Обработка ответа в состоянии 'age' и завершение
@dp.message_handler(state=UserPoll.age)
async def process_age(message: types.Message, state: FSMContext):
    # ... (сохранение возраста)
    await state.finish() # Завершаем сценарий и сбрасываем состояние
    await message.answer("Спасибо за участие!")

Ключевые преимущества:

  • Структурированная логика: Код для каждого шага диалога изолирован в своем handler.
  • Хранение данных: FSMContext предоставляет временное хранилище для данных, собранных в ходе диалога.
  • Контроль диалога: Позволяет создавать нелинейные сценарии с ветвлениями и возвратами.

Ответ 18+ 🔞

О, смотри-ка, что у нас тут! Конечный автомат, блядь, в aiogram! Ну это ж такая штука, чтобы бот не был как та мартышка, которая всё забывает через пять секунд. Без него твой бот — как тот мужик с похмелья: спросил имя, повернулся, и уже нихуя не помнит, кто ты такой.

Представь, ты пытаешься у пользователя анкету собрать. Без FSM это выглядит так:

Пользователь: /poll Бот: Как вас зовут? Пользователь: Василий Бот: О, привет! И... э... нахуя ты мне имя сказал? — потому что следующий хэндлер про возраст уже понятия не имеет, что было до него. Пиздец, а не диалог.

А с FSM — это как вести пользователя за ручку по чёткому сценарию. Типа: "Стой тут, не ёрзай, сейчас имя спрошу... Запомнил. А теперь, сука, возраст давай".

Вот смотри, как это по-человечески, с состоянием, делается:

from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup

# 1. Это как объявить план допроса, этапы
class UserPoll(StatesGroup):
    name = State()      # Ждём, пока назовётся
    age = State()       # Ждём, пока сознается в годах
    feedback = State()  # Ждём, пока выскажется

# ... (тут бота заводишь как обычно)

# 2. Начало спектакля. Пользователь команду дал.
@dp.message_handler(commands=['poll'])
async def start_poll(message: types.Message):
    await UserPoll.name.set() # Ставим его в состояние "допроса по имени"
    await message.answer("Ну-ка, падаль, как звать-то?")

# 3. Ловим ответ именно в состоянии 'name'. Уже контекст есть!
@dp.message_handler(state=UserPoll.name)
async def process_name(message: types.Message, state: FSMContext):
    async with state.proxy() as data: # Это типа временная папка "Дело №..."
        data['name'] = message.text # Пишем имя в дело
    await UserPoll.next() # Щёлкаем его на следующий этап — возраст
    await message.answer(f"Так, {message.text}, я запомнил. Теперь годков сколько накопил?")

# 4. А вот тут он уже в состоянии 'age' ловится
@dp.message_handler(state=UserPoll.age)
async def process_age(message: types.Message, state: FSMContext):
    # ... (тут возраст в то же 'data' пихаешь)
    await state.finish() # Всё, спектакль окончен, состояние обнуляется
    await message.answer("Вот и славно, свободен. Данные твои... куда-нибудь денутся.")

И в чём, блядь, магия-то?

  • Порядок в голове: Каждый шаг диалога сидит в своём углу и не лезет в чужой. За имя отвечает один хэндлер, за возраст — другой. Красота, ёпта!
  • Память, как у слона: FSMContext — это такая временная памятная записка, куда ты можешь писать всё, что пользователь наговорил, пока сценарий идёт. Потом всё это разом вытащить и использовать.
  • Власть над диалогом: Хочешь — вперёд гони (next()), хочешь — назад отправь (previous()), хочешь — нахуй сбрось (finish()). Полный контроль, как в хорошем фильме про гипноз.

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