mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-02-04 13:40:59 +03:00
Create a custom.Button class and support send_message(buttons=...)
This commit is contained in:
parent
a50d013ee6
commit
8c28be04bc
|
@ -45,3 +45,11 @@ telethon\.tl\.custom\.forward module
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
telethon\.tl\.custom\.button module
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
.. automodule:: telethon.tl.custom.button
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
|
@ -13,10 +13,11 @@ from .telegrambaseclient import TelegramBaseClient
|
||||||
from .users import UserMethods # Required for everything
|
from .users import UserMethods # Required for everything
|
||||||
from .messageparse import MessageParseMethods # Required for messages
|
from .messageparse import MessageParseMethods # Required for messages
|
||||||
from .uploads import UploadMethods # Required for messages to send files
|
from .uploads import UploadMethods # Required for messages to send files
|
||||||
|
from .updates import UpdateMethods # Required for buttons (register callbacks)
|
||||||
|
from .buttons import ButtonMethods # Required for messages to use buttons
|
||||||
from .messages import MessageMethods
|
from .messages import MessageMethods
|
||||||
from .chats import ChatMethods
|
from .chats import ChatMethods
|
||||||
from .dialogs import DialogMethods
|
from .dialogs import DialogMethods
|
||||||
from .downloads import DownloadMethods
|
from .downloads import DownloadMethods
|
||||||
from .auth import AuthMethods
|
from .auth import AuthMethods
|
||||||
from .updates import UpdateMethods
|
|
||||||
from .telegramclient import TelegramClient
|
from .telegramclient import TelegramClient
|
||||||
|
|
48
telethon/client/buttons.py
Normal file
48
telethon/client/buttons.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
from .updates import UpdateMethods
|
||||||
|
from ..tl import types, custom
|
||||||
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonMethods(UpdateMethods):
|
||||||
|
def _build_reply_markup(self, buttons):
|
||||||
|
if buttons is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if buttons.SUBCLASS_OF_ID == 0xe2e10ef2:
|
||||||
|
return buttons # crc32(b'ReplyMarkup'):
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not utils.is_list_like(buttons):
|
||||||
|
buttons = [[buttons]]
|
||||||
|
elif not utils.is_list_like(buttons[0]):
|
||||||
|
buttons = [buttons]
|
||||||
|
|
||||||
|
is_inline = False
|
||||||
|
is_normal = False
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for row in buttons:
|
||||||
|
current = []
|
||||||
|
for button in row:
|
||||||
|
inline = custom.Button._is_inline(button)
|
||||||
|
is_inline |= inline
|
||||||
|
is_normal |= not inline
|
||||||
|
if isinstance(button, custom.Button):
|
||||||
|
# TODO actually register callbacks
|
||||||
|
button = button.button
|
||||||
|
|
||||||
|
if button.SUBCLASS_OF_ID == 0xbad74a3:
|
||||||
|
# 0xbad74a3 == crc32(b'KeyboardButton')
|
||||||
|
current.append(button)
|
||||||
|
|
||||||
|
if current:
|
||||||
|
rows.append(types.KeyboardButtonRow(current))
|
||||||
|
|
||||||
|
if is_inline == is_normal 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)
|
|
@ -8,13 +8,14 @@ from async_generator import async_generator, yield_
|
||||||
|
|
||||||
from .messageparse import MessageParseMethods
|
from .messageparse import MessageParseMethods
|
||||||
from .uploads import UploadMethods
|
from .uploads import UploadMethods
|
||||||
|
from .buttons import ButtonMethods
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from ..tl import types, functions, custom
|
from ..tl import types, functions, custom
|
||||||
|
|
||||||
__log__ = logging.getLogger(__name__)
|
__log__ = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MessageMethods(UploadMethods, MessageParseMethods):
|
class MessageMethods(ButtonMethods, UploadMethods, MessageParseMethods):
|
||||||
|
|
||||||
# region Public methods
|
# region Public methods
|
||||||
|
|
||||||
|
@ -333,7 +334,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
||||||
async def send_message(
|
async def send_message(
|
||||||
self, entity, message='', *, reply_to=None,
|
self, entity, message='', *, reply_to=None,
|
||||||
parse_mode=utils.Default, link_preview=True, file=None,
|
parse_mode=utils.Default, link_preview=True, file=None,
|
||||||
force_document=False, clear_draft=False):
|
force_document=False, clear_draft=False, buttons=None):
|
||||||
"""
|
"""
|
||||||
Sends the given message to the specified entity (user/chat/channel).
|
Sends the given message to the specified entity (user/chat/channel).
|
||||||
|
|
||||||
|
@ -382,8 +383,15 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
||||||
Whether the existing draft should be cleared or not.
|
Whether the existing draft should be cleared or not.
|
||||||
Has no effect when sending a file.
|
Has no effect when sending a file.
|
||||||
|
|
||||||
|
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`,
|
||||||
|
:tl:`KeyboardButton`):
|
||||||
|
The matrix (list of lists), row list or button to be shown
|
||||||
|
after sending the message. This parameter will only work if
|
||||||
|
you have signed in as a bot. You can also pass your own
|
||||||
|
:tl:`ReplyMarkup` here.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The sent `telethon.tl.custom.message.Message`.
|
The sent `custom.Message <telethon.tl.custom.message.Message>`.
|
||||||
"""
|
"""
|
||||||
if file is not None:
|
if file is not None:
|
||||||
return await self.send_file(
|
return await self.send_file(
|
||||||
|
@ -417,12 +425,17 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
||||||
else:
|
else:
|
||||||
reply_id = None
|
reply_id = None
|
||||||
|
|
||||||
|
if buttons is None:
|
||||||
|
markup = message.reply_markup
|
||||||
|
else:
|
||||||
|
markup = self._build_reply_markup(buttons)
|
||||||
|
|
||||||
request = functions.messages.SendMessageRequest(
|
request = functions.messages.SendMessageRequest(
|
||||||
peer=entity,
|
peer=entity,
|
||||||
message=message.message or '',
|
message=message.message or '',
|
||||||
silent=message.silent,
|
silent=message.silent,
|
||||||
reply_to_msg_id=reply_id,
|
reply_to_msg_id=reply_id,
|
||||||
reply_markup=message.reply_markup,
|
reply_markup=markup,
|
||||||
entities=message.entities,
|
entities=message.entities,
|
||||||
clear_draft=clear_draft,
|
clear_draft=clear_draft,
|
||||||
no_webpage=not isinstance(
|
no_webpage=not isinstance(
|
||||||
|
@ -438,7 +451,8 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
||||||
entities=msg_ent,
|
entities=msg_ent,
|
||||||
no_webpage=not link_preview,
|
no_webpage=not link_preview,
|
||||||
reply_to_msg_id=utils.get_message_id(reply_to),
|
reply_to_msg_id=utils.get_message_id(reply_to),
|
||||||
clear_draft=clear_draft
|
clear_draft=clear_draft,
|
||||||
|
reply_markup=self._build_reply_markup(buttons)
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await self(request)
|
result = await self(request)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from . import (
|
from . import (
|
||||||
UpdateMethods, AuthMethods, DownloadMethods, DialogMethods,
|
AuthMethods, DownloadMethods, DialogMethods, ChatMethods,
|
||||||
ChatMethods, MessageMethods, UploadMethods, MessageParseMethods,
|
MessageMethods, ButtonMethods, UpdateMethods, UploadMethods,
|
||||||
UserMethods
|
MessageParseMethods, UserMethods
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TelegramClient(
|
class TelegramClient(
|
||||||
UpdateMethods, AuthMethods, DownloadMethods, DialogMethods,
|
AuthMethods, DownloadMethods, DialogMethods, ChatMethods,
|
||||||
ChatMethods, MessageMethods, UploadMethods, MessageParseMethods,
|
MessageMethods, ButtonMethods, UpdateMethods, UploadMethods,
|
||||||
UserMethods
|
MessageParseMethods, UserMethods
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -4,3 +4,4 @@ from .input_sized_file import InputSizedFile
|
||||||
from .messagebutton import MessageButton
|
from .messagebutton import MessageButton
|
||||||
from .forward import Forward
|
from .forward import Forward
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
from .button import Button
|
||||||
|
|
129
telethon/tl/custom/button.py
Normal file
129
telethon/tl/custom/button.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
from .. import types
|
||||||
|
|
||||||
|
|
||||||
|
class Button:
|
||||||
|
"""
|
||||||
|
Helper class to allow defining ``reply_markup`` when
|
||||||
|
sending a message with inline or keyboard buttons.
|
||||||
|
|
||||||
|
You should make use of the defined class methods to create button
|
||||||
|
instances instead making them yourself (i.e. don't do ``Button(...)``
|
||||||
|
but instead use methods line `Button.inline(...) <inline>` etc.)
|
||||||
|
|
||||||
|
You can use `inline`, `switch_inline` and `url`
|
||||||
|
together to create inline buttons (under the message).
|
||||||
|
|
||||||
|
You can use `text`, `request_location` and `request_phone`
|
||||||
|
together to create a reply markup (replaces the user keyboard).
|
||||||
|
|
||||||
|
You **cannot** mix the two type of buttons together,
|
||||||
|
and it will error if you try to do so.
|
||||||
|
|
||||||
|
The text for all buttons may be at most 142 characters.
|
||||||
|
If more characters are given, Telegram will cut the text
|
||||||
|
to 128 characters and add the ellipsis (…) character as
|
||||||
|
the 129.
|
||||||
|
"""
|
||||||
|
def __init__(self, button, callback=None):
|
||||||
|
self.button = button
|
||||||
|
self.callback = callback
|
||||||
|
self.is_inline = self._is_inline(button)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _is_inline(cls, button):
|
||||||
|
"""
|
||||||
|
Returns ``True`` if the button belongs to an inline keyboard.
|
||||||
|
"""
|
||||||
|
if isinstance(button, cls):
|
||||||
|
return button.is_inline
|
||||||
|
else:
|
||||||
|
return isinstance(button, (
|
||||||
|
types.KeyboardButtonCallback,
|
||||||
|
types.KeyboardButtonSwitchInline,
|
||||||
|
types.KeyboardButtonUrl
|
||||||
|
))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def inline(cls, text, callback=None, data=None):
|
||||||
|
"""
|
||||||
|
Creates a new inline button.
|
||||||
|
|
||||||
|
The `callback` parameter should be a function callback accepting
|
||||||
|
a single parameter (the triggered event on click) if specified.
|
||||||
|
Otherwise, you should register the event manually.
|
||||||
|
|
||||||
|
If `data` is omitted, the given `text` will be used as `data`.
|
||||||
|
In any case `data` should be either ``bytes`` or ``str``.
|
||||||
|
|
||||||
|
Note that the given `data` must be less or equal to 64 bytes.
|
||||||
|
If more than 64 bytes are passed as data, ``ValueError`` is raised.
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
data = text.encode('utf-8')
|
||||||
|
|
||||||
|
if len(data) > 64:
|
||||||
|
raise ValueError('Too many bytes for the data')
|
||||||
|
|
||||||
|
return cls(types.KeyboardButtonCallback(text, data), callback)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def switch_inline(cls, text, query='', same_peer=False):
|
||||||
|
"""
|
||||||
|
Creates a new button to switch to inline query.
|
||||||
|
|
||||||
|
If `query` is given, it will be the default text to be used
|
||||||
|
when making the inline query.
|
||||||
|
|
||||||
|
If ``same_peer is True`` the inline query will directly be
|
||||||
|
set under the currently opened chat. Otherwise, the user will
|
||||||
|
have to select a different dialog to make the query.
|
||||||
|
"""
|
||||||
|
return cls(types.KeyboardButtonSwitchInline(text, query, same_peer))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def url(cls, text, url=None):
|
||||||
|
"""
|
||||||
|
Creates a new button to open the desired URL upon clicking it.
|
||||||
|
|
||||||
|
If no `url` is given, the `text` will be used as said URL instead.
|
||||||
|
"""
|
||||||
|
return cls(types.KeyboardButtonUrl(text, url or text))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def text(cls, text):
|
||||||
|
"""
|
||||||
|
Creates a new button with the given text.
|
||||||
|
"""
|
||||||
|
return cls(types.KeyboardButton(text))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def request_location(cls, text):
|
||||||
|
"""
|
||||||
|
Creates a new button that will request
|
||||||
|
the user's location upon being clicked.
|
||||||
|
"""
|
||||||
|
return cls(types.KeyboardButtonRequestGeoLocation(text))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def request_phone(cls, text):
|
||||||
|
"""
|
||||||
|
Creates a new button that will request
|
||||||
|
the user's phone number upon being clicked.
|
||||||
|
"""
|
||||||
|
return cls(types.KeyboardButtonRequestPhone(text))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear(cls):
|
||||||
|
"""
|
||||||
|
Clears all the buttons. When used, no other
|
||||||
|
button should be present or it will be ignored.
|
||||||
|
"""
|
||||||
|
return types.ReplyKeyboardHide()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def force_reply(cls):
|
||||||
|
"""
|
||||||
|
Forces a reply. If used, no other button
|
||||||
|
should be present or it will be ignored.
|
||||||
|
"""
|
||||||
|
return types.ReplyKeyboardForceReply()
|
Loading…
Reference in New Issue
Block a user