update get_message and @property chat for ButtonCallback

This commit is contained in:
apepenkov 2023-12-18 17:30:01 +03:00
parent 5b33b4b9c1
commit fbf0adb87b

View File

@ -4,8 +4,10 @@ import struct
import typing import typing
from typing import TYPE_CHECKING, Dict, Optional, Self, Union from typing import TYPE_CHECKING, Dict, Optional, Self, Union
from .. import errors
from ...session import PackedType
from ...tl import abcs, functions, types from ...tl import abcs, functions, types
from ..types import Chat, Message, Channel from ..types import Chat, Message, Channel, Group
from .event import Event from .event import Event
from ..types.chat import peer_id from ..types.chat import peer_id
from ..client.messages import CherryPickedList from ..client.messages import CherryPickedList
@ -64,8 +66,8 @@ class ButtonCallback(Event):
@property @property
def message_id(self) -> typing.Union[int, abcs.InputBotInlineMessageId]: def message_id(self) -> typing.Union[int, abcs.InputBotInlineMessageId]:
""" """
The ID of the message containing the button that was clicked. The identifier of the message containing the button that was clicked.
If the message is inline, :class:`abcs.InputBotInlineMessageId` will be returned. If the message is inline, :class:`abcs.InputBotInlineMessageId` will be returned.
You can use it in :meth:`~telethon._tl.functions.messages.edit_inline_bot_message` to edit the message. You can use it in :meth:`~telethon._tl.functions.messages.edit_inline_bot_message` to edit the message.
@ -81,38 +83,56 @@ class ButtonCallback(Event):
This may be :data:`None` if :data:`via_inline` is :data:`True`, as the bot might not be part of the chat. This may be :data:`None` if :data:`via_inline` is :data:`True`, as the bot might not be part of the chat.
""" """
if isinstance(self._raw, types.UpdateInlineBotCallbackQuery): if isinstance(self._raw, types.UpdateInlineBotCallbackQuery):
# for that type of update, the msg_id and owner_id are present, however bot is not guaranteed
# to have "access" to the owner_id.
owner_id = None owner_id = None
if isinstance(self._raw.msg_id, types.InputBotInlineMessageId): if isinstance(self._raw.msg_id, types.InputBotInlineMessageId):
_, owner_id = struct.unpack("<ii", struct.pack("<q", self._raw.msg_id.id)) # telegram used to pack msg_id and peer_id into InputBotInlineMessageId.id
# I assume this is for the chats with IDs, fitting into 32-bit integer.
_, owner_id = struct.unpack(
"<ii", struct.pack("<q", self._raw.msg_id.id)
)
elif isinstance(self._raw.msg_id, types.InputBotInlineMessageId64): elif isinstance(self._raw.msg_id, types.InputBotInlineMessageId64):
_ = self._raw.msg_id.id _ = self._raw.msg_id.id
owner_id = self._raw.msg_id.owner_id owner_id = self._raw.msg_id.owner_id
else:
raise RuntimeError(f"unexpected type of msg_id: {type(self._raw.msg_id)}")
if owner_id is None: if owner_id is None:
return None return None
if owner_id > 0: if owner_id > 0:
# We can't know if it's really a chat with user, or an ID of the user who issued the inline query. # We can't know if it's really a chat with user, or an ID of the user who issued the inline query.
# So it's better to return None, then to return wrong chat. # So it's better to return None, than to return wrong chat.
return None return None
owner_id = -owner_id owner_id = -owner_id
access_hash = 0 if owner := self._chat_map.get(owner_id):
packed = self.client._chat_hashes.get(owner_id) return owner
if packed:
access_hash = packed.access_hash
return Channel._from_raw( packed = self.client._chat_hashes.get(owner_id)
types.ChannelForbidden(
broadcast=True, raw = types.ChannelForbidden(
megagroup=False, broadcast=False,
id=owner_id, megagroup=False,
access_hash=access_hash, id=owner_id,
title="", access_hash=0,
until_date=None, title="",
) until_date=None,
) )
if packed:
raw.access_hash = packed.access_hash
if packed.ty == PackedType.MEGAGROUP or packed.ty == PackedType.GIGAGROUP:
if packed.ty == PackedType.GIGAGROUP:
raw.gigagroup = True
else:
raw.megagroup = True
return Group._from_raw(self.client, raw)
raw.broadcast = True
return Channel._from_raw(raw)
return self._chat_map.get(peer_id(self._raw.peer)) return self._chat_map.get(peer_id(self._raw.peer))
async def answer( async def answer(
@ -149,54 +169,37 @@ class ButtonCallback(Event):
If the message is inline, or too old and is no longer accessible, :data:`None` is returned instead. If the message is inline, or too old and is no longer accessible, :data:`None` is returned instead.
""" """
chat = None chat = self.chat
# numeric_id, received in this case, is:
# - a correct message id, if it was sent in a channel (or megagroup, gigagroup)
# - a sender's message id, if it was sent in a private chat. So it's not a correct ID from bot perspective,
# as each account has its own message id counter for private chats (pm, small group chats).
if isinstance(self._raw, types.UpdateInlineBotCallbackQuery): if isinstance(self._raw, types.UpdateInlineBotCallbackQuery):
# for that type of update, the msg_id and owner_id are present, however bot is not guaranteed
# to have "access" to the owner_id.
if isinstance(self._raw.msg_id, types.InputBotInlineMessageId): if isinstance(self._raw.msg_id, types.InputBotInlineMessageId):
# telegram used to pack msg_id and peer_id into InputBotInlineMessageId.id numeric_id, _ = struct.unpack("<ii", struct.pack("<q", self._raw.msg_id.id))
# I assume this is for the chats with IDs, fitting into 32-bit integer.
msg_id, owner_id = struct.unpack(
"<ii", struct.pack("<q", self._raw.msg_id.id)
)
elif isinstance(self._raw.msg_id, types.InputBotInlineMessageId64): elif isinstance(self._raw.msg_id, types.InputBotInlineMessageId64):
msg_id = self._raw.msg_id.id numeric_id = self._raw.msg_id.id
owner_id = self._raw.msg_id.owner_id
else: else:
return None raise RuntimeError(f"unexpected type of msg_id: {type(self._raw.msg_id)}")
msg = types.InputMessageCallbackQuery(
id=msg_id, query_id=self._raw.query_id
)
if owner_id < 0:
# that means update's owner_id actually is the peer (channel), where the button was clicked.
# if it was positive, it'd be the user who issued the inline query.
try:
chat = await self._client._resolve_to_packed(-owner_id)
except ValueError:
pass
else: else:
pid = peer_id(self._raw.peer) numeric_id = self._raw.msg_id
msg = types.InputMessageCallbackQuery(
id=self._raw.msg_id, query_id=self._raw.query_id
)
chat = self._chat_map.get(pid)
if not chat:
chat = await self._client._resolve_to_packed(pid)
if chat: if chat:
lst = CherryPickedList(self._client, chat, []) lst = CherryPickedList(self._client, chat, [numeric_id])
lst._ids.append(msg) try:
res = await lst
res = await lst if res:
if res: return res[0] or None
return res[0] or None except errors.RpcError as e:
if e.name != "CHANNEL_INVALID" and e.name != "MESSAGE_IDS_EMPTY":
raise
# I didn't notice a single case, when this request returns a message.
# However, theoretically, self._raw.msg_id is a valid type of InputMessageID, so it may be possible.
res = await self._client( res = await self._client(
functions.messages.get_messages( functions.messages.get_messages(
id=[msg], id=[self._raw.msg_id],
) )
) )
return res.messages[0] if res.messages else None return res.messages[0] if res.messages else None