[docs]classCallbackData(BaseModel):""" Base class for callback data wrapper This class should be used as super-class of user-defined callbacks. The class-keyword :code:`prefix` is required to define prefix and also the argument :code:`sep` can be passed to define separator (default is :code:`:`). """ifTYPE_CHECKING:__separator__:ClassVar[str]"""Data separator (default is :code:`:`)"""__prefix__:ClassVar[str]"""Callback prefix"""def__init_subclass__(cls,**kwargs:Any)->None:if"prefix"notinkwargs:msg=(f"prefix required, usage example: "f"`class {cls.__name__}(CallbackData, prefix='my_callback'): ...`")raiseValueError(msg)cls.__separator__=kwargs.pop("sep",":")cls.__prefix__=kwargs.pop("prefix")ifcls.__separator__incls.__prefix__:msg=(f"Separator symbol {cls.__separator__!r} can not be used "f"inside prefix {cls.__prefix__!r}")raiseValueError(msg)super().__init_subclass__(**kwargs)def_encode_value(self,key:str,value:Any)->str:ifvalueisNone:return""ifisinstance(value,Enum):returnstr(value.value)ifisinstance(value,UUID):returnvalue.hexifisinstance(value,bool):returnstr(int(value))ifisinstance(value,(int,str,float,Decimal,Fraction)):returnstr(value)msg=(f"Attribute {key}={value!r} of type {type(value).__name__!r}"f" can not be packed to callback data")raiseValueError(msg)
[docs]defpack(self)->str:""" Generate callback data string :return: valid callback data for Telegram Bot API """result=[self.__prefix__]forkey,valueinself.model_dump(mode="python").items():encoded=self._encode_value(key,value)ifself.__separator__inencoded:msg=(f"Separator symbol {self.__separator__!r} can not be used "f"in value {key}={encoded!r}")raiseValueError(msg)result.append(encoded)callback_data=self.__separator__.join(result)iflen(callback_data.encode())>MAX_CALLBACK_LENGTH:msg=(f"Resulted callback data is too long! "f"len({callback_data!r}.encode()) > {MAX_CALLBACK_LENGTH}")raiseValueError(msg)returncallback_data
[docs]@classmethoddefunpack(cls,value:str)->Self:""" Parse callback data string :param value: value from Telegram :return: instance of CallbackData """prefix,*parts=value.split(cls.__separator__)names=cls.model_fields.keys()iflen(parts)!=len(names):msg=(f"Callback data {cls.__name__!r} takes {len(names)} arguments "f"but {len(parts)} were given")raiseTypeError(msg)ifprefix!=cls.__prefix__:msg=f"Bad prefix ({prefix!r} != {cls.__prefix__!r})"raiseValueError(msg)payload={}fork,vinzip(names,parts,strict=True):# type: str, strif((field:=cls.model_fields.get(k))andv==""and_check_field_is_nullable(field)andfield.default!=""):v=field.defaultiffield.defaultisnotPydanticUndefinedelseNonepayload[k]=vreturncls(**payload)
[docs]@classmethoddeffilter(cls,rule:MagicFilter|None=None)->CallbackQueryFilter:""" Generates a filter for callback query with rule :param rule: magic rule :return: instance of filter """returnCallbackQueryFilter(callback_data=cls,rule=rule)
classCallbackQueryFilter(Filter):""" This filter helps to handle callback query. Should not be used directly, you should create the instance of this filter via callback data instance """__slots__=("callback_data","rule",)def__init__(self,*,callback_data:type[CallbackData],rule:MagicFilter|None=None,):""" :param callback_data: Expected type of callback data :param rule: Magic rule """self.callback_data=callback_dataself.rule=ruledef__str__(self)->str:returnself._signature_to_string(callback_data=self.callback_data,rule=self.rule,)asyncdef__call__(self,query:CallbackQuery)->Literal[False]|dict[str,Any]:ifnotisinstance(query,CallbackQuery)ornotquery.data:returnFalsetry:callback_data=self.callback_data.unpack(query.data)except(TypeError,ValueError):returnFalseifself.ruleisNoneorself.rule.resolve(callback_data):return{"callback_data":callback_data}returnFalsedef_check_field_is_nullable(field:FieldInfo)->bool:""" Check if the given field is nullable. :param field: The FieldInfo object representing the field to check. :return: True if the field is nullable, False otherwise. """ifnotfield.is_required():returnTruereturntyping.get_origin(field.annotation)in_UNION_TYPESandtype(None)intyping.get_args(field.annotation,)