From 78467e30a2a96a1da1e0a43300185cecaa4d0e53 Mon Sep 17 00:00:00 2001 From: KurimuzonAkuma Date: Tue, 10 Jun 2025 21:49:47 +0700 Subject: [PATCH] pyrofork: Add search_gifts_for_resale, send_resold_gift, set_gift_resale_price, set_pinned_gifts methods Co-authored-by: "ALi.w" Signed-off-by: wulan17 --- compiler/docs/compiler.py | 4 + docs/source/api/enums/GiftForResaleOrder.rst | 8 ++ pyrogram/enums/__init__.py | 2 + pyrogram/enums/gift_for_resale_order.py | 35 ++++++ pyrogram/methods/payments/__init__.py | 8 ++ .../payments/search_gifts_for_resale.py | 110 ++++++++++++++++++ pyrogram/methods/payments/send_resold_gift.py | 89 ++++++++++++++ .../methods/payments/set_gift_resale_price.py | 82 +++++++++++++ pyrogram/methods/payments/set_pinned_gifts.py | 95 +++++++++++++++ 9 files changed, 433 insertions(+) create mode 100644 docs/source/api/enums/GiftForResaleOrder.rst create mode 100644 pyrogram/enums/gift_for_resale_order.py create mode 100644 pyrogram/methods/payments/search_gifts_for_resale.py create mode 100644 pyrogram/methods/payments/send_resold_gift.py create mode 100644 pyrogram/methods/payments/set_gift_resale_price.py create mode 100644 pyrogram/methods/payments/set_pinned_gifts.py diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 10e20a1f..3a9e913c 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -365,11 +365,15 @@ def pyrogram_api(): get_chat_gifts hide_gift refund_star_payment + search_gifts_for_resale send_invoice send_paid_media send_paid_reaction send_payment_form send_gift + send_resold_gift + set_gift_resale_price + set_pinned_gift show_gift transfer_gift upgrade_gift diff --git a/docs/source/api/enums/GiftForResaleOrder.rst b/docs/source/api/enums/GiftForResaleOrder.rst new file mode 100644 index 00000000..7d8d0b46 --- /dev/null +++ b/docs/source/api/enums/GiftForResaleOrder.rst @@ -0,0 +1,8 @@ +GiftAttributeType +================= + +.. autoclass:: pyrogram.enums.GiftForResaleOrder() + :members: + +.. raw:: html + :file: ./cleanup.html diff --git a/pyrogram/enums/__init__.py b/pyrogram/enums/__init__.py index aac9db33..c67f64b7 100644 --- a/pyrogram/enums/__init__.py +++ b/pyrogram/enums/__init__.py @@ -27,6 +27,7 @@ from .chat_type import ChatType from .client_platform import ClientPlatform from .folder_color import FolderColor from .gift_attribute_type import GiftAttributeType +from .gift_for_resale_order import GiftForResaleOrder from .listerner_types import ListenerTypes from .message_entity_type import MessageEntityType from .message_media_type import MessageMediaType @@ -55,6 +56,7 @@ __all__ = [ 'ClientPlatform', 'FolderColor', 'GiftAttributeType', + 'GiftForResaleOrder', 'ListenerTypes', 'MessageEntityType', 'MessageMediaType', diff --git a/pyrogram/enums/gift_for_resale_order.py b/pyrogram/enums/gift_for_resale_order.py new file mode 100644 index 00000000..403b3691 --- /dev/null +++ b/pyrogram/enums/gift_for_resale_order.py @@ -0,0 +1,35 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork 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. +# +# Pyrofork 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 Pyrofork. If not, see . + +from enum import auto + +from .auto_name import AutoName + + +class GiftForResaleOrder(AutoName): + """Describes order in which upgraded gifts for resale will be sorted. Used in :meth:`~pyrogram.Client.search_gifts_for_resale`.""" + + PRICE = auto() + "The gifts will be sorted by their price from the lowest to the highest" + + CHANGE_DATE = auto() + "The gifts will be sorted by the last date when their price was changed from the newest to the oldest" + + NUMBER = auto() + "The gifts will be sorted by their number from the smallest to the largest" diff --git a/pyrogram/methods/payments/__init__.py b/pyrogram/methods/payments/__init__.py index d67a26aa..5164f94c 100644 --- a/pyrogram/methods/payments/__init__.py +++ b/pyrogram/methods/payments/__init__.py @@ -31,11 +31,15 @@ from .get_chat_gifts_count import GetChatGiftsCount from .get_chat_gifts import GetChatGifts from .hide_gift import HideGift from .refund_stars_payment import RefundStarPayment +from .search_gifts_for_resale import SearchGiftsForResale from .send_invoice import SendInvoice from .send_paid_media import SendPaidMedia from .send_paid_reaction import SendPaidReaction from .send_payment_form import SendPaymentForm from .send_gift import SendGift +from .send_resold_gift import SendResoldGift +from .set_gift_resale_price import SetGiftResalePrice +from .set_pinned_gift import SetPinnedGift from .show_gift import ShowGift from .transfer_gift import TransferGift from .upgrade_gift import UpgradeGift @@ -55,11 +59,15 @@ class Payments( GetChatGifts, HideGift, RefundStarPayment, + SearchGiftsForResale, SendPaidReaction, SendPaidMedia, SendInvoice, SendPaymentForm, SendGift, + SendResoldGift, + SetGiftResalePrice, + SetPinnedGift, ShowGift, TransferGift, UpgradeGift diff --git a/pyrogram/methods/payments/search_gifts_for_resale.py b/pyrogram/methods/payments/search_gifts_for_resale.py new file mode 100644 index 00000000..c576766c --- /dev/null +++ b/pyrogram/methods/payments/search_gifts_for_resale.py @@ -0,0 +1,110 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork 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. +# +# Pyrofork 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 Pyrofork. If not, see . + +from typing import List, Optional + +import pyrogram +from pyrogram import enums, raw, types + + +class SearchGiftsForResale: + async def search_gifts_for_resale( + self: "pyrogram.Client", + gift_id: int, + order: "enums.GiftForResaleOrder" = enums.GiftForResaleOrder.CHANGE_DATE, + attributes: Optional[List["types.UpgradedGiftAttributeId"]] = None, + limit: int = 0, + offset: str = "" + ): + """Get upgraded gifts that can be bought from other owners. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + gift_id (``int``): + Identifier of the regular gift that was upgraded to a unique gift. + + order (:obj:`~pyrogram.enums.GiftForResaleOrder`): + Order in which the results will be sorted. + + attributes (List of :obj:`~pyrogram.types.UpgradedGiftAttributeId`, *optional*): + Attributes used to filter received gifts. + If multiple attributes of the same type are specified, then all of them are allowed. + If none attributes of specific type are specified, then all values for this attribute type are allowed. + + limit (``int``, *optional*): + The maximum number of gifts to return. Default is 0 (no limit). + + offset (``str``, *optional*): + The offset from which to start returning results. Default is "" (no offset). + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Gift` objects. + + Example: + .. code-block:: python + + async for gift in app.search_gifts_for_resale(gift_id=123456): + print(gift) + + # Buy first gift from resale market + async for gift in app.search_gifts_for_resale(gift_id=123456, limit=1): + await app.send_resold_gift(gift_link=gift.link, new_owner_chat_id="me") # or just use await gift.buy() + """ + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + while True: + r = await self.invoke( + raw.functions.payments.GetResaleStarGifts( + gift_id=gift_id, + offset=offset, + limit=limit, + sort_by_price=order == enums.GiftForResaleOrder.PRICE, + sort_by_num=order == enums.GiftForResaleOrder.NUMBER, + attributes=[attr.write() for attr in attributes] if attributes else None, + + ), + sleep_threshold=60 + ) + + users = {i.id: i for i in r.users} + chats = {i.id: i for i in r.chats} + + gifts = [ + await types.Gift._parse(self, gift, users, chats) + for gift in r.gifts + ] + + if not gifts: + return + + for gift in gifts: + yield gift + + current += 1 + + if current >= total: + return + + offset = r.next_offset + + if not offset: + return diff --git a/pyrogram/methods/payments/send_resold_gift.py b/pyrogram/methods/payments/send_resold_gift.py new file mode 100644 index 00000000..21f66489 --- /dev/null +++ b/pyrogram/methods/payments/send_resold_gift.py @@ -0,0 +1,89 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork 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. +# +# Pyrofork 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 Pyrofork. If not, see . + + +from typing import Optional, Union + +import pyrogram +from pyrogram import raw, types, utils + + +class SendResoldGift: + async def send_resold_gift( + self: "pyrogram.Client", + gift_link: str, + new_owner_chat_id: Union[int, str], + ) -> Optional["types.Message"]: + """Send an upgraded gift that is available for resale to another user or channel chat. + + .. note:: + + Gifts already owned by the current user must be transferred using :meth:`~pyrogram.Client.transfer_gift` and can't be passed to this method. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + gift_link (``str``): + Link to the gift. + + new_owner_chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat you want to transfer the star gift to. + 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). + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent message is returned. + + Example: + .. code-block:: python + + # Transfer gift to another user + await app.send_resold_gift(gift_link="https://t.me/nft/NekoHelmet-9215", new_owner_chat_id=123) + """ + match = self.UPGRADED_GIFT_RE.match(gift_link) + + if not match: + raise ValueError( + "Invalid gift link provided." + ) + + peer = await self.resolve_peer(new_owner_chat_id) + + invoice = raw.types.InputInvoiceStarGiftResale( + slug=match.group(1), + to_id=peer + ) + + r = await self.invoke( + raw.functions.payments.SendStarsForm( + form_id=(await self.invoke( + raw.functions.payments.GetPaymentForm( + invoice=invoice + ), + )).form_id, + invoice=invoice + ), + ) + + messages = await utils.parse_messages( + client=self, + messages=r.updates if isinstance(r, raw.types.payments.PaymentResult) else r + ) + + return messages[0] if messages else None diff --git a/pyrogram/methods/payments/set_gift_resale_price.py b/pyrogram/methods/payments/set_gift_resale_price.py new file mode 100644 index 00000000..52ce7d6e --- /dev/null +++ b/pyrogram/methods/payments/set_gift_resale_price.py @@ -0,0 +1,82 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork 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. +# +# Pyrofork 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 Pyrofork. If not, see . + +import re + +import pyrogram +from pyrogram import raw + + +class SetGiftResalePrice: + async def set_gift_resale_price( + self: "pyrogram.Client", + owned_gift_id: str, + resale_star_count: int + ) -> bool: + """Change resale price of a unique gift owned by the current user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + owned_gift_id (``str``): + Unique identifier of the target gift. + For a user gift, you can use the message ID (int) of the gift message. + For a channel gift, you can use the packed format `chatID_savedID` (str). + For a upgraded gift, you can use the gift link. + + resale_star_count (``int``): + The new price for the unique gift. Pass 0 to disallow gift resale. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Change resale price of a unique gift + await app.set_gift_resale_price(owned_gift_id="123456", resale_star_count=100) + """ + if not isinstance(owned_gift_id, str): + raise ValueError(f"owned_gift_id has to be str, but {type(owned_gift_id)} was provided") + + saved_gift_match = re.match(r"^(-\d+)_(\d+)$", owned_gift_id) + slug_match = self.UPGRADED_GIFT_RE.match(owned_gift_id) + + if saved_gift_match: + stargift = raw.types.InputSavedStarGiftChat( + peer=await self.resolve_peer(saved_gift_match.group(1)), + saved_id=int(saved_gift_match.group(2)) + ) + elif slug_match: + stargift = raw.types.InputSavedStarGiftSlug( + slug=slug_match.group(1) + ) + else: + stargift = raw.types.InputSavedStarGiftUser( + msg_id=int(owned_gift_id) + ) + + await self.invoke( + raw.functions.payments.UpdateStarGiftPrice( + stargift=stargift, + resell_stars=resale_star_count + ) + ) + + return True diff --git a/pyrogram/methods/payments/set_pinned_gifts.py b/pyrogram/methods/payments/set_pinned_gifts.py new file mode 100644 index 00000000..56352d43 --- /dev/null +++ b/pyrogram/methods/payments/set_pinned_gifts.py @@ -0,0 +1,95 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork 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. +# +# Pyrofork 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 Pyrofork. If not, see . + +import re +from typing import List, Union + +import pyrogram +from pyrogram import raw + + +class SetPinnedGifts: + async def set_pinned_gifts( + self: "pyrogram.Client", + owner_id: Union[int, str], + owned_gift_ids: List[str], + ) -> bool: + """Change the list of pinned gifts on the current user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + owner_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + 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). + + owned_gift_ids (List of ``str``): + New list of pinned gifts. + All gifts must be upgraded and saved on the profile page first. + For a user gift, you can use the message ID (int) of the gift message. + For a channel gift, you can use the packed format `chatID_savedID` (str). + For a upgraded gift, you can use the gift link. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Set pinned gifts in user profile + await app.set_pinned_gifts(received_gift_ids=["123", "456"]) + """ + stargifts = [] + + for gift in owned_gift_ids: + if not isinstance(gift, str): + raise ValueError(f"gift id has to be str, but {type(gift)} was provided") + + saved_gift_match = re.match(r"^(-\d+)_(\d+)$", gift) + slug_match = self.UPGRADED_GIFT_RE.match(gift) + + if saved_gift_match: + stargifts.append( + raw.types.InputSavedStarGiftChat( + peer=await self.resolve_peer(saved_gift_match.group(1)), + saved_id=int(saved_gift_match.group(2)) + ) + ) + elif slug_match: + stargifts.append( + raw.types.InputSavedStarGiftSlug( + slug=slug_match.group(1) + ) + ) + else: + stargifts.append( + raw.types.InputSavedStarGiftUser( + msg_id=int(gift) + ) + ) + + r = await self.invoke( + raw.functions.payments.ToggleStarGiftsPinnedToTop( + peer=await self.resolve_peer(owner_id), + stargift=stargifts + ) + ) + + return r