import re import struct import asyncio import functools from .common import EventBuilder, EventCommon, name_inner_event from .._misc import utils from .. import _tl from ..types import _custom def auto_answer(func): @functools.wraps(func) async def wrapped(self, *args, **kwargs): if self._answered: return await func(*args, **kwargs) else: return (await asyncio.gather( self._answer(), func(*args, **kwargs), ))[1] return wrapped @name_inner_event class CallbackQuery(EventBuilder): """ Occurs whenever you sign in as a bot and a user clicks one of the inline buttons on your messages. Note that the `chats` parameter will **not** work with normal IDs or peers if the clicked inline button comes from a "via bot" message. The `chats` parameter also supports checking against the `chat_instance` which should be used for inline callbacks. Args: data (`bytes`, `str`, `callable`, optional): If set, the inline button payload data must match this data. A UTF-8 string can also be given, a regex or a callable. For instance, to check against ``'data_1'`` and ``'data_2'`` you can use ``re.compile(b'data_')``. pattern (`bytes`, `str`, `callable`, `Pattern`, optional): If set, only buttons with payload matching this pattern will be handled. You can specify a regex-like string which will be matched against the payload data, a callable function that returns `True` if a the payload data is acceptable, or a compiled regex pattern. Example .. code-block:: python from telethon import events, Button # Handle all callback queries and check data inside the handler @client.on(events.CallbackQuery) async def handler(event): if event.data == b'yes': await event.answer('Correct answer!') # Handle only callback queries with data being b'no' @client.on(events.CallbackQuery(data=b'no')) async def handler(event): # Pop-up message with alert await event.answer('Wrong answer!', alert=True) # Send a message with buttons users can click async def main(): await client.send_message(user, 'Yes or no?', buttons=[ Button.inline('Yes!', b'yes'), Button.inline('Nope', b'no') ]) """ def __init__( self, chats=None, *, blacklist_chats=False, func=None, data=None, pattern=None): super().__init__(chats, blacklist_chats=blacklist_chats, func=func) if data and pattern: raise ValueError("Only pass either data or pattern not both.") if isinstance(data, str): data = data.encode('utf-8') if isinstance(pattern, str): pattern = pattern.encode('utf-8') match = data if data else pattern if isinstance(match, bytes): self.match = data if data else re.compile(pattern).match elif not match or callable(match): self.match = match elif hasattr(match, 'match') and callable(match.match): if not isinstance(getattr(match, 'pattern', b''), bytes): match = re.compile(match.pattern.encode('utf-8'), match.flags & (~re.UNICODE)) self.match = match.match else: raise TypeError('Invalid data or pattern type given') self._no_check = all(x is None for x in ( self.chats, self.func, self.match, )) @classmethod def build(cls, update, others=None, self_id=None, *todo, **todo2): if isinstance(update, _tl.UpdateBotCallbackQuery): return cls.Event(update, update.peer, update.msg_id) elif isinstance(update, _tl.UpdateInlineBotCallbackQuery): # See https://github.com/LonamiWebs/Telethon/pull/1005 # The long message ID is actually just msg_id + peer_id mid, pid = struct.unpack('`, since the message object is normally not present. """ if isinstance(self.query.msg_id, _tl.InputBotInlineMessageID): return await self._client.edit_message( None, self.query.msg_id, *args, **kwargs ) else: return await self._client.edit_message( await self.get_input_chat(), self.query.msg_id, *args, **kwargs ) @auto_answer async def delete(self, *args, **kwargs): """ Deletes the message. Shorthand for `telethon.client.messages.MessageMethods.delete_messages` with ``entity`` and ``message_ids`` already set. If you need to delete more than one message at once, don't use this `delete` method. Use a `telethon.client.telegramclient.TelegramClient` instance directly. This method will also `answer` the callback if necessary. This method will likely fail if `via_inline` is `True`. """ return await self._client.delete_messages( await self.get_input_chat(), [self.query.msg_id], *args, **kwargs )