From cd14f6a164ebc183a5a85f74955a01fc6a018e51 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:21:00 +0100 Subject: [PATCH 1/7] Message.gift_upgrade_sent + filter --- src/telegram/_message.py | 12 ++++++++++++ src/telegram/constants.py | 5 +++++ src/telegram/ext/filters.py | 13 +++++++++++++ tests/ext/test_filters.py | 5 +++++ tests/test_message.py | 10 ++++++++++ 5 files changed, 45 insertions(+) diff --git a/src/telegram/_message.py b/src/telegram/_message.py index 08c37cf12a5..999f3ed715e 100644 --- a/src/telegram/_message.py +++ b/src/telegram/_message.py @@ -558,6 +558,10 @@ class Message(MaybeInaccessibleMessage): was sent or received .. versionadded:: 22.1 + gift_upgrade_sent (:class:`telegram.GiftInfo`, optional): Service message: upgrade of a + gift was purchased after the gift was sent + + .. versionadded:: NEXT.VERSION giveaway_created (:class:`telegram.GiveawayCreated`, optional): Service message: a scheduled giveaway was created @@ -957,6 +961,10 @@ class Message(MaybeInaccessibleMessage): was sent or received .. versionadded:: 22.1 + gift_upgrade_sent (:class:`telegram.GiftInfo`): Otional. Service message: upgrade of a + gift was purchased after the gift was sent + + .. versionadded:: NEXT.VERSION giveaway_created (:class:`telegram.GiveawayCreated`): Optional. Service message: a scheduled giveaway was created @@ -1125,6 +1133,7 @@ class Message(MaybeInaccessibleMessage): "general_forum_topic_hidden", "general_forum_topic_unhidden", "gift", + "gift_upgrade_sent", "giveaway", "giveaway_completed", "giveaway_created", @@ -1296,6 +1305,7 @@ def __init__( suggested_post_info: "SuggestedPostInfo | None" = None, suggested_post_approved: "SuggestedPostApproved | None" = None, suggested_post_approval_failed: "SuggestedPostApprovalFailed | None" = None, + gift_upgrade_sent: GiftInfo | None = None, *, api_kwargs: JSONDict | None = None, ): @@ -1422,6 +1432,7 @@ def __init__( self.suggested_post_approval_failed: SuggestedPostApprovalFailed | None = ( suggested_post_approval_failed ) + self.gift_upgrade_sent: GiftInfo | None = gift_upgrade_sent self._effective_attachment = DEFAULT_NONE @@ -1637,6 +1648,7 @@ def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "Message": data["suggested_post_approval_failed"] = de_json_optional( data.get("suggested_post_approval_failed"), SuggestedPostApprovalFailed, bot ) + data["gift_upgrade_sent"] = de_json_optional(data.get("gift_upgrade_sent"), GiftInfo, bot) api_kwargs = {} # This is a deprecated field that TG still returns for backwards compatibility diff --git a/src/telegram/constants.py b/src/telegram/constants.py index 80c44eda10e..c399a233eee 100644 --- a/src/telegram/constants.py +++ b/src/telegram/constants.py @@ -2180,6 +2180,11 @@ class MessageType(StringEnum): .. versionadded:: 22.1 """ + GIFT_UPGRADE_SENT = "gift_upgrade_sent" + """:obj:`str`: Messages with :attr:`telegram.Message.gift_upgrade_sent`. + + .. versionadded:: NEXT.VERSION + """ GIVEAWAY = "giveaway" """:obj:`str`: Messages with :attr:`telegram.Message.giveaway`. diff --git a/src/telegram/ext/filters.py b/src/telegram/ext/filters.py index 548dba00313..fe933d130fb 100644 --- a/src/telegram/ext/filters.py +++ b/src/telegram/ext/filters.py @@ -1978,6 +1978,7 @@ def filter(self, update: Update) -> bool: or StatusUpdate.GENERAL_FORUM_TOPIC_HIDDEN.check_update(update) or StatusUpdate.GENERAL_FORUM_TOPIC_UNHIDDEN.check_update(update) or StatusUpdate.GIFT.check_update(update) + or StatusUpdate.GIFT_UPGRADE_SENT.check_update(update) or StatusUpdate.GIVEAWAY_COMPLETED.check_update(update) or StatusUpdate.GIVEAWAY_CREATED.check_update(update) or StatusUpdate.LEFT_CHAT_MEMBER.check_update(update) @@ -2188,6 +2189,18 @@ def filter(self, message: Message) -> bool: .. versionadded:: 22.1 """ + class _GiftUpgradeSent(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.gift_upgrade_sent) + + GIFT_UPGRADE_SENT = _GiftUpgradeSent(name="filters.StatusUpdate.GIFT_UPGRADE_SENT") + """Messages that contain :attr:`telegram.Message.gift_upgrade_sent`. + + .. versionadded:: NEXT.VERSION + """ + class _GiveawayCreated(MessageFilter): __slots__ = () diff --git a/tests/ext/test_filters.py b/tests/ext/test_filters.py index fdaa673f922..f568eb65a1a 100644 --- a/tests/ext/test_filters.py +++ b/tests/ext/test_filters.py @@ -1136,6 +1136,11 @@ def test_filters_status_update(self, update): assert filters.StatusUpdate.UNIQUE_GIFT.check_update(update) update.message.unique_gift = None + update.message.gift_upgrade_sent = "gift_upgrade_sent" + assert filters.StatusUpdate.ALL.check_update(update) + assert filters.StatusUpdate.GIFT_UPGRADE_SENT.check_update(update) + update.message.gift_upgrade_sent = None + update.message.paid_message_price_changed = "paid_message_price_changed" assert filters.StatusUpdate.ALL.check_update(update) assert filters.StatusUpdate.PAID_MESSAGE_PRICE_CHANGED.check_update(update) diff --git a/tests/test_message.py b/tests/test_message.py index 46d975942f4..d1668d6bceb 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -420,6 +420,15 @@ def message(bot): send_date=dtm.datetime.utcnow(), ) }, + { + "gift_upgrade_sent": GiftInfo( + gift=Gift( + "gift_id", + Sticker("file_id", "file_unique_id", 512, 512, False, False, "regular"), + 5, + ) + ) + }, ], ids=[ "reply", @@ -510,6 +519,7 @@ def message(bot): "suggested_post_approved", "suggested_post_approval_failed", "suggested_post_info", + "gift_upgrade_sent", ], ) def message_params(bot, request): From 9685da57fe5d17999ac66679966f126c3041e913 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:24:33 +0100 Subject: [PATCH 2/7] AcceptedGiftTypesTestBase.gifts_from_channels --- src/telegram/_gifts.py | 16 +++++++++++++++- tests/test_gifts.py | 12 +++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/telegram/_gifts.py b/src/telegram/_gifts.py index dd8fb1fb345..181f017561e 100644 --- a/src/telegram/_gifts.py +++ b/src/telegram/_gifts.py @@ -319,9 +319,11 @@ class AcceptedGiftTypes(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal if their :attr:`unlimited_gifts`, :attr:`limited_gifts`, - :attr:`unique_gifts` and :attr:`premium_subscription` are equal. + :attr:`unique_gifts`, :attr:`premium_subscription` and :attr:`gifts_from_channels` are equal. .. versionadded:: 22.1 + .. versionchanged:: NEXT.VERSION + :attr:`gifts_from_channels` is now considered for equality checks. Args: unlimited_gifts (:class:`bool`): :obj:`True`, if unlimited regular gifts are accepted. @@ -330,6 +332,10 @@ class AcceptedGiftTypes(TelegramObject): to unique for free are accepted. premium_subscription (:class:`bool`): :obj:`True`, if a Telegram Premium subscription is accepted. + gifts_from_channels (:obj:`bool`): :obj:`True`, if transfers of unique gifts from channels + are accepted + + .. versionadded:: NEXT.VERSION Attributes: unlimited_gifts (:class:`bool`): :obj:`True`, if unlimited regular gifts are accepted. @@ -338,10 +344,15 @@ class AcceptedGiftTypes(TelegramObject): to unique for free are accepted. premium_subscription (:class:`bool`): :obj:`True`, if a Telegram Premium subscription is accepted. + gifts_from_channels (:obj:`bool`): :obj:`True`, if transfers of unique gifts from channels + are accepted + + .. versionadded:: NEXT.VERSION """ __slots__ = ( + "gifts_from_channels", "limited_gifts", "premium_subscription", "unique_gifts", @@ -354,6 +365,7 @@ def __init__( limited_gifts: bool, unique_gifts: bool, premium_subscription: bool, + gifts_from_channels: bool, *, api_kwargs: JSONDict | None = None, ): @@ -362,12 +374,14 @@ def __init__( self.limited_gifts: bool = limited_gifts self.unique_gifts: bool = unique_gifts self.premium_subscription: bool = premium_subscription + self.gifts_from_channels: bool = gifts_from_channels self._id_attrs = ( self.unlimited_gifts, self.limited_gifts, self.unique_gifts, self.premium_subscription, + self.gifts_from_channels, ) self._freeze() diff --git a/tests/test_gifts.py b/tests/test_gifts.py index 8929b3cd6bd..427a53772dc 100644 --- a/tests/test_gifts.py +++ b/tests/test_gifts.py @@ -430,6 +430,7 @@ def accepted_gift_types(): limited_gifts=AcceptedGiftTypesTestBase.limited_gifts, unique_gifts=AcceptedGiftTypesTestBase.unique_gifts, premium_subscription=AcceptedGiftTypesTestBase.premium_subscription, + gifts_from_channels=AcceptedGiftTypesTestBase.gifts_from_channels, ) @@ -438,6 +439,7 @@ class AcceptedGiftTypesTestBase: limited_gifts = True unique_gifts = True premium_subscription = True + gifts_from_channels = False class TestAcceptedGiftTypesWithoutRequest(AcceptedGiftTypesTestBase): @@ -454,6 +456,7 @@ def test_de_json(self, offline_bot): "limited_gifts": self.limited_gifts, "unique_gifts": self.unique_gifts, "premium_subscription": self.premium_subscription, + "gifts_from_channels": self.gifts_from_channels, } accepted_gift_types = AcceptedGiftTypes.de_json(json_dict, offline_bot) assert accepted_gift_types.api_kwargs == {} @@ -461,6 +464,7 @@ def test_de_json(self, offline_bot): assert accepted_gift_types.limited_gifts == self.limited_gifts assert accepted_gift_types.unique_gifts == self.unique_gifts assert accepted_gift_types.premium_subscription == self.premium_subscription + assert accepted_gift_types.gifts_from_channels == self.gifts_from_channels def test_to_dict(self, accepted_gift_types): json_dict = accepted_gift_types.to_dict() @@ -468,17 +472,23 @@ def test_to_dict(self, accepted_gift_types): assert json_dict["limited_gifts"] == self.limited_gifts assert json_dict["unique_gifts"] == self.unique_gifts assert json_dict["premium_subscription"] == self.premium_subscription + assert json_dict["gifts_from_channels"] == self.gifts_from_channels def test_equality(self, accepted_gift_types): a = accepted_gift_types b = AcceptedGiftTypes( - self.unlimited_gifts, self.limited_gifts, self.unique_gifts, self.premium_subscription + self.unlimited_gifts, + self.limited_gifts, + self.unique_gifts, + self.premium_subscription, + self.gifts_from_channels, ) c = AcceptedGiftTypes( not self.unlimited_gifts, self.limited_gifts, self.unique_gifts, self.premium_subscription, + self.gifts_from_channels, ) d = BotCommand("start", "description") From b4c6b47d974ca6798e14af8e33ea42f36179aefd Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:27:27 +0100 Subject: [PATCH 3/7] chango --- changes/unreleased/5078.FoNwUYLbXQFRebTFhR6UPn.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/changes/unreleased/5078.FoNwUYLbXQFRebTFhR6UPn.toml b/changes/unreleased/5078.FoNwUYLbXQFRebTFhR6UPn.toml index 2a4a3549b0f..81ff5ff732e 100644 --- a/changes/unreleased/5078.FoNwUYLbXQFRebTFhR6UPn.toml +++ b/changes/unreleased/5078.FoNwUYLbXQFRebTFhR6UPn.toml @@ -4,4 +4,5 @@ pull_requests = [ { uid = "5078", author_uid = "aelkheir", closes_threads = ["5077"] }, { uid = "5079", author_uid = "aelkheir" }, { uid = "5085", author_uids = ["Bibo-Joshi"] }, + { uid = "5089", author_uids = ["Bibo-Joshi"] }, ] From 56c03bf1727e8e2d0710f9dbfbc9b13582abed6a Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:34:41 +0100 Subject: [PATCH 4/7] test fix --- tests/auxil/dummy_objects.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/auxil/dummy_objects.py b/tests/auxil/dummy_objects.py index 0fbf738e916..84bba3aa45b 100644 --- a/tests/auxil/dummy_objects.py +++ b/tests/auxil/dummy_objects.py @@ -81,7 +81,11 @@ accent_color_id=1, max_reaction_count=1, accepted_gift_types=AcceptedGiftTypes( - unlimited_gifts=True, limited_gifts=True, unique_gifts=True, premium_subscription=True + unlimited_gifts=True, + limited_gifts=True, + unique_gifts=True, + premium_subscription=True, + gifts_from_channels=True, ), ), "ChatInviteLink": ChatInviteLink( From 294d14e2500d5ff7651b920944f5747623473be9 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:48:57 +0100 Subject: [PATCH 5/7] chango --- tests/test_chatfullinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_chatfullinfo.py b/tests/test_chatfullinfo.py index 79d55e2fa8b..dd1a583da09 100644 --- a/tests/test_chatfullinfo.py +++ b/tests/test_chatfullinfo.py @@ -147,7 +147,7 @@ class ChatFullInfoTestBase: first_name = "first_name" last_name = "last_name" can_send_paid_media = True - accepted_gift_types = AcceptedGiftTypes(True, True, True, True) + accepted_gift_types = AcceptedGiftTypes(True, True, True, True, True) is_direct_messages = True parent_chat = Chat(4, "channel", "channel") From 27bb168a5f591cf7badad21100d8abf0c20227b6 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:11:50 +0100 Subject: [PATCH 6/7] test fix --- tests/test_business_methods.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_business_methods.py b/tests/test_business_methods.py index 4e44a84e37d..3b3741cebb4 100644 --- a/tests/test_business_methods.py +++ b/tests/test_business_methods.py @@ -214,7 +214,7 @@ async def make_assertion(*args, **kwargs): async def test_set_business_account_gift_settings(self, offline_bot, monkeypatch): show_gift_button = True - accepted_gift_types = AcceptedGiftTypes(True, True, True, True) + accepted_gift_types = AcceptedGiftTypes(True, True, True, True, True) async def make_assertion(*args, **kwargs): data = kwargs.get("request_data").json_parameters From fe074dd215856888cde241af8d9ff3e093b72c90 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 8 Jan 2026 18:49:10 +0100 Subject: [PATCH 7/7] Update src/telegram/_message.py Co-authored-by: Abdelrahman Elkheir <90580077+aelkheir@users.noreply.github.com> --- src/telegram/_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/telegram/_message.py b/src/telegram/_message.py index 999f3ed715e..db34521e7bc 100644 --- a/src/telegram/_message.py +++ b/src/telegram/_message.py @@ -961,7 +961,7 @@ class Message(MaybeInaccessibleMessage): was sent or received .. versionadded:: 22.1 - gift_upgrade_sent (:class:`telegram.GiftInfo`): Otional. Service message: upgrade of a + gift_upgrade_sent (:class:`telegram.GiftInfo`): Optional. Service message: upgrade of a gift was purchased after the gift was sent .. versionadded:: NEXT.VERSION