Support clicking on buttons asking for phone/location

Closes #1492.
This commit is contained in:
Lonami Exo 2020-07-04 13:12:22 +02:00
parent 7b852206f1
commit 326f70b678
2 changed files with 90 additions and 22 deletions

View File

@ -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 <telethon.tl.custom.messagebutton.MessageButton.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):
"""

View File

@ -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)