diff --git a/telethon/tl/custom/message.py b/telethon/tl/custom/message.py index 56f4c725..5f286a40 100644 --- a/telethon/tl/custom/message.py +++ b/telethon/tl/custom/message.py @@ -764,7 +764,8 @@ class Message(ChatGetter, SenderGetter, TLObject, abc.ABC): return await self._client.download_media(self, *args, **kwargs) async def click(self, i=None, j=None, - *, text=None, filter=None, data=None): + *, text=None, filter=None, data=None, share_phone=None, + share_geo=None): """ Calls `button.click ` on the specified button. @@ -813,6 +814,28 @@ class Message(ChatGetter, SenderGetter, TLObject, abc.ABC): that if the message does not have this data, it will ``raise DataInvalidError``. + share_phone (`bool` | `str` | tl:`InputMediaContact`): + When clicking on a keyboard button requesting a phone number + (:tl:`KeyboardButtonRequestPhone`), this argument must be + explicitly set to avoid accidentally sharing the number. + + It can be `True` to automatically share the current user's + phone, a string to share a specific phone number, or a contact + media to specify all details. + + If the button is pressed without this, `ValueError` is raised. + + share_geo (`tuple` | `list` | tl:`InputMediaGeoPoint`): + When clicking on a keyboard button requesting a geo location + (:tl:`KeyboardButtonRequestGeoLocation`), this argument must + be explicitly set to avoid accidentally sharing the location. + + It must be a `tuple` of `float` as ``(longitude, latitude)``, + or a :tl:`InputGeoPoint` instance to avoid accidentally using + the wrong roder. + + If the button is pressed without this, `ValueError` is raised. + Example: .. code-block:: python @@ -828,6 +851,9 @@ class Message(ChatGetter, SenderGetter, TLObject, abc.ABC): # Click by data await message.click(data=b'payload') + + # Click on a button requesting a phone + await message.click(0, share_phone=True) """ if not self._client: return @@ -853,29 +879,35 @@ class Message(ChatGetter, SenderGetter, TLObject, abc.ABC): if not await self.get_buttons(): return # Accessing the property sets self._buttons[_flat] - if text is not None: - if callable(text): + def find_button(): + nonlocal i + if text is not None: + if callable(text): + for button in self._buttons_flat: + if text(button.text): + return button + else: + for button in self._buttons_flat: + if button.text == text: + return button + return + + if filter is not None: for button in self._buttons_flat: - if text(button.text): - return await button.click() + if filter(button): + return button + return + + if i is None: + i = 0 + if j is None: + return self._buttons_flat[i] else: - for button in self._buttons_flat: - if button.text == text: - return await button.click() - return + return self._buttons[i][j] - if filter is not None: - for button in self._buttons_flat: - if filter(button): - return await button.click() - return - - if i is None: - i = 0 - if j is None: - return await self._buttons_flat[i].click() - else: - return await self._buttons[i][j].click() + button = find_button() + if button: + return await button.click(share_phone=share_phone, share_geo=share_geo) async def mark_read(self): """ diff --git a/telethon/tl/custom/messagebutton.py b/telethon/tl/custom/messagebutton.py index 2bd0a68d..c99b8474 100644 --- a/telethon/tl/custom/messagebutton.py +++ b/telethon/tl/custom/messagebutton.py @@ -59,7 +59,7 @@ class MessageButton: if isinstance(self.button, types.KeyboardButtonUrl): return self.button.url - async def click(self): + async def click(self, share_phone=None, share_geo=None): """ Emulates the behaviour of clicking this button. @@ -75,6 +75,19 @@ class MessageButton: If it's a :tl:`KeyboardButtonUrl`, the URL of the button will be passed to ``webbrowser.open`` and return `True` on success. + + If it's a :tl:`KeyboardButtonRequestPhone`, you must indicate that you + want to ``share_phone=True`` in order to share it. Sharing it is not a + default because it is a privacy concern and could happen accidentally. + + You may also use ``share_phone=phone`` to share a specific number, in + which case either `str` or :tl:`InputMediaContact` should be used. + + If it's a :tl:`KeyboardButtonRequestGeoLocation`, you must pass a + tuple in ``share_geo=(longitude, latitude)``. Note that Telegram seems + to have some heuristics to determine impossible locations, so changing + this value a lot quickly may not work as expected. You may also pass a + :tl:`InputGeoPoint` if you find the order confusing. """ if isinstance(self.button, types.KeyboardButton): return await self._client.send_message( @@ -101,3 +114,26 @@ class MessageButton: return await self._client(req) except BotResponseTimeoutError: return None + elif isinstance(self.button, types.KeyboardButtonRequestPhone): + if not share_phone: + raise ValueError('cannot click on phone buttons unless share_phone=True') + + if share_phone == True or isinstance(share_phone, str): + me = await self._client.get_me() + share_phone = types.InputMediaContact( + phone_number=me.phone if share_phone == True else share_phone, + first_name=me.first_name or '', + last_name=me.last_name or '', + vcard='' + ) + + return await self._client.send_file(self._chat, share_phone) + elif isinstance(self.button, types.KeyboardButtonRequestGeoLocation): + if not share_geo: + raise ValueError('cannot click on geo buttons unless share_geo=(longitude, latitude)') + + if isinstance(share_geo, (tuple, list)): + long, lat = share_geo + share_geo = types.InputMediaGeoPoint(types.InputGeoPoint(lat=lat, long=long)) + + return await self._client.send_file(self._chat, share_geo)