from abc import ABC, abstractmethod
from typing import Iterator, Optional, Union
from .actions import Action
from .item_contents import ItemContent
from .responses import Message, Response
[docs]class BaseItem(ABC):
"""A generic menu item.
Args:
name: The internal item name.
action: The action to execute on item select.
"""
__slots__ = 'name', 'action'
def __init__(self, name: str = None, action: Action = None) -> None:
self.name = name
self.action = action
def __repr__(self) -> str:
return f'BaseItem({repr(self.name)}, {self.action})'
[docs] @abstractmethod
def get_content(self) -> dict:
"""Get the menu item content.
Returns:
The item content.
"""
pass
[docs] def is_available(self, payload: Optional[dict] = None) -> bool:
"""Check whether the menu item is available.
Args:
payload: An incoming message payload.
Returns:
A boolean indicating whether the menu item is available.
"""
return True
[docs] def on_select(self, payload: Optional[dict] = None) -> Iterator[Union[Message, Response]]:
"""Process the payload and return actions.
Args:
payload: An incoming message payload.
Returns:
A sequence of responses.
"""
if self.action is None:
return ()
actions = (self.action, ) if isinstance(self.action, Action) else self.action
return (action.process(payload) for action in actions)
[docs] def serialize(self) -> dict:
"""Serialize the class instance to a dictionary.
Returns:
A serialized class instance.
"""
res = {}
if self.name is not None:
res['name'] = self.name
if self.action is not None:
res['action'] = {
'type': self.action.__class__.__name__,
**self.action.serialize(),
}
return res
[docs]class Item(BaseItem):
"""A menu item with content.
Args:
name: The internal item name.
content: The item content.
action: The action to execute on item select.
"""
__slots__ = 'content',
def __init__(self, name: str, content: ItemContent, action: Action = None) -> None:
super().__init__(name, action)
self.content = content
def __repr__(self) -> str:
return f'Item({repr(self.name)}, {self.content}, {self.action})'
[docs] def get_content(self) -> dict:
return self.content.get_content(self.name)
[docs] def serialize(self) -> dict:
res = super().serialize()
res['content'] = {
'type': self.content.__class__.__name__,
**self.content.serialize(),
}
return res
[docs]class LineBreakItem(BaseItem):
"""A line break."""
__slots__ = ()
def __repr__(self) -> str:
return 'LineBreakItem()'
[docs] def get_content(self) -> dict:
return {
'type': 'line_break',
}
[docs]class ConditionalItem(Item):
"""A menu item that is available only on certain condition.
Args:
name: The internal item name.
content: The item content.
action: The action to execute on item select.
condition: The condition to check.
"""
__slots__ = '_condition_func', 'condition'
def __init__(self, name: str, content: ItemContent, action: Action, condition: str) -> None:
super().__init__(name, content, action)
self.condition = condition
self._condition_func = eval(condition)
def __repr__(self) -> str:
return f'ConditionalItem({repr(self.name)}, {self.content}, {self.action}, {repr(self.condition)})'
[docs] def is_available(self, payload: Optional[dict] = None) -> bool:
return self._condition_func(payload)
[docs] def serialize(self) -> dict:
res = super().serialize()
res['condition'] = self.condition
return res