feat(keyboard): Add persistent and placeholder options

Introduce `persistent` and `placeholder` parameters for `ReplyKeyboardMarkup`.
These new options control keyboard visibility and input field text.
Available for text, location, phone, and poll buttons.

Remove vestigial `inline_only` from `build_reply_markup` as its functionality is
implicitly covered by the general rule against mixing inline and
normal buttons.
This commit is contained in:
Darskiy 2025-05-28 02:19:03 +03:00
parent 69e4493c04
commit 863365983d
2 changed files with 82 additions and 41 deletions

View File

@ -7,8 +7,8 @@ from ..tl import types, custom
class ButtonMethods:
@staticmethod
def build_reply_markup(
buttons: 'typing.Optional[hints.MarkupLike]',
inline_only: bool = False) -> 'typing.Optional[types.TypeReplyMarkup]':
buttons: 'typing.Optional[hints.MarkupLike]'
) -> 'typing.Optional[types.TypeReplyMarkup]':
"""
Builds a :tl:`ReplyInlineMarkup` or :tl:`ReplyKeyboardMarkup` for
the given buttons.
@ -26,9 +26,6 @@ class ButtonMethods:
The button, list of buttons, array of buttons or markup
to convert into a markup.
inline_only (`bool`, optional):
Whether the buttons **must** be inline buttons only or not.
Example
.. code-block:: python
@ -42,8 +39,8 @@ class ButtonMethods:
return None
try:
if buttons.SUBCLASS_OF_ID == 0xe2e10ef2:
return buttons # crc32(b'ReplyMarkup'):
if buttons.SUBCLASS_OF_ID == 0xe2e10ef2: # crc32(b'ReplyMarkup'):
return buttons
except AttributeError:
pass
@ -57,6 +54,8 @@ class ButtonMethods:
resize = None
single_use = None
selective = None
persistent = None
placeholder = None
rows = []
for row in buttons:
@ -69,6 +68,10 @@ class ButtonMethods:
single_use = button.single_use
if button.selective is not None:
selective = button.selective
if button.persistent is not None:
persistent = button.persistent
if button.placeholder is not None:
placeholder = button.placeholder
button = button.button
elif isinstance(button, custom.MessageButton):
@ -78,19 +81,21 @@ class ButtonMethods:
is_inline |= inline
is_normal |= not inline
if button.SUBCLASS_OF_ID == 0xbad74a3:
# 0xbad74a3 == crc32(b'KeyboardButton')
if button.SUBCLASS_OF_ID == 0xbad74a3: # crc32(b'KeyboardButton')
current.append(button)
if current:
rows.append(types.KeyboardButtonRow(current))
if inline_only and is_normal:
raise ValueError('You cannot use non-inline buttons here')
elif is_inline == is_normal and is_normal:
if is_inline and is_normal:
raise ValueError('You cannot mix inline with normal buttons')
elif is_inline:
return types.ReplyInlineMarkup(rows)
# elif is_normal:
return types.ReplyKeyboardMarkup(
rows, resize=resize, single_use=single_use, selective=selective)
rows=rows,
resize=resize,
single_use=single_use,
selective=selective,
persistent=persistent,
placeholder=placeholder
)

View File

@ -37,11 +37,14 @@ class Button:
to 128 characters and add the ellipsis () character as
the 129.
"""
def __init__(self, button, *, resize, single_use, selective):
def __init__(self, button, *, resize, single_use, selective,
persistent, placeholder):
self.button = button
self.resize = resize
self.single_use = single_use
self.selective = selective
self.persistent = persistent
self.placeholder = placeholder
@staticmethod
def _is_inline(button):
@ -168,11 +171,15 @@ class Button:
)
@classmethod
def text(cls, text, *, resize=None, single_use=None, selective=None):
def text(cls, text, *, resize=None, single_use=None, selective=None,
persistent=None, placeholder=None):
"""
Creates a new keyboard button with the given text.
Args:
text (`str`):
The title of the button.
resize (`bool`):
If present, the entire keyboard will be reconfigured to
be resized and be smaller if there are not many buttons.
@ -187,48 +194,77 @@ class Button:
users. It will target users that are @mentioned in the text
of the message or to the sender of the message you reply to.
persistent (`bool`):
If present, always show the keyboard when the regular keyboard
is hidden. Defaults to false, in which case the custom keyboard
can be hidden and revealed via the keyboard icon.
placeholder (`str`):
The placeholder to be shown in the input field when the keyboard is active;
1-64 characters
When the user clicks this button, a text message with the same text
as the button will be sent, and can be handled with `events.NewMessage
<telethon.events.newmessage.NewMessage>`. You cannot distinguish
between a button press and the user typing and sending exactly the
same text on their own.
"""
return cls(types.KeyboardButton(text),
resize=resize, single_use=single_use, selective=selective)
return cls(
types.KeyboardButton(text),
resize=resize,
single_use=single_use,
selective=selective,
persistent=persistent,
placeholder=placeholder
)
@classmethod
def request_location(cls, text, *,
resize=None, single_use=None, selective=None):
def request_location(cls, text, *, resize=None, single_use=None, selective=None,
persistent=None, placeholder=None):
"""
Creates a new keyboard button to request the user's location on click.
``resize``, ``single_use`` and ``selective`` are documented in `text`.
``resize``, ``single_use``, ``selective``, ``persistent`` and ``placeholder``
are documented in `text`.
When the user clicks this button, a confirmation box will be shown
to the user asking whether they want to share their location with the
bot, and if confirmed a message with geo media will be sent.
"""
return cls(types.KeyboardButtonRequestGeoLocation(text),
resize=resize, single_use=single_use, selective=selective)
return cls(
types.KeyboardButtonRequestGeoLocation(text),
resize=resize,
single_use=single_use,
selective=selective,
persistent=persistent,
placeholder=placeholder
)
@classmethod
def request_phone(cls, text, *,
resize=None, single_use=None, selective=None):
def request_phone(cls, text, *, resize=None, single_use=None,
selective=None, persistent=None, placeholder=None):
"""
Creates a new keyboard button to request the user's phone on click.
``resize``, ``single_use`` and ``selective`` are documented in `text`.
``resize``, ``single_use``, ``selective``, ``persistent`` and ``placeholder``
are documented in `text`.
When the user clicks this button, a confirmation box will be shown
to the user asking whether they want to share their phone with the
bot, and if confirmed a message with contact media will be sent.
"""
return cls(types.KeyboardButtonRequestPhone(text),
resize=resize, single_use=single_use, selective=selective)
return cls(
types.KeyboardButtonRequestPhone(text),
resize=resize,
single_use=single_use,
selective=selective,
placeholder=placeholder,
persistent=persistent
)
@classmethod
def request_poll(cls, text, *, force_quiz=False,
resize=None, single_use=None, selective=None):
def request_poll(cls, text, *, force_quiz=False, resize=None, single_use=None,
selective=None, persistent=None, placeholder=None):
"""
Creates a new keyboard button to request the user to create a poll.
@ -240,13 +276,20 @@ class Button:
the votes cannot be retracted. Otherwise, users can vote and retract
the vote, and the pol might be multiple choice.
``resize``, ``single_use`` and ``selective`` are documented in `text`.
``resize``, ``single_use``, ``selective``, ``persistent`` and ``placeholder``
are documented in `text`.
When the user clicks this button, a screen letting the user create a
poll will be shown, and if they do create one, the poll will be sent.
"""
return cls(types.KeyboardButtonRequestPoll(text, quiz=force_quiz),
resize=resize, single_use=single_use, selective=selective)
return cls(
types.KeyboardButtonRequestPoll(text, quiz=force_quiz),
resize=resize,
single_use=single_use,
selective=selective,
persistent=persistent,
placeholder=placeholder
)
@staticmethod
def clear(selective=None):
@ -265,15 +308,8 @@ class Button:
Forces a reply to the message with this markup. If used,
no other button should be present or it will be ignored.
``single_use`` and ``selective`` are as documented in `text`.
``single_use``, ``selective`` and ``placeholder`` are as documented in `text`.
Args:
placeholder (str):
text to show the user at typing place of message.
If the placeholder is too long, Telegram applications will
crop the text (for example, to 64 characters and adding an
ellipsis () character as the 65th).
"""
return types.ReplyKeyboardForceReply(
single_use=single_use,