Как выйти из вложенного обработчика (handler) в библиотеке aiogram3 на языке Python?

Как можно изменить код таким образом, чтобы при нажатии кнопки "Следующий вопрос" вложенный хендлер активировался снова и приводил обратно к основному хендлеру, вместо того чтобы застрять во вложенном хендлере?
  • 16 февраля 2024 г. 8:58
Ответы на вопрос 3
Для выхода из вложенного обработчика в библиотеке aiogram, вы можете использовать метод `stop_propagation()`. Этот метод позволяет остановить дальнейшую обработку событий в текущем обработчике и вернуться к предыдущему обработчику.

Пример:

```python
from aiogram import Bot, Dispatcher, types
from aiogram.dispatcher.filters import Command, Text
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup

bot = Bot(token='TOKEN')
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)


class MyStates(StatesGroup):
    default = State()
    nested = State()


@dp.message_handler(Command('start'))
async def start(message: types.Message):
    await message.answer("Привет! Нажми кнопку 'Следующий вопрос'.", reply_markup=create_keyboard())
    await MyStates.default.set()


@dp.callback_query_handler(text='next_question', state=MyStates.default)
async def next_question_callback(query: types.CallbackQuery, state: FSMContext):
    await query.message.edit_text("Новый вопрос! Нажми кнопку 'Ответить'", reply_markup=InlineKeyboardMarkup().add(
        InlineKeyboardButton('Ответить', callback_data='answer')))
    # Переключаемся во вложенное состояние
    await MyStates.next()
    await state.update_data(question='Какой-то вопрос')


@dp.callback_query_handler(text='answer', state=MyStates.nested)
async def answer_callback(query: types.CallbackQuery, state: FSMContext):
    data = await state.get_data()
    question = data.get('question')
    await query.message.answer(f"Ответ на вопрос: {question}")
    
    # Переключаемся обратно к основному состоянию
    await MyStates.default.set()
    await query.message.edit_text("Привет! Нажми кнопку 'Следующий вопрос'.", reply_markup=create_keyboard())
    # Останавливаем дальнейшую обработку событий в этом обработчике
    raise Dispatcher.StopPropagation()


def create_keyboard():
    keyboard = InlineKeyboardMarkup()
    keyboard.add(InlineKeyboardButton(text="Следующий вопрос", callback_data="next_question"))
    return keyboard


if __name__ == '__main__':
    dp.loop.run_until_complete(dp.skip_updates())
    dp.run_polling()
```

В примере выше использована машина состояний (FSM) для хранения состояния пользователя. Когда пользователь нажимает кнопку "Следующий вопрос", мы переключаемся во вложенное состояние и показываем новый вопрос. Когда пользователь нажимает кнопку "Ответить", мы показываем ответ на текущий вопрос и возвращаемся к основному состоянию снова. Используется метод `stop_propagation()` для остановки дальнейшей обработки событий в текущем обработчике и возврата к предыдущему.

Надеюсь, это поможет!
Не видел раньше вложенных хендлеров. 
Наспех вышел рабочий вариант
Понимаю что решение ужасно, но всё же попробовал добавить условие во вложенном хендлере 

@disp.message(ai.F.text == 'Начать игру' or ai.F.text == 'Следующий вопрос') # основной хендлер
async def start_game(message):
    await message(f'Зимой и летом одним цветом?')
    await message.answer('Варианты ответа: \n a)Ясень  \n b)Ёлка  \n c)Кедр',  reply_markup = answer_btn) # клавиатура с кнопками ответов 
    @disp.message(ai.F.text) # вложенный хендлер
    async def check_answer(message):
        if message.text == 'Следующий вопрос':
            await start_game(message)
        else:
            result = check(message.text)
            if result:
                await message.answer('Верно!', reply_markup = next_question_keyboard) # клавиатура с кнопкой "Следующий вопрос"             
            else:
                await message.answer(f'Неверно!', reply_markup = next_question_keyboard) # клавиатура с кнопкой "Следующий вопрос"
Похожие вопросы