diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 8c381d60..b56e8899 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -361,6 +361,7 @@ def pyrogram_api(): get_chat_menu_button answer_web_app_query answer_pre_checkout_query + answer_shipping_query get_bot_info set_bot_info get_collectible_item_info @@ -539,6 +540,8 @@ def pyrogram_api(): BotBusinessConnection PaymentInfo ShippingAddress + ShippingOption + ShippingQuery SuccessfulPayment """, bot_keyboards=""" @@ -614,6 +617,10 @@ def pyrogram_api(): PreCheckoutQuery PreCheckoutQuery.answer """, + shipping_query=""" + ShippingQuery + ShippingQuery.answer + """, input_message_content=""" InputMessageContent InputMessageContent @@ -768,6 +775,10 @@ def pyrogram_api(): PreCheckoutQuery PreCheckoutQuery.answer """, + shipping_query=""" + ShippingQuery + ShippingQuery.answer + """, chat_join_request=""" ChatJoinRequest ChatJoinRequest.approve diff --git a/compiler/docs/template/bound-methods.rst b/compiler/docs/template/bound-methods.rst index 61efa7db..13ca9740 100644 --- a/compiler/docs/template/bound-methods.rst +++ b/compiler/docs/template/bound-methods.rst @@ -102,7 +102,7 @@ InlineQuery {inline_query_toctree} PreCheckoutQuery ------------ +---------------- .. hlist:: :columns: 2 @@ -114,6 +114,19 @@ PreCheckoutQuery {pre_checkout_query_toctree} +ShippingQuery +------------- + +.. hlist:: + :columns: 2 + + {shipping_query_hlist} + +.. toctree:: + :hidden: + + {shipping_query_toctree} + ChatJoinRequest --------------- diff --git a/docs/source/api/decorators.rst b/docs/source/api/decorators.rst index 7551c321..6da69369 100644 --- a/docs/source/api/decorators.rst +++ b/docs/source/api/decorators.rst @@ -67,6 +67,8 @@ Details .. autodecorator:: pyrogram.Client.on_edited_message() .. autodecorator:: pyrogram.Client.on_edited_bot_business_message() .. autodecorator:: pyrogram.Client.on_callback_query() +.. autodecorator:: pyrogram.Client.on_shipping_query() +.. autodecorator:: pyrogram.Client.on_pre_checkout_query() .. autodecorator:: pyrogram.Client.on_message_reaction_updated() .. autodecorator:: pyrogram.Client.on_message_reaction_count_updated() .. autodecorator:: pyrogram.Client.on_inline_query() diff --git a/docs/source/api/handlers.rst b/docs/source/api/handlers.rst index ea6b30a0..617823b5 100644 --- a/docs/source/api/handlers.rst +++ b/docs/source/api/handlers.rst @@ -68,6 +68,8 @@ Details .. autoclass:: DeletedMessagesHandler() .. autoclass:: DeletedBotBusinessMessagesHandler() .. autoclass:: CallbackQueryHandler() +.. autoclass:: ShippingQueryHandler() +.. autoclass:: PreCheckoutQueryHandler() .. autoclass:: MessageReactionUpdatedHandler() .. autoclass:: MessageReactionCountUpdatedHandler() .. autoclass:: InlineQueryHandler() diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py index ccb1f6d7..852856ad 100644 --- a/pyrogram/dispatcher.py +++ b/pyrogram/dispatcher.py @@ -41,6 +41,7 @@ from pyrogram.handlers import ( RawUpdateHandler, InlineQueryHandler, PollHandler, + ShippingQueryHandler, PreCheckoutQueryHandler, ConversationHandler, ChosenInlineResultHandler, @@ -59,7 +60,8 @@ from pyrogram.raw.types import ( UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant, UpdateBotStopped, UpdateBotChatInviteRequester, UpdateStory, UpdateBotMessageReaction, - UpdateBotMessageReactions + UpdateBotMessageReactions, + UpdateBotShippingQuery ) log = logging.getLogger(__name__) @@ -84,6 +86,7 @@ class Dispatcher: MESSAGE_BOT_A_REACTION_UPDATES = (UpdateBotMessageReactions,) BOT_BUSSINESS_CONNECT_UPDATES = (UpdateBotBusinessConnect,) PRE_CHECKOUT_QUERY_UPDATES = (UpdateBotPrecheckoutQuery,) + SHIPPING_QUERY_UPDATES = (UpdateBotShippingQuery,) def __init__(self, client: "pyrogram.Client"): self.client = client @@ -195,6 +198,12 @@ class Dispatcher: StoryHandler ) + async def shipping_query_parser(update, users, chats): + return ( + await pyrogram.types.ShippingQuery._parse(self.client, update, users), + ShippingQueryHandler + ) + async def pre_checkout_query_parser(update, users, chats): return ( await pyrogram.types.PreCheckoutQuery._parse(self.client, update, users), @@ -234,6 +243,7 @@ class Dispatcher: Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser, Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser, Dispatcher.NEW_STORY_UPDATES: story_parser, + Dispatcher.SHIPPING_QUERY_UPDATES: shipping_query_parser, Dispatcher.PRE_CHECKOUT_QUERY_UPDATES: pre_checkout_query_parser, Dispatcher.MESSAGE_BOT_NA_REACTION_UPDATES: message_bot_na_reaction_parser, Dispatcher.MESSAGE_BOT_A_REACTION_UPDATES: message_bot_a_reaction_parser, diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py index 90890994..29f798ab 100644 --- a/pyrogram/handlers/__init__.py +++ b/pyrogram/handlers/__init__.py @@ -37,4 +37,6 @@ from .raw_update_handler import RawUpdateHandler from .user_status_handler import UserStatusHandler from .story_handler import StoryHandler from .message_reaction_updated_handler import MessageReactionUpdatedHandler -from .message_reaction_count_updated_handler import MessageReactionCountUpdatedHandler \ No newline at end of file +from .message_reaction_count_updated_handler import MessageReactionCountUpdatedHandler +from .pre_checkout_query_handler import PreCheckoutQueryHandler +from .shipping_query_handler import ShippingQueryHandler diff --git a/pyrogram/handlers/pre_checkout_query_handler.py b/pyrogram/handlers/pre_checkout_query_handler.py index 7d2fc5a1..17114f70 100644 --- a/pyrogram/handlers/pre_checkout_query_handler.py +++ b/pyrogram/handlers/pre_checkout_query_handler.py @@ -43,7 +43,8 @@ class PreCheckoutQueryHandler(Handler): 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. + New incoming pre-checkout query. Contains full information about checkout. + """ def __init__(self, callback: Callable, filters=None): diff --git a/pyrogram/handlers/shipping_query_handler.py b/pyrogram/handlers/shipping_query_handler.py new file mode 100644 index 00000000..478a3745 --- /dev/null +++ b/pyrogram/handlers/shipping_query_handler.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 typing import Callable + +from .handler import Handler + + +class ShippingQueryHandler(Handler): + """The ShippingQueryHandler handler class. Used to handle shipping queries coming only from invoice buttons with flexible price. + + 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_shipping_query` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new PreCheckoutQuery arrives. It takes *(client, shipping_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. + + shipping_query (:obj:`~pyrogram.types.ShippingQuery`): + New incoming shipping query. Only for invoices with flexible price. + + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/methods/bots/answer_shipping_query.py b/pyrogram/methods/bots/answer_shipping_query.py new file mode 100644 index 00000000..c9b51b71 --- /dev/null +++ b/pyrogram/methods/bots/answer_shipping_query.py @@ -0,0 +1,83 @@ +# 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 AnswerShippingQuery: + async def answer_shipping_query( + self: "pyrogram.Client", + shipping_query_id: str, + ok: bool, + shipping_options: List["types.ShippingOptions"] = None, + error_message: str = None + ): + """If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the API sends the confirmation in the form of an :obj:`~pyrogram.handlers.ShippingQueryHandler`. + + Use this method to reply to shipping queries. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + shipping_query_id (``str``): + Unique identifier for the query to be answered. + + ok (``bool``): + Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. + + shipping_options (List of :obj:`~pyrogram.types.ShippingOptions`, *optional*): + Required if ok is True. A array of available shipping options. + + error_message (``str``, *optional*): + Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. + + Returns: + ``bool``: True, on success. + + Example: + .. code-block:: python + + # Proceed with the order + await app.answer_shipping_query(query_id, ok=True, shipping_options=shipping_options) + + # Answer with error message + await app.answer_shipping_query(query_id, ok=False, error_message="Error Message displayed to the user") + + """ + r = None + if ok: + r = await self.invoke( + raw.functions.messages.SetBotShippingResults( + query_id=int(shipping_query_id), + shipping_options=[ + so.write() + for so in shipping_options + ] + ) + ) + else: + r = await self.invoke( + raw.functions.messages.SetBotShippingResults( + query_id=int(shipping_query_id), + error=error_message or None + ) + ) + return r diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py index cf2000a9..8a1818dc 100644 --- a/pyrogram/methods/decorators/__init__.py +++ b/pyrogram/methods/decorators/__init__.py @@ -37,6 +37,7 @@ from .on_user_status import OnUserStatus from .on_story import OnStory from .on_message_reaction_updated import OnMessageReactionUpdated from .on_message_reaction_count_updated import OnMessageReactionCountUpdated +from .on_shipping_query import OnShippingQuery class Decorators( @@ -48,6 +49,8 @@ class Decorators( OnDeletedMessages, OnDeletedBotBusinessMessages, OnCallbackQuery, + OnShippingQuery, + OnPreCheckoutQuery, OnRawUpdate, OnDisconnect, OnUserStatus, @@ -58,7 +61,6 @@ class Decorators( OnChatJoinRequest, OnStory, OnMessageReactionUpdated, - OnMessageReactionCountUpdated, - OnPreCheckoutQuery + OnMessageReactionCountUpdated ): pass diff --git a/pyrogram/methods/decorators/on_shipping_query.py b/pyrogram/methods/decorators/on_shipping_query.py new file mode 100644 index 00000000..7128a88b --- /dev/null +++ b/pyrogram/methods/decorators/on_shipping_query.py @@ -0,0 +1,62 @@ +# 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 OnShippingQuery: + def on_shipping_query( + self=None, + filters=None, + group: int = 0, + ) -> Callable: + """Decorator for handling shipping queries. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.ShippingQueryHandler`. + + 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.ShippingQueryHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.ShippingQueryHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/types/bots_and_keyboards/__init__.py b/pyrogram/types/bots_and_keyboards/__init__.py index d23b000c..70d2c703 100644 --- a/pyrogram/types/bots_and_keyboards/__init__.py +++ b/pyrogram/types/bots_and_keyboards/__init__.py @@ -53,6 +53,8 @@ from .request_peer_type_chat import RequestPeerTypeChat from .request_peer_type_user import RequestPeerTypeUser from .sent_web_app_message import SentWebAppMessage from .shipping_address import ShippingAddress +from .shipping_option import ShippingOption +from .shipping_query import ShippingQuery from .successful_payment import SuccessfulPayment from .web_app_info import WebAppInfo @@ -92,6 +94,8 @@ __all__ = [ "MenuButtonDefault", "SentWebAppMessage", "ShippingAddress", + "ShippingOption", + "ShippingQuery", "PaymentInfo", "PreCheckoutQuery", "SuccessfulPayment" diff --git a/pyrogram/types/bots_and_keyboards/pre_checkout_query.py b/pyrogram/types/bots_and_keyboards/pre_checkout_query.py index 09f3bdaf..2a5dc552 100644 --- a/pyrogram/types/bots_and_keyboards/pre_checkout_query.py +++ b/pyrogram/types/bots_and_keyboards/pre_checkout_query.py @@ -116,7 +116,7 @@ class PreCheckoutQuery(Object, Update): await client.answer_pre_checkout_query( pre_checkout_query.id, - success=True + ok=True ) Example: diff --git a/pyrogram/types/bots_and_keyboards/shipping_option.py b/pyrogram/types/bots_and_keyboards/shipping_option.py new file mode 100644 index 00000000..a0fa1934 --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/shipping_option.py @@ -0,0 +1,74 @@ +# 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 + +from ..object import Object + + +class ShippingOption(Object): + """This object represents one shipping option. + + Parameters: + id (``str``): + Shipping option identifier. + + title (``str``): + Option title. + + prices (List of :obj:`~pyrogram.types.LabeledPrice`): + List of price portions. + + """ + + def __init__( + self, + id: str, + title: str, + prices: "types.LabeledPrice" + ): + super().__init__() + + self.id = id + self.title = title + self.prices = prices + + @staticmethod + def _parse(shipping_option: "raw.types.ShippingOption") -> "ShippingOption": + if isinstance(shipping_option, raw.types.ShippingOption): + return ShippingOption( + id=shipping_option.id, + title=shipping_option.title, + prices=[ + types.LabeledPrice._parse(price) + for price in shipping_option.prices + ] + ) + + def write(self): + return raw.types.ShippingOption( + id=self.id, + title=self.title, + prices=[ + price.write() + for price in self.prices + ] + ) diff --git a/pyrogram/types/bots_and_keyboards/shipping_query.py b/pyrogram/types/bots_and_keyboards/shipping_query.py new file mode 100644 index 00000000..89114491 --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/shipping_query.py @@ -0,0 +1,131 @@ +# 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 + +import pyrogram +from pyrogram import raw, types + +from ..object import Object +from ..update import Update + + +class ShippingQuery(Object, Update): + """This object contains information about an incoming shipping query. + + Parameters: + id (``str``): + Unique query identifier. + + from_user (:obj:`~pyrogram.types.User`): + User who sent the query. + + invoice_payload (``str``): + Bot specified invoice payload. Only available to the bot that received the payment. + + shipping_address (:obj:`~pyrogram.types.ShippingAddress`): + User specified shipping address. Only available to the bot that received the payment. + + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str, + from_user: "types.User", + invoice_payload: str, + shipping_address: "types.ShippingAddress" = None + ): + super().__init__(client) + + self.id = id + self.from_user = from_user + self.invoice_payload = invoice_payload + self.shipping_address = shipping_address + + @staticmethod + async def _parse( + client: "pyrogram.Client", + shipping_query: "raw.types.updateBotShippingQuery", + users: dict + ) -> "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 = shipping_query.payload.decode() + except (UnicodeDecodeError, AttributeError): + payload = shipping_query.payload + + return PreCheckoutQuery( + id=str(shipping_query.query_id), + from_user=types.User._parse(client, users[shipping_query.user_id]), + invoice_payload=payload, + shipping_address=types.ShippingAddress( + country_code=shipping_query.shipping_address.country_iso2, + state=shipping_query.shipping_address.state, + city=shipping_query.shipping_address.city, + street_line1=shipping_query.shipping_address.street_line1, + street_line2=shipping_query.shipping_address.street_line2, + post_code=shipping_query.shipping_address.post_code + ), + client=client + ) + + async def answer( + self, + ok: bool, + shipping_options: "types.ShippingOptions" = None, + error_message: str = None + ): + """Bound method *answer* of :obj:`~pyrogram.types.ShippingQuery`. + + Use this method as a shortcut for: + + .. code-block:: python + + await client.answer_shipping_query( + shipping_query.id, + ok=True + ) + + Example: + .. code-block:: python + + await shipping_query.answer(ok=True) + + Parameters: + ok (``bool``): + Pass True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible). + + shipping_options (:obj:`~pyrogram.types.ShippingOptions`, *optional*): + Required if ok is True. A JSON-serialized array of available shipping options. + + error_message (``str``, *optional*): + Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. + + Returns: + ``bool``: True, on success. + + """ + return await self._client.answer_shipping_query( + shipping_query_id=self.id, + ok=ok, + shipping_options=shipping_options, + error_message=error_message + )