From a46e4d593ba508b15449fb10c7e9b11002aea9d9 Mon Sep 17 00:00:00 2001 From: coglione <74505123+coglione@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:01:22 +0200 Subject: [PATCH] Add high level support for pre-checkout queries and service messages for successful payments (#67) * Add high level support for pre-checkout queries * Add high level support for incoming payments * Update and fix documentation --------- Co-authored-by: KurimuzonAkuma <31959970+KurimuzonAkuma@users.noreply.github.com> --- compiler/docs/compiler.py | 9 ++ compiler/docs/template/bound-methods.rst | 13 ++ pyrogram/dispatcher.py | 14 +- pyrogram/enums/message_service_type.py | 3 + pyrogram/filters.py | 18 ++- pyrogram/handlers/__init__.py | 1 + .../handlers/pre_checkout_query_handler.py | 49 ++++++ pyrogram/methods/bots/__init__.py | 4 +- .../methods/bots/answer_pre_checkout_query.py | 64 ++++++++ pyrogram/methods/chats/get_chat_member.py | 2 +- pyrogram/methods/decorators/__init__.py | 4 +- .../decorators/on_pre_checkout_query.py | 61 ++++++++ pyrogram/methods/users/unblock_user.py | 2 +- pyrogram/types/bots_and_keyboards/__init__.py | 10 +- .../types/bots_and_keyboards/payment_info.py | 51 +++++++ .../bots_and_keyboards/pre_checkout_query.py | 139 ++++++++++++++++++ .../bots_and_keyboards/shipping_address.py | 61 ++++++++ .../bots_and_keyboards/successful_payment.py | 111 ++++++++++++++ pyrogram/types/messages_and_media/message.py | 10 ++ 19 files changed, 616 insertions(+), 10 deletions(-) create mode 100644 pyrogram/handlers/pre_checkout_query_handler.py create mode 100644 pyrogram/methods/bots/answer_pre_checkout_query.py create mode 100644 pyrogram/methods/decorators/on_pre_checkout_query.py create mode 100644 pyrogram/types/bots_and_keyboards/payment_info.py create mode 100644 pyrogram/types/bots_and_keyboards/pre_checkout_query.py create mode 100644 pyrogram/types/bots_and_keyboards/shipping_address.py create mode 100644 pyrogram/types/bots_and_keyboards/successful_payment.py diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 8d14d0eb..81c0bbae 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -345,6 +345,7 @@ def pyrogram_api(): set_chat_menu_button get_chat_menu_button answer_web_app_query + answer_pre_checkout_query """, business=""" Business @@ -507,6 +508,7 @@ def pyrogram_api(): GiveawayResult GiftCode CheckedGiftCode + SuccessfulPayment """, bot_keyboards=""" Bot keyboards @@ -531,6 +533,9 @@ def pyrogram_api(): RequestChatInfo RequestUserInfo RequestPollInfo + PaymentInfo + PreCheckoutQuery + ShippingAddress """, bot_commands=""" Bot commands @@ -701,6 +706,10 @@ def pyrogram_api(): InlineQuery InlineQuery.answer """, + pre_checkout_query=""" + PreCheckoutQuery + PreCheckoutQuery.answer + """, chat_join_request=""" ChatJoinRequest ChatJoinRequest.approve diff --git a/compiler/docs/template/bound-methods.rst b/compiler/docs/template/bound-methods.rst index 504da4f4..163753dd 100644 --- a/compiler/docs/template/bound-methods.rst +++ b/compiler/docs/template/bound-methods.rst @@ -88,6 +88,19 @@ InlineQuery {inline_query_toctree} +PreCheckoutQuery +----------- + +.. hlist:: + :columns: 2 + + {pre_checkout_query_hlist} + +.. toctree:: + :hidden: + + {pre_checkout_query_toctree} + ChatJoinRequest --------------- diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py index 1a9b235f..1e6439c7 100644 --- a/pyrogram/dispatcher.py +++ b/pyrogram/dispatcher.py @@ -27,7 +27,7 @@ from pyrogram import utils from pyrogram import raw from pyrogram.handlers import ( CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler, - UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, + UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, PreCheckoutQueryHandler, ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler, StoryHandler ) from pyrogram.raw.types import ( @@ -35,7 +35,7 @@ from pyrogram.raw.types import ( UpdateBotNewBusinessMessage, UpdateBotEditBusinessMessage, UpdateBotDeleteBusinessMessage, UpdateEditMessage, UpdateEditChannelMessage, UpdateDeleteMessages, UpdateDeleteChannelMessages, - UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, + UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateBotPrecheckoutQuery, UpdateUserStatus, UpdateBotInlineQuery, UpdateMessagePoll, UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant, UpdateBotChatInviteRequester, UpdateStory @@ -56,6 +56,7 @@ class Dispatcher: CHOSEN_INLINE_RESULT_UPDATES = (UpdateBotInlineSend,) CHAT_JOIN_REQUEST_UPDATES = (UpdateBotChatInviteRequester,) NEW_STORY_UPDATES = (UpdateStory,) + PRE_CHECKOUT_QUERY_UPDATES = (UpdateBotPrecheckoutQuery,) def __init__(self, client: "pyrogram.Client"): self.client = client @@ -144,6 +145,12 @@ class Dispatcher: StoryHandler ) + async def pre_checkout_query_parser(update, users, chats): + return ( + await pyrogram.types.PreCheckoutQuery._parse(self.client, update, users), + PreCheckoutQueryHandler + ) + self.update_parsers = { Dispatcher.NEW_MESSAGE_UPDATES: message_parser, Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser, @@ -155,7 +162,8 @@ class Dispatcher: Dispatcher.CHOSEN_INLINE_RESULT_UPDATES: chosen_inline_result_parser, Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser, Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser, - Dispatcher.NEW_STORY_UPDATES: story_parser + Dispatcher.NEW_STORY_UPDATES: story_parser, + Dispatcher.PRE_CHECKOUT_QUERY_UPDATES: pre_checkout_query_parser } self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} diff --git a/pyrogram/enums/message_service_type.py b/pyrogram/enums/message_service_type.py index 0d4bb0a8..4f456c86 100644 --- a/pyrogram/enums/message_service_type.py +++ b/pyrogram/enums/message_service_type.py @@ -102,6 +102,9 @@ class MessageServiceType(AutoName): REQUESTED_CHAT = auto() "Requested chat" + SUCCESSFUL_PAYMENT = auto() + "Successful payment" + CHAT_TTL_CHANGED = auto() "Chat TTL changed" diff --git a/pyrogram/filters.py b/pyrogram/filters.py index 2ef7f242..b9f77d23 100644 --- a/pyrogram/filters.py +++ b/pyrogram/filters.py @@ -22,7 +22,7 @@ from typing import Callable, Union, List, Pattern import pyrogram from pyrogram import enums -from pyrogram.types import Message, CallbackQuery, InlineQuery, InlineKeyboardMarkup, ReplyKeyboardMarkup, Update +from pyrogram.types import Message, CallbackQuery, InlineQuery, PreCheckoutQuery, InlineKeyboardMarkup, ReplyKeyboardMarkup, Update class Filter: @@ -788,6 +788,17 @@ video_chat_members_invited = create(video_chat_members_invited_filter) """Filter messages for voice chat invited members""" +# endregion + +# region successful_payment_filter +async def successful_payment_filter(_, __, m: Message): + return bool(m.successful_payment) + + +successful_payment = create(successful_payment_filter) +"""Filter messages for successful payments""" + + # endregion # region service_filter @@ -801,7 +812,7 @@ service = create(service_filter) A service message contains any of the following fields set: *left_chat_member*, *new_chat_title*, *new_chat_photo*, *delete_chat_photo*, *group_chat_created*, *supergroup_chat_created*, *channel_chat_created*, *migrate_to_chat_id*, *migrate_from_chat_id*, *pinned_message*, *game_score*, -*video_chat_started*, *video_chat_ended*, *video_chat_members_invited*. +*video_chat_started*, *video_chat_ended*, *video_chat_members_invited*, *successful_payment*. """ @@ -939,6 +950,7 @@ def regex(pattern: Union[str, Pattern], flags: int = 0): - :obj:`~pyrogram.types.Message`: The filter will match ``text`` or ``caption``. - :obj:`~pyrogram.types.CallbackQuery`: The filter will match ``data``. - :obj:`~pyrogram.types.InlineQuery`: The filter will match ``query``. + - :obj:`~pyrogram.types.PreCheckoutQuery`: The filter will match ``payload``. When a pattern matches, all the `Match Objects `_ are stored in the ``matches`` field of the update object itself. @@ -958,6 +970,8 @@ def regex(pattern: Union[str, Pattern], flags: int = 0): value = update.data elif isinstance(update, InlineQuery): value = update.query + elif isinstance(update, PreCheckoutQuery): + value = update.payload else: raise ValueError(f"Regex filter doesn't work with {type(update)}") diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py index 2d9ca60d..b6f3aacf 100644 --- a/pyrogram/handlers/__init__.py +++ b/pyrogram/handlers/__init__.py @@ -26,6 +26,7 @@ from .edited_message_handler import EditedMessageHandler from .inline_query_handler import InlineQueryHandler from .message_handler import MessageHandler from .poll_handler import PollHandler +from .pre_checkout_query_handler import PreCheckoutQueryHandler from .raw_update_handler import RawUpdateHandler from .story_handler import StoryHandler from .user_status_handler import UserStatusHandler diff --git a/pyrogram/handlers/pre_checkout_query_handler.py b/pyrogram/handlers/pre_checkout_query_handler.py new file mode 100644 index 00000000..ba8c6505 --- /dev/null +++ b/pyrogram/handlers/pre_checkout_query_handler.py @@ -0,0 +1,49 @@ +# 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 Callable + +from .handler import Handler + + +class PreCheckoutQueryHandler(Handler): + """The PreCheckoutQueryHandler handler class. Used to handle pre-checkout queries coming from buy buttons. + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_pre_checkout_query` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new PreCheckoutQuery arrives. It takes *(client, pre_checkout_query)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of callback queries to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the message handler. + + pre_checkout_query (:obj:`~pyrogram.types.PreCheckoutQuery`): + The received callback query. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/methods/bots/__init__.py b/pyrogram/methods/bots/__init__.py index da52fcfb..cc4dc3f1 100644 --- a/pyrogram/methods/bots/__init__.py +++ b/pyrogram/methods/bots/__init__.py @@ -18,6 +18,7 @@ from .answer_callback_query import AnswerCallbackQuery from .answer_inline_query import AnswerInlineQuery +from .answer_pre_checkout_query import AnswerPreCheckoutQuery from .answer_web_app_query import AnswerWebAppQuery from .delete_bot_commands import DeleteBotCommands from .get_bot_commands import GetBotCommands @@ -50,6 +51,7 @@ class Bots( GetBotDefaultPrivileges, SetChatMenuButton, GetChatMenuButton, - AnswerWebAppQuery + AnswerWebAppQuery, + AnswerPreCheckoutQuery ): pass diff --git a/pyrogram/methods/bots/answer_pre_checkout_query.py b/pyrogram/methods/bots/answer_pre_checkout_query.py new file mode 100644 index 00000000..e9cc452c --- /dev/null +++ b/pyrogram/methods/bots/answer_pre_checkout_query.py @@ -0,0 +1,64 @@ +# 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 . + +import pyrogram +from pyrogram import raw + + +class AnswerPreCheckoutQuery: + async def answer_pre_checkout_query( + self: "pyrogram.Client", + pre_checkout_query_id: str, + success: bool = None, + error: str = None + ): + """Send answers to pre-checkout queries. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + pre_checkout_query_id (``str``): + Unique identifier for the query to be answered. + + success (``bool``, *optional*): + Set this flag if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. + Otherwise do not set it, and set the error field, instead. + + error (``str``, *optional*): + Error message in human readable form that explains the reason for failure to proceed with the checkout. + Required if ``success`` isn't set. + + Returns: + ``bool``: True, on success. + + Example: + .. code-block:: python + + # Proceed with the order + await app.answer_pre_checkout_query(query_id, success=True) + + # Answer with error message + await app.answer_pre_checkout_query(query_id, error=error) + """ + return await self.invoke( + raw.functions.messages.SetBotPrecheckoutResults( + query_id=int(pre_checkout_query_id), + success=success or None, + error=error or None + ) + ) diff --git a/pyrogram/methods/chats/get_chat_member.py b/pyrogram/methods/chats/get_chat_member.py index 53d2faad..25cc4dbb 100644 --- a/pyrogram/methods/chats/get_chat_member.py +++ b/pyrogram/methods/chats/get_chat_member.py @@ -38,7 +38,7 @@ class GetChatMember: chat_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target chat. - user_id (``int`` | ``str``):: + user_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target user. For you yourself you can simply use "me" or "self". For a contact that exists in your Telegram address book you can use his phone number (str). diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py index 80c5f9c4..77e65c64 100644 --- a/pyrogram/methods/decorators/__init__.py +++ b/pyrogram/methods/decorators/__init__.py @@ -26,6 +26,7 @@ from .on_edited_message import OnEditedMessage from .on_inline_query import OnInlineQuery from .on_message import OnMessage from .on_poll import OnPoll +from .on_pre_checkout_query import OnPreCheckoutQuery from .on_raw_update import OnRawUpdate from .on_user_status import OnUserStatus from .on_story import OnStory @@ -44,6 +45,7 @@ class Decorators( OnChosenInlineResult, OnChatMemberUpdated, OnChatJoinRequest, - OnStory + OnStory, + OnPreCheckoutQuery ): pass diff --git a/pyrogram/methods/decorators/on_pre_checkout_query.py b/pyrogram/methods/decorators/on_pre_checkout_query.py new file mode 100644 index 00000000..9e076fda --- /dev/null +++ b/pyrogram/methods/decorators/on_pre_checkout_query.py @@ -0,0 +1,61 @@ +# 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 Callable, Optional, Union + +import pyrogram +from pyrogram.filters import Filter + + +class OnPreCheckoutQuery: + def on_pre_checkout_query( + self: Union["OnPreCheckoutQuery", Filter, None] = None, + filters: Optional[Filter] = None, + group: int = 0, + ) -> Callable: + """Decorator for handling pre-checkout queries. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.PreCheckoutQueryHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of callback queries to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.PreCheckoutQueryHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.PreCheckoutQueryHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/users/unblock_user.py b/pyrogram/methods/users/unblock_user.py index db4fdddc..e1d1b3d8 100644 --- a/pyrogram/methods/users/unblock_user.py +++ b/pyrogram/methods/users/unblock_user.py @@ -32,7 +32,7 @@ class UnblockUser: .. include:: /_includes/usable-by/users.rst Parameters: - user_id (``int`` | ``str``):: + user_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target user. For you yourself you can simply use "me" or "self". For a contact that exists in your Telegram address book you can use his phone number (str). diff --git a/pyrogram/types/bots_and_keyboards/__init__.py b/pyrogram/types/bots_and_keyboards/__init__.py index 66dbd339..ebec18ea 100644 --- a/pyrogram/types/bots_and_keyboards/__init__.py +++ b/pyrogram/types/bots_and_keyboards/__init__.py @@ -37,6 +37,8 @@ from .menu_button import MenuButton from .menu_button_commands import MenuButtonCommands from .menu_button_default import MenuButtonDefault from .menu_button_web_app import MenuButtonWebApp +from .payment_info import PaymentInfo +from .pre_checkout_query import PreCheckoutQuery from .reply_keyboard_markup import ReplyKeyboardMarkup from .reply_keyboard_remove import ReplyKeyboardRemove from .request_channel_info import RequestChannelInfo @@ -45,6 +47,8 @@ from .request_user_info import RequestUserInfo from .request_poll_info import RequestPollInfo from .requested_chats import RequestedChats from .sent_web_app_message import SentWebAppMessage +from .shipping_address import ShippingAddress +from .successful_payment import SuccessfulPayment from .web_app_info import WebAppInfo __all__ = [ @@ -77,5 +81,9 @@ __all__ = [ "MenuButtonCommands", "MenuButtonWebApp", "MenuButtonDefault", - "SentWebAppMessage" + "SentWebAppMessage", + "ShippingAddress", + "PaymentInfo", + "PreCheckoutQuery", + "SuccessfulPayment" ] diff --git a/pyrogram/types/bots_and_keyboards/payment_info.py b/pyrogram/types/bots_and_keyboards/payment_info.py new file mode 100644 index 00000000..690fe9e4 --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/payment_info.py @@ -0,0 +1,51 @@ +# 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 ..object import Object + + +class PaymentInfo(Object): + """Contains information about a payment. + + Parameters: + name (``str``, *optional*): + User's name. + + phone_number (``str``, *optional*): + User's phone number. + + email (``str``, *optional*): + User's email. + + shipping_address (:obj:`~pyrogram.types.ShippingAddress`, *optional*): + User's shipping address. + """ + + def __init__( + self, *, + name: str = None, + phone_number: str = None, + email: str = None, + shipping_address: "types.ShippingAddress" = None + ): + super().__init__() + + self.name = name + self.phone_number = phone_number + self.email = email + self.shipping_address = shipping_address diff --git a/pyrogram/types/bots_and_keyboards/pre_checkout_query.py b/pyrogram/types/bots_and_keyboards/pre_checkout_query.py new file mode 100644 index 00000000..f8fde554 --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/pre_checkout_query.py @@ -0,0 +1,139 @@ +# 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, List, Match, Optional + +import pyrogram +from pyrogram import raw, enums +from pyrogram import types +from ..object import Object +from ..update import Update +from ... import utils + + +class PreCheckoutQuery(Object, Update): + """An incoming pre-checkout query from a buy button in an inline keyboard. + + Parameters: + id (``str``): + Unique identifier for this query. + + from_user (:obj:`~pyrogram.types.User`): + User who sent the query. + + currency (``str``): + Three-letter ISO 4217 currency code. + + total_amount (``int``): + Total price in the smallest units of the currency. + + payload (``str``): + Bot specified invoice payload. + + shipping_option_id (``str``, *optional*): + Identifier of the shipping option chosen by the user. + + payment_info (:obj:`~pyrogram.types.PaymentInfo`, *optional*): + Payment information provided by the user. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str, + from_user: "types.User", + currency: str, + total_amount: int, + payload: str, + shipping_option_id: str = None, + payment_info: "types.PaymentInfo" = None + ): + super().__init__(client) + + self.id = id + self.from_user = from_user + self.currency = currency + self.total_amount = total_amount + self.payload = payload + self.shipping_option_id = shipping_option_id + self.payment_info = payment_info + + @staticmethod + async def _parse(client: "pyrogram.Client", pre_checkout_query, users) -> "PreCheckoutQuery": + # Try to decode pre-checkout query payload into string. If that fails, fallback to bytes instead of decoding by + # ignoring/replacing errors, this way, button clicks will still work. + try: + payload = pre_checkout_query.payload.decode() + except (UnicodeDecodeError, AttributeError): + payload = pre_checkout_query.payload + + return PreCheckoutQuery( + id=str(pre_checkout_query.query_id), + from_user=types.User._parse(client, users[pre_checkout_query.user_id]), + currency=pre_checkout_query.currency, + total_amount=pre_checkout_query.total_amount, + payload=payload, + shipping_option_id=pre_checkout_query.shipping_option_id, + payment_info=types.PaymentInfo( + name=pre_checkout_query.info.name, + phone_number=pre_checkout_query.info.phone, + email=pre_checkout_query.info.email, + shipping_address=types.ShippingAddress( + street_line1=pre_checkout_query.info.shipping_address.street_line1, + street_line2=pre_checkout_query.info.shipping_address.street_line2, + city=pre_checkout_query.info.shipping_address.city, + state=pre_checkout_query.info.shipping_address.state, + post_code=pre_checkout_query.info.shipping_address.post_code, + country_code=pre_checkout_query.info.shipping_address.country_iso2 + ) + ) if pre_checkout_query.info else None, + client=client + ) + + async def answer(self, success: bool = None, error: str = None): + """Bound method *answer* of :obj:`~pyrogram.types.PreCheckoutQuery`. + + Use this method as a shortcut for: + + .. code-block:: python + + await client.answer_pre_checkout_query( + pre_checkout_query.id, + success=True + ) + + Example: + .. code-block:: python + + await pre_checkout_query.answer(success=True) + + Parameters: + success (``bool`` *optional*): + If true, an alert will be shown by the client instead of a notification at the top of the chat screen. + Defaults to False. + + error (``bool`` *optional*): + If true, an alert will be shown by the client instead of a notification at the top of the chat screen. + Defaults to False. + """ + return await self._client.answer_pre_checkout_query( + pre_checkout_query_id=self.id, + success=success, + error=error + ) diff --git a/pyrogram/types/bots_and_keyboards/shipping_address.py b/pyrogram/types/bots_and_keyboards/shipping_address.py new file mode 100644 index 00000000..8e373f31 --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/shipping_address.py @@ -0,0 +1,61 @@ +# 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 ..object import Object + + +class ShippingAddress(Object): + """Contains information about a shipping address. + + Parameters: + street_line1 (``str``): + First line for the address. + + street_line1 (``str``): + Second line for the address. + + city (``str``): + City for the address. + + state (``str``): + State for the address, if applicable. + + post_code (``str``): + Post code for the address. + + country_code (``str``): + Two-letter ISO 3166-1 alpha-2 country code. + """ + + def __init__( + self, *, + street_line1: str, + street_line2: str, + city: str, + state: str, + post_code: str, + country_code: str + ): + super().__init__() + + self.street_line1 = street_line1 + self.street_line2 = street_line2 + self.city = city + self.state = state + self.post_code = post_code + self.country_code = country_code diff --git a/pyrogram/types/bots_and_keyboards/successful_payment.py b/pyrogram/types/bots_and_keyboards/successful_payment.py new file mode 100644 index 00000000..d362f2f7 --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/successful_payment.py @@ -0,0 +1,111 @@ +# 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 pyrogram import raw +from pyrogram import types +from ..object import Object + + +class SuccessfulPayment(Object): + """Contains information about a successful payment. + + Parameters: + currency (``str``): + Three-letter ISO 4217 currency code. + + total_amount (``int``): + Total price in the smallest units of the currency. + + payload (``str``, *optional*): + Bot specified invoice payload. Only available to the bot that received the payment. + + telegram_payment_charge_id (``str``, *optional*): + Telegram payment identifier. Only available to the bot that received the payment. + + provider_payment_charge_id (``str``, *optional*): + Provider payment identifier. Only available to the bot that received the payment. + + shipping_option_id (``str``, *optional*): + Identifier of the shipping option chosen by the user. Only available to the bot that received the payment. + + payment_info (:obj:`~pyrogram.types.PaymentInfo`, *optional*): + Payment information provided by the user. Only available to the bot that received the payment. + """ + + def __init__( + self, *, + currency: str, + total_amount: str, + payload: str, + telegram_payment_charge_id: str, + provider_payment_charge_id: str, + shipping_option_id: str = None, + payment_info: "types.PaymentInfo" = None + ): + super().__init__() + + self.currency = currency + self.total_amount = total_amount + self.payload = payload + self.telegram_payment_charge_id = telegram_payment_charge_id + self.provider_payment_charge_id = provider_payment_charge_id + self.shipping_option_id = shipping_option_id + self.payment_info = payment_info + + @staticmethod + def _parse(client: "pyrogram.Client", successful_payment) -> "SuccessfulPayment": + payload = None + telegram_payment_charge_id = None + provider_payment_charge_id = None + shipping_option_id = None + payment_info = None + + if isinstance(successful_payment, raw.types.MessageActionPaymentSentMe): + # Try to decode invoice payload into string. If that fails, fallback to bytes instead of decoding by + # ignoring/replacing errors, this way, button clicks will still work. + try: + payload = successful_payment.payload.decode() + except (UnicodeDecodeError, AttributeError): + payload = successful_payment.payload + + telegram_payment_charge_id = successful_payment.charge.id + provider_payment_charge_id = successful_payment.charge.provider_charge_id + shipping_option_id = successful_payment.shipping_option_id + payment_info = types.PaymentInfo( + name=successful_payment.info.name, + phone_number=successful_payment.info.phone, + email=successful_payment.info.email, + shipping_address=types.ShippingAddress( + street_line1=successful_payment.info.shipping_address.street_line1, + street_line2=successful_payment.info.shipping_address.street_line2, + city=successful_payment.info.shipping_address.city, + state=successful_payment.info.shipping_address.state, + post_code=successful_payment.info.shipping_address.post_code, + country_code=successful_payment.info.shipping_address.country_iso2 + ) + ) if successful_payment.info else None + + return SuccessfulPayment( + currency=successful_payment.currency, + total_amount=successful_payment.total_amount, + payload=payload, + telegram_payment_charge_id=telegram_payment_charge_id, + provider_payment_charge_id=shipping_option_id, + shipping_option_id=shipping_option_id, + payment_info=payment_info + ) diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index 8f17288d..376e2b6d 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -367,6 +367,9 @@ class Message(Object, Update): requested_chats (:obj:`~pyrogram.types.RequestedChats`, *optional*): Service message: requested chats information. + successful_payment (:obj:`~pyrogram.types.SuccessfulPayment`, *optional*): + Service message: successful payment. + giveaway_launched (``bool``, *optional*): Service message: giveaway launched. @@ -494,6 +497,7 @@ class Message(Object, Update): web_app_data: "types.WebAppData" = None, gift_code: "types.GiftCode" = None, requested_chats: "types.RequestedChats" = None, + successful_payment: "types.SuccessfulPayment" = None, giveaway_launched: bool = None, chat_ttl_period: int = None, boosts_applied: int = None, @@ -602,6 +606,7 @@ class Message(Object, Update): self.web_app_data = web_app_data self.gift_code = gift_code self.requested_chats = requested_chats + self.successful_payment = successful_payment self.giveaway_launched = giveaway_launched self.chat_ttl_period = chat_ttl_period self.boosts_applied = boosts_applied @@ -678,6 +683,7 @@ class Message(Object, Update): gift_code = None giveaway_launched = None requested_chats = None + successful_payment = None chat_ttl_period = None boosts_applied = None join_request_approved = None @@ -762,6 +768,9 @@ class Message(Object, Update): elif isinstance(action, (raw.types.MessageActionRequestedPeer, raw.types.MessageActionRequestedPeerSentMe)): requested_chats = types.RequestedChats._parse(client, action) service_type = enums.MessageServiceType.REQUESTED_CHAT + elif isinstance(action, (raw.types.MessageActionPaymentSent, raw.types.MessageActionPaymentSentMe)): + successful_payment = types.SuccessfulPayment._parse(client, action) + service_type = enums.MessageServiceType.SUCCESSFUL_PAYMENT elif isinstance(action, raw.types.MessageActionSetMessagesTTL): chat_ttl_period = action.period service_type = enums.MessageServiceType.CHAT_TTL_CHANGED @@ -807,6 +816,7 @@ class Message(Object, Update): giveaway_launched=giveaway_launched, gift_code=gift_code, requested_chats=requested_chats, + successful_payment=successful_payment, chat_ttl_period=chat_ttl_period, boosts_applied=boosts_applied, join_request_approved=join_request_approved,