mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-11 18:01:04 +00:00
Move skeleton for new types from https://bitbucket.org/illemius/demo_telegramobject
This commit is contained in:
parent
d2694a4d3b
commit
9b96133b7a
3 changed files with 289 additions and 229 deletions
|
|
@ -1,133 +1,6 @@
|
||||||
from . import base
|
from . import base
|
||||||
from .animation import Animation
|
from . import fields
|
||||||
from .audio import Audio
|
|
||||||
from .callback_query import CallbackQuery
|
|
||||||
from .chat import Chat, ChatType, ChatActions
|
|
||||||
from .chat_member import ChatMember, ChatMemberStatus
|
|
||||||
from .chat_photo import ChatPhoto
|
|
||||||
from .chosen_inline_result import ChosenInlineResult
|
|
||||||
from .contact import Contact
|
|
||||||
from .document import Document
|
|
||||||
from .file import File
|
|
||||||
from .force_reply import ForceReply
|
|
||||||
from .game import Game
|
|
||||||
from .game_high_score import GameHighScore
|
|
||||||
from .inline_keyboard import InlineKeyboardButton, InlineKeyboardMarkup
|
|
||||||
from .inline_query import InlineQuery
|
|
||||||
from .inline_query_result import InlineQueryResult, InlineQueryResultArticle, InlineQueryResultAudio, \
|
|
||||||
InlineQueryResultCachedAudio, InlineQueryResultCachedDocument, InlineQueryResultCachedGif, \
|
|
||||||
InlineQueryResultCachedMpeg4Gif, InlineQueryResultCachedPhoto, InlineQueryResultCachedSticker, \
|
|
||||||
InlineQueryResultCachedVideo, InlineQueryResultCachedVoice, InlineQueryResultContact, InlineQueryResultDocument, \
|
|
||||||
InlineQueryResultGame, InlineQueryResultGif, InlineQueryResultLocation, InlineQueryResultMpeg4Gif, \
|
|
||||||
InlineQueryResultPhoto, InlineQueryResultVenue, InlineQueryResultVideo, InlineQueryResultVoice, \
|
|
||||||
InputMessageContent, InputContactMessageContent, InputContactMessageContent, InputLocationMessageContent, \
|
|
||||||
InputLocationMessageContent, InputMessageContent, InputTextMessageContent, InputTextMessageContent, \
|
|
||||||
InputVenueMessageContent, InputVenueMessageContent
|
|
||||||
from .invoice import Invoice
|
|
||||||
from .labeled_price import LabeledPrice
|
|
||||||
from .location import Location
|
|
||||||
from .mask_position import MaskPosition
|
|
||||||
from .message import Message, ContentType, ParseMode
|
|
||||||
from .message_entity import MessageEntity
|
|
||||||
from .order_info import OrderInfo
|
|
||||||
from .photo_size import PhotoSize
|
|
||||||
from .pre_checkout_query import PreCheckoutQuery
|
|
||||||
from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
|
||||||
from .response_parameters import ResponseParameters
|
|
||||||
from .shipping_address import ShippingAddress
|
|
||||||
from .shipping_option import ShippingOption
|
|
||||||
from .shipping_query import ShippingQuery
|
|
||||||
from .sticker import Sticker
|
|
||||||
from .sticker_set import StickerSet
|
|
||||||
from .successful_payment import SuccessfulPayment
|
|
||||||
from .update import Update
|
|
||||||
from .user import User
|
|
||||||
from .user_profile_photos import UserProfilePhotos
|
|
||||||
from .venue import Venue
|
|
||||||
from .video import Video
|
|
||||||
from .video_note import VideoNote
|
|
||||||
from .voice import Voice
|
|
||||||
from .webhook_info import WebhookInfo
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = (
|
||||||
'base',
|
'base', 'fields'
|
||||||
'Animation',
|
)
|
||||||
'Audio',
|
|
||||||
'CallbackQuery',
|
|
||||||
'Chat',
|
|
||||||
'ChatActions',
|
|
||||||
'ChatMember',
|
|
||||||
'ChatMemberStatus',
|
|
||||||
'ChatPhoto',
|
|
||||||
'ChatType',
|
|
||||||
'ChosenInlineResult',
|
|
||||||
'Contact',
|
|
||||||
'ContentType',
|
|
||||||
'Document',
|
|
||||||
'File',
|
|
||||||
'ForceReply',
|
|
||||||
'Game',
|
|
||||||
'GameHighScore',
|
|
||||||
'InlineKeyboardButton',
|
|
||||||
'InlineKeyboardMarkup',
|
|
||||||
'InlineQuery',
|
|
||||||
'InlineQueryResult',
|
|
||||||
'InlineQueryResultArticle',
|
|
||||||
'InlineQueryResultAudio',
|
|
||||||
'InlineQueryResultCachedAudio',
|
|
||||||
'InlineQueryResultCachedDocument',
|
|
||||||
'InlineQueryResultCachedGif',
|
|
||||||
'InlineQueryResultCachedMpeg4Gif',
|
|
||||||
'InlineQueryResultCachedPhoto',
|
|
||||||
'InlineQueryResultCachedSticker',
|
|
||||||
'InlineQueryResultCachedVideo',
|
|
||||||
'InlineQueryResultCachedVoice',
|
|
||||||
'InlineQueryResultContact',
|
|
||||||
'InlineQueryResultDocument',
|
|
||||||
'InlineQueryResultGame',
|
|
||||||
'InlineQueryResultGif',
|
|
||||||
'InlineQueryResultLocation',
|
|
||||||
'InlineQueryResultMpeg4Gif',
|
|
||||||
'InlineQueryResultPhoto',
|
|
||||||
'InlineQueryResultVenue',
|
|
||||||
'InlineQueryResultVideo',
|
|
||||||
'InlineQueryResultVoice',
|
|
||||||
'InputMessageContent',
|
|
||||||
'InputContactMessageContent',
|
|
||||||
'InputContactMessageContent',
|
|
||||||
'InputLocationMessageContent',
|
|
||||||
'InputLocationMessageContent',
|
|
||||||
'InputMessageContent',
|
|
||||||
'InputTextMessageContent',
|
|
||||||
'InputTextMessageContent',
|
|
||||||
'InputVenueMessageContent',
|
|
||||||
'InputVenueMessageContent',
|
|
||||||
'Invoice',
|
|
||||||
'KeyboardButton',
|
|
||||||
'LabeledPrice',
|
|
||||||
'Location',
|
|
||||||
'MaskPosition'
|
|
||||||
'Message',
|
|
||||||
'MessageEntity',
|
|
||||||
'OrderInfo',
|
|
||||||
'ParseMode',
|
|
||||||
'PhotoSize',
|
|
||||||
'PreCheckoutQuery',
|
|
||||||
'ReplyKeyboardMarkup',
|
|
||||||
'ReplyKeyboardRemove',
|
|
||||||
'ResponseParameters',
|
|
||||||
'ShippingAddress',
|
|
||||||
'ShippingOption',
|
|
||||||
'ShippingQuery',
|
|
||||||
'Sticker',
|
|
||||||
'StickerSet'
|
|
||||||
'SuccessfulPayment',
|
|
||||||
'Update',
|
|
||||||
'User',
|
|
||||||
'UserProfilePhotos',
|
|
||||||
'Venue',
|
|
||||||
'Video',
|
|
||||||
'VideoNote',
|
|
||||||
'Voice',
|
|
||||||
'WebhookInfo'
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -1,119 +1,171 @@
|
||||||
import datetime
|
import typing
|
||||||
import time
|
import ujson
|
||||||
|
|
||||||
|
from .fields import BaseField
|
||||||
|
|
||||||
|
PROPS_ATTR_NAME = '_props'
|
||||||
|
VALUES_ATTR_NAME = '_values'
|
||||||
|
ALIASES_ATTR_NAME = '_aliases'
|
||||||
|
|
||||||
|
__all__ = ('MetaTelegramObject', 'TelegramObject')
|
||||||
|
|
||||||
|
|
||||||
def deserialize(deserializable, data):
|
class MetaTelegramObject(type):
|
||||||
"""
|
"""
|
||||||
Deserialize object if have data
|
Metaclass for telegram objects
|
||||||
|
"""
|
||||||
|
_objects = {}
|
||||||
|
|
||||||
|
def __new__(mcs, name, bases, namespace, **kwargs):
|
||||||
|
cls = super(MetaTelegramObject, mcs).__new__(mcs, name, bases, namespace)
|
||||||
|
|
||||||
|
props = {}
|
||||||
|
values = {}
|
||||||
|
aliases = {}
|
||||||
|
|
||||||
|
# Get props, values, aliases from parent objects
|
||||||
|
for base in bases:
|
||||||
|
if not isinstance(base, MetaTelegramObject):
|
||||||
|
continue
|
||||||
|
props.update(getattr(base, PROPS_ATTR_NAME))
|
||||||
|
values.update(getattr(base, VALUES_ATTR_NAME))
|
||||||
|
aliases.update(getattr(base, ALIASES_ATTR_NAME))
|
||||||
|
|
||||||
|
# Scan current object for props
|
||||||
|
for name, prop in ((name, prop) for name, prop in namespace.items() if isinstance(prop, BaseField)):
|
||||||
|
props[prop.alias] = prop
|
||||||
|
if prop.default is not None:
|
||||||
|
values[prop.alias] = prop.default
|
||||||
|
aliases[name] = prop.alias
|
||||||
|
|
||||||
|
# Set attributes
|
||||||
|
setattr(cls, PROPS_ATTR_NAME, props)
|
||||||
|
setattr(cls, VALUES_ATTR_NAME, values)
|
||||||
|
setattr(cls, ALIASES_ATTR_NAME, aliases)
|
||||||
|
|
||||||
|
mcs._objects[cls.__name__] = cls
|
||||||
|
return cls
|
||||||
|
|
||||||
|
@property
|
||||||
|
def telegram_types(cls):
|
||||||
|
return cls._objects
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramObject(metaclass=MetaTelegramObject):
|
||||||
|
"""
|
||||||
|
Abstract class for telegram objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, conf=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Deserialize object
|
||||||
|
|
||||||
|
:param conf:
|
||||||
|
:param kwargs:
|
||||||
|
"""
|
||||||
|
if conf is None:
|
||||||
|
conf = {}
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
if key in self.props:
|
||||||
|
self.props[key].set_value(self, value)
|
||||||
|
else:
|
||||||
|
self.values[key] = value
|
||||||
|
self._conf = conf
|
||||||
|
|
||||||
|
@property
|
||||||
|
def conf(self) -> typing.Dict[str, typing.Any]:
|
||||||
|
return self.conf
|
||||||
|
|
||||||
|
@property
|
||||||
|
def props(self) -> typing.Dict[str, BaseField]:
|
||||||
|
"""
|
||||||
|
Get props
|
||||||
|
|
||||||
|
:return: dict with props
|
||||||
|
"""
|
||||||
|
return getattr(self, PROPS_ATTR_NAME, {})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def props_aliases(self) -> typing.Dict[str, str]:
|
||||||
|
"""
|
||||||
|
Get aliases for props
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return getattr(self, ALIASES_ATTR_NAME, {})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self):
|
||||||
|
"""
|
||||||
|
Get values
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return getattr(self, VALUES_ATTR_NAME, {})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def telegram_types(self):
|
||||||
|
return type(self).telegram_types
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_object(cls, data):
|
||||||
|
"""
|
||||||
|
Deserialize object
|
||||||
|
|
||||||
:param deserializable: :class:`aiogram.types.Deserializable`
|
|
||||||
:param data:
|
:param data:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if data:
|
return cls(**data)
|
||||||
return deserializable.de_json(data)
|
|
||||||
|
|
||||||
|
def to_python(self) -> typing.Dict:
|
||||||
def deserialize_array(deserializable, array):
|
|
||||||
"""
|
"""
|
||||||
Deserialize array of objects
|
Get object as JSON serializable
|
||||||
|
|
||||||
:param deserializable:
|
|
||||||
:param array:
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if array:
|
self.clean()
|
||||||
return [deserialize(deserializable, item) for item in array]
|
|
||||||
|
|
||||||
|
|
||||||
class Serializable:
|
|
||||||
"""
|
|
||||||
Subclasses of this class are guaranteed to be able to be created from a json-style dict.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
"""
|
|
||||||
Returns a JSON representation of this class.
|
|
||||||
|
|
||||||
:return: dict
|
|
||||||
"""
|
|
||||||
return {k: v.to_json() if hasattr(v, 'to_json') else v for k, v in self.__dict__.items() if
|
|
||||||
not k.startswith('_')}
|
|
||||||
|
|
||||||
|
|
||||||
class Deserializable:
|
|
||||||
"""
|
|
||||||
Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string.
|
|
||||||
All subclasses of this class must override de_json.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
result = {}
|
result = {}
|
||||||
for name, attr in self.__dict__.items():
|
for name, value in self.values.items():
|
||||||
if not attr or name in ['_bot', '_parent']:
|
if name in self.props:
|
||||||
continue
|
value = self.props[name].export(self)
|
||||||
if hasattr(attr, 'to_json'):
|
if isinstance(value, TelegramObject):
|
||||||
attr = getattr(attr, 'to_json')()
|
value = value.to_python()
|
||||||
elif isinstance(attr, datetime.datetime):
|
result[self.props_aliases.get(name, name)] = value
|
||||||
attr = int(time.mktime(attr.timetuple()))
|
|
||||||
result[name] = attr
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
def clean(self):
|
||||||
def _parse_date(cls, unix_time):
|
|
||||||
if unix_time:
|
|
||||||
return datetime.datetime.fromtimestamp(unix_time)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bot(self) -> 'Bot':
|
|
||||||
"""
|
"""
|
||||||
Bot instance
|
Remove empty values
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, '_bot'):
|
for key, value in self.values.copy().items():
|
||||||
raise AttributeError("{0} is not configured.".format(self.__class__.__name__))
|
if value is None:
|
||||||
return getattr(self, '_bot')
|
del self.values[key]
|
||||||
|
|
||||||
@bot.setter
|
def as_json(self) -> str:
|
||||||
def bot(self, bot):
|
|
||||||
setattr(self, '_bot', bot)
|
|
||||||
for name, attr in self.__dict__.items():
|
|
||||||
if hasattr(attr, 'de_json'):
|
|
||||||
attr.bot = bot
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parent(self):
|
|
||||||
"""
|
"""
|
||||||
Parent object
|
Get object as JSON string
|
||||||
|
|
||||||
|
:return: JSON
|
||||||
|
:rtype: :obj:`str`
|
||||||
"""
|
"""
|
||||||
return getattr(self, '_parent', None)
|
return ujson.dumps(self.to_python())
|
||||||
|
|
||||||
@parent.setter
|
def __str__(self) -> str:
|
||||||
def parent(self, value):
|
|
||||||
setattr(self, '_parent', value)
|
|
||||||
for name, attr in self.__dict__.items():
|
|
||||||
if name.startswith('_'):
|
|
||||||
continue
|
|
||||||
if hasattr(attr, 'de_json'):
|
|
||||||
attr.parent = self
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def de_json(cls, raw_data):
|
|
||||||
"""
|
"""
|
||||||
Returns an instance of this class from the given json dict or string.
|
Return object as string. Alias for '.as_json()'
|
||||||
|
|
||||||
This function must be overridden by subclasses.
|
:return: str
|
||||||
:return: an instance of this class created from the given json dict or string.
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
return self.as_json()
|
||||||
|
|
||||||
def __str__(self):
|
def __getitem__(self, item):
|
||||||
return str(self.to_json())
|
if item in self.props:
|
||||||
|
return getattr(self, item)
|
||||||
|
elif item in self.values:
|
||||||
|
return self.values[item]
|
||||||
|
|
||||||
def __repr__(self):
|
def __setitem__(self, key, value):
|
||||||
return str(self)
|
if key in self.props:
|
||||||
|
setattr(self, key, value)
|
||||||
@classmethod
|
else:
|
||||||
def deserialize(cls, obj):
|
self.values[key] = value
|
||||||
if isinstance(obj, list):
|
|
||||||
return deserialize_array(cls, obj)
|
|
||||||
return deserialize(cls, obj)
|
|
||||||
|
|
|
||||||
135
aiogram/types/fields.py
Normal file
135
aiogram/types/fields.py
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import abc
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
__all__ = ('BaseField', 'Field', 'ListField', 'DateTimeField')
|
||||||
|
|
||||||
|
|
||||||
|
class BaseField(metaclass=abc.ABCMeta):
|
||||||
|
"""
|
||||||
|
Base field (prop)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *, base=None, default=None, alias=None):
|
||||||
|
"""
|
||||||
|
Init prop
|
||||||
|
|
||||||
|
:param base: class for child element
|
||||||
|
:param default: default value
|
||||||
|
:param alias: alias name (for e.g. field named 'from' must be has name 'from_user'
|
||||||
|
('from' is builtin Python keyword)
|
||||||
|
"""
|
||||||
|
self.base_object = base
|
||||||
|
self.default = default
|
||||||
|
self.alias = alias
|
||||||
|
|
||||||
|
def __set_name__(self, owner, name):
|
||||||
|
if self.alias is None:
|
||||||
|
self.alias = name
|
||||||
|
|
||||||
|
def resolve_base(self, instance):
|
||||||
|
if self.base_object is None or hasattr(self.base_object, 'telegram_types'):
|
||||||
|
return
|
||||||
|
elif isinstance(self.base_object, str):
|
||||||
|
self.base_object = instance.telegram_types.get(self.base_object)
|
||||||
|
|
||||||
|
def get_value(self, instance):
|
||||||
|
"""
|
||||||
|
Get value for current object instance
|
||||||
|
|
||||||
|
:param instance:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return instance.values.get(self.alias)
|
||||||
|
|
||||||
|
def set_value(self, instance, value):
|
||||||
|
"""
|
||||||
|
Set prop value
|
||||||
|
|
||||||
|
:param instance:
|
||||||
|
:param value:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.resolve_base(instance)
|
||||||
|
value = self.deserialize(value)
|
||||||
|
instance.values[self.alias] = value
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
return self.get_value(instance)
|
||||||
|
|
||||||
|
def __set__(self, instance, value):
|
||||||
|
self.set_value(instance, value)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def serialize(self, value):
|
||||||
|
"""
|
||||||
|
Serialize value to python
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def deserialize(self, value):
|
||||||
|
"""Deserialize python object value to TelegramObject value"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def export(self, instance):
|
||||||
|
"""
|
||||||
|
Alias for `serialize` but for current Object instance
|
||||||
|
|
||||||
|
:param instance:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return self.serialize(self.get_value(instance))
|
||||||
|
|
||||||
|
|
||||||
|
class Field(BaseField):
|
||||||
|
"""
|
||||||
|
Simple field
|
||||||
|
"""
|
||||||
|
|
||||||
|
def serialize(self, value):
|
||||||
|
if self.base_object is not None:
|
||||||
|
return value.to_python()
|
||||||
|
return value
|
||||||
|
|
||||||
|
def deserialize(self, value):
|
||||||
|
if self.base_object is not None and not hasattr(value, 'base_object'):
|
||||||
|
return self.base_object(**value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class ListField(Field):
|
||||||
|
"""
|
||||||
|
Field contains list ob objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
def serialize(self, value):
|
||||||
|
result = []
|
||||||
|
serialize = super(ListField, self).serialize
|
||||||
|
for item in value:
|
||||||
|
result.append(serialize(item))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def deserialize(self, value):
|
||||||
|
result = []
|
||||||
|
deserialize = super(ListField, self).deserialize
|
||||||
|
for item in value:
|
||||||
|
result.append(deserialize(item))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class DateTimeField(BaseField):
|
||||||
|
"""
|
||||||
|
In this field stored datetime
|
||||||
|
|
||||||
|
in: unixtime
|
||||||
|
out: datetime
|
||||||
|
"""
|
||||||
|
|
||||||
|
def serialize(self, value: datetime.datetime):
|
||||||
|
return round(value.timestamp())
|
||||||
|
|
||||||
|
def deserialize(self, value):
|
||||||
|
return datetime.datetime.fromtimestamp(value)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue