Source code for navmenu.menu_manager
from typing import Dict, Optional, Sequence
from .menus import BaseMenu
from .state import StateHandler
from .responses import Message, Response
[docs]class MenuManager:
"""A class that manages menus and transitions between them.
Args:
menus: A dictionary mapping menu names to menus.
state_handler: The menu state manager to store users' states.
"""
__slots__ = 'menus', 'state_handler'
def __init__(self, menus: Dict[str, BaseMenu], state_handler: StateHandler) -> None:
self.menus = menus
self.state_handler = state_handler
def __repr__(self) -> str:
return f'MenuManager({self.menus}, {self.state_handler})'
def _switch_menu(self, user_id: int, menu_name: str, payload: dict) -> Sequence[Message]:
self.state_handler.set(user_id, menu_name)
enter_res = self.menus[menu_name].enter(payload)
if isinstance(enter_res, Message):
return enter_res,
else:
return ()
[docs] def get_message(self, user_id: int = None, payload: Optional[dict] = None) -> Message:
"""Get a message representing the current menu.
Args:
user_id: A value used to identify the user.
payload: An incoming message payload.
Returns:
A message representing the current menu.
"""
state = self.state_handler.get(user_id)
return self.menus[state].get_message(payload)
[docs] def select(self, action: str, user_id: int = None, payload: Optional[dict] = None) -> Sequence[Message]:
"""Select an item in the current menu based on action and payload.
This method handles current menu changes.
Args:
action: A string indicating selected menu button.
user_id: A value used to identify the user.
payload: An incoming message payload.
Returns:
A list of messages.
Raises:
ValueError: An invalid action was provided.
"""
state = self.state_handler.get(user_id)
actions = self.menus[state].select(action, payload)
if actions is not None:
messages = []
for res in actions:
if isinstance(res, Message):
messages.append(res)
elif isinstance(res, Response):
if res.message:
messages.append(res.message)
if res.go_back_count:
self.state_handler.go_back(user_id, res.go_back_count)
if res.menu:
messages += self._switch_menu(user_id, res.menu, payload)
return messages
else:
for menu_name, menu in self.menus.items():
if action.lower() in menu.aliases:
return self._switch_menu(user_id, menu_name, payload)
raise ValueError('An invalid action was provided')
[docs] def serialize(self) -> dict:
"""Serialize the class instance to a dictionary.
Returns:
A serialized class instance.
"""
return {
'menus': {menu_name: {
'type': menu.__class__.__name__,
**menu.serialize(),
} for menu_name, menu in self.menus.items()},
}