Ответ
Для организации последовательных диалогов (опросов, анкет) в Telegram-ботах на Python используется механизм машины состояний (Finite State Machine, FSM).
Он позволяет "запоминать", на каком шаге диалога находится пользователь, и направлять его следующее сообщение в нужный обработчик. В популярной библиотеке aiogram это реализуется через FSMContext.
Принцип работы:
- Определение состояний: Создается класс, наследуемый от
StatesGroup, где каждое состояние — это шаг в диалоге. - Установка состояния: При старте диалога бот устанавливает пользователю начальное состояние (например,
await Form.question1.set()). - Обработка по состоянию: Создаются хендлеры, которые срабатывают только для пользователей в определенном состоянии.
- Сохранение данных и переход: Внутри хендлера ответ пользователя сохраняется во временное хранилище (
state.proxy()) и бот переводит пользователя в следующее состояние (await Form.next()). - Завершение: После последнего ответа состояние сбрасывается (
await state.finish()).
Пример на aiogram:
from aiogram import Bot, Dispatcher, types
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage
# 1. Определяем состояния
class UserPoll(StatesGroup):
name = State() # Состояние для ожидания имени
age = State() # Состояние для ожидания возраста
city = State() # Состояние для ожидания города
# Хендлер для начала опроса
async def start_poll(message: types.Message):
await UserPoll.name.set() # 2. Устанавливаем первое состояние
await message.answer("Как вас зовут?")
# 3. Хендлер, который ловит ответ, когда пользователь в состоянии 'name'
async def process_name(message: types.Message, state: FSMContext):
async with state.proxy() as data: # 4. Сохраняем данные
data['name'] = message.text
await UserPoll.next() # Переходим к следующему состоянию (age)
await message.answer("Сколько вам лет?")
# Хендлер для состояния 'age'
async def process_age(message: types.Message, state: FSMContext):
async with state.proxy() as data:
data['age'] = message.text
await UserPoll.next() # Переходим к следующему состоянию (city)
await message.answer("Из какого вы города?")
# Хендлер для последнего состояния 'city'
async def process_city(message: types.Message, state: FSMContext):
async with state.proxy() as data:
data['city'] = message.text
# Выводим собранные данные
await message.answer(f"Спасибо! Ваши данные: Имя - {data['name']}, Возраст - {data['age']}, Город - {data['city']}")
await state.finish() # 5. Завершаем диалог и сбрасываем состояние
# Регистрация хендлеров
def register_handlers(dp: Dispatcher):
dp.register_message_handler(start_poll, commands=['poll'], state=None)
dp.register_message_handler(process_name, state=UserPoll.name)
dp.register_message_handler(process_age, state=UserPoll.age)
dp.register_message_handler(process_city, state=UserPoll.city)
Важно: По умолчанию aiogram использует хранилище в памяти (MemoryStorage), которое сбрасывается при перезапуске бота. Для продакшена следует использовать внешние хранилища, такие как RedisStorage или MongoStorage, чтобы состояния пользователей не терялись.