mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-10 19:46:36 +03:00
Work towards dialogs and drafts
This commit is contained in:
parent
864d5cd444
commit
b8b9836cf7
|
@ -73,6 +73,29 @@ To send a message with formatted text, use the ``markdown`` or ``html`` paramete
|
|||
When sending files, the format is appended to the name of the ``caption`` parameter, either ``caption_markdown`` or ``caption_html``.
|
||||
|
||||
|
||||
Link previews
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Link previews are treated as a type of media automatically generated by Telegram.
|
||||
This means you cannot have both a link preview and other media in the same message.
|
||||
|
||||
The ``link_preview`` parameter indicates whether link previews are *allowed* to be present.
|
||||
If Telegram is unable to generate a link preview for any of the links, there won't be a link preview.
|
||||
|
||||
By default, link previews are not enabled.
|
||||
This is done to prevent sending things you did not explicitly intend to send.
|
||||
Unlike the official clients, which do not have a GUI to "enable" the preview, you can easily enable them from code.
|
||||
|
||||
Telegram will attempt to generate a preview for all links contained in the message in order.
|
||||
You can use this to your advantage, and hide a link to a photo in the first space or invisible character like ``'\u2063'``.
|
||||
Note that avid users *will* be able to find out the link. It is not secret!
|
||||
|
||||
Link previews of photos won't show under the photos of the chat,
|
||||
but it requires a server hosting the image, a public address, and Telegram to be able to generate a preview.
|
||||
|
||||
To regenerate a preview, send the corresponding link to `@WebpageBot <https://t.me/WebpageBot>`_.
|
||||
|
||||
|
||||
Message identifiers
|
||||
-------------------
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ from .chats import (
|
|||
set_banned_rights,
|
||||
set_default_rights,
|
||||
)
|
||||
from .dialogs import delete_dialog, get_dialogs, get_drafts
|
||||
from .dialogs import delete_dialog, edit_draft, get_dialogs, get_drafts
|
||||
from .files import (
|
||||
download,
|
||||
get_file_bytes,
|
||||
|
@ -499,7 +499,7 @@ class Client:
|
|||
text: Optional[str] = None,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: Optional[bool] = None,
|
||||
link_preview: bool = False,
|
||||
) -> Message:
|
||||
"""
|
||||
Edit a message.
|
||||
|
@ -1247,6 +1247,7 @@ class Client:
|
|||
title: Optional[str] = None,
|
||||
performer: Optional[str] = None,
|
||||
emoji: Optional[str] = None,
|
||||
emoji_sticker: Optional[str] = None,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
round: bool = False,
|
||||
|
@ -1396,6 +1397,7 @@ class Client:
|
|||
title=title,
|
||||
performer=performer,
|
||||
emoji=emoji,
|
||||
emoji_sticker=emoji_sticker,
|
||||
width=width,
|
||||
height=height,
|
||||
round=round,
|
||||
|
@ -1425,14 +1427,7 @@ class Client:
|
|||
:param text: See :ref:`formatting`.
|
||||
:param markdown: See :ref:`formatting`.
|
||||
:param html: See :ref:`formatting`.
|
||||
|
||||
:param link_preview:
|
||||
Whether the link preview is allowed.
|
||||
|
||||
Setting this to :data:`True` does not guarantee a preview.
|
||||
Telegram must be able to generate a preview from the first link in the message text.
|
||||
|
||||
To regenerate the preview, send the link to `@WebpageBot <https://t.me/WebpageBot>`_.
|
||||
:param link_preview: See :ref:`formatting`.
|
||||
|
||||
:param reply_to:
|
||||
The message identifier of the message to reply to.
|
||||
|
@ -1578,6 +1573,58 @@ class Client:
|
|||
def set_default_rights(self, chat: ChatLike, user: ChatLike) -> None:
|
||||
set_default_rights(self, chat, user)
|
||||
|
||||
async def edit_draft(
|
||||
self,
|
||||
chat: ChatLike,
|
||||
text: Optional[str] = None,
|
||||
*,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: bool = False,
|
||||
reply_to: Optional[int] = None,
|
||||
) -> Draft:
|
||||
"""
|
||||
Set a draft message in a chat.
|
||||
|
||||
This can also be used to clear the draft by setting the text to an empty string ``""``.
|
||||
|
||||
:param chat:
|
||||
The :term:`chat` where the draft will be saved to.
|
||||
|
||||
:param text: See :ref:`formatting`.
|
||||
:param markdown: See :ref:`formatting`.
|
||||
:param html: See :ref:`formatting`.
|
||||
:param link_preview: See :ref:`formatting`.
|
||||
|
||||
:param reply_to:
|
||||
The message identifier of the message to reply to.
|
||||
|
||||
:return: The created draft.
|
||||
|
||||
.. rubric:: Example
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Edit message to have text without formatting
|
||||
await client.edit_message(chat, msg_id, text='New text')
|
||||
|
||||
# Remove the link preview without changing the text
|
||||
await client.edit_message(chat, msg_id, link_preview=False)
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`telethon.types.Message.edit`
|
||||
"""
|
||||
return await edit_draft(
|
||||
self,
|
||||
chat,
|
||||
text,
|
||||
markdown=markdown,
|
||||
html=html,
|
||||
link_preview=link_preview,
|
||||
reply_to=reply_to,
|
||||
)
|
||||
|
||||
def set_handler_filter(
|
||||
self,
|
||||
handler: Callable[[Event], Awaitable[Any]],
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from ...tl import functions, types
|
||||
from ..types import AsyncList, ChatLike, Dialog, Draft
|
||||
from ..utils import build_chat_map
|
||||
from ..utils import build_chat_map, build_msg_map
|
||||
from .messages import parse_message
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .client import Client
|
||||
|
@ -40,8 +42,11 @@ class DialogList(AsyncList[Dialog]):
|
|||
assert isinstance(result, (types.messages.Dialogs, types.messages.DialogsSlice))
|
||||
|
||||
chat_map = build_chat_map(result.users, result.chats)
|
||||
msg_map = build_msg_map(self._client, result.messages, chat_map)
|
||||
|
||||
self._buffer.extend(Dialog._from_raw(d, chat_map) for d in result.dialogs)
|
||||
self._buffer.extend(
|
||||
Dialog._from_raw(self._client, d, chat_map, msg_map) for d in result.dialogs
|
||||
)
|
||||
|
||||
|
||||
def get_dialogs(self: Client) -> AsyncList[Dialog]:
|
||||
|
@ -93,7 +98,7 @@ class DraftList(AsyncList[Draft]):
|
|||
chat_map = build_chat_map(result.users, result.chats)
|
||||
|
||||
self._buffer.extend(
|
||||
Draft._from_raw(u, chat_map)
|
||||
Draft._from_raw_update(self._client, u, chat_map)
|
||||
for u in result.updates
|
||||
if isinstance(u, types.UpdateDraftMessage)
|
||||
)
|
||||
|
@ -104,3 +109,47 @@ class DraftList(AsyncList[Draft]):
|
|||
|
||||
def get_drafts(self: Client) -> AsyncList[Draft]:
|
||||
return DraftList(self)
|
||||
|
||||
|
||||
async def edit_draft(
|
||||
self: Client,
|
||||
chat: ChatLike,
|
||||
text: Optional[str] = None,
|
||||
*,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: bool = False,
|
||||
reply_to: Optional[int] = None,
|
||||
) -> Draft:
|
||||
packed = await self._resolve_to_packed(chat)
|
||||
peer = (await self._resolve_to_packed(chat))._to_input_peer()
|
||||
message, entities = parse_message(
|
||||
text=text, markdown=markdown, html=html, allow_empty=False
|
||||
)
|
||||
assert isinstance(message, str)
|
||||
|
||||
result = await self(
|
||||
functions.messages.save_draft(
|
||||
no_webpage=not link_preview,
|
||||
reply_to_msg_id=reply_to,
|
||||
top_msg_id=None,
|
||||
peer=peer,
|
||||
message=message,
|
||||
entities=entities,
|
||||
)
|
||||
)
|
||||
assert result
|
||||
|
||||
return Draft._from_raw(
|
||||
client=self,
|
||||
peer=packed._to_peer(),
|
||||
top_msg_id=0,
|
||||
draft=types.DraftMessage(
|
||||
no_webpage=not link_preview,
|
||||
reply_to_msg_id=reply_to,
|
||||
message=message,
|
||||
entities=entities,
|
||||
date=int(time.time()),
|
||||
),
|
||||
chat_map={},
|
||||
)
|
||||
|
|
|
@ -46,7 +46,7 @@ async def send_message(
|
|||
*,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: Optional[bool] = None,
|
||||
link_preview: bool = False,
|
||||
reply_to: Optional[int] = None,
|
||||
) -> Message:
|
||||
packed = await self._resolve_to_packed(chat)
|
||||
|
@ -99,26 +99,15 @@ async def send_message(
|
|||
)
|
||||
)
|
||||
if isinstance(result, types.UpdateShortSentMessage):
|
||||
return Message._from_raw(
|
||||
return Message._from_defaults(
|
||||
self,
|
||||
types.Message(
|
||||
{},
|
||||
out=result.out,
|
||||
mentioned=False,
|
||||
media_unread=False,
|
||||
silent=False,
|
||||
post=False,
|
||||
from_scheduled=False,
|
||||
legacy=False,
|
||||
edit_hide=False,
|
||||
pinned=False,
|
||||
noforwards=False,
|
||||
id=result.id,
|
||||
from_id=types.PeerUser(user_id=self._session.user.id)
|
||||
if self._session.user
|
||||
else None,
|
||||
peer_id=packed._to_peer(),
|
||||
fwd_from=None,
|
||||
via_bot_id=None,
|
||||
reply_to=types.MessageReplyHeader(
|
||||
reply_to_scheduled=False,
|
||||
forum_topic=False,
|
||||
|
@ -131,19 +120,8 @@ async def send_message(
|
|||
date=result.date,
|
||||
message=message if isinstance(message, str) else (message.text or ""),
|
||||
media=result.media,
|
||||
reply_markup=None,
|
||||
entities=result.entities,
|
||||
views=None,
|
||||
forwards=None,
|
||||
replies=None,
|
||||
edit_date=None,
|
||||
post_author=None,
|
||||
grouped_id=None,
|
||||
reactions=None,
|
||||
restriction_reason=None,
|
||||
ttl_period=result.ttl_period,
|
||||
),
|
||||
{},
|
||||
)
|
||||
else:
|
||||
return self._build_message_map(result, peer).with_random_id(random_id)
|
||||
|
@ -157,7 +135,7 @@ async def edit_message(
|
|||
text: Optional[str] = None,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: Optional[bool] = None,
|
||||
link_preview: bool = False,
|
||||
) -> Message:
|
||||
peer = (await self._resolve_to_packed(chat))._to_input_peer()
|
||||
message, entities = parse_message(
|
||||
|
|
|
@ -52,7 +52,7 @@ class Group(Chat, metaclass=NoPublicConstructor):
|
|||
|
||||
This property is always present, but may be the empty string.
|
||||
"""
|
||||
return self._raw.title
|
||||
return getattr(self._raw, "title", None) or ""
|
||||
|
||||
@property
|
||||
def username(self) -> Optional[str]:
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
from typing import Dict, Self
|
||||
from __future__ import annotations
|
||||
|
||||
from ...tl import abcs
|
||||
from typing import TYPE_CHECKING, Dict, Optional, Self, Union
|
||||
|
||||
from ...tl import abcs, types
|
||||
from ..utils import peer_id
|
||||
from .chat import Chat
|
||||
from .draft import Draft
|
||||
from .message import Message
|
||||
from .meta import NoPublicConstructor
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client import Client
|
||||
|
||||
|
||||
class Dialog(metaclass=NoPublicConstructor):
|
||||
"""
|
||||
|
@ -16,12 +24,76 @@ class Dialog(metaclass=NoPublicConstructor):
|
|||
You can obtain dialogs with methods such as :meth:`telethon.Client.get_dialogs`.
|
||||
"""
|
||||
|
||||
__slots__ = ("_raw", "_chat_map")
|
||||
|
||||
def __init__(self, raw: abcs.Dialog, chat_map: Dict[int, Chat]) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
client: Client,
|
||||
raw: Union[types.Dialog, types.DialogFolder],
|
||||
chat_map: Dict[int, Chat],
|
||||
msg_map: Dict[int, Message],
|
||||
) -> None:
|
||||
self._client = client
|
||||
self._raw = raw
|
||||
self._chat_map = chat_map
|
||||
self._msg_map = msg_map
|
||||
|
||||
@classmethod
|
||||
def _from_raw(cls, dialog: abcs.Dialog, chat_map: Dict[int, Chat]) -> Self:
|
||||
return cls._create(dialog, chat_map)
|
||||
def _from_raw(
|
||||
cls,
|
||||
client: Client,
|
||||
dialog: abcs.Dialog,
|
||||
chat_map: Dict[int, Chat],
|
||||
msg_map: Dict[int, Message],
|
||||
) -> Self:
|
||||
assert isinstance(dialog, (types.Dialog, types.DialogFolder))
|
||||
return cls._create(client, dialog, chat_map, msg_map)
|
||||
|
||||
@property
|
||||
def chat(self) -> Chat:
|
||||
"""
|
||||
The chat where messages are sent in this dialog.
|
||||
"""
|
||||
return self._chat_map[peer_id(self._raw.peer)]
|
||||
|
||||
@property
|
||||
def draft(self) -> Optional[Draft]:
|
||||
"""
|
||||
The message draft within this dialog, if any.
|
||||
|
||||
This property does not update when the draft changes.
|
||||
"""
|
||||
if isinstance(self._raw, types.Dialog) and self._raw.draft:
|
||||
return Draft._from_raw(
|
||||
self._client,
|
||||
self._raw.peer,
|
||||
self._raw.top_message,
|
||||
self._raw.draft,
|
||||
self._chat_map,
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def latest_message(self) -> Optional[Message]:
|
||||
"""
|
||||
The latest message sent or received in this dialog, if any.
|
||||
|
||||
This property does not update when new messages arrive.
|
||||
"""
|
||||
return self._msg_map.get(self._raw.top_message)
|
||||
|
||||
@property
|
||||
def unread_count(self) -> int:
|
||||
"""
|
||||
The amount of unread messages in this dialog.
|
||||
|
||||
This property does not update when messages are read or sent.
|
||||
"""
|
||||
if isinstance(self._raw, types.Dialog):
|
||||
return self._raw.unread_count
|
||||
elif isinstance(self._raw, types.DialogPeerFolder):
|
||||
return (
|
||||
self._raw.unread_unmuted_messages_count
|
||||
+ self._raw.unread_muted_messages_count
|
||||
)
|
||||
else:
|
||||
raise RuntimeError("unexpected case")
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
from typing import Dict, Self
|
||||
from __future__ import annotations
|
||||
|
||||
from ...tl import types
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Dict, Optional, Self
|
||||
|
||||
from ...session import PackedChat
|
||||
from ...tl import abcs, functions, types
|
||||
from ..parsers import generate_html_message, generate_markdown_message
|
||||
from ..utils import expand_peer, generate_random_id, peer_id
|
||||
from .chat import Chat
|
||||
from .message import Message
|
||||
from .meta import NoPublicConstructor
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..client import Client
|
||||
|
||||
|
||||
class Draft(metaclass=NoPublicConstructor):
|
||||
"""
|
||||
|
@ -12,19 +22,226 @@ class Draft(metaclass=NoPublicConstructor):
|
|||
You can obtain drafts with methods such as :meth:`telethon.Client.get_drafts`.
|
||||
"""
|
||||
|
||||
__slots__ = ("_raw", "_chat_map")
|
||||
|
||||
def __init__(
|
||||
self, raw: types.UpdateDraftMessage, chat_map: Dict[int, Chat]
|
||||
self,
|
||||
client: Client,
|
||||
peer: abcs.Peer,
|
||||
top_msg_id: Optional[int],
|
||||
raw: abcs.DraftMessage,
|
||||
chat_map: Dict[int, Chat],
|
||||
) -> None:
|
||||
assert isinstance(raw, (types.DraftMessage, types.DraftMessageEmpty))
|
||||
self._client = client
|
||||
self._peer = peer
|
||||
self._raw = raw
|
||||
self._top_msg_id = top_msg_id
|
||||
self._chat_map = chat_map
|
||||
|
||||
@classmethod
|
||||
def _from_raw(
|
||||
cls, draft: types.UpdateDraftMessage, chat_map: Dict[int, Chat]
|
||||
def _from_raw_update(
|
||||
cls, client: Client, draft: types.UpdateDraftMessage, chat_map: Dict[int, Chat]
|
||||
) -> Self:
|
||||
return cls._create(draft, chat_map)
|
||||
return cls._create(client, draft.peer, draft.top_msg_id, draft.draft, chat_map)
|
||||
|
||||
@classmethod
|
||||
def _from_raw(
|
||||
cls,
|
||||
client: Client,
|
||||
peer: abcs.Peer,
|
||||
top_msg_id: int,
|
||||
draft: abcs.DraftMessage,
|
||||
chat_map: Dict[int, Chat],
|
||||
) -> Self:
|
||||
return cls._create(client, peer, top_msg_id, draft, chat_map)
|
||||
|
||||
@property
|
||||
def chat(self) -> Chat:
|
||||
"""
|
||||
The chat where the draft will be sent to.
|
||||
"""
|
||||
return self._chat_map.get(peer_id(self._peer)) or expand_peer(
|
||||
self._peer, broadcast=None
|
||||
)
|
||||
|
||||
@property
|
||||
def link_preview(self) -> bool:
|
||||
"""
|
||||
:data:`True` if the link preview is allowed to exist when sending the message.
|
||||
"""
|
||||
return not getattr(self._raw, "no_webpage", False)
|
||||
|
||||
@property
|
||||
def replied_message_id(self) -> Optional[int]:
|
||||
"""
|
||||
Get the message identifier of message this draft will reply to once sent.
|
||||
"""
|
||||
return getattr(self._raw, "reply_to_msg_id") or None
|
||||
|
||||
@property
|
||||
def text(self) -> Optional[str]:
|
||||
"""
|
||||
The :attr:`~Message.text` of the message that will be sent.
|
||||
"""
|
||||
return getattr(self._raw, "message", None)
|
||||
|
||||
@property
|
||||
def text_html(self) -> Optional[str]:
|
||||
"""
|
||||
The :attr:`~Message.text_html` of the message that will be sent.
|
||||
"""
|
||||
if text := getattr(self._raw, "message", None):
|
||||
return generate_html_message(
|
||||
text, getattr(self._raw, "entities", None) or []
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def text_markdown(self) -> Optional[str]:
|
||||
"""
|
||||
The :attr:`~Message.text_markdown` of the message that will be sent.
|
||||
"""
|
||||
if text := getattr(self._raw, "message", None):
|
||||
return generate_markdown_message(
|
||||
text, getattr(self._raw, "entities", None) or []
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def date(self) -> Optional[datetime.datetime]:
|
||||
"""
|
||||
The date when the draft was last updated.
|
||||
"""
|
||||
date = getattr(self._raw, "date", None)
|
||||
return (
|
||||
datetime.datetime.fromtimestamp(date, tz=datetime.timezone.utc)
|
||||
if date is not None
|
||||
else None
|
||||
)
|
||||
|
||||
async def edit(
|
||||
self,
|
||||
text: Optional[str] = None,
|
||||
*,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: bool = False,
|
||||
reply_to: Optional[int] = None,
|
||||
) -> Draft:
|
||||
"""
|
||||
Replace the current draft with a new one.
|
||||
|
||||
:param text: See :ref:`formatting`.
|
||||
:param markdown: See :ref:`formatting`.
|
||||
:param html: See :ref:`formatting`.
|
||||
:param link_preview: See :ref:`formatting`.
|
||||
|
||||
:param reply_to:
|
||||
The message identifier of the message to reply to.
|
||||
|
||||
:return: The edited draft.
|
||||
|
||||
.. rubric:: Example
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
new_draft = await old_draft.edit('new text', link_preview=False)
|
||||
"""
|
||||
return await self._client.edit_draft(
|
||||
await self._packed_chat(),
|
||||
text,
|
||||
markdown=markdown,
|
||||
html=html,
|
||||
link_preview=link_preview,
|
||||
reply_to=reply_to,
|
||||
)
|
||||
|
||||
async def _packed_chat(self) -> PackedChat:
|
||||
packed = None
|
||||
if chat := self._chat_map.get(peer_id(self._peer)):
|
||||
packed = chat.pack()
|
||||
if packed is None:
|
||||
packed = await self._client.resolve_to_packed(peer_id(self._peer))
|
||||
return packed
|
||||
|
||||
async def send(self) -> Message:
|
||||
"""
|
||||
Send the contents of this draft to the chat.
|
||||
|
||||
The draft will be cleared after being sent.
|
||||
|
||||
:return: The sent message.
|
||||
|
||||
.. rubric:: Example
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
await draft.send(clear=False)
|
||||
"""
|
||||
|
||||
packed = await self._packed_chat()
|
||||
peer = packed._to_input_peer()
|
||||
|
||||
reply_to = self.replied_message_id
|
||||
message = getattr(self._raw, "message", "")
|
||||
entities = getattr(self._raw, "entities", None)
|
||||
random_id = generate_random_id()
|
||||
|
||||
result = await self._client(
|
||||
functions.messages.send_message(
|
||||
no_webpage=not self.link_preview,
|
||||
silent=False,
|
||||
background=False,
|
||||
clear_draft=True,
|
||||
noforwards=False,
|
||||
update_stickersets_order=False,
|
||||
peer=peer,
|
||||
reply_to=types.InputReplyToMessage(
|
||||
reply_to_msg_id=reply_to, top_msg_id=None
|
||||
)
|
||||
if reply_to
|
||||
else None,
|
||||
message=message,
|
||||
random_id=random_id,
|
||||
reply_markup=None,
|
||||
entities=entities,
|
||||
schedule_date=None,
|
||||
send_as=None,
|
||||
)
|
||||
)
|
||||
if isinstance(result, types.UpdateShortSentMessage):
|
||||
return Message._from_defaults(
|
||||
self._client,
|
||||
{},
|
||||
out=result.out,
|
||||
id=result.id,
|
||||
from_id=types.PeerUser(user_id=self._client._session.user.id)
|
||||
if self._client._session.user
|
||||
else None,
|
||||
peer_id=packed._to_peer(),
|
||||
reply_to=types.MessageReplyHeader(
|
||||
reply_to_scheduled=False,
|
||||
forum_topic=False,
|
||||
reply_to_msg_id=reply_to,
|
||||
reply_to_peer_id=None,
|
||||
reply_to_top_id=None,
|
||||
)
|
||||
if reply_to
|
||||
else None,
|
||||
date=result.date,
|
||||
message=message,
|
||||
media=result.media,
|
||||
entities=result.entities,
|
||||
ttl_period=result.ttl_period,
|
||||
)
|
||||
else:
|
||||
return self._client._build_message_map(result, peer).with_random_id(
|
||||
random_id
|
||||
)
|
||||
|
||||
async def delete(self) -> None:
|
||||
pass
|
||||
"""
|
||||
Clear the contents of this draft to delete it.
|
||||
"""
|
||||
await self.edit("")
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Dict, Optional, Self, Union
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Self, Union
|
||||
|
||||
from ...tl import abcs, types
|
||||
from ..parsers import generate_html_message, generate_markdown_message
|
||||
from ..utils import expand_peer, peer_id
|
||||
from ..utils import adapt_date, expand_peer, peer_id
|
||||
from .chat import Chat, ChatLike
|
||||
from .file import File
|
||||
from .meta import NoPublicConstructor
|
||||
|
@ -40,6 +40,52 @@ class Message(metaclass=NoPublicConstructor):
|
|||
) -> Self:
|
||||
return cls._create(client, message, chat_map)
|
||||
|
||||
@classmethod
|
||||
def _from_defaults(
|
||||
cls,
|
||||
client: Client,
|
||||
chat_map: Dict[int, Chat],
|
||||
id: int,
|
||||
peer_id: abcs.Peer,
|
||||
date: int,
|
||||
message: str,
|
||||
**kwargs: Any,
|
||||
) -> Self:
|
||||
default_kwargs: Dict[str, Any] = {
|
||||
"out": False,
|
||||
"mentioned": False,
|
||||
"media_unread": False,
|
||||
"silent": False,
|
||||
"post": False,
|
||||
"from_scheduled": False,
|
||||
"legacy": False,
|
||||
"edit_hide": False,
|
||||
"pinned": False,
|
||||
"noforwards": False,
|
||||
"id": id,
|
||||
"from_id": None,
|
||||
"peer_id": peer_id,
|
||||
"fwd_from": None,
|
||||
"via_bot_id": None,
|
||||
"reply_to": None,
|
||||
"date": date,
|
||||
"message": message,
|
||||
"media": None,
|
||||
"reply_markup": None,
|
||||
"entities": None,
|
||||
"views": None,
|
||||
"forwards": None,
|
||||
"replies": None,
|
||||
"edit_date": None,
|
||||
"post_author": None,
|
||||
"grouped_id": None,
|
||||
"reactions": None,
|
||||
"restriction_reason": None,
|
||||
"ttl_period": None,
|
||||
}
|
||||
default_kwargs.update(kwargs)
|
||||
return cls._create(client, types.Message(**default_kwargs), chat_map)
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
"""
|
||||
|
@ -86,12 +132,7 @@ class Message(metaclass=NoPublicConstructor):
|
|||
|
||||
@property
|
||||
def date(self) -> Optional[datetime.datetime]:
|
||||
date = getattr(self._raw, "date", None)
|
||||
return (
|
||||
datetime.datetime.fromtimestamp(date, tz=datetime.timezone.utc)
|
||||
if date is not None
|
||||
else None
|
||||
)
|
||||
return adapt_date(getattr(self._raw, "date", None))
|
||||
|
||||
@property
|
||||
def chat(self) -> Chat:
|
||||
|
@ -236,7 +277,7 @@ class Message(metaclass=NoPublicConstructor):
|
|||
text: Optional[str] = None,
|
||||
markdown: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
link_preview: Optional[bool] = None,
|
||||
link_preview: bool = False,
|
||||
) -> Message:
|
||||
"""
|
||||
Alias for :meth:`telethon.Client.edit_message`.
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import itertools
|
||||
import sys
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from typing import DefaultDict, Dict, List, Optional, Union
|
||||
from typing import TYPE_CHECKING, DefaultDict, Dict, List, Optional, Union
|
||||
|
||||
from ..tl import abcs, types
|
||||
from .types import Channel, Chat, Group, User
|
||||
from .types import Channel, Chat, Group, Message, User
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .client import Client
|
||||
|
||||
_last_id = 0
|
||||
|
||||
|
@ -51,6 +57,15 @@ def build_chat_map(users: List[abcs.User], chats: List[abcs.Chat]) -> Dict[int,
|
|||
return result
|
||||
|
||||
|
||||
def build_msg_map(
|
||||
client: Client, messages: List[abcs.Message], chat_map: Dict[int, Chat]
|
||||
) -> Dict[int, Message]:
|
||||
return {
|
||||
msg.id: msg
|
||||
for msg in (Message._from_raw(client, m, chat_map) for m in messages)
|
||||
}
|
||||
|
||||
|
||||
def peer_id(peer: abcs.Peer) -> int:
|
||||
if isinstance(peer, types.PeerUser):
|
||||
return peer.user_id
|
||||
|
@ -83,3 +98,11 @@ def expand_peer(peer: abcs.Peer, *, broadcast: Optional[bool]) -> Chat:
|
|||
return Channel._from_raw(channel) if broadcast else Group._from_raw(channel)
|
||||
else:
|
||||
raise RuntimeError("unexpected case")
|
||||
|
||||
|
||||
def adapt_date(date: Optional[int]) -> Optional[datetime.datetime]:
|
||||
return (
|
||||
datetime.datetime.fromtimestamp(date, tz=datetime.timezone.utc)
|
||||
if date is not None
|
||||
else None
|
||||
)
|
||||
|
|
|
@ -208,7 +208,7 @@ class Encrypted(Mtp):
|
|||
)
|
||||
|
||||
if self._msg_count == 1:
|
||||
container_msg_id = Single
|
||||
container_msg_id: Union[Type[Single], int] = Single
|
||||
else:
|
||||
container_msg_id = self._get_new_msg_id()
|
||||
self._buffer[HEADER_LEN : HEADER_LEN + CONTAINER_HEADER_LEN] = struct.pack(
|
||||
|
|
Loading…
Reference in New Issue
Block a user