Как правильно применять конечные автомат (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`.
<blockquote>1. Нужно ли мне создавать для этого отдельный класс</blockquote> <br/> Да, для стейтов задается отдельный класс <br/> <blockquote>2. Нужно ли мне использовать state.clear()</blockquote> <br/> Конечно нужно, если хочешь сбросить стейты. <br/> <blockquote> если да, то как мне получить state</blockquote> <br/> Также, как ты его получил в первой функции, когда устанавливал значение. <br/> <code class="python">state: FSMContext</code> <br/> <br/> <a href="https://mastergroosha.github.io/aiogram-3-guide/fsm/" rel="nofollow">Вот почитай, все вопросы должны отпасть</a> <br/> <br/> <code class="python">global acceptable_time</code> <br/> глобальные переменные зло. Ты же понимаешь, что значение этой переменной одно для всех юзеров? <br/> <code class="python">eval(await message.text)</code> <br/> а это вообще кошмар)) тут надо просто message.text к инту привести, но не евалом же это делать xD
Похожие вопросы