Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changes/unreleased/4849.4mPSpBY2r77o75ycywXPiz.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
features = "API 9.1 Gifts "
[[pull_requests]]
uid = "4849"
author_uid = "harshil21"
closes_threads = []
18 changes: 16 additions & 2 deletions src/telegram/_ownedgift.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,17 @@ class OwnedGiftUnique(OwnedGift):
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|.
|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:: NEXT.VERSION

Attributes:
type (:obj:`str`): Type of the owned gift, always :tg-const:`~telegram.OwnedGift.UNIQUE`.
Expand All @@ -362,19 +366,24 @@ class OwnedGiftUnique(OwnedGift):
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|.
|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:: NEXT.VERSION
"""

__slots__ = (
"can_be_transferred",
"gift",
"is_saved",
"next_transfer_date",
"owned_gift_id",
"send_date",
"sender_user",
Expand All @@ -390,6 +399,7 @@ def __init__(
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:
Expand All @@ -403,6 +413,7 @@ def __init__(
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)

Expand All @@ -415,5 +426,8 @@ def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "OwnedGiftUniqu
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]
50 changes: 46 additions & 4 deletions src/telegram/_uniquegift.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
# 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 classes related to unique gifs."""
import datetime as dtm
from typing import TYPE_CHECKING, Final, Optional

from telegram import constants
from telegram._files.sticker import Sticker
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.argumentparsing import de_json_optional
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.types import JSONDict

if TYPE_CHECKING:
Expand Down Expand Up @@ -340,31 +342,63 @@ class UniqueGiftInfo(TelegramObject):

Args:
gift (:class:`UniqueGift`): Information about the gift.
origin (:obj:`str`): Origin of the gift. Currently, either :attr:`UPGRADE`
or :attr:`TRANSFER`.
origin (:obj:`str`): Origin of the gift. Currently, either :attr:`UPGRADE` for gifts
upgraded from regular gifts, :attr:`TRANSFER` for gifts transferred from other users
or channels, or :attr:`RESALE` for gifts bought from other users.

.. versionchanged:: NEXT.VERSION
The :attr:`RESALE` origin was added.
owned_gift_id (:obj:`str`, optional) Unique identifier of the received gift for the
bot; only present for gifts received on behalf of business accounts.
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.
last_resale_star_count (:obj:`int`, optional): For gifts bought from other users, the price
paid for the gift.

.. versionadded:: NEXT.VERSION
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:: NEXT.VERSION

Attributes:
gift (:class:`UniqueGift`): Information about the gift.
origin (:obj:`str`): Origin of the gift. Currently, either :attr:`UPGRADE`
or :attr:`TRANSFER`.
origin (:obj:`str`): Origin of the gift. Currently, either :attr:`UPGRADE` for gifts
upgraded from regular gifts, :attr:`TRANSFER` for gifts transferred from other users
or channels, or :attr:`RESALE` for gifts bought from other users.

.. versionchanged:: NEXT.VERSION
The :attr:`RESALE` origin was added.
owned_gift_id (:obj:`str`) Optional. Unique identifier of the received gift for the
bot; only present for gifts received on behalf of business accounts.
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.
last_resale_star_count (:obj:`int`): Optional. For gifts bought from other users, the price
paid for the gift.

.. versionadded:: NEXT.VERSION
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:: NEXT.VERSION
"""

UPGRADE: Final[str] = constants.UniqueGiftInfoOrigin.UPGRADE
""":const:`telegram.constants.UniqueGiftInfoOrigin.UPGRADE`"""
TRANSFER: Final[str] = constants.UniqueGiftInfoOrigin.TRANSFER
""":const:`telegram.constants.UniqueGiftInfoOrigin.TRANSFER`"""
RESALE: Final[str] = constants.UniqueGiftInfoOrigin.RESALE
""":const:`telegram.constants.UniqueGiftInfoOrigin.RESALE`

.. versionadded:: NEXT.VERSION
"""

__slots__ = (
"gift",
"last_resale_star_count",
"next_transfer_date",
"origin",
"owned_gift_id",
"transfer_star_count",
Expand All @@ -376,6 +410,8 @@ def __init__(
origin: str,
owned_gift_id: Optional[str] = None,
transfer_star_count: Optional[int] = None,
last_resale_star_count: Optional[int] = None,
next_transfer_date: Optional[dtm.datetime] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
Expand All @@ -386,6 +422,8 @@ def __init__(
# Optional
self.owned_gift_id: Optional[str] = owned_gift_id
self.transfer_star_count: Optional[int] = transfer_star_count
self.last_resale_star_count: Optional[int] = last_resale_star_count
self.next_transfer_date: Optional[dtm.datetime] = next_transfer_date

self._id_attrs = (self.gift, self.origin)

Expand All @@ -396,6 +434,10 @@ def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "UniqueGiftInfo
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

loc_tzinfo = extract_tzinfo_from_defaults(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)
5 changes: 5 additions & 0 deletions src/telegram/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3211,6 +3211,11 @@ class UniqueGiftInfoOrigin(StringEnum):
""":obj:`str` gift upgraded"""
TRANSFER = "transfer"
""":obj:`str` gift transfered"""
RESALE = "resale"
""":obj:`str` gift bought from other users

.. versionadded:: NEXT.VERSION
"""


class UpdateType(StringEnum):
Expand Down
9 changes: 7 additions & 2 deletions tests/test_ownedgift.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].

import datetime as dtm
from collections.abc import Sequence
from copy import deepcopy

import pytest
Expand Down Expand Up @@ -96,6 +95,7 @@ class OwnedGiftTestBase:
prepaid_upgrade_star_count = 200
can_be_transferred = True
transfer_star_count = 300
next_transfer_date = dtm.datetime.now(tz=UTC).replace(microsecond=0)


class TestOwnedGiftWithoutRequest(OwnedGiftTestBase):
Expand Down Expand Up @@ -139,6 +139,7 @@ def test_de_json_subclass(self, offline_bot, og_type, subclass, gift):
"prepaid_upgrade_star_count": self.prepaid_upgrade_star_count,
"can_be_transferred": self.can_be_transferred,
"transfer_star_count": self.transfer_star_count,
"next_transfer_date": to_timestamp(self.next_transfer_date),
}
og = OwnedGift.de_json(json_dict, offline_bot)

Expand Down Expand Up @@ -292,6 +293,7 @@ def owned_gift_unique():
is_saved=TestOwnedGiftUniqueWithoutRequest.is_saved,
can_be_transferred=TestOwnedGiftUniqueWithoutRequest.can_be_transferred,
transfer_star_count=TestOwnedGiftUniqueWithoutRequest.transfer_star_count,
next_transfer_date=TestOwnedGiftUniqueWithoutRequest.next_transfer_date,
)


Expand All @@ -313,6 +315,7 @@ def test_de_json(self, offline_bot):
"is_saved": self.is_saved,
"can_be_transferred": self.can_be_transferred,
"transfer_star_count": self.transfer_star_count,
"next_transfer_date": to_timestamp(self.next_transfer_date),
}
ogu = OwnedGiftUnique.de_json(json_dict, offline_bot)
assert ogu.gift == self.unique_gift
Expand All @@ -322,6 +325,7 @@ def test_de_json(self, offline_bot):
assert ogu.is_saved == self.is_saved
assert ogu.can_be_transferred == self.can_be_transferred
assert ogu.transfer_star_count == self.transfer_star_count
assert ogu.next_transfer_date == self.next_transfer_date
assert ogu.api_kwargs == {}

def test_to_dict(self, owned_gift_unique):
Expand All @@ -335,6 +339,7 @@ def test_to_dict(self, owned_gift_unique):
assert json_dict["is_saved"] == self.is_saved
assert json_dict["can_be_transferred"] == self.can_be_transferred
assert json_dict["transfer_star_count"] == self.transfer_star_count
assert json_dict["next_transfer_date"] == to_timestamp(self.next_transfer_date)

def test_equality(self, owned_gift_unique):
a = owned_gift_unique
Expand Down Expand Up @@ -365,7 +370,7 @@ def owned_gifts(request):
class OwnedGiftsTestBase:
total_count = 2
next_offset = "next_offset_str"
gifts: Sequence[OwnedGifts] = [
gifts: list[OwnedGift] = [
OwnedGiftRegular(
gift=Gift(
id="id1",
Expand Down
42 changes: 42 additions & 0 deletions tests/test_uniquegift.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].

import datetime as dtm

import pytest

from telegram import (
Expand All @@ -29,6 +31,8 @@
UniqueGiftModel,
UniqueGiftSymbol,
)
from telegram._utils.datetime import UTC, to_timestamp
from telegram.constants import UniqueGiftInfoOrigin
from tests.auxil.slots import mro_slots


Expand Down Expand Up @@ -383,6 +387,8 @@ def unique_gift_info():
origin=UniqueGiftInfoTestBase.origin,
owned_gift_id=UniqueGiftInfoTestBase.owned_gift_id,
transfer_star_count=UniqueGiftInfoTestBase.transfer_star_count,
last_resale_star_count=UniqueGiftInfoTestBase.last_resale_star_count,
next_transfer_date=UniqueGiftInfoTestBase.next_transfer_date,
)


Expand Down Expand Up @@ -410,6 +416,8 @@ class UniqueGiftInfoTestBase:
origin = UniqueGiftInfo.UPGRADE
owned_gift_id = "some_id"
transfer_star_count = 10
last_resale_star_count = 5
next_transfer_date = dtm.datetime.now(tz=UTC).replace(microsecond=0)
Comment thread
harshil21 marked this conversation as resolved.


class TestUniqueGiftInfoWithoutRequest(UniqueGiftInfoTestBase):
Expand All @@ -426,20 +434,54 @@ def test_de_json(self, offline_bot):
"origin": self.origin,
"owned_gift_id": self.owned_gift_id,
"transfer_star_count": self.transfer_star_count,
"last_resale_star_count": self.last_resale_star_count,
"next_transfer_date": to_timestamp(self.next_transfer_date),
}
unique_gift_info = UniqueGiftInfo.de_json(json_dict, offline_bot)
assert unique_gift_info.api_kwargs == {}
assert unique_gift_info.gift == self.gift
assert unique_gift_info.origin == self.origin
assert unique_gift_info.owned_gift_id == self.owned_gift_id
assert unique_gift_info.transfer_star_count == self.transfer_star_count
assert unique_gift_info.last_resale_star_count == self.last_resale_star_count
assert unique_gift_info.next_transfer_date == self.next_transfer_date

def test_de_json_localization(self, tz_bot, offline_bot, raw_bot):
json_dict = {
"gift": self.gift.to_dict(),
"origin": self.origin,
"owned_gift_id": self.owned_gift_id,
"transfer_star_count": self.transfer_star_count,
"last_resale_star_count": self.last_resale_star_count,
"next_transfer_date": to_timestamp(self.next_transfer_date),
}

unique_gift_info_raw = UniqueGiftInfo.de_json(json_dict, raw_bot)
unique_gift_info_offline = UniqueGiftInfo.de_json(json_dict, offline_bot)
unique_gift_info_tz = UniqueGiftInfo.de_json(json_dict, tz_bot)

# comparing utcoffsets because comparing timezones is unpredicatable
unique_gift_info_tz_offset = unique_gift_info_tz.next_transfer_date.utcoffset()
tz_bot_offset = tz_bot.defaults.tzinfo.utcoffset(
unique_gift_info_tz.next_transfer_date.replace(tzinfo=None)
)

assert unique_gift_info_raw.next_transfer_date.tzinfo == UTC
assert unique_gift_info_offline.next_transfer_date.tzinfo == UTC
assert unique_gift_info_tz_offset == tz_bot_offset

def test_to_dict(self, unique_gift_info):
json_dict = unique_gift_info.to_dict()
assert json_dict["gift"] == self.gift.to_dict()
assert json_dict["origin"] == self.origin
assert json_dict["owned_gift_id"] == self.owned_gift_id
assert json_dict["transfer_star_count"] == self.transfer_star_count
assert json_dict["last_resale_star_count"] == self.last_resale_star_count
assert json_dict["next_transfer_date"] == to_timestamp(self.next_transfer_date)

def test_enum_type_conversion(self, unique_gift_info):
assert type(unique_gift_info.origin) is UniqueGiftInfoOrigin
assert unique_gift_info.origin == UniqueGiftInfoOrigin.UPGRADE

def test_equality(self, unique_gift_info):
a = unique_gift_info
Expand Down
Loading