mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-01 02:39:54 +03:00
Merge 9db309b60c
into aea459c293
This commit is contained in:
commit
b186331465
|
@ -1,9 +1,13 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Dict, Optional, Self
|
import struct
|
||||||
|
import typing
|
||||||
|
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
|
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
|
||||||
|
@ -22,7 +26,7 @@ class ButtonCallback(Event):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
client: Client,
|
client: Client,
|
||||||
update: types.UpdateBotCallbackQuery,
|
update: Union[types.UpdateBotCallbackQuery, types.UpdateInlineBotCallbackQuery],
|
||||||
chat_map: Dict[int, Chat],
|
chat_map: Dict[int, Chat],
|
||||||
):
|
):
|
||||||
self._client = client
|
self._client = client
|
||||||
|
@ -33,7 +37,13 @@ class ButtonCallback(Event):
|
||||||
def _try_from_update(
|
def _try_from_update(
|
||||||
cls, client: Client, update: abcs.Update, chat_map: Dict[int, Chat]
|
cls, client: Client, update: abcs.Update, chat_map: Dict[int, Chat]
|
||||||
) -> Optional[Self]:
|
) -> Optional[Self]:
|
||||||
if isinstance(update, types.UpdateBotCallbackQuery) and update.data is not None:
|
if (
|
||||||
|
isinstance(
|
||||||
|
update,
|
||||||
|
(types.UpdateBotCallbackQuery, types.UpdateInlineBotCallbackQuery),
|
||||||
|
)
|
||||||
|
and update.data is not None
|
||||||
|
):
|
||||||
return cls._create(client, update, chat_map)
|
return cls._create(client, update, chat_map)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -43,6 +53,87 @@ class ButtonCallback(Event):
|
||||||
assert self._raw.data is not None
|
assert self._raw.data is not None
|
||||||
return self._raw.data
|
return self._raw.data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def via_inline(self) -> bool:
|
||||||
|
"""
|
||||||
|
Whether the button was clicked in an inline message.
|
||||||
|
|
||||||
|
If it was, it might indicate that the bot is not in chat.
|
||||||
|
If this is the case, both the :meth:`chat` and :meth:`get_message` will return :data:`None`.
|
||||||
|
"""
|
||||||
|
return isinstance(self._raw, types.UpdateInlineBotCallbackQuery)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message_id(self) -> typing.Union[int, abcs.InputBotInlineMessageId]:
|
||||||
|
"""
|
||||||
|
The identifier of the message containing the button that was clicked.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Else, usual message ID will be returned.
|
||||||
|
"""
|
||||||
|
return self._raw.msg_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def chat(self) -> Optional[Chat]:
|
||||||
|
"""
|
||||||
|
The :term:`chat` where the button was clicked.
|
||||||
|
|
||||||
|
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):
|
||||||
|
# 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):
|
||||||
|
# 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):
|
||||||
|
_ = self._raw.msg_id.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:
|
||||||
|
return None
|
||||||
|
|
||||||
|
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.
|
||||||
|
# So it's better to return None, than to return wrong chat.
|
||||||
|
return None
|
||||||
|
|
||||||
|
owner_id = -owner_id
|
||||||
|
|
||||||
|
if owner := self._chat_map.get(owner_id):
|
||||||
|
return owner
|
||||||
|
|
||||||
|
packed = self.client._chat_hashes.get(owner_id)
|
||||||
|
|
||||||
|
raw = types.ChannelForbidden(
|
||||||
|
broadcast=False,
|
||||||
|
megagroup=False,
|
||||||
|
id=owner_id,
|
||||||
|
access_hash=0,
|
||||||
|
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))
|
||||||
|
|
||||||
async def answer(
|
async def answer(
|
||||||
self,
|
self,
|
||||||
text: Optional[str] = None,
|
text: Optional[str] = None,
|
||||||
|
@ -75,20 +166,42 @@ class ButtonCallback(Event):
|
||||||
"""
|
"""
|
||||||
Get the :class:`~telethon.types.Message` containing the button that was clicked.
|
Get the :class:`~telethon.types.Message` containing the button that was clicked.
|
||||||
|
|
||||||
If the message is 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 = self.chat
|
||||||
|
|
||||||
pid = peer_id(self._raw.peer)
|
# numeric_id, received in this case, is:
|
||||||
chat = self._chat_map.get(pid)
|
# - a correct message id, if it was sent in a channel (or megagroup, gigagroup)
|
||||||
if not chat:
|
# - a sender's message id, if it was sent in a private chat. So it's not a correct ID from bot perspective,
|
||||||
chat = await self._client._resolve_to_packed(pid)
|
# 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.msg_id, types.InputBotInlineMessageId):
|
||||||
|
numeric_id, _ = struct.unpack("<ii", struct.pack("<q", self._raw.msg_id.id))
|
||||||
|
elif isinstance(self._raw.msg_id, types.InputBotInlineMessageId64):
|
||||||
|
numeric_id = self._raw.msg_id.id
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"unexpected type of msg_id: {type(self._raw.msg_id)}")
|
||||||
|
else:
|
||||||
|
numeric_id = self._raw.msg_id
|
||||||
|
|
||||||
lst = CherryPickedList(self._client, chat, [])
|
if chat:
|
||||||
lst._ids.append(types.InputMessageCallbackQuery(id=self._raw.msg_id, query_id=self._raw.query_id))
|
lst = CherryPickedList(self._client, chat, [numeric_id])
|
||||||
|
try:
|
||||||
|
res = await lst
|
||||||
|
if res:
|
||||||
|
return res[0] or None
|
||||||
|
except errors.RpcError as e:
|
||||||
|
if e.name != "CHANNEL_INVALID" and e.name != "MESSAGE_IDS_EMPTY":
|
||||||
|
raise
|
||||||
|
|
||||||
message = (await lst)[0]
|
# 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.
|
||||||
return message or None
|
res = await self._client(
|
||||||
|
functions.messages.get_messages(
|
||||||
|
id=[self._raw.msg_id],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return res.messages[0] if res.messages else None
|
||||||
|
|
||||||
|
|
||||||
class InlineQuery(Event):
|
class InlineQuery(Event):
|
||||||
|
|
|
@ -54,10 +54,14 @@ class Channel(Chat, metaclass=NoPublicConstructor):
|
||||||
if self._raw.access_hash is None:
|
if self._raw.access_hash is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
if getattr(self._raw, "megagroup", False):
|
||||||
|
ty = PackedType.MEGAGROUP
|
||||||
|
elif getattr(self._raw, "gigagroup", False):
|
||||||
|
ty = PackedType.GIGAGROUP
|
||||||
|
else:
|
||||||
|
ty = PackedType.BROADCAST
|
||||||
return PackedChat(
|
return PackedChat(
|
||||||
ty=PackedType.GIGAGROUP
|
ty=ty,
|
||||||
if getattr(self._raw, "gigagroup", False)
|
|
||||||
else PackedType.BROADCAST,
|
|
||||||
id=self._raw.id,
|
id=self._raw.id,
|
||||||
access_hash=self._raw.access_hash,
|
access_hash=self._raw.access_hash,
|
||||||
)
|
)
|
||||||
|
|
|
@ -72,7 +72,7 @@ class Group(Chat, metaclass=NoPublicConstructor):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return PackedChat(
|
return PackedChat(
|
||||||
ty=PackedType.MEGAGROUP,
|
ty=PackedType.GIGAGROUP if getattr(self._raw, "gigagroup", False) else PackedType.MEGAGROUP,
|
||||||
id=self._raw.id,
|
id=self._raw.id,
|
||||||
access_hash=self._raw.access_hash,
|
access_hash=self._raw.access_hash,
|
||||||
)
|
)
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Dialog(metaclass=NoPublicConstructor):
|
||||||
@property
|
@property
|
||||||
def chat(self) -> Chat:
|
def chat(self) -> Chat:
|
||||||
"""
|
"""
|
||||||
The chat where messages are sent in this dialog.
|
The :term:`chat` where messages are sent in this dialog.
|
||||||
"""
|
"""
|
||||||
return self._chat_map[peer_id(self._raw.peer)]
|
return self._chat_map[peer_id(self._raw.peer)]
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ class Draft(metaclass=NoPublicConstructor):
|
||||||
@property
|
@property
|
||||||
def chat(self) -> Chat:
|
def chat(self) -> Chat:
|
||||||
"""
|
"""
|
||||||
The chat where the draft is saved.
|
The :term:`chat` where the draft is saved.
|
||||||
|
|
||||||
This is also the chat where the message will be sent to by :meth:`send`.
|
This is also the chat where the message will be sent to by :meth:`send`.
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user