Майcтер сцен#

Нове в версії 3.2.

Попередження

Дана фіча є експериментальною, тому у наступних оновленнях може змінюватись.

Базовий інтерфейс aiogram-у простий та потужний у використанні, що дозволяє реалізувати прості взаємодії, такі як обробка команд або повідомлень і відповідей. Однак деякі завдання вимагають поетапного діалогу між користувачем і ботом. Ось де сцени вступають у гру.

Що ж таке сцени?#

Сцена в aiogram схожа на абстрактний, ізольований простір імен або кімнату, до якої користувач може потрапити за допомогою коду. Коли користувач перебуває в межах Сцени, більшість інших глобальних команд або обробників повідомлень пропускаються, якщо тільки вони не призначені для роботи поза Сценами.Сцени забезпечують структуру для більш складних взаємодій, ефективно ізолюючи та керуючи контекстами для різних етапів розмови. Вони дозволяють більш організовано контролювати та керувати розмовою.

Життєвий цикл#

У кожну сцену можна «увійти», «покинути» або «вийти», що забезпечує чіткі переходи між різними етапами розмови. Наприклад, у багатоетапній взаємодії заповнення форми кожен крок може бути сценою - бот направляє користувача від однієї сцени до наступної, коли вони надають необхідну інформацію.

Слухачі подій#

Сцени мають власні хуки, які є слухачами команд або повідомлень, які діють лише тоді, коли користувач знаходиться всередині сцени. Ці хуки реагують на дії користувача, коли користувач перебуває «всередині» Сцени, надаючи відповіді або дії, відповідні цьому контексту. Коли користувач переходить від однієї сцени до іншої, дії та відповіді відповідно змінюються, оскільки користувач тепер взаємодіє з групою слухачів у новій сцені. Ці «специфічні для сцени» хуки або слухачі, відірвані від глобального контексту прослуховування, дозволяють більш оптимізовану та організовану взаємодію бот-користувач.

Взаємодія#

Кожна сцена схожа на самодостатній світ із взаємодіями, визначеними в межах цієї сцени. Таким чином, лише обробники, визначені в конкретній сцені, реагуватимуть на введення користувача протягом життєвого циклу цієї сцени.

Переваги#

Сцени можуть допомогти керувати більш складними робочими процесами взаємодії та забезпечити більш інтерактивні та динамічні діалоги між користувачем і ботом. Це забезпечує велику гнучкість у обробці багатоетапних взаємодій або розмов з користувачами.

Як це використовувати?#

Наприклад, у нас є тестовий бот, який задає користувачеві серію запитань, а потім відображає результати - назвемо його гра-вікторина.

Почнемо з моделей даних. У цьому прикладі прості моделі даних використовуються для представлення запитань і відповідей, у реальному житті ви, ймовірно, використовували б базу даних для зберігання даних.

Запитання#
@dataclass
class Answer:
    """
    Represents an answer to a question.
    """

    text: str
    """The answer text"""
    is_correct: bool = False
    """Indicates if the answer is correct"""


@dataclass
class Question:
    """
    Class representing a quiz with a question and a list of answers.
    """

    text: str
    """The question text"""
    answers: list[Answer]
    """List of answers"""

    correct_answer: str = field(init=False)

    def __post_init__(self):
        self.correct_answer = next(answer.text for answer in self.answers if answer.is_correct)


# Fake data, in real application you should use a database or something else
QUESTIONS = [
    Question(
        text="What is the capital of France?",
        answers=[
            Answer("Paris", is_correct=True),
            Answer("London"),
            Answer("Berlin"),
            Answer("Madrid"),
        ],
    ),
    Question(
        text="What is the capital of Spain?",
        answers=[
            Answer("Paris"),
            Answer("London"),
            Answer("Berlin"),
            Answer("Madrid", is_correct=True),
        ],
    ),
    Question(
        text="What is the capital of Germany?",
        answers=[
            Answer("Paris"),
            Answer("London"),
            Answer("Berlin", is_correct=True),
            Answer("Madrid"),
        ],
    ),
    Question(
        text="What is the capital of England?",
        answers=[
            Answer("Paris"),
            Answer("London", is_correct=True),
            Answer("Berlin"),
            Answer("Madrid"),
        ],
    ),
    Question(
        text="What is the capital of Italy?",
        answers=[
            Answer("Paris"),
            Answer("London"),
            Answer("Berlin"),
            Answer("Rome", is_correct=True),
        ],
    ),
]

Потім нам потрібно створити клас Scene, який представлятиме сцену вікторини:

Примітка

Іменований аргумент, переданий у визначення класу, описує ім’я сцени - те саме, що стан сцени.

Сцена вікторини#
class QuizScene(Scene, state="quiz"):
    """
    This class represents a scene for a quiz game.

    It inherits from Scene class and is associated with the state "quiz".
    It handles the logic and flow of the quiz game.
    """

Також нам потрібно визначити обробник, який допоможе запустити вікторину:

Обробник для запуску вікторини#
quiz_router = Router(name=__name__)
# Add handler that initializes the scene
quiz_router.message.register(QuizScene.as_handler(), Command("quiz"))

Після визначення сцени нам потрібно зареєструвати її в SceneRegistry:

Реєстрація сцени#
def create_dispatcher():
    # Event isolation is needed to correctly handle fast user responses
    dispatcher = Dispatcher(
        events_isolation=SimpleEventIsolation(),
    )
    dispatcher.include_router(quiz_router)

    # To use scenes, you should create a SceneRegistry and register your scenes there
    scene_registry = SceneRegistry(dispatcher)
    # ... and then register a scene in the registry
    # by default, Scene will be mounted to the router that passed to the SceneRegistry,
    # but you can specify the router explicitly using the `router` argument
    scene_registry.add(QuizScene)

    return dispatcher

Отже, тепер ми можемо реалізувати логіку гри-вікторини, кожне запитання надсилається користувачеві одне за одним, а відповідь користувача перевіряється в кінці всіх запитань.

Тепер нам потрібно написати точку входу для обробника запитань:

Точка входу обробника запитань#
    @on.message.enter()
    async def on_enter(self, message: Message, state: FSMContext, step: int | None = 0) -> Any:
        """
        Method triggered when the user enters the quiz scene.

        It displays the current question and answer options to the user.

        :param message:
        :param state:
        :param step: Scene argument, can be passed to the scene using the wizard
        :return:
        """
        if not step:
            # This is the first step, so we should greet the user
            await message.answer("Welcome to the quiz!")

        try:
            quiz = QUESTIONS[step]
        except IndexError:
            # This error means that the question's list is over
            return await self.wizard.exit()

        markup = ReplyKeyboardBuilder()
        markup.add(*[KeyboardButton(text=answer.text) for answer in quiz.answers])

        if step > 0:
            markup.button(text="🔙 Back")
        markup.button(text="🚫 Exit")

        await state.update_data(step=step)
        return await message.answer(
            text=QUESTIONS[step].text,
            reply_markup=markup.adjust(2).as_markup(resize_keyboard=True),
        )

Після входу в сцену ми маємо очікувати відповіді користувача, тому нам потрібно написати для неї обробник, цей обробник має очікувати текстове повідомлення, зберегти відповідь і повторно виконати обробник запитання для наступного запитання:

Обробник відповідей#
    @on.message(F.text)
    async def answer(self, message: Message, state: FSMContext) -> None:
        """
        Method triggered when the user selects an answer.

        It stores the answer and proceeds to the next question.

        :param message:
        :param state:
        :return:
        """
        data = await state.get_data()
        step = data["step"]
        answers = data.get("answers", {})
        answers[step] = message.text
        await state.update_data(answers=answers)

        await self.wizard.retake(step=step + 1)

Коли користувач відповідає невідомим повідомленням, ми повинні знову очікувати текстове повідомлення:

Невідомий обробник повідомлень#
    @on.message()
    async def unknown_message(self, message: Message) -> None:
        """
        Method triggered when the user sends a message that is not a command or an answer.

        It asks the user to select an answer.

        :param message: The message received from the user.
        :return: None
        """
        await message.answer("Please select an answer.")

Після відповіді на всі запитання ми маємо показати результати користувачеві, як ви можете бачити в коді нижче, ми використовуємо await self.wizard.exit(), щоб вийти зі сцени, коли список запитань у QuizScene» закінчено .on_enter обробник.

Це означає, що нам потрібно написати обробник виходу, щоб показати результати користувачеві:

Обробник показу результатів#
    @on.message.exit()
    async def on_exit(self, message: Message, state: FSMContext) -> None:
        """
        Method triggered when the user exits the quiz scene.

        It calculates the user's answers, displays the summary, and clears the stored answers.

        :param message:
        :param state:
        :return:
        """
        data = await state.get_data()
        answers = data.get("answers", {})

        correct = 0
        incorrect = 0
        user_answers = []
        for step, quiz in enumerate(QUESTIONS):
            answer = answers.get(step)
            is_correct = answer == quiz.correct_answer
            if is_correct:
                correct += 1
                icon = "✅"
            else:
                incorrect += 1
                icon = "❌"
            if answer is None:
                answer = "no answer"
            user_answers.append(f"{quiz.text} ({icon} {html.quote(answer)})")

        content = as_list(
            as_section(
                Bold("Your answers:"),
                as_numbered_list(*user_answers),
            ),
            "",
            as_section(
                Bold("Summary:"),
                as_list(
                    as_key_value("Correct", correct),
                    as_key_value("Incorrect", incorrect),
                ),
            ),
        )

        await message.answer(**content.as_kwargs(), reply_markup=ReplyKeyboardRemove())
        await state.set_data({})

Також ми можемо виконати дії для виходу з вікторини або повернення до попереднього запитання:

Обробник виходу#
    @on.message(F.text == "🚫 Exit")
    async def exit(self, message: Message) -> None:
        """
        Method triggered when the user selects the "Exit" button.

        It exits the quiz.

        :param message:
        :return:
        """
        await self.wizard.exit()
Обробник дії «повернутись»#
    @on.message(F.text == "🔙 Back")
    async def back(self, message: Message, state: FSMContext) -> None:
        """
        Method triggered when the user selects the "Back" button.

        It allows the user to go back to the previous question.

        :param message:
        :param state:
        :return:
        """
        data = await state.get_data()
        step = data["step"]

        previous_step = step - 1
        if previous_step < 0:
            # In case when the user tries to go back from the first question,
            # we just exit the quiz
            return await self.wizard.exit()
        return await self.wizard.back(step=previous_step)

Тепер ми можемо запустити бота і протестувати гру-вікторину:

Запустіть бота#
async def main():
    dispatcher = create_dispatcher()
    bot = Bot(TOKEN)
    await dispatcher.start_polling(bot)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    asyncio.run(main())
    # Alternatively, you can use aiogram-cli:
    # `aiogram run polling quiz_scene:create_dispatcher --log-level info --token BOT_TOKEN`

Зберемо все разом

Приклад вікторини#
import asyncio
import logging
from dataclasses import dataclass, field
from os import getenv
from typing import Any

from aiogram import Bot, Dispatcher, F, Router, html
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.scene import Scene, SceneRegistry, ScenesManager, on
from aiogram.fsm.storage.memory import SimpleEventIsolation
from aiogram.types import KeyboardButton, Message, ReplyKeyboardRemove
from aiogram.utils.formatting import (
    Bold,
    as_key_value,
    as_list,
    as_numbered_list,
    as_section,
)
from aiogram.utils.keyboard import ReplyKeyboardBuilder

TOKEN = getenv("BOT_TOKEN")


@dataclass
class Answer:
    """
    Represents an answer to a question.
    """

    text: str
    """The answer text"""
    is_correct: bool = False
    """Indicates if the answer is correct"""


@dataclass
class Question:
    """
    Class representing a quiz with a question and a list of answers.
    """

    text: str
    """The question text"""
    answers: list[Answer]
    """List of answers"""

    correct_answer: str = field(init=False)

    def __post_init__(self):
        self.correct_answer = next(answer.text for answer in self.answers if answer.is_correct)


# Fake data, in real application you should use a database or something else
QUESTIONS = [
    Question(
        text="What is the capital of France?",
        answers=[
            Answer("Paris", is_correct=True),
            Answer("London"),
            Answer("Berlin"),
            Answer("Madrid"),
        ],
    ),
    Question(
        text="What is the capital of Spain?",
        answers=[
            Answer("Paris"),
            Answer("London"),
            Answer("Berlin"),
            Answer("Madrid", is_correct=True),
        ],
    ),
    Question(
        text="What is the capital of Germany?",
        answers=[
            Answer("Paris"),
            Answer("London"),
            Answer("Berlin", is_correct=True),
            Answer("Madrid"),
        ],
    ),
    Question(
        text="What is the capital of England?",
        answers=[
            Answer("Paris"),
            Answer("London", is_correct=True),
            Answer("Berlin"),
            Answer("Madrid"),
        ],
    ),
    Question(
        text="What is the capital of Italy?",
        answers=[
            Answer("Paris"),
            Answer("London"),
            Answer("Berlin"),
            Answer("Rome", is_correct=True),
        ],
    ),
]


class QuizScene(Scene, state="quiz"):
    """
    This class represents a scene for a quiz game.

    It inherits from Scene class and is associated with the state "quiz".
    It handles the logic and flow of the quiz game.
    """

    @on.message.enter()
    async def on_enter(self, message: Message, state: FSMContext, step: int | None = 0) -> Any:
        """
        Method triggered when the user enters the quiz scene.

        It displays the current question and answer options to the user.

        :param message:
        :param state:
        :param step: Scene argument, can be passed to the scene using the wizard
        :return:
        """
        if not step:
            # This is the first step, so we should greet the user
            await message.answer("Welcome to the quiz!")

        try:
            quiz = QUESTIONS[step]
        except IndexError:
            # This error means that the question's list is over
            return await self.wizard.exit()

        markup = ReplyKeyboardBuilder()
        markup.add(*[KeyboardButton(text=answer.text) for answer in quiz.answers])

        if step > 0:
            markup.button(text="🔙 Back")
        markup.button(text="🚫 Exit")

        await state.update_data(step=step)
        return await message.answer(
            text=QUESTIONS[step].text,
            reply_markup=markup.adjust(2).as_markup(resize_keyboard=True),
        )

    @on.message.exit()
    async def on_exit(self, message: Message, state: FSMContext) -> None:
        """
        Method triggered when the user exits the quiz scene.

        It calculates the user's answers, displays the summary, and clears the stored answers.

        :param message:
        :param state:
        :return:
        """
        data = await state.get_data()
        answers = data.get("answers", {})

        correct = 0
        incorrect = 0
        user_answers = []
        for step, quiz in enumerate(QUESTIONS):
            answer = answers.get(step)
            is_correct = answer == quiz.correct_answer
            if is_correct:
                correct += 1
                icon = "✅"
            else:
                incorrect += 1
                icon = "❌"
            if answer is None:
                answer = "no answer"
            user_answers.append(f"{quiz.text} ({icon} {html.quote(answer)})")

        content = as_list(
            as_section(
                Bold("Your answers:"),
                as_numbered_list(*user_answers),
            ),
            "",
            as_section(
                Bold("Summary:"),
                as_list(
                    as_key_value("Correct", correct),
                    as_key_value("Incorrect", incorrect),
                ),
            ),
        )

        await message.answer(**content.as_kwargs(), reply_markup=ReplyKeyboardRemove())
        await state.set_data({})

    @on.message(F.text == "🔙 Back")
    async def back(self, message: Message, state: FSMContext) -> None:
        """
        Method triggered when the user selects the "Back" button.

        It allows the user to go back to the previous question.

        :param message:
        :param state:
        :return:
        """
        data = await state.get_data()
        step = data["step"]

        previous_step = step - 1
        if previous_step < 0:
            # In case when the user tries to go back from the first question,
            # we just exit the quiz
            return await self.wizard.exit()
        return await self.wizard.back(step=previous_step)

    @on.message(F.text == "🚫 Exit")
    async def exit(self, message: Message) -> None:
        """
        Method triggered when the user selects the "Exit" button.

        It exits the quiz.

        :param message:
        :return:
        """
        await self.wizard.exit()

    @on.message(F.text)
    async def answer(self, message: Message, state: FSMContext) -> None:
        """
        Method triggered when the user selects an answer.

        It stores the answer and proceeds to the next question.

        :param message:
        :param state:
        :return:
        """
        data = await state.get_data()
        step = data["step"]
        answers = data.get("answers", {})
        answers[step] = message.text
        await state.update_data(answers=answers)

        await self.wizard.retake(step=step + 1)

    @on.message()
    async def unknown_message(self, message: Message) -> None:
        """
        Method triggered when the user sends a message that is not a command or an answer.

        It asks the user to select an answer.

        :param message: The message received from the user.
        :return: None
        """
        await message.answer("Please select an answer.")


quiz_router = Router(name=__name__)
# Add handler that initializes the scene
quiz_router.message.register(QuizScene.as_handler(), Command("quiz"))


@quiz_router.message(Command("start"))
async def command_start(message: Message, scenes: ScenesManager):
    await scenes.close()
    await message.answer(
        "Hi! This is a quiz bot. To start the quiz, use the /quiz command.",
        reply_markup=ReplyKeyboardRemove(),
    )


def create_dispatcher():
    # Event isolation is needed to correctly handle fast user responses
    dispatcher = Dispatcher(
        events_isolation=SimpleEventIsolation(),
    )
    dispatcher.include_router(quiz_router)

    # To use scenes, you should create a SceneRegistry and register your scenes there
    scene_registry = SceneRegistry(dispatcher)
    # ... and then register a scene in the registry
    # by default, Scene will be mounted to the router that passed to the SceneRegistry,
    # but you can specify the router explicitly using the `router` argument
    scene_registry.add(QuizScene)

    return dispatcher


async def main():
    dispatcher = create_dispatcher()
    bot = Bot(TOKEN)
    await dispatcher.start_polling(bot)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    asyncio.run(main())
    # Alternatively, you can use aiogram-cli:
    # `aiogram run polling quiz_scene:create_dispatcher --log-level info --token BOT_TOKEN`

Компоненти#

  • aiogram.fsm.scene.Scene - представляє сцену, містить обробники

  • aiogram.fsm.scene.SceneRegistry - контейнер для всіх сцен у боті, використовується для реєстрації сцен та їх вирішення за назвою

  • aiogram.fsm.scene.ScenesManager - керує сценами для кожного користувача, використовується для входу, виходу та вирішення поточної сцени для користувача

  • aiogram.fsm.scene.SceneConfig - конфігурація сцени, використовується для налаштування сцени

  • aiogram.fsm.scene.SceneWizard - майстер сцени, який використовується для взаємодії з користувачем у сцені з активного обробника сцени

  • Markers - маркер для обробників сцен, використовується для позначення обробників сцен

class aiogram.fsm.scene.Scene(wizard: SceneWizard)[source]#

Представляє крок в діалозі.

Сцена — це певний стан розмови, де можуть відбуватися певні дії.

Кожна сцена має набір фільтрів, які визначають, коли вона має бути запущена, і набір обробників, які визначають дії, які мають виконуватися, коли сцена активна.

Примітка

Цей клас не призначений для безпосереднього використання. Замість цього слід створити підкласи для визначення власних сцен.

classmethod add_to_router(router: Router) None[source]#

Додає сцену до заданого маршрутизатора.

Параметри:

router

Повертає:

classmethod as_handler(**kwargs: Any) Callable[[...], Any][source]#

Створіть обробник точки входу для сцени, який можна використовувати для спрощення обробника, який запускає сцену.

>>> router.message.register(MyScene.as_handler(), Command("start"))
classmethod as_router(name: str | None = None) Router[source]#

Returns the scene as a router.

Повертає:

новий роутер

class aiogram.fsm.scene.SceneRegistry(router: Router, register_on_add: bool = True)[source]#

Клас, який представляє реєстр для сцен.

add(*scenes: Type[Scene], router: Router | None = None) None[source]#

Цей метод додає вказані сцени до реєстру та додатково реєструє їх на маршрутизаторі.

Якщо сцена з таким самим станом уже існує в реєстрі, виникає SceneException.

Попередження

If the router is not specified, the scenes will not be registered to the router. You will need to include the scenes manually to the router or use the register method.

Параметри:
  • scenes – Параметр змінної довжини, який приймає один або кілька типів сцен. Ці сцени є екземплярами класу Scene.

  • router – Додатковий параметр, який визначає маршрутизатор, до якого слід додати сцени.

Повертає:

None

get(scene: Type[Scene] | str | None) Type[Scene][source]#

Цей метод повертає зареєстрований об’єкт Scene для вказаної сцени. Параметром сцени може бути або об’єкт Scene, або рядок, що представляє назву сцени. Якщо надається об’єкт Scene, атрибут стану об’єкта SceneConfig, пов’язаного з об’єктом Scene, використовуватиметься як ім’я сцени. Якщо вказано None або недійсний тип, буде викликано SceneException.

Якщо вказану сцену не зареєстровано в об’єкті SceneRegistry, буде породжено помилку SceneException.

Параметри:

scene – Об’єкт Scene або рядок, що представляє назву сцени.

Повертає:

Зареєстрований об’єкт Scene, що відповідає даному параметру сцени.

register(*scenes: Type[Scene]) None[source]#

Реєструє одну або кілька сцен у SceneRegistry.

Параметри:

scenes – Один або кілька класів сцен для реєстрації.

Повертає:

None

class aiogram.fsm.scene.ScenesManager(registry: SceneRegistry, update_type: str, event: TelegramObject, state: FSMContext, data: Dict[str, Any])[source]#

Клас ScenesManager відповідає за керування сценами в програмі. Він надає методи входу та виходу зі сцен, а також відновлення активної сцени.

async close(**kwargs: Any) None[source]#

Метод Close використовується для виходу з поточної активної сцени в ScenesManager.

Параметри:

kwargs – Додаткові аргументи ключового слова, передані в метод виходу сцени.

Повертає:

None

async enter(scene_type: Type[Scene] | str | None, _check_active: bool = True, **kwargs: Any) None[source]#

Виходить на вказану сцену.

Параметри:
  • scene_type – Додатково Type[Scene] або str, що представляє тип сцени, який потрібно ввести.

  • _check_active – Необов’язковий параметр, що вказує, чи перевіряти наявність активної сцени для виходу перед входом у нову сцену. За замовчуванням значення True.

  • kwargs – Додаткові іменовані аргументи для передачі в метод wizard.enter() сцени.

Повертає:

None

class aiogram.fsm.scene.SceneConfig(state: 'Optional[str]', handlers: 'List[HandlerContainer]', actions: 'Dict[SceneAction, Dict[str, CallableObject]]', reset_data_on_enter: 'Optional[bool]' = None, reset_history_on_enter: 'Optional[bool]' = None, callback_query_without_state: 'Optional[bool]' = None)[source]#
actions: Dict[SceneAction, Dict[str, CallableObject]]#

Дії сцени

callback_query_without_state: bool | None = None#

Створювати обробники кнопок без перевірки стану поточної сцени

handlers: List[HandlerContainer]#

Обробники сцени

reset_data_on_enter: bool | None = None#

Скинути дані сцени після входу

reset_history_on_enter: bool | None = None#

Скинути історію сцени під час входу

state: str | None#

Стан сцени

class aiogram.fsm.scene.SceneWizard(scene_config: SceneConfig, manager: ScenesManager, state: FSMContext, update_type: str, event: TelegramObject, data: Dict[str, Any])[source]#

Клас, який представляє майстер сцен.

Екземпляр цього класу передається кожній сцені як параметр. Отже, ви можете використовувати його для переходу між сценами, отримання та встановлення даних тощо.

Примітка

Цей клас не призначений для безпосереднього використання. Натомість його слід використовувати як параметр у конструкторі сцени.

async back(**kwargs: Any) None[source]#

Цей метод використовується для повернення до попередньої сцени.

Параметри:

kwargs – Аргументи ключових слів, які можна передати в метод.

Повертає:

None

async clear_data() None[source]#

Очищає дані.

Повертає:

None

async enter(**kwargs: Any) None[source]#

Метод Enter використовується для переходу в сцену в класі SceneWizard. Він встановлює стан, очищає дані та історію, якщо вказано, і запускає введення події сцени.

Параметри:

kwargs – Додаткові іменовані аргументи.

Повертає:

None

async exit(**kwargs: Any) None[source]#

Вийти з поточної сцени та перейти до стандартного стану чи сцени.

Параметри:

kwargs – Додаткові іменовані аргументи.

Повертає:

None

async get_data() Dict[str, Any][source]#

Цей метод повертає дані, що зберігаються в поточному стані.

Повертає:

Словник, що містить дані, що зберігаються в стані сцени.

async goto(scene: Type[Scene] | str, **kwargs: Any) None[source]#

Метод goto переходить до нової сцени. Спочатку він викликає метод leave, щоб виконати будь-яке необхідне очищення в поточній сцені, а потім викликає подію enter, щоб увійти до вказаної сцени.

Параметри:
  • scene – Сцена для переходу. Може бути екземпляром Scene або рядком, що представляє сцену.

  • kwargs – Додаткові іменовані аргументи для точки входу до enter менеджера сцен.

Повертає:

None

async leave(_with_history: bool = True, **kwargs: Any) None[source]#

Залишає поточну сцену. Цей метод використовується для виходу зі сцени та переходу до наступної сцени.

Параметри:
  • _with_history – Чи включати історію в знімок. За замовчуванням значення True.

  • kwargs – Додаткові іменовані аргументи.

Повертає:

None

async retake(**kwargs: Any) None[source]#

Цей метод дозволяє повторно увійти до поточної сцени.

Параметри:

kwargs – Додаткові іменовані аргументи для передачі до сцени.

Повертає:

None

async set_data(data: Dict[str, Any]) None[source]#

Встановлює настроювані дані в поточний стан.

Параметри:

data – Словник, що містить настроювані дані, які потрібно встановити в поточному стані.

Повертає:

None

async update_data(data: Dict[str, Any] | None = None, **kwargs: Any) Dict[str, Any][source]#

Цей метод оновлює дані, що зберігаються в поточному стані

Параметри:
  • data – Додатковий словник даних для оновлення.

  • kwargs – Додаткові пари ключ-значення даних для оновлення.

Повертає:

Словник оновлених даних

Маркери#

Маркери подібні до механізму реєстрації подій Router, але вони використовуються для позначення обробників сцени в класі Scene.

Його можна імпортувати з from aiogram.fsm.scene import on і слід використовувати як декоратор.

Дозволені типи подій:

  • message

  • edited_message

  • channel_post

  • edited_channel_post

  • inline_query

  • chosen_inline_result

  • callback_query

  • shipping_query

  • pre_checkout_query

  • poll

  • poll_answer

  • my_chat_member

  • chat_member

  • chat_join_request

Кожен тип події можна відфільтрувати так само, як і в маршрутизаторі.

Також кожен тип події можна позначити як точку входу в сцену, точку виходу або точку переходу.

Якщо ви хочете позначити, що до сцени можна потрапити з повідомлення або ін-лайн кнопки, вам слід використовувати маркер on.message або on.inline_query:

class MyScene(Scene, name="my_scene"):
    @on.message.enter()
    async def on_enter(self, message: types.Message):
        pass

    @on.callback_query.enter()
    async def on_enter(self, callback_query: types.CallbackQuery):
        pass

Сцени мають три точки для переходів:

  • Точка входу - коли користувач входить до сцени

  • Точка переходу - коли користувач переходить до іншої сцени

  • Точка виходу - коли користувач завершує сцену