mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-14 19:00:23 +00:00
* Base implementation
* Small refactoring + added possibility to specify post-action on handlers
* Move scene properties to config object
* Revise aiogram/scenes with wizard-based design pattern
Modified files in aiogram/scenes to incorporate the Wizard design pattern. Files affected are _marker.py, _registry.py, _wizard.py and __init__.py. The changes introduced a SceneWizard Class and ScenesManager, both of which aid in controlling navigation between different scenes or states. This helps clarifying the codebase, streamline scene transitions and offer more control over the app flow.
* Added example
* Small optimizations
* Replace ValueError with SceneException in scenes. Added error safety in scene resolver.
* str
* Added possibility to reset context on scene entered and to handle callback query in any state
* Remove inline markup in example
* Small changes
* Docs + example
* Small refactoring
* Remove scene inclusion methods from router
The methods for including scenes as sub-routers have been removed from the router.py file. Instead, the SceneRegistry class is now set to register scenes by default upon initializing. This streamlines the scene management process by removing redundant routers and making registration automatic.
* Init tests
* Small fix in tests
* Add support for State instance in the scene
The aiogram FSM scene now allows the use of State instance as an argument, enabling more customization. Modified the 'as_handler' method to receive **kwargs arguments, allowing passing of attributes to the handler. An additional type check has been also added to ensure the 'scene' is either a subclass of Scene or a string.
* Fixed test
* Expand test coverage for test_fsm module
The commit enhances tests for the test_fsm module to improve code reliability. It includes additional unit tests for the ObserverDecorator and ActionContainer classes and introduces new tests for the SceneHandlerWrapper class. This ensures the correct functionality of the decorator methods, the action container execution, and the handler wrapper.
* Reformat code
* Fixed long line in the example
* Skip some tests on PyPy
* Change mock return_value
* Compatibility...
* Compatibility...
* Compatibility...
* Added base changes description
* Scenes Tests (#1369)
* ADD tests for `SceneRegistry`
* ADD tests for `ScenesManager`
* ADD Changelog
* Revert "ADD Changelog"
This reverts commit 6dd9301252.
* Remove `@pytest.mark.asyncio`, Reformat code
* Scenes Tests. Part 2 (#1371)
* ADD tests for `SceneWizard`
* ADD tests for `Scene`
* Refactor ObserverDecorator to use on.message syntax in test_scene.py
Cover `Scene::__init_subclass__::if isinstance(value, ObserverDecorator):`
* Refactor `HistoryManager` in `aiogram/fsm/scene.py`
Removed condition that checked if 'history' is empty before calling 'update_data' in 'Scene'.
* ADD tests for `HistoryManager`
* Small changes in the documentation
* Small changes in the documentation
* Small changes in the documentation
---------
Co-authored-by: Andrew <11490628+andrew000@users.noreply.github.com>
243 lines
7.8 KiB
ReStructuredText
243 lines
7.8 KiB
ReStructuredText
.. _Scenes:
|
|
|
|
=============
|
|
Scenes Wizard
|
|
=============
|
|
|
|
.. versionadded:: 3.2
|
|
|
|
.. warning::
|
|
|
|
This feature is experimental and may be changed in future versions.
|
|
|
|
**aiogram's** basics API is easy to use and powerful,
|
|
allowing the implementation of simple interactions such as triggering a command or message
|
|
for a response.
|
|
However, certain tasks require a dialogue between the user and the bot.
|
|
This is where Scenes come into play.
|
|
|
|
Understanding Scenes
|
|
====================
|
|
|
|
A Scene in **aiogram** is like an abstract, isolated namespace or room that a user can be
|
|
ushered into via the code. When a user is inside a Scene, all other global commands or
|
|
message handlers are isolated, and they stop responding to user actions.
|
|
Scenes provide a structure for more complex interactions,
|
|
effectively isolating and managing contexts for different stages of the conversation.
|
|
They allow you to control and manage the flow of the conversation in a more organized manner.
|
|
|
|
Scene Lifecycle
|
|
---------------
|
|
|
|
Each Scene can be "entered", "left" of "exited", allowing for clear transitions between different
|
|
stages of the conversation.
|
|
For instance, in a multi-step form filling interaction, each step could be a Scene -
|
|
the bot guides the user from one Scene to the next as they provide the required information.
|
|
|
|
Scene Listeners
|
|
---------------
|
|
|
|
Scenes have their own hooks which are command or message listeners that only act while
|
|
the user is within the Scene.
|
|
These hooks react to user actions while the user is 'inside' the Scene,
|
|
providing the responses or actions appropriate for that context.
|
|
When the user is ushered from one Scene to another, the actions and responses change
|
|
accordingly as the user is now interacting with the set of listeners inside the new Scene.
|
|
These 'Scene-specific' hooks or listeners, detached from the global listening context,
|
|
allow for more streamlined and organized bot-user interactions.
|
|
|
|
|
|
Scene Interactions
|
|
------------------
|
|
|
|
Each Scene is like a self-contained world, with interactions defined within the scope of that Scene.
|
|
As such, only the handlers defined within the specific Scene will react to user's input during
|
|
the lifecycle of that Scene.
|
|
|
|
|
|
Scene Benefits
|
|
--------------
|
|
|
|
Scenes can help manage more complex interaction workflows and enable more interactive and dynamic
|
|
dialogs between the user and the bot.
|
|
This offers great flexibility in handling multi-step interactions or conversations with the users.
|
|
|
|
How to use Scenes
|
|
=================
|
|
|
|
For example we have a quiz bot, which asks the user a series of questions and then displays the results.
|
|
|
|
Lets start with the data models, in this example simple data models are used to represent
|
|
the questions and answers, in real life you would probably use a database to store the data.
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:lines: 18-94
|
|
:caption: Questions list
|
|
|
|
Then, we need to create a Scene class that will represent the quiz game scene:
|
|
|
|
.. note::
|
|
|
|
Keyword argument passed into class definition describes the scene name - is the same as state of the scene.
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:pyobject: QuizScene
|
|
:emphasize-lines: 1
|
|
:lines: -7
|
|
:caption: Quiz Scene
|
|
|
|
|
|
Also we need to define a handler that helps to start the quiz game:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Start command handler
|
|
:lines: 260-262
|
|
|
|
Once the scene is defined, we need to register it in the SceneRegistry:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:pyobject: create_dispatcher
|
|
:caption: Registering the scene
|
|
|
|
So, now we can implement the quiz game logic, each question is sent to the user one by one,
|
|
and the user's answer is checked at the end of all questions.
|
|
|
|
Now we need to write an entry point for the question handler:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Question handler entry point
|
|
:pyobject: QuizScene.on_enter
|
|
|
|
|
|
Once scene is entered, we should expect the user's answer, so we need to write a handler for it,
|
|
this handler should expect the text message, save the answer and retake
|
|
the question handler for the next question:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Answer handler
|
|
:pyobject: QuizScene.answer
|
|
|
|
When user answer with unknown message, we should expect the text message again:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Unknown message handler
|
|
:pyobject: QuizScene.unknown_message
|
|
|
|
When all questions are answered, we should show the results to the user, as you can see in the code below,
|
|
we use `await self.wizard.exit()` to exit from the scene when questions list is over in the `QuizScene.on_enter` handler.
|
|
|
|
Thats means that we need to write an exit handler to show the results to the user:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Show results handler
|
|
:pyobject: QuizScene.on_exit
|
|
|
|
Also we can implement a actions to exit from the quiz game or go back to the previous question:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Exit handler
|
|
:pyobject: QuizScene.exit
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Back handler
|
|
:pyobject: QuizScene.back
|
|
|
|
Now we can run the bot and test the quiz game:
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Run the bot
|
|
:lines: 291-
|
|
|
|
Complete them all
|
|
|
|
.. literalinclude:: ../../../examples/quiz_scene.py
|
|
:language: python
|
|
:caption: Quiz Example
|
|
|
|
|
|
Components
|
|
==========
|
|
|
|
- :class:`aiogram.fsm.scene.Scene` - represents a scene, contains handlers
|
|
- :class:`aiogram.fsm.scene.SceneRegistry` - container for all scenes in the bot, used to register scenes and resolve them by name
|
|
- :class:`aiogram.fsm.scene.ScenesManager` - manages scenes for each user, used to enter, leave and resolve current scene for user
|
|
- :class:`aiogram.fsm.scene.SceneConfig` - scene configuration, used to configure scene
|
|
- :class:`aiogram.fsm.scene.SceneWizard` - scene wizard, used to interact with user in scene from active scene handler
|
|
- Markers - marker for scene handlers, used to mark scene handlers
|
|
|
|
|
|
.. autoclass:: aiogram.fsm.scene.Scene
|
|
:members:
|
|
|
|
.. autoclass:: aiogram.fsm.scene.SceneRegistry
|
|
:members:
|
|
|
|
.. autoclass:: aiogram.fsm.scene.ScenesManager
|
|
:members:
|
|
|
|
.. autoclass:: aiogram.fsm.scene.SceneConfig
|
|
:members:
|
|
|
|
.. autoclass:: aiogram.fsm.scene.SceneWizard
|
|
:members:
|
|
|
|
Markers
|
|
-------
|
|
|
|
Markers are similar to the Router event registering mechanism,
|
|
but they are used to mark scene handlers in the Scene class.
|
|
|
|
It can be imported from :code:`from aiogram.fsm.scene import on` and should be used as decorator.
|
|
|
|
Allowed event types:
|
|
|
|
- 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
|
|
|
|
Each event type can be filtered in the same way as in the Router.
|
|
|
|
Also each event type can be marked as scene entry point, exit point or leave point.
|
|
|
|
If you want to mark the scene can be entered from message or inline query,
|
|
you should use :code:`on.message` or :code:`on.inline_query` marker:
|
|
|
|
.. code-block:: python
|
|
|
|
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
|
|
|
|
|
|
Scene has only tree points for transitions:
|
|
|
|
- enter point - when user enters to the scene
|
|
- leave point - when user leaves the scene and the enter another scene
|
|
- exit point - when user exits from the scene
|