diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 4909b3ee..0c9464e2 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -345,6 +345,13 @@ def pyrogram_api(): get_contacts get_contacts_count """, + payments=""" + Payments + apply_gift_code + check_gift_code + get_payment_form + send_payment_form + """, password=""" Password enable_cloud_password @@ -537,7 +544,6 @@ def pyrogram_api(): ChatTheme ChatWallpaper ContactRegistered - GiftCode ReadParticipant ScreenshotTaken Wallpaper @@ -557,6 +563,12 @@ def pyrogram_api(): InputMediaArea InputMediaAreaChannelPost """, + payment=""" + Payment + GiftCode + CheckedGiftCode + PaymentForm + """, pyromod=""" Pyromod Identifier diff --git a/pyrogram/methods/__init__.py b/pyrogram/methods/__init__.py index 2da4627c..e700b0cc 100644 --- a/pyrogram/methods/__init__.py +++ b/pyrogram/methods/__init__.py @@ -28,6 +28,7 @@ from .messages import Messages from .password import Password from .pyromod import Pyromod from .stickers import Stickers +from .payments import Payments from .users import Users from .utilities import Utilities from .business import TelegramBusiness @@ -40,6 +41,7 @@ class Methods( Contacts, Password, Pyromod, + Payments, Chats, Stickers, Users, diff --git a/pyrogram/methods/messages/copy_media_group.py b/pyrogram/methods/messages/copy_media_group.py index b0e14afe..8d73b862 100644 --- a/pyrogram/methods/messages/copy_media_group.py +++ b/pyrogram/methods/messages/copy_media_group.py @@ -1,6 +1,3 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-present Dan -# # Pyrofork - Telegram MTProto API Client Library for Python # Copyright (C) 2017-present Dan # Copyright (C) 2022-present Mayuri-Chan diff --git a/pyrogram/methods/payments/__init__.py b/pyrogram/methods/payments/__init__.py new file mode 100644 index 00000000..806f2d37 --- /dev/null +++ b/pyrogram/methods/payments/__init__.py @@ -0,0 +1,31 @@ +# 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 .apply_gift_code import ApplyGiftCode +from .check_giftcode import CheckGiftCode +from .get_payment_form import GetPaymentForm +from .send_payment_form import SendPaymentForm + +class Payments( + ApplyGiftCode, + CheckGiftCode, + GetPaymentForm, + SendPaymentForm +): + pass diff --git a/pyrogram/methods/payments/apply_gift_code.py b/pyrogram/methods/payments/apply_gift_code.py new file mode 100644 index 00000000..38c257d7 --- /dev/null +++ b/pyrogram/methods/payments/apply_gift_code.py @@ -0,0 +1,54 @@ +# 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 ApplyGiftCode: + async def apply_gift_code( + self: "pyrogram.Client", + link: str, + ) -> bool: + """Apply a gift code. + .. include:: /_includes/usable-by/users.rst + Parameters: + link (``str``): + The gift code link. + Returns: + ``bool``: On success, True is returned. + Raises: + ValueError: In case the gift code link is invalid. + Example: + .. code-block:: python + # apply a gift code + app.apply_gift_code("t.me/giftcode/abc1234567def") + """ + match = re.match(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/(?:giftcode/|\+))([\w-]+)$", link) + if match: + slug = match.group(1) + elif isinstance(link, str): + slug = link + else: + raise ValueError("Invalid gift code link") + await self.invoke( + raw.functions.payments.ApplyGiftCode( + slug=slug + ) + ) + return True diff --git a/pyrogram/methods/payments/check_giftcode.py b/pyrogram/methods/payments/check_giftcode.py new file mode 100644 index 00000000..378a678b --- /dev/null +++ b/pyrogram/methods/payments/check_giftcode.py @@ -0,0 +1,69 @@ +# 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, types + + +class CheckGiftCode: + async def check_gift_code( + self: "pyrogram.Client", + link: str, + ) -> "types.CheckedGiftCode": + """Get information about a gift code. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + link (``str``): + The gift code link. + + Returns: + :obj:`~pyrogram.types.CheckedGiftCode`: On success, a checked gift code is returned. + + Raises: + ValueError: In case the gift code link is invalid. + + Example: + .. code-block:: python + + # get information about a gift code + app.check_gift_code("t.me/giftcode/abc1234567def") + """ + match = re.match(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/(?:giftcode/|\+))([\w-]+)$", link) + + if match: + slug = match.group(1) + elif isinstance(link, str): + slug = link + else: + raise ValueError("Invalid gift code link") + + r = await self.invoke( + raw.functions.payments.CheckGiftCode( + slug=slug + ) + ) + + users = {i.id: i for i in r.users} + chats = {i.id: i for i in r.chats} + + return types.CheckedGiftCode._parse(self, r, users, chats) diff --git a/pyrogram/methods/payments/get_payment_form.py b/pyrogram/methods/payments/get_payment_form.py new file mode 100644 index 00000000..bd00af62 --- /dev/null +++ b/pyrogram/methods/payments/get_payment_form.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 . + +import re +from typing import Union + +import pyrogram +from pyrogram import raw, types + + +class GetPaymentForm: + async def get_payment_form( + 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. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + of the target channel/supergroup (in the format @username). + + message_id (``int``): + Pass a message identifier or to get the invoice from message. + + invoice_link (``str``): + Pass a invoice link in form of a *t.me/$...* link or slug itself to get the payment form from link. + + Returns: + :obj:`~pyrogram.types.PaymentForm`: On success, a payment form is returned. + + Example: + .. code-block:: python + + # get payment form from message + app.get_payment_form(chat_id=chat_id, message_id=123) + + # get payment form from link + app.get_payment_form(invoice_link="https://t.me/$xvbzUtt5sUlJCAAATqZrWRy9Yzk") + """ + if not any((all((chat_id, message_id)), invoice_link)): + raise ValueError("You should pass at least one parameter to this method.") + + invoice = None + + if message_id: + invoice = raw.types.InputInvoiceMessage( + peer=await self.resolve_peer(chat_id), + msg_id=message_id + ) + elif invoice_link: + match = re.match(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/\$)([\w-]+)$", invoice_link) + + if match: + slug = match.group(1) + else: + slug = invoice_link + + invoice = raw.types.InputInvoiceSlug( + slug=slug + ) + + r = await self.invoke( + raw.functions.payments.GetPaymentForm( + invoice=invoice + ) + ) + + return types.PaymentForm._parse(self, r) diff --git a/pyrogram/methods/payments/send_payment_form.py b/pyrogram/methods/payments/send_payment_form.py new file mode 100644 index 00000000..7c66af1d --- /dev/null +++ b/pyrogram/methods/payments/send_payment_form.py @@ -0,0 +1,133 @@ +# 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 Union, List + +import pyrogram +from pyrogram import raw, types + + +class SendPaymentForm: + async def send_payment_form( + self: "pyrogram.Client", *, + chat_id: Union[int, str] = None, + message_id: int = None, + invoice_link: str = None + ) -> List[Union["types.Photo", "types.Video"]]: + """Pay an invoice. + + .. note:: + + For now only stars invoices are supported. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + of the target channel/supergroup (in the format @username). + + message_id (``int``): + Pass a message identifier or to get the invoice from message. + + invoice_link (``str``): + Pass a invoice link in form of a *t.me/$...* link or slug itself to pay this invoice. + + Returns: + List of :obj:`~pyrogram.types.Photo` | :obj:`~pyrogram.types.Video`: On success, the list of bought photos and videos is returned. + + Example: + .. code-block:: python + + # Pay invoice from message + app.send_payment_form(chat_id=chat_id, message_id=123) + + # Pay invoice form from link + app.send_payment_form(invoice_link="https://t.me/$xvbzUtt5sUlJCAAATqZrWRy9Yzk") + """ + if not any((all((chat_id, message_id)), invoice_link)): + raise ValueError("You should pass at least one parameter to this method.") + + form = None + invoice = None + + if message_id: + invoice = raw.types.InputInvoiceMessage( + peer=await self.resolve_peer(chat_id), + msg_id=message_id + ) + elif invoice_link: + match = re.match(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/\$)([\w-]+)$", invoice_link) + + if match: + slug = match.group(1) + else: + slug = invoice_link + + invoice = raw.types.InputInvoiceSlug( + slug=slug + ) + + form = await self.get_payment_form(chat_id=chat_id, message_id=message_id, invoice_link=invoice_link) + + # if form.invoice.currency == "XTR": + r = await self.invoke( + raw.functions.payments.SendStarsForm( + form_id=form.id, + invoice=invoice + ) + ) + # TODO: Add support for regular invoices (credentials) + # else: + # r = await self.invoke( + # raw.functions.payments.SendPaymentForm( + # form_id=form.id, + # invoice=invoice, + # credentials=raw.types.InputPaymentCredentials(data=raw.types.DataJSON(data={})) + # ) + # ) + + medias = [] + + if isinstance(r, raw.types.payments.PaymentResult): + for i in r.updates.updates: + if isinstance(i, raw.types.UpdateMessageExtendedMedia): + for ext_media in i.extended_media: + media = ext_media.media + + if isinstance(media, raw.types.MessageMediaPhoto): + medias.append(types.Photo._parse(self, media.photo)) + elif isinstance(media, raw.types.MessageMediaDocument): + doc = media.document + + attributes = {type(i): i for i in doc.attributes} + + file_name = getattr( + attributes.get( + raw.types.DocumentAttributeFilename, None + ), "file_name", None + ) + + video_attributes = attributes[raw.types.DocumentAttributeVideo] + + medias.append(types.Video._parse(self, doc, video_attributes, file_name)) + + return types.List(medias) + # elif isinstance(r, raw.types.payments.PaymentVerificationNeeded): diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index fb738c99..24c46fe8 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -22,6 +22,7 @@ from .audio import Audio from .available_effect import AvailableEffect from .chat_theme import ChatTheme from .chat_wallpaper import ChatWallpaper +from .checked_gift_code import CheckedGiftCode from .contact import Contact from .contact_registered import ContactRegistered from .dice import Dice @@ -39,6 +40,7 @@ from .media_area_channel_post import MediaAreaChannelPost from .media_area_coordinates import MediaAreaCoordinates from .message import Message from .message_entity import MessageEntity +from .payment_form import PaymentForm from .photo import Photo from .poll import Poll from .poll_option import PollOption @@ -84,6 +86,7 @@ __all__ = [ "Document", "Game", "GiftCode", + "CheckedGiftCode", "GiftedPremium", "Giveaway", "GiveawayLaunched", @@ -95,6 +98,7 @@ __all__ = [ "MediaAreaCoordinates", "Message", "MessageEntity", + "PaymentForm", "Photo", "Thumbnail", "StrippedThumbnail", diff --git a/pyrogram/types/messages_and_media/checked_gift_code.py b/pyrogram/types/messages_and_media/checked_gift_code.py new file mode 100644 index 00000000..fa325c14 --- /dev/null +++ b/pyrogram/types/messages_and_media/checked_gift_code.py @@ -0,0 +1,93 @@ +# 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 datetime import datetime + +from pyrogram import raw, types, utils +from ..object import Object + + +class CheckedGiftCode(Object): + """Contains checked gift code data. + + Parameters: + date (:py:obj:`~datetime.datetime`): + Date when the giveaway was launched. + + months (``int``): + Number of months of subscription. + + via_giveaway (``bool``, *optional*): + True if the gift code is received via giveaway. + + from_chat (:obj:`~pyrogram.types.Chat`, *optional*): + The channel where the gift code was won. + + winner (:obj:`~pyrogram.types.User`, *optional*): + The user who won the giveaway. + + giveaway_message_id (``int``, *optional*): + Identifier of the message from chat where the giveaway was launched. + + used_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the gift code was used. + """ + + def __init__( + self, + *, + date: datetime, + months: int, + via_giveaway: bool = None, + from_chat: "types.Chat" = None, + winner: "types.User" = None, + giveaway_message_id: int = None, + used_date: datetime = None + ): + super().__init__() + + self.date = date + self.months = months + self.via_giveaway = via_giveaway + self.from_chat = from_chat + self.winner = winner + self.giveaway_message_id = giveaway_message_id + self.used_date = used_date + + @staticmethod + def _parse(client, checked_gift_code: "raw.types.payments.CheckedGiftCode", users, chats): + from_chat = None + winner = None + + if getattr(checked_gift_code, "from_id", None): + from_chat = types.Chat._parse_chat( + client, chats.get(utils.get_raw_peer_id(checked_gift_code.from_id)) + ) + if getattr(checked_gift_code, "to_id", None): + winner = types.User._parse(client, users.get(checked_gift_code.to_id)) + + return CheckedGiftCode( + date=utils.timestamp_to_datetime(checked_gift_code.date), + months=checked_gift_code.months, + via_giveaway=getattr(checked_gift_code, "via_giveaway", None), + from_chat=from_chat, + winner=winner, + giveaway_message_id=getattr(checked_gift_code, "giveaway_msg_id", None), + used_date=utils.timestamp_to_datetime(checked_gift_code.used_date) if getattr(checked_gift_code, "used_date") else None, + ) diff --git a/pyrogram/types/messages_and_media/payment_form.py b/pyrogram/types/messages_and_media/payment_form.py new file mode 100644 index 00000000..0d6f0ef2 --- /dev/null +++ b/pyrogram/types/messages_and_media/payment_form.py @@ -0,0 +1,117 @@ +# 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 types, raw +from ..object import Object + + +class PaymentForm(Object): + """This object contains basic information about an payment form. + + Parameters: + id (``int``): + Form id. + + bot (``str``): + Bot. + + title (``str``): + Form title. + + description (``str``): + Form description. + + invoice (``str``): + Invoice. + + provider (``str``, *optional*): + Payment provider. + + url (``str``, *optional*): + Payment form URL. + + can_save_credentials (``str``, *optional*): + Whether the user can choose to save credentials. + + is_password_missing (``str``, *optional*): + Indicates that the user can save payment credentials, + but only after setting up a 2FA password + (currently the account doesn't have a 2FA password). + + native_provider (``str``, *optional*): + Payment provider name. + + raw (:obj:`~raw.base.payments.PaymentForm`, *optional*): + The raw object, as received from the Telegram API. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + bot: "types.User", + title: str, + description: str, + invoice: "types.Invoice", + provider: Optional["types.User"] = None, + url: Optional[str] = None, + can_save_credentials: Optional[bool] = None, + is_password_missing: Optional[bool] = None, + native_provider: Optional[str] = None, + raw: "raw.base.payments.PaymentForm" = None, + # TODO: Add support for other params: + # native_params + # additional_params + # saved_info + # saved_credentials + ): + super().__init__(client) + + self.id = id + self.bot = bot + self.title = title + self.description = description + self.invoice = invoice + self.provider = provider + self.url = url + self.can_save_credentials = can_save_credentials + self.is_password_missing = is_password_missing + self.native_provider = native_provider + self.raw = raw + + @staticmethod + def _parse(client, payment_form: "raw.base.payments.PaymentForm") -> "PaymentForm": + users = {i.id: i for i in payment_form.users} + + return PaymentForm( + id=payment_form.form_id, + bot=types.User._parse(client, users.get(payment_form.bot_id)), + title=payment_form.title, + description=payment_form.description, + invoice=types.Invoice._parse(client, payment_form.invoice), + provider=types.User._parse(client, users.get(getattr(payment_form, "provider_id", None))), + url=getattr(payment_form, "url", None), + can_save_credentials=getattr(payment_form, "can_save_credentials", None), + is_password_missing=getattr(payment_form, "password_missing", None), + native_provider=getattr(payment_form, "native_provider", None), + raw=payment_form + )