from typing import Self, Tuple, TypeVar, Union
from telegram import CallbackQuery
from ..coder_interface import CoderInterface
T = TypeVar("T", bound="Params")
[docs]
class Params:
"""
`Telegram-button` callback query stores some data. When the User clicks the button, this data is returned to the bot.
So, `Params` formalizes storing more than one value in the callback query and fetching it after the button click.
"""
action: str
""" str: All params must be associated with some actions, which are used for handlers reaction route.."""
def __new__(cls, action, **kwargs):
if getattr(cls, "keys", None) is None:
raise Exception(f"classmethod `keys()` not defined in class {cls}")
if getattr(cls, "create", None) is None:
raise Exception(f"classmethod `create()` not defined in class {cls}")
if getattr(cls, "fetch", None) is None:
raise Exception(f"classmethod `fetch()` not defined in class {cls}")
if cls.keys()[0] != "action":
raise Exception(f"first `{cls.__name__}.keys()` item must be `action`")
self = super().__new__(cls)
self.action = action
return self
[docs]
@classmethod
def fetch(
cls,
callback_query: Union[CallbackQuery, None],
coder: CoderInterface,
) -> Self:
"""
Fetch data from `Telegram-button` callback query into a special structure named `Params`.
:param callback_query: `Telegram-button` callback query data
:param coder: algorithm of serializing and deserializing stored in a `Telegram-button` data
:raises CallbackQueryIsNone: when `Telegram-button` has no callback query
:raises CallbackQueryDataIsNone: when `Telegram-button` has empty callback query
"""
if not callback_query:
raise CallbackQueryIsNone()
if not callback_query.data:
raise CallbackQueryDataIsNone()
params_values = coder.fetch_deserialized_data(callback_query.data)
return cls.create(params_values)
[docs]
@classmethod
def create(cls, fields_values):
kwargs = dict()
for i in range(0, len(cls.keys())):
kwargs[cls.keys()[i]] = fields_values[i]
return cls(**kwargs)
[docs]
@classmethod
def keys(cls) -> Tuple[str, ...]:
list_of_attrs = set(cls.__annotations__.keys())
for i in cls.__mro__[1:]:
list_of_super_me = (
getattr(i, "keys")()
if getattr(i, "keys", None) and callable(getattr(i, "keys"))
else []
)
list_of_attrs = list_of_attrs.union(list_of_super_me)
list_of_attrs = list(list_of_attrs)
if "action" in list_of_attrs:
list_of_attrs.remove("action")
list_of_attrs.sort()
list_of_attrs.insert(0, "action")
return tuple(list_of_attrs)
[docs]
def set_action(self, action: str) -> "Params":
self.action = action
return self
[docs]
def get_action(self) -> str:
return self.action
[docs]
def values(self) -> tuple:
return tuple(getattr(self, x) for x in self.keys())
def __str__(self) -> str:
return str(self.values())
def __eq__(self, other: object) -> bool:
if not isinstance(other, self.__class__):
return False
self_values = self.values()
other_values = other.values()
return self_values == other_values
[docs]
class CallbackQueryDataIsNone(Exception):
def __str__(self) -> str:
return "CallbackQuery-data is None"
[docs]
class CallbackQueryIsNone(Exception):
def __str__(self) -> str:
return "CallbackQuery is None"