Как правильно применять конечные автомат (FSM) в библиотеке aiogram для обработки последующих сообщений?

Как я могу создать аналог функции register_next_step_handler из telebot для alogram с использованием FSM? У меня есть следующий пример кода: <br/> <br/> <pre><code class="python">@bot.message_handler(func=lambda message: message.text == " Prohibit publication for a while")
def prohibit_sending_messages_button(message):
    msg = bot.send_message(message.chat.id, "Enter the blocking time in minutes (0 for cancellation; multiply by 60/1440 to indicate hours/days):")
    bot.register_next_step_handler(msg, block_sending_for_minutes)

def block_sending_for_minutes(message):
    global acceptable_time
    # Change acceptable_time value to actual time + chosen time (can be mathematical expression)
    acceptable_time = message.date + eval(message.text) * 60
    bot.send_message(message.chat.id, f"The ban on sending messages will be in effect until {datetime.fromtimestamp(acceptable_time).strftime("%H:%M:%S %d/%m/%Y")}")</code></pre> <br/> <br/> Я пробовал адаптировать это для alogram следующим образом: <br/> <br/> <pre><code class="python">@router.message(is_admin &amp; F.text == " Prohibit publication for a while")
async def prohibit_sending_messages_button(message: Message, state: FSMContext) -&gt; None:
    await state.set_state(Form.blocking_time)
    await message.answer("Enter the blocking time in minutes (0 for cancellation; multiply by 60/1440 to indicate hours/days):")


@router.message(Form.blocking_time)
async def block_sending_for_minutes(message: Message) -&gt; None:
    global acceptable_time
    # Change acceptable_time value to actual time + chosen time (can be mathematical expression)
    acceptable_time = message.date + eval(await message.tex
  • 23 сентября 2024 г. 19:03
Ответы на вопрос 2
Ваша идея по адаптации конечных автоматов (FSM) в библиотеке `aiogram` выглядит правильно, но немного требует доработки. Я покажу, как правильно организовать обработку сообщений с использованием FSM и как корректно обрабатывать состояние для введенного времени блокировки.

Вот пример того, как это можно сделать:

```python
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
from aiogram.utils import executor
from datetime import datetime

# Инициализация бота и диспетчера
bot = Bot(token='YOUR_TOKEN')
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)

# Определяем состояния
class Form(StatesGroup):
    blocking_time = State()

acceptable_time = 0

@dp.message_handler(lambda message: message.text == "Prohibit publication for a while", state="*")
async def prohibit_sending_messages_button(message: types.Message, state: FSMContext) -> None:
    await Form.blocking_time.set()  # Устанавливаем состояние
    await message.answer("Enter the blocking time in minutes (0 for cancellation; multiply by 60/1440 to indicate hours/days):")

@dp.message_handler(state=Form.blocking_time)
async def block_sending_for_minutes(message: types.Message, state: FSMContext) -> None:
    global acceptable_time
    try:
        blocking_time = int(message.text)

        # Если введено 0, отменяем блокировку
        if blocking_time == 0:
            acceptable_time = 0
            await message.answer("Blocking has been cancelled.")
        else:
            acceptable_time = message.date.timestamp() + blocking_time * 60
            await message.answer(f"The ban on sending messages will be in effect until {datetime.fromtimestamp(acceptable_time).strftime('%H:%M:%S %d/%m/%Y')}")

    except ValueError:
        await message.answer("Please enter a valid number.")

    await state.finish()  # Завершаем состояние

if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=True)
```

### Объяснение структуры:

1. **Импортируйте необходимые компоненты**: Здесь вы импортировали `FSMSContext`, `StatesGroup`, и другие важные компоненты.

2. **Определение состояний**: В классе `Form` мы определяем состоянии, которые будут использоваться для управления потоком сообщений.

3. **Обработка команды**: Обработчик для кнопки "Prohibit publication for a while" активирует состояние блокировки времени и запрашивает пользователя ввести время.

4. **Обработка ввода времени**: Здесь обрабатывается ввод времени. Если введенное значение валидно, мы вычисляем конечное время блокировки и выводим его пользователю. В случае некорректного ввода, пользователю возвращается сообщение об ошибке.

5. **Завершение состояния**: После того как обработка завершена, состояние сбрасывается с помощью `await state.finish()`.

### Важные моменты:

- Обратите внимание на обработку ошибок при вводе времени. Если пользователь вводит некорректные данные, обработчик отправляет сообщение об ошибке, но не вызывает сброс состояния.
- Также не забудьте изменять глобальную переменную `acceptable_time` только после правильной валидации входных данных. 

Попробуйте этот код, и он должен работать как аналог `register_next_step_handler` из `telebot`.
1. Нужно ли мне создавать для этого отдельный класс

Да, для стейтов задается отдельный класс
2. Нужно ли мне использовать state.clear()

Конечно нужно, если хочешь сбросить стейты.
если да, то как мне получить state

Также, как ты его получил в первой функции, когда устанавливал значение.
state: FSMContext

Вот почитай, все вопросы должны отпасть

global acceptable_time
глобальные переменные зло. Ты же понимаешь, что значение этой переменной одно для всех юзеров?
eval(await message.text)
а это вообще кошмар)) тут надо просто message.text к инту привести, но не евалом же это делать xD
Похожие вопросы