434 lines
19 KiB
Python
434 lines
19 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# A library that provides a Python interface to the Telegram Bot API
|
|
# Copyright (C) 2015-2025
|
|
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser Public License
|
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
|
"""This module contains objects that represent owned gifts."""
|
|
|
|
import datetime as dtm
|
|
from collections.abc import Sequence
|
|
from typing import TYPE_CHECKING, Final, Optional
|
|
|
|
from telegram import constants
|
|
from telegram._gifts import Gift
|
|
from telegram._messageentity import MessageEntity
|
|
from telegram._telegramobject import TelegramObject
|
|
from telegram._uniquegift import UniqueGift
|
|
from telegram._user import User
|
|
from telegram._utils import enum
|
|
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
|
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
|
from telegram._utils.entities import parse_message_entities, parse_message_entity
|
|
from telegram._utils.types import JSONDict
|
|
|
|
if TYPE_CHECKING:
|
|
from telegram import Bot
|
|
|
|
|
|
class OwnedGift(TelegramObject):
|
|
"""This object describes a gift received and owned by a user or a chat. Currently, it
|
|
can be one of:
|
|
|
|
* :class:`telegram.OwnedGiftRegular`
|
|
* :class:`telegram.OwnedGiftUnique`
|
|
|
|
Objects of this class are comparable in terms of equality. Two objects of this class are
|
|
considered equal, if their :attr:`type` is equal.
|
|
|
|
.. versionadded:: 22.1
|
|
|
|
Args:
|
|
type (:obj:`str`): Type of the owned gift.
|
|
|
|
Attributes:
|
|
type (:obj:`str`): Type of the owned gift.
|
|
"""
|
|
|
|
__slots__ = ("type",)
|
|
|
|
REGULAR: Final[str] = constants.OwnedGiftType.REGULAR
|
|
""":const:`telegram.constants.OwnedGiftType.REGULAR`"""
|
|
UNIQUE: Final[str] = constants.OwnedGiftType.UNIQUE
|
|
""":const:`telegram.constants.OwnedGiftType.UNIQUE`"""
|
|
|
|
def __init__(
|
|
self,
|
|
type: str, # pylint: disable=redefined-builtin
|
|
*,
|
|
api_kwargs: Optional[JSONDict] = None,
|
|
) -> None:
|
|
super().__init__(api_kwargs=api_kwargs)
|
|
self.type: str = enum.get_member(constants.OwnedGiftType, type, type)
|
|
|
|
self._id_attrs = (self.type,)
|
|
self._freeze()
|
|
|
|
@classmethod
|
|
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "OwnedGift":
|
|
"""Converts JSON data to the appropriate :class:`OwnedGift` object, i.e. takes
|
|
care of selecting the correct subclass.
|
|
|
|
Args:
|
|
data (dict[:obj:`str`, ...]): The JSON data.
|
|
bot (:class:`telegram.Bot`, optional): The bot associated with this object.
|
|
|
|
Returns:
|
|
The Telegram object.
|
|
|
|
"""
|
|
data = cls._parse_data(data)
|
|
|
|
_class_mapping: dict[str, type[OwnedGift]] = {
|
|
cls.REGULAR: OwnedGiftRegular,
|
|
cls.UNIQUE: OwnedGiftUnique,
|
|
}
|
|
|
|
if cls is OwnedGift and data.get("type") in _class_mapping:
|
|
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
|
|
|
return super().de_json(data=data, bot=bot)
|
|
|
|
|
|
class OwnedGifts(TelegramObject):
|
|
"""Contains the list of gifts received and owned by a user or a chat.
|
|
|
|
Objects of this class are comparable in terms of equality. Two objects of this class are
|
|
considered equal, if their :attr:`total_count` and :attr:`gifts` are equal.
|
|
|
|
.. versionadded:: 22.1
|
|
|
|
Args:
|
|
total_count (:obj:`int`): The total number of gifts owned by the user or the chat.
|
|
gifts (Sequence[:class:`telegram.OwnedGift`]): The list of gifts.
|
|
next_offset (:obj:`str`, optional): Offset for the next request. If empty,
|
|
then there are no more results.
|
|
|
|
Attributes:
|
|
total_count (:obj:`int`): The total number of gifts owned by the user or the chat.
|
|
gifts (Sequence[:class:`telegram.OwnedGift`]): The list of gifts.
|
|
next_offset (:obj:`str`): Optional. Offset for the next request. If empty,
|
|
then there are no more results.
|
|
"""
|
|
|
|
__slots__ = (
|
|
"gifts",
|
|
"next_offset",
|
|
"total_count",
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
total_count: int,
|
|
gifts: Sequence[OwnedGift],
|
|
next_offset: Optional[str] = None,
|
|
*,
|
|
api_kwargs: Optional[JSONDict] = None,
|
|
):
|
|
super().__init__(api_kwargs=api_kwargs)
|
|
self.total_count: int = total_count
|
|
self.gifts: tuple[OwnedGift, ...] = parse_sequence_arg(gifts)
|
|
self.next_offset: Optional[str] = next_offset
|
|
|
|
self._id_attrs = (self.total_count, self.gifts)
|
|
|
|
self._freeze()
|
|
|
|
@classmethod
|
|
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "OwnedGifts":
|
|
"""See :meth:`telegram.TelegramObject.de_json`."""
|
|
data = cls._parse_data(data)
|
|
|
|
data["gifts"] = de_list_optional(data.get("gifts"), OwnedGift, bot)
|
|
return super().de_json(data=data, bot=bot)
|
|
|
|
|
|
class OwnedGiftRegular(OwnedGift):
|
|
"""Describes a regular gift owned by a user or a chat.
|
|
|
|
Objects of this class are comparable in terms of equality. Two objects of this class are
|
|
considered equal, if their :attr:`gift` and :attr:`send_date` are equal.
|
|
|
|
.. versionadded:: 22.1
|
|
|
|
Args:
|
|
gift (:class:`telegram.Gift`): Information about the regular gift.
|
|
owned_gift_id (:obj:`str`, optional): Unique identifier of the gift for the bot; for
|
|
gifts received on behalf of business accounts only.
|
|
sender_user (:class:`telegram.User`, optional): Sender of the gift if it is a known user.
|
|
send_date (:obj:`datetime.datetime`): Date the gift was sent as :class:`datetime.datetime`.
|
|
|datetime_localization|.
|
|
text (:obj:`str`, optional): Text of the message that was added to the gift.
|
|
entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities that
|
|
appear in the text.
|
|
is_private (:obj:`bool`, optional): :obj:`True`, if the sender and gift text are shown
|
|
only to the gift receiver; otherwise, everyone will be able to see them.
|
|
is_saved (:obj:`bool`, optional): :obj:`True`, if the gift is displayed on the account's
|
|
profile page; for gifts received on behalf of business accounts only.
|
|
can_be_upgraded (:obj:`bool`, optional): :obj:`True`, if the gift can be upgraded to a
|
|
unique gift; for gifts received on behalf of business accounts only.
|
|
was_refunded (:obj:`bool`, optional): :obj:`True`, if the gift was refunded and isn't
|
|
available anymore.
|
|
convert_star_count (:obj:`int`, optional): Number of Telegram Stars that can be
|
|
claimed by the receiver instead of the gift; omitted if the gift cannot be converted
|
|
to Telegram Stars.
|
|
prepaid_upgrade_star_count (:obj:`int`, optional): Number of Telegram Stars that were
|
|
paid by the sender for the ability to upgrade the gift.
|
|
|
|
Attributes:
|
|
type (:obj:`str`): Type of the gift, always :attr:`~telegram.OwnedGift.REGULAR`.
|
|
gift (:class:`telegram.Gift`): Information about the regular gift.
|
|
owned_gift_id (:obj:`str`): Optional. Unique identifier of the gift for the bot; for
|
|
gifts received on behalf of business accounts only.
|
|
sender_user (:class:`telegram.User`): Optional. Sender of the gift if it is a known user.
|
|
send_date (:obj:`datetime.datetime`): Date the gift was sent as :class:`datetime.datetime`.
|
|
|datetime_localization|.
|
|
text (:obj:`str`): Optional. Text of the message that was added to the gift.
|
|
entities (Sequence[:class:`telegram.MessageEntity`]): Optional. Special entities that
|
|
appear in the text.
|
|
is_private (:obj:`bool`): Optional. :obj:`True`, if the sender and gift text are shown
|
|
only to the gift receiver; otherwise, everyone will be able to see them.
|
|
is_saved (:obj:`bool`): Optional. :obj:`True`, if the gift is displayed on the account's
|
|
profile page; for gifts received on behalf of business accounts only.
|
|
can_be_upgraded (:obj:`bool`): Optional. :obj:`True`, if the gift can be upgraded to a
|
|
unique gift; for gifts received on behalf of business accounts only.
|
|
was_refunded (:obj:`bool`): Optional. :obj:`True`, if the gift was refunded and isn't
|
|
available anymore.
|
|
convert_star_count (:obj:`int`): Optional. Number of Telegram Stars that can be
|
|
claimed by the receiver instead of the gift; omitted if the gift cannot be converted
|
|
to Telegram Stars.
|
|
prepaid_upgrade_star_count (:obj:`int`): Optional. Number of Telegram Stars that were
|
|
paid by the sender for the ability to upgrade the gift.
|
|
|
|
"""
|
|
|
|
__slots__ = (
|
|
"can_be_upgraded",
|
|
"convert_star_count",
|
|
"entities",
|
|
"gift",
|
|
"is_private",
|
|
"is_saved",
|
|
"owned_gift_id",
|
|
"prepaid_upgrade_star_count",
|
|
"send_date",
|
|
"sender_user",
|
|
"text",
|
|
"was_refunded",
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
gift: Gift,
|
|
send_date: dtm.datetime,
|
|
owned_gift_id: Optional[str] = None,
|
|
sender_user: Optional[User] = None,
|
|
text: Optional[str] = None,
|
|
entities: Optional[Sequence[MessageEntity]] = None,
|
|
is_private: Optional[bool] = None,
|
|
is_saved: Optional[bool] = None,
|
|
can_be_upgraded: Optional[bool] = None,
|
|
was_refunded: Optional[bool] = None,
|
|
convert_star_count: Optional[int] = None,
|
|
prepaid_upgrade_star_count: Optional[int] = None,
|
|
*,
|
|
api_kwargs: Optional[JSONDict] = None,
|
|
) -> None:
|
|
super().__init__(type=OwnedGift.REGULAR, api_kwargs=api_kwargs)
|
|
|
|
with self._unfrozen():
|
|
self.gift: Gift = gift
|
|
self.send_date: dtm.datetime = send_date
|
|
self.owned_gift_id: Optional[str] = owned_gift_id
|
|
self.sender_user: Optional[User] = sender_user
|
|
self.text: Optional[str] = text
|
|
self.entities: tuple[MessageEntity, ...] = parse_sequence_arg(entities)
|
|
self.is_private: Optional[bool] = is_private
|
|
self.is_saved: Optional[bool] = is_saved
|
|
self.can_be_upgraded: Optional[bool] = can_be_upgraded
|
|
self.was_refunded: Optional[bool] = was_refunded
|
|
self.convert_star_count: Optional[int] = convert_star_count
|
|
self.prepaid_upgrade_star_count: Optional[int] = prepaid_upgrade_star_count
|
|
|
|
self._id_attrs = (self.type, self.gift, self.send_date)
|
|
|
|
@classmethod
|
|
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "OwnedGiftRegular":
|
|
"""See :meth:`telegram.OwnedGift.de_json`."""
|
|
data = cls._parse_data(data)
|
|
|
|
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
|
data["send_date"] = from_timestamp(data.get("send_date"), tzinfo=loc_tzinfo)
|
|
data["sender_user"] = de_json_optional(data.get("sender_user"), User, bot)
|
|
data["gift"] = de_json_optional(data.get("gift"), Gift, bot)
|
|
data["entities"] = de_list_optional(data.get("entities"), MessageEntity, bot)
|
|
|
|
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
|
|
|
def parse_entity(self, entity: MessageEntity) -> str:
|
|
"""Returns the text in :attr:`text`
|
|
from a given :class:`telegram.MessageEntity` of :attr:`entities`.
|
|
|
|
Note:
|
|
This method is present because Telegram calculates the offset and length in
|
|
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
|
|
(That is, you can't just slice ``OwnedGiftRegular.text`` with the offset and length.)
|
|
|
|
Args:
|
|
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
|
|
be an entity that belongs to :attr:`entities`.
|
|
|
|
Returns:
|
|
:obj:`str`: The text of the given entity.
|
|
|
|
Raises:
|
|
RuntimeError: If the owned gift has no text.
|
|
|
|
"""
|
|
if not self.text:
|
|
raise RuntimeError("This OwnedGiftRegular has no 'text'.")
|
|
|
|
return parse_message_entity(self.text, entity)
|
|
|
|
def parse_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]:
|
|
"""
|
|
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
|
|
It contains entities from this owned gift's text filtered by their ``type`` attribute as
|
|
the key, and the text that each entity belongs to as the value of the :obj:`dict`.
|
|
|
|
Note:
|
|
This method should always be used instead of the :attr:`entities`
|
|
attribute, since it calculates the correct substring from the message text based on
|
|
UTF-16 codepoints. See :attr:`parse_entity` for more info.
|
|
|
|
Args:
|
|
types (list[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the
|
|
``type`` attribute of an entity is contained in this list, it will be returned.
|
|
Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`.
|
|
|
|
Returns:
|
|
dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
|
|
the text that belongs to them, calculated based on UTF-16 codepoints.
|
|
|
|
Raises:
|
|
RuntimeError: If the owned gift has no text.
|
|
|
|
"""
|
|
if not self.text:
|
|
raise RuntimeError("This OwnedGiftRegular has no 'text'.")
|
|
|
|
return parse_message_entities(self.text, self.entities, types)
|
|
|
|
|
|
class OwnedGiftUnique(OwnedGift):
|
|
"""
|
|
Describes a unique gift received and owned by a user or a chat.
|
|
|
|
Objects of this class are comparable in terms of equality. Two objects of this class are
|
|
considered equal, if their :attr:`gift` and :attr:`send_date` are equal.
|
|
|
|
.. versionadded:: 22.1
|
|
|
|
Args:
|
|
gift (:class:`telegram.UniqueGift`): Information about the unique gift.
|
|
owned_gift_id (:obj:`str`, optional): Unique identifier of the received gift for the
|
|
bot; for gifts received on behalf of business accounts only.
|
|
sender_user (:class:`telegram.User`, optional): Sender of the gift if it is a known user.
|
|
send_date (:obj:`datetime.datetime`): Date the gift was sent as :class:`datetime.datetime`.
|
|
|datetime_localization|
|
|
is_saved (:obj:`bool`, optional): :obj:`True`, if the gift is displayed on the account's
|
|
profile page; for gifts received on behalf of business accounts only.
|
|
can_be_transferred (:obj:`bool`, optional): :obj:`True`, if the gift can be transferred to
|
|
another owner; for gifts received on behalf of business accounts only.
|
|
transfer_star_count (:obj:`int`, optional): Number of Telegram Stars that must be paid
|
|
to transfer the gift; omitted if the bot cannot transfer the gift.
|
|
next_transfer_date (:obj:`datetime.datetime`, optional): Date when the gift can be
|
|
transferred. If it's in the past, then the gift can be transferred now.
|
|
|datetime_localization|
|
|
.. versionadded:: 22.3
|
|
|
|
Attributes:
|
|
type (:obj:`str`): Type of the owned gift, always :tg-const:`~telegram.OwnedGift.UNIQUE`.
|
|
gift (:class:`telegram.UniqueGift`): Information about the unique gift.
|
|
owned_gift_id (:obj:`str`): Optional. Unique identifier of the received gift for the
|
|
bot; for gifts received on behalf of business accounts only.
|
|
sender_user (:class:`telegram.User`): Optional. Sender of the gift if it is a known user.
|
|
send_date (:obj:`datetime.datetime`): Date the gift was sent as :class:`datetime.datetime`.
|
|
|datetime_localization|
|
|
is_saved (:obj:`bool`): Optional. :obj:`True`, if the gift is displayed on the account's
|
|
profile page; for gifts received on behalf of business accounts only.
|
|
can_be_transferred (:obj:`bool`): Optional. :obj:`True`, if the gift can be transferred to
|
|
another owner; for gifts received on behalf of business accounts only.
|
|
transfer_star_count (:obj:`int`): Optional. Number of Telegram Stars that must be paid
|
|
to transfer the gift; omitted if the bot cannot transfer the gift.
|
|
next_transfer_date (:obj:`datetime.datetime`): Optional. Date when the gift can be
|
|
transferred. If it's in the past, then the gift can be transferred now.
|
|
|datetime_localization|
|
|
.. versionadded:: 22.3
|
|
"""
|
|
|
|
__slots__ = (
|
|
"can_be_transferred",
|
|
"gift",
|
|
"is_saved",
|
|
"next_transfer_date",
|
|
"owned_gift_id",
|
|
"send_date",
|
|
"sender_user",
|
|
"transfer_star_count",
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
gift: UniqueGift,
|
|
send_date: dtm.datetime,
|
|
owned_gift_id: Optional[str] = None,
|
|
sender_user: Optional[User] = None,
|
|
is_saved: Optional[bool] = None,
|
|
can_be_transferred: Optional[bool] = None,
|
|
transfer_star_count: Optional[int] = None,
|
|
next_transfer_date: Optional[dtm.datetime] = None,
|
|
*,
|
|
api_kwargs: Optional[JSONDict] = None,
|
|
) -> None:
|
|
super().__init__(type=OwnedGift.UNIQUE, api_kwargs=api_kwargs)
|
|
|
|
with self._unfrozen():
|
|
self.gift: UniqueGift = gift
|
|
self.send_date: dtm.datetime = send_date
|
|
self.owned_gift_id: Optional[str] = owned_gift_id
|
|
self.sender_user: Optional[User] = sender_user
|
|
self.is_saved: Optional[bool] = is_saved
|
|
self.can_be_transferred: Optional[bool] = can_be_transferred
|
|
self.transfer_star_count: Optional[int] = transfer_star_count
|
|
self.next_transfer_date: Optional[dtm.datetime] = next_transfer_date
|
|
|
|
self._id_attrs = (self.type, self.gift, self.send_date)
|
|
|
|
@classmethod
|
|
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "OwnedGiftUnique":
|
|
"""See :meth:`telegram.OwnedGift.de_json`."""
|
|
data = cls._parse_data(data)
|
|
|
|
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
|
data["send_date"] = from_timestamp(data.get("send_date"), tzinfo=loc_tzinfo)
|
|
data["sender_user"] = de_json_optional(data.get("sender_user"), User, bot)
|
|
data["gift"] = de_json_optional(data.get("gift"), UniqueGift, bot)
|
|
data["next_transfer_date"] = from_timestamp(
|
|
data.get("next_transfer_date"), tzinfo=loc_tzinfo
|
|
)
|
|
|
|
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|