Как правильно использовать FSM в aiogram для регистрации следующего обработчика сообщений?

Вопрос или проблема

Я хочу сделать аналог register_next_step_handler из telebot для aiogram. Как я уже понял, мне нужно использовать FSM для этого. Однако я не могу понять, что мне делать с этим примером:

@bot.message_handler(func=lambda message: message.text == "🚫 Запретить публикацию на время")
def prohibit_sending_messages_button(message):
    msg = bot.send_message(message.chat.id, "Введите время блокировки в минутах (0 для отмены; умножьте на 60/1440, чтобы указать часы/дни):")
    bot.register_next_step_handler(msg, block_sending_for_minutes)

def block_sending_for_minutes(message):
    global acceptable_time
    # Измените значение acceptable_time на текущее время + выбранное время (может быть математическое выражение)
    acceptable_time = message.date + eval(message.text) * 60
    bot.send_message(message.chat.id, f"Запрет на отправку сообщений будет действовать до {datetime.fromtimestamp(acceptable_time).strftime('%H:%M:%S %d/%m/%Y')}")

Я пытаюсь сделать это:

@router.message(is_admin & F.text == "🚫 Запретить публикацию на время")
async def prohibit_sending_messages_button(message: Message, state: FSMContext) -> None:
    await state.set_state(Form.blocking_time)
    await message.answer("Введите время блокировки в минутах (0 для отмены; умножьте на 60/1440, чтобы указать часы/дни):")


@router.message(Form.blocking_time)
async def block_sending_for_minutes(message: Message) -> None:
    global acceptable_time
    # Измените значение acceptable_time на текущее время + выбранное время (может быть математическое выражение)
    acceptable_time = message.date + eval(await message.text) * 60
    await message.answer(f"Запрет на отправку сообщений будет действовать до {datetime.fromtimestamp(acceptable_time).strftime('%H:%M:%S %d/%m/%Y')}")

Мои вопросы:

  1. Нужно ли создавать отдельный класс для этого, если да, то следует ли помещать все классы FSM в отдельный файл и каково правильное название для класса и самой переменной?
  2. Нужно ли использовать state.clear(), если да, то как мне получить state, если нет, то получается, что я просто больше не использую этот класс?

Ответ или решение

Для того чтобы корректно использовать состояние конечного автомата (FSM) в библиотеке aiogram для регистрации обработчика следующего сообщения, а именно для реализации логики аналогичной register_next_step_handler из telebot, давайте детально разберем ваш вопрос и предложим правильное решение.

Пример использования FSM в aiogram

  1. Создание класса состояний

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

    Пример класса:

    from aiogram.dispatcher import FSMContext
    from aiogram.dispatcher.filters.state import State, StatesGroup
    
    class Form(StatesGroup):
       blocking_time = State()
  2. Регистрация обработчиков

    Используя состояние, вы можете зарегистрировать обработчики сообщений. Первый обработчик будет запрашивать у пользователя ввод времени блокировки, а второй обработчик будет обрабатывать этот ввод.

    Пример кода с учетом ваших требований:

    from aiogram import Bot, Dispatcher, types
    from aiogram.contrib.fsm_storage.memory import MemoryStorage
    from aiogram.dispatcher import FSMContext, Dispatcher
    from aiogram.dispatcher.filters import State
    from aiogram.utils import executor
    from datetime import datetime
    
    bot = Bot(token='YOUR_BOT_TOKEN')
    dp = Dispatcher(bot, storage=MemoryStorage())
    
    class Form(StatesGroup):
       blocking_time = State()
    
    @dp.message_handler(lambda message: message.text == "🚫 Prohibit publication for a while")
    async def prohibit_sending_messages_button(message: types.Message, state: FSMContext) -> None:
       await Form.blocking_time.set()
       await message.answer("Введите время блокировки в минутах (0 для отмены; умножьте на 60/1440 для часов/дней):")
    
    @dp.message_handler(state=Form.blocking_time)
    async def block_sending_for_minutes(message: types.Message, state: FSMContext) -> None:
       global acceptable_time
       try:
           blocking_duration = int(message.text)
           acceptable_time = message.date.timestamp() + blocking_duration * 60
           await message.answer(f"Запрет на отправку сообщений будет действовать до {datetime.fromtimestamp(acceptable_time).strftime('%H:%M:%S %d/%m/%Y')}")
       except ValueError:
           await message.answer("Пожалуйста, введите корректное число минут.")
    
       await state.finish()  # Завершаем текущее состояние

Ответы на ваши вопросы:

  1. Необходимость создания отдельного класса для состояний

    Да, создание класса для состояний является необходимым шагом для работы с FSM в aiogram. Все классы FSM можно разместить в отдельном файле, чтобы поддерживать чистоту кода и модульность. Примерное имя для класса — Form, а переменная состояния может называться blocking_time.

  2. Использование state.clear()

    Функция state.finish() завершает текущее состояние, и вы можете использовать ее в конце вашего обработчика. Очистка состояния (например, с помощью state.clear()) не является обязательной, если состояние завершается корректно. Тем не менее, если вы предполагаете, что бот может остаться в состоянии после частичного выполнения логики, вам следует явно очистить состояние, чтобы избежать конфликтов в будущем.

Завершение

Таким образом, в aiogram вы можете легко создать FSM для обработки последовательных сообщений от пользователя. Используйте классы состояний, корректно обрабатывайте входные данные и не забывайте очищать состояние после завершения процесса.

Оцените материал
Добавить комментарий

Капча загружается...