diff --git a/pyrogram/client/types/message.py b/pyrogram/client/types/message.py index 74f2a0a6..43eaba2a 100644 --- a/pyrogram/client/types/message.py +++ b/pyrogram/client/types/message.py @@ -17,6 +17,7 @@ # along with Pyrogram. If not, see . from pyrogram.api.core import Object +from .reply_markup import InlineKeyboardMarkup, ReplyKeyboardMarkup class Message(Object): @@ -460,3 +461,115 @@ class Message(Object): ) return True + + def click(self, x: int or str, y: int = None, quote: bool = None): + """Use this method to click a button attached to the message. + It's a shortcut for: + + - Clicking inline buttons: + + .. code-block:: python + + client.request_callback_answer( + chat_id=message.chat.id, + message_id=message.message_id, + callback_data=message.reply_markup[i][j].callback_data + ) + + - Clicking normal buttons: + + .. code-block:: python + + client.send_message( + chat_id=message.chat.id, + text=message.reply_markup[i][j].text + ) + + This method can be used in three different ways: + + 1. Pass one integer argument only (e.g.: ``.click(2)``, to click a button at index 2). + Buttons are counted left to right, starting from the top. + + 2. Pass two integer arguments (e.g.: ``.click(1, 0)``, to click a button at position (1, 0)). + The origin (0, 0) is top-left. + + 3. Pass one string argument only (e.g.: ``.click("Settings")``, to click a button by using its label). + Only the first matching button will be pressed. + + Args: + x (``int`` | ``str``): + Used as integer index, integer abscissa (in pair with y) or as string label. + + y (``int``, *optional*): + Used as ordinate only (in pair with x). + + quote (``bool``, *optional*): + Useful for normal buttons only, where pressing it will result in a new message sent. + If ``True``, the message will be sent as a reply to this message. + Defaults to ``True`` in group chats and ``False`` in private chats. + + Returns: + - The result of *request_callback_answer()* in case of inline callback button clicks. + - The result of *reply_text()* in case of normal button clicks. + - A string in case the inline button is an URL, switch_inline_query or switch_inline_query_current_chat + button. + + Raises: + :class:`Error ` + ``ValueError``: If the provided index or position is out of range or the button label was not found. + """ + if isinstance(self.reply_markup, ReplyKeyboardMarkup): + if quote is None: + quote = self.chat.type != "private" + + return self.reply_text(x, quote=quote) + elif isinstance(self.reply_markup, InlineKeyboardMarkup): + if isinstance(x, int) and y is None: + try: + button = [ + button + for row in self.reply_markup.inline_keyboard + for button in row + ][x] + except IndexError: + raise ValueError("The button at index {} doesn't exist".format(x)) from None + elif isinstance(x, int) and isinstance(y, int): + try: + button = self.reply_markup.inline_keyboard[y][x] + except IndexError: + raise ValueError("The button at position ({}, {}) doesn't exist".format(x, y)) from None + elif isinstance(x, str): + x = x.encode("utf-16", "surrogatepass").decode("utf-16") + + try: + button = [ + button + for row in self.reply_markup.inline_keyboard + for button in row + if x == button.text + ][0] + except IndexError: + raise ValueError( + "The button with label '{}' doesn't exists".format( + x.encode("unicode_escape").decode() + ) + ) from None + else: + raise ValueError("Invalid arguments") + + if button.callback_data: + return self._client.request_callback_answer( + chat_id=self.chat.id, + message_id=self.message_id, + data=button.callback_data + ) + elif button.url: + return button.url + elif button.switch_inline_query: + return button.switch_inline_query + elif button.switch_inline_query_current_chat: + return button.switch_inline_query_current_chat + else: + raise ValueError("This button is not supported yet") + else: + raise ValueError("The message doesn't contain any keyboard")