diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 7f4becf2a..9ecb9cf47 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -396,6 +396,11 @@ def get_title_list(s: str) -> list: get_collectible_item_info get_payment_form send_payment_form + get_available_gifts + get_user_gifts + sell_gift + send_gift + toggle_gift_is_saved """, advanced=""" Advanced @@ -509,6 +514,8 @@ def get_title_list(s: str) -> list: Poll Location Venue + Gift + UserGift WebAppData MessageAutoDeleteTimerChanged ChatBoostAdded @@ -735,6 +742,7 @@ def get_title_list(s: str) -> list: Message.translate Message.pay Message.star + UserGift.toggle """, chat=""" Chat diff --git a/compiler/errors/source/400_BAD_REQUEST.tsv b/compiler/errors/source/400_BAD_REQUEST.tsv index fe4b31230..01a155b9d 100644 --- a/compiler/errors/source/400_BAD_REQUEST.tsv +++ b/compiler/errors/source/400_BAD_REQUEST.tsv @@ -357,6 +357,7 @@ PHOTO_SAVE_FILE_INVALID Internal issues, try again later. PHOTO_THUMB_URL_EMPTY Photo thumbnail URL is empty. PHOTO_THUMB_URL_INVALID The photo thumb URL is invalid PINNED_DIALOGS_TOO_MUCH Too many pinned dialogs. +PINNED_TOPIC_NOT_MODIFIED The pinned topic was not modified. PIN_RESTRICTED You can't pin messages. PLATFORM_INVALID The provided platform is invalid. Allowed values are "android", "ios", "wp", "bb", "desktop", "web", "ubp", "other". POLL_ANSWERS_INVALID Invalid poll answers were provided. diff --git a/docs/source/releases/changes-in-this-fork.rst b/docs/source/releases/changes-in-this-fork.rst index 4873d9c48..9a262af79 100644 --- a/docs/source/releases/changes-in-this-fork.rst +++ b/docs/source/releases/changes-in-this-fork.rst @@ -14,6 +14,7 @@ If you found any issue or have any suggestions, feel free to make `an issue `_. - View `new and changed `__ `raw API methods `__. diff --git a/pyrogram/enums/message_service_type.py b/pyrogram/enums/message_service_type.py index aa2fc8d21..f435dde43 100644 --- a/pyrogram/enums/message_service_type.py +++ b/pyrogram/enums/message_service_type.py @@ -141,5 +141,8 @@ class MessageServiceType(AutoName): WRITE_ACCESS_ALLOWED = auto() "The user accepted webapp bot's request to send messages" + USER_GIFT = auto() + "Star gift" + UNKNOWN = auto() "This service message is unsupported by the current version of Pyrogram" diff --git a/pyrogram/filters.py b/pyrogram/filters.py index 88826d44a..b7169dfe7 100644 --- a/pyrogram/filters.py +++ b/pyrogram/filters.py @@ -333,6 +333,49 @@ async def game_filter(_, __, m: Message) -> bool: # endregion +# region giveaway_filter +async def giveaway_filter(_, __, m: Message): + return bool(m.giveaway) + + +giveaway = create(giveaway_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Giveaway` objects.""" + + +# endregion + +# region giveaway_result_filter +async def giveaway_result_filter(_, __, m: Message): + return bool(m.giveaway_winners or m.giveaway_completed) + + +giveaway_result = create(giveaway_result_filter) +"""Filter messages that contain :obj:`~pyrogram.types.GiveawayWinners` or :obj:`~pyrogram.types.GiveawayCompleted` objects.""" + + +# endregion + +# region gift_code_filter +async def gift_code_filter(_, __, m: Message): + return bool(m.gift_code) + + +gift_code = create(gift_code_filter) +"""Filter messages that contain :obj:`~pyrogram.types.GiftCode` objects.""" + + +# endregion + +# region user_gift +async def user_gift_filter(_, __, m: Message): + return bool(m.user_gift) + + +user_gift = create(user_gift_filter) +"""Filter messages that contain :obj:`~pyrogram.types.UserGift` objects.""" + + +# endregion # region video_filter async def video_filter(_, __, m: Message) -> bool: diff --git a/pyrogram/methods/business/__init__.py b/pyrogram/methods/business/__init__.py index 88111fbc5..0761fe9d0 100644 --- a/pyrogram/methods/business/__init__.py +++ b/pyrogram/methods/business/__init__.py @@ -23,8 +23,13 @@ from .get_collectible_item_info import GetCollectibleItemInfo from .refund_star_payment import RefundStarPayment from .send_invoice import SendInvoice -from .get_payment_from import GetPaymentForm -from .send_payment_from import SendPaymentForm +from .get_payment_form import GetPaymentForm +from .send_payment_form import SendPaymentForm +from .get_available_gifts import GetAvailableGifts +from .get_user_gifts import GetUserGifts +from .sell_gift import SellGift +from .send_gift import SendGift +from .toggle_gift_is_saved import ToggleGiftIsSaved class TelegramBusiness( @@ -37,5 +42,10 @@ class TelegramBusiness( SendInvoice, GetPaymentForm, SendPaymentForm, + GetAvailableGifts, + GetUserGifts, + SellGift, + SendGift, + ToggleGiftIsSaved, ): pass diff --git a/pyrogram/methods/business/get_available_gifts.py b/pyrogram/methods/business/get_available_gifts.py new file mode 100644 index 000000000..65a1c5214 --- /dev/null +++ b/pyrogram/methods/business/get_available_gifts.py @@ -0,0 +1,44 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . +from typing import List + +import pyrogram +from pyrogram import raw, types + + +class GetAvailableGifts: + async def get_available_gifts( + self: "pyrogram.Client", + ) -> List["types.Gift"]: + """Get all gifts that can be sent to other users. + + .. include:: /_includes/usable-by/users.rst + + Returns: + List of :obj:`~pyrogram.types.Gift`: On success, a list of star gifts is returned. + + Example: + .. code-block:: python + + app.get_available_gifts() + """ + r = await self.invoke( + raw.functions.payments.GetStarGifts(hash=0) + ) + + return types.List([await types.Gift._parse(self, gift) for gift in r.gifts]) diff --git a/pyrogram/methods/business/get_payment_from.py b/pyrogram/methods/business/get_payment_form.py similarity index 94% rename from pyrogram/methods/business/get_payment_from.py rename to pyrogram/methods/business/get_payment_form.py index ca1771e3a..586501460 100644 --- a/pyrogram/methods/business/get_payment_from.py +++ b/pyrogram/methods/business/get_payment_form.py @@ -24,10 +24,10 @@ class GetPaymentForm: async def get_payment_form( - self: "pyrogram.Client", *, - chat_id: Union[int, str] = None, - message_id: int = None, - invoice_link: str = None + self: "pyrogram.Client", *, + chat_id: Union[int, str] = None, + message_id: int = None, + invoice_link: str = None ) -> "types.PaymentForm": """Get information about a invoice or paid media. diff --git a/pyrogram/methods/business/get_user_gifts.py b/pyrogram/methods/business/get_user_gifts.py new file mode 100644 index 000000000..fa83477f3 --- /dev/null +++ b/pyrogram/methods/business/get_user_gifts.py @@ -0,0 +1,97 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import raw, types + + +class GetUserGifts: + async def get_user_gifts( + self: "pyrogram.Client", + user_id: Union[int, str], + offset: str = "", + limit: int = 0, + ) -> Optional[AsyncGenerator["types.UserGift", None]]: + """Get gifts saved to profile by the given user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + offset (``str``, *optional*): + Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results. + + limit (``int``, *optional*): + The maximum number of gifts to be returned; must be positive and can't be greater than 100. For optimal performance, the number of returned objects is chosen by Telegram Server and can be smaller than the specified limit. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.UserGift` objects. + + Example: + .. code-block:: python + + async for user_gift in app.get_user_gifts(user_id): + print(user_gift) + """ + peer = await self.resolve_peer(user_id) + + if not isinstance(peer, (raw.types.InputPeerUser, raw.types.InputPeerSelf)): + raise ValueError("user_id must belong to a user.") + + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + while True: + r = await self.invoke( + raw.functions.payments.GetUserStarGifts( + user_id=peer, + offset=offset, + limit=limit + ), + sleep_threshold=max(60, self.sleep_threshold) + ) + + users = {u.id: u for u in r.users} + + user_gifts = [ + await types.UserGift._parse(self, gift, users) + for gift in r.gifts + ] + + if not user_gifts: + return + + for user_gift in user_gifts: + yield user_gift + + current += 1 + + if current >= total: + return + + offset = r.next_offset + + if not offset: + return diff --git a/pyrogram/methods/business/sell_gift.py b/pyrogram/methods/business/sell_gift.py new file mode 100644 index 000000000..9deec455d --- /dev/null +++ b/pyrogram/methods/business/sell_gift.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +from typing import Union + +import pyrogram +from pyrogram import raw + + +class SellGift: + async def sell_gift( + self: "pyrogram.Client", + sender_user_id: Union[int, str], + message_id: int + ) -> bool: + """Sells a gift received by the current user for Telegram Stars. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + sender_user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the user that sent the gift. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Unique identifier of the message with the gift in the chat with the user. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Convert gift + app.sell_gift(sender_user_id=user_id, message_id=123) + + """ + peer = await self.resolve_peer(sender_user_id) + + if not isinstance(peer, (raw.types.InputPeerUser, raw.types.InputPeerSelf)): + raise ValueError("sender_user_id must belong to a user.") + + r = await self.invoke( + raw.functions.payments.ConvertStarGift( + user_id=peer, + msg_id=message_id + ) + ) + + return r diff --git a/pyrogram/methods/business/send_gift.py b/pyrogram/methods/business/send_gift.py new file mode 100644 index 000000000..3f5a2f7db --- /dev/null +++ b/pyrogram/methods/business/send_gift.py @@ -0,0 +1,105 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +from typing import Optional, Union, List + +import pyrogram +from pyrogram import raw, types, enums, utils + + +class SendGift: + async def send_gift( + self: "pyrogram.Client", + user_id: Union[int, str], + gift_id: int, + text: Optional[str] = None, + parse_mode: Optional["enums.ParseMode"] = None, + entities: Optional[List["types.MessageEntity"]] = None, + is_private: Optional[bool] = None, + ) -> bool: + """Sends a gift to another user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the user that will receive the gift. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + gift_id (``int``): + Unique identifier of the gift to send. + + text (``str``, *optional*): + Text of the message to be sent. 0-``gift_text_length_max`` characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + List of special entities that appear in message text, which can be specified instead of *parse_mode*. + Only Bold, Italic, Underline, Strikethrough, Spoiler, and CustomEmoji entities are allowed. + + is_private (``bool``, *optional*): + Pass True to show the current user as sender and gift text only to the gift receiver; otherwise, everyone will be able to see them. + + Returns: + ``bool``: On success, True is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + + Example: + .. code-block:: python + + # Send gift + app.send_gift(user_id=user_id, gift_id=123) + + """ + peer = await self.resolve_peer(user_id) + + if not isinstance(peer, (raw.types.InputPeerUser, raw.types.InputPeerSelf)): + raise ValueError("user_id must belong to a user.") + + text, entities = (await utils.parse_text_entities(self, text, parse_mode, entities)).values() + + invoice = raw.types.InputInvoiceStarGift( + user_id=peer, + gift_id=gift_id, + hide_name=is_private, + message=raw.types.TextWithEntities( + text=text, entities=entities or [] + ) if text else None + ) + + form = await self.invoke( + raw.functions.payments.GetPaymentForm( + invoice=invoice + ) + ) + + await self.invoke( + raw.functions.payments.SendStarsForm( + form_id=form.form_id, + invoice=invoice + ) + ) + + return True diff --git a/pyrogram/methods/business/send_payment_from.py b/pyrogram/methods/business/send_payment_form.py similarity index 96% rename from pyrogram/methods/business/send_payment_from.py rename to pyrogram/methods/business/send_payment_form.py index 1131b3558..097894a4c 100644 --- a/pyrogram/methods/business/send_payment_from.py +++ b/pyrogram/methods/business/send_payment_form.py @@ -24,11 +24,11 @@ class SendPaymentForm: async def send_payment_form( - self: "pyrogram.Client", - *, - chat_id: Union[int, str] = None, - message_id: int = None, - invoice_link: str = None + self: "pyrogram.Client", + *, + chat_id: Union[int, str] = None, + message_id: int = None, + invoice_link: str = None ) -> Union[ bool, List["types.PaidMediaPhoto"], diff --git a/pyrogram/methods/business/toggle_gift_is_saved.py b/pyrogram/methods/business/toggle_gift_is_saved.py new file mode 100644 index 000000000..b4592eaee --- /dev/null +++ b/pyrogram/methods/business/toggle_gift_is_saved.py @@ -0,0 +1,71 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +from typing import Union + +import pyrogram +from pyrogram import raw + + +class ToggleGiftIsSaved: + async def toggle_gift_is_saved( + self: "pyrogram.Client", + sender_user_id: Union[int, str], + message_id: int, + is_saved: bool + ) -> bool: + """Toggles whether a gift is shown on the current user's profile page. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + sender_user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user that sent the gift. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Unique message identifier of the message with the gift in the chat with the user. + + is_saved (``bool``): + Pass True to display the gift on the user's profile page; pass False to remove it from the profile page. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Hide gift + app.toggle_gift_is_saved(sender_user_id=user_id, message_id=123, is_saved=False) + """ + peer = await self.resolve_peer(sender_user_id) + + if not isinstance(peer, (raw.types.InputPeerUser, raw.types.InputPeerSelf)): + raise ValueError("sender_user_id must belong to a user.") + + r = await self.invoke( + raw.functions.payments.SaveStarGift( + user_id=peer, + msg_id=message_id, + unsave=not is_saved + ) + ) + + return r diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index 8df9bcef1..925747b60 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -39,6 +39,8 @@ ReactionCount ) from .sponsored_message import SponsoredMessage +from .gift import Gift +from .user_gift import UserGift from .sticker import Sticker from .stripped_thumbnail import StrippedThumbnail from .thumbnail import Thumbnail @@ -53,7 +55,7 @@ from .message_reaction_count_updated import MessageReactionCountUpdated from .chat_boost_added import ChatBoostAdded from .story import Story -from .payment_from import PaymentForm +from .payment_form import PaymentForm from .giveaway import Giveaway from .giveaway_created import GiveawayCreated from .giveaway_completed import GiveawayCompleted @@ -105,6 +107,8 @@ "Poll", "PollOption", "SponsoredMessage", + "Gift", + "UserGift", "Sticker", "Story", "Venue", diff --git a/pyrogram/types/messages_and_media/gift.py b/pyrogram/types/messages_and_media/gift.py new file mode 100644 index 000000000..7f7b5dd44 --- /dev/null +++ b/pyrogram/types/messages_and_media/gift.py @@ -0,0 +1,92 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class Gift(Object): + """Describes a gift that can be sent to another user. + + Parameters: + id (``int``): + Unique identifier of the gift. + + sticker (:obj:`~pyrogram.types.Sticker`): + The sticker representing the gift. + + star_count (``int``): + Number of Telegram Stars that must be paid for the gift. + + default_sell_star_count (``int``): + Number of Telegram Stars that can be claimed by the receiver instead of the gift by default. If the gift was paid with just bought Telegram Stars, then full value can be claimed. + + remaining_count (``int``, *optional*): + Number of remaining times the gift can be purchased by all users; None if not limited or the gift was sold out. + + total_count (``int``, *optional*): + Number of total times the gift can be purchased by all users; None if not limited. + + is_limited (``bool``, *optional*): + True, if the number of gifts is limited. + + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + sticker: "types.Sticker", + star_count: int, + default_sell_star_count: int, + remaining_count: Optional[int] = None, + total_count: Optional[int] = None, + is_limited: Optional[bool] = None, + ): + super().__init__(client) + + self.id = id + self.sticker = sticker + self.star_count = star_count + self.default_sell_star_count = default_sell_star_count + self.remaining_count = remaining_count + self.total_count = total_count + self.is_limited = is_limited + + @staticmethod + async def _parse( + client, + star_gift: "raw.types.StarGift", + ) -> "Gift": + doc = star_gift.sticker + attributes = {type(i): i for i in doc.attributes} + + return Gift( + id=star_gift.id, + sticker=await types.Sticker._parse(client, doc, attributes), + star_count=star_gift.stars, + default_sell_star_count=star_gift.convert_stars, + remaining_count=getattr(star_gift, "availability_remains", None), + total_count=getattr(star_gift, "availability_total", None), + is_limited=getattr(star_gift, "limited", None), + client=client + ) diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index e1096848e..9de511015 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -392,6 +392,7 @@ class Message(Object, Update): Custom action (most likely not supported by the current layer, an upgrade might be needed) gift_code (:obj:`~pyrogram.types.GiftCode`, *optional*): + Service message: gift code information. Contains a `Telegram Premium giftcode link `_. gifted_premium (:obj:`~pyrogram.types.GiftedPremium`, *optional*): @@ -400,6 +401,9 @@ class Message(Object, Update): gifted_stars (:obj:`~pyrogram.types.GiftedStars`, *optional*): Info about gifted Telegram Stars + user_gift (:obj:`~pyrogram.types.UserGift`, *optional*): + Service message: Represents a gift received by a user. + contact_registered (:obj:`~pyrogram.types.ContactRegistered`, *optional*): A service message that a contact has registered with Telegram. @@ -516,6 +520,7 @@ def __init__( gift_code: "types.GiftCode" = None, gifted_premium: "types.GiftedPremium" = None, gifted_stars: "types.GiftedStars" = None, + user_gift: "types.UserGift" = None, empty: bool = None, mentioned: bool = None, service: "enums.MessageServiceType" = None, @@ -637,6 +642,7 @@ def __init__( self.custom_action = custom_action self.sender_business_bot = sender_business_bot self.business_connection_id = business_connection_id + self.user_gift = user_gift self.successful_payment = successful_payment self.paid_media = paid_media self.refunded_payment = refunded_payment @@ -745,6 +751,8 @@ async def _parse( chat_join_type = None screenshot_taken = None + user_gift = None + service_type = enums.MessageServiceType.UNKNOWN if isinstance(action, raw.types.MessageActionChatAddUser): @@ -991,6 +999,10 @@ async def _parse( write_access_allowed = types.WriteAccessAllowed._parse(action) service_type = enums.MessageServiceType.WRITE_ACCESS_ALLOWED + elif isinstance(action, raw.types.MessageActionStarGift): + user_gift = await types.UserGift._parse_action(client, message, users) + service_type = enums.MessageServiceType.USER_GIFT + parsed_message = Message( id=message.id, date=utils.timestamp_to_datetime(message.date), @@ -1022,6 +1034,7 @@ async def _parse( chat_shared=chat_shared, connected_website=connected_website, write_access_allowed=write_access_allowed, + user_gift=user_gift, successful_payment=successful_payment, message_auto_delete_timer_changed=message_auto_delete_timer_changed, boost_added=boost_added, diff --git a/pyrogram/types/messages_and_media/payment_from.py b/pyrogram/types/messages_and_media/payment_form.py similarity index 100% rename from pyrogram/types/messages_and_media/payment_from.py rename to pyrogram/types/messages_and_media/payment_form.py diff --git a/pyrogram/types/messages_and_media/user_gift.py b/pyrogram/types/messages_and_media/user_gift.py new file mode 100644 index 000000000..69c9e51ec --- /dev/null +++ b/pyrogram/types/messages_and_media/user_gift.py @@ -0,0 +1,178 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from datetime import datetime +from typing import Optional, List + +import pyrogram +from pyrogram import raw, types, utils +from .message import Str +from ..object import Object + + +class UserGift(Object): + """Represents a gift received by a user. + + Parameters: + sender_user (:obj:`~pyrogram.types.User`, *optional*): + Identifier of the user that sent the gift; None if unknown. + + text (``str``, *optional*): + Message added to the gift. + + entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text. + + is_private (``bool``, *optional*): + True, if the sender and gift text are shown only to the gift receiver; otherwise, everyone are able to see them. + + is_saved (``bool``, *optional*): + True, if the gift is displayed on the user's profile page; may be False only for the receiver of the gift. + + date (``datetime``): + Date when the gift was sent. + + gift (:obj:`~pyrogram.types.Gift`, *optional*): + Information about the gift. + + message_id (``int``, *optional*): + Identifier of the message with the gift in the chat with the sender of the gift; can be None or an identifier of a deleted message; only for the gift receiver. + + sell_star_count (``int``, *optional*): + Number of Telegram Stars that can be claimed by the receiver instead of the gift; only for the gift receiver. + + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + sender_user: Optional["types.User"] = None, + text: Optional[str] = None, + entities: List["types.MessageEntity"] = None, + date: datetime, + is_private: Optional[bool] = None, + is_saved: Optional[bool] = None, + gift: Optional["types.Gift"] = None, + message_id: Optional[int] = None, + sell_star_count: Optional[int] = None + ): + super().__init__(client) + + self.date = date + self.gift = gift + self.is_private = is_private + self.is_saved = is_saved + self.sender_user = sender_user + self.text = text + self.entities = entities + self.message_id = message_id + self.sell_star_count = sell_star_count + + @staticmethod + async def _parse( + client, + user_star_gift: "raw.types.UserStarGift", + users: dict + ) -> "UserGift": + text, entities = None, None + if getattr(user_star_gift, "message", None): + text = user_star_gift.message.text or None + entities = [types.MessageEntity._parse(client, entity, users) for entity in user_star_gift.message.entities] + entities = types.List(filter(lambda x: x is not None, entities)) + + return UserGift( + date=utils.timestamp_to_datetime(user_star_gift.date), + gift=await types.Gift._parse(client, user_star_gift.gift), + is_private=getattr(user_star_gift, "name_hidden", None), + is_saved=not user_star_gift.unsaved if getattr(user_star_gift, "unsaved", None) else None, + sender_user=types.User._parse(client, users.get(user_star_gift.from_id)) if getattr(user_star_gift, "from_id", None) else None, + message_id=getattr(user_star_gift, "msg_id", None), + sell_star_count=getattr(user_star_gift, "convert_stars", None), + text=Str(text).init(entities) if text else None, + entities=entities, + client=client + ) + + @staticmethod + async def _parse_action( + client, + message: "raw.base.Message", + users: dict + ) -> "UserGift": + action = message.action + + doc = action.gift.sticker + attributes = {type(i): i for i in doc.attributes} + + text, entities = None, None + if getattr(action, "message", None): + text = action.message.text or None + entities = [types.MessageEntity._parse(client, entity, users) for entity in action.message.entities] + entities = types.List(filter(lambda x: x is not None, entities)) + + return UserGift( + gift=types.Gift( + id=action.gift.id, + sticker=await types.Sticker._parse(client, doc, attributes), + star_count=action.gift.stars, + default_sell_star_count=action.gift.convert_stars, + remaining_count=getattr(action.gift, "availability_remains", None), + total_count=getattr(action.gift, "availability_total", None), + is_limited=getattr(action.gift, "limited", None), + ), + date=utils.timestamp_to_datetime(message.date), + is_private=getattr(action, "name_hidden", None), + is_saved=getattr(action, "saved", None), + sender_user=types.User._parse(client, users.get(utils.get_raw_peer_id(message.peer_id))), + message_id=message.id, + text=Str(text).init(entities) if text else None, + entities=entities, + client=client + ) + + async def toggle(self, is_saved: bool) -> bool: + """Bound method *toggle* of :obj:`~pyrogram.types.UserGift`. + + Use as a shortcut for: + + .. code-block:: python + + await client.toggle_gift_is_saved( + sender_user_id=user_id, + message_id=message_id + ) + + Parameters: + is_saved (``bool``): + Pass True to display the gift on the user's profile page; pass False to remove it from the profile page. + + Example: + .. code-block:: python + + await user_gift.toggle(is_saved=False) + + Returns: + ``bool``: On success, True is returned. + + """ + return await self._client.toggle_gift_is_saved( + sender_user_id=self.sender_user.id, + message_id=self.message_id, + is_saved=is_saved + )