Feature: Added the ability to apply formatted_entities when sending files.

This commit is contained in:
EntireMusic 2024-10-05 11:34:13 +03:00
parent f652f3f01a
commit d58fa32827

View File

@ -6,11 +6,13 @@ import pathlib
import re import re
import typing import typing
from io import BytesIO from io import BytesIO
from typing import List, Any, Dict
from ..crypto import AES from ..crypto import AES
from .. import utils, helpers, hints from .. import utils, helpers, hints
from ..tl import types, functions, custom from ..tl import types, functions, custom
from ..tl.types import MessageEmpty, Message, MessageService
try: try:
import PIL import PIL
@ -18,7 +20,6 @@ try:
except ImportError: except ImportError:
PIL = None PIL = None
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from .telegramclient import TelegramClient from .telegramclient import TelegramClient
@ -119,7 +120,11 @@ class UploadMethods:
thumb: 'hints.FileLike' = None, thumb: 'hints.FileLike' = None,
allow_cache: bool = True, allow_cache: bool = True,
parse_mode: str = (), parse_mode: str = (),
formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, formatting_entities: typing.Optional[
typing.Union[
typing.List[types.TypeMessageEntity], typing.List[typing.List[types.TypeMessageEntity]]
]
] = None,
voice_note: bool = False, voice_note: bool = False,
video_note: bool = False, video_note: bool = False,
buttons: typing.Optional['hints.MarkupLike'] = None, buttons: typing.Optional['hints.MarkupLike'] = None,
@ -130,7 +135,7 @@ class UploadMethods:
comment_to: 'typing.Union[int, types.Message]' = None, comment_to: 'typing.Union[int, types.Message]' = None,
ttl: int = None, ttl: int = None,
nosound_video: bool = None, nosound_video: bool = None,
**kwargs) -> 'types.Message': **kwargs) -> list[Any] | Any:
""" """
Sends message with the given file to the specified entity. Sends message with the given file to the specified entity.
@ -243,7 +248,11 @@ class UploadMethods:
default. default.
formatting_entities (`list`, optional): formatting_entities (`list`, optional):
A list of message formatting entities. When provided, the ``parse_mode`` is ignored. Optional formatting entities for the sent media message. When sending an album,
`formatting_entities` can be a list of lists, where each inner list contains
`types.TypeMessageEntity`. Each inner list will be assigned to the corresponding
file in a pairwise manner with the caption. If provided, the ``parse_mode``
parameter will be ignored.
voice_note (`bool`, optional): voice_note (`bool`, optional):
If `True` the audio will be sent as a voice note. If `True` the audio will be sent as a voice note.
@ -365,6 +374,9 @@ class UploadMethods:
if not caption: if not caption:
caption = '' caption = ''
if not formatting_entities:
formatting_entities = []
entity = await self.get_input_entity(entity) entity = await self.get_input_entity(entity)
if comment_to is not None: if comment_to is not None:
entity, reply_to = await self._get_comment_data(entity, comment_to) entity, reply_to = await self._get_comment_data(entity, comment_to)
@ -384,10 +396,22 @@ class UploadMethods:
else: else:
captions = [caption] captions = [caption]
# Check that formatting_entities list is valid
if all(utils.is_list_like(obj) for obj in formatting_entities):
formatting_entities = formatting_entities
elif utils.is_list_like(formatting_entities):
formatting_entities = [formatting_entities]
else:
raise TypeError('The formatting_entities argument must be a list or a sequence of lists')
# Check that all entities in all lists are of the correct type
if not all(isinstance(ent, types.TypeMessageEntity) for sublist in formatting_entities for ent in sublist):
raise TypeError('All entities must be instances of <types.TypeMessageEntity>')
result = [] result = []
while file: while file:
result += await self._send_album( result += await self._send_album(
entity, file[:10], caption=captions[:10], entity, file[:10], caption=captions[:10], formatting_entities=formatting_entities[:10],
progress_callback=used_callback, reply_to=reply_to, progress_callback=used_callback, reply_to=reply_to,
parse_mode=parse_mode, silent=silent, schedule=schedule, parse_mode=parse_mode, silent=silent, schedule=schedule,
supports_streaming=supports_streaming, clear_draft=clear_draft, supports_streaming=supports_streaming, clear_draft=clear_draft,
@ -395,6 +419,7 @@ class UploadMethods:
) )
file = file[10:] file = file[10:]
captions = captions[10:] captions = captions[10:]
formatting_entities = formatting_entities[10:]
sent_count += 10 sent_count += 10
return result return result
@ -409,7 +434,7 @@ class UploadMethods:
file, force_document=force_document, file, force_document=force_document,
file_size=file_size, file_size=file_size,
progress_callback=progress_callback, progress_callback=progress_callback,
attributes=attributes, allow_cache=allow_cache, thumb=thumb, attributes=attributes, allow_cache=allow_cache, thumb=thumb,
voice_note=voice_note, video_note=video_note, voice_note=voice_note, video_note=video_note,
supports_streaming=supports_streaming, ttl=ttl, supports_streaming=supports_streaming, ttl=ttl,
nosound_video=nosound_video, nosound_video=nosound_video,
@ -430,6 +455,7 @@ class UploadMethods:
return self._get_response_message(request, await self(request), entity) return self._get_response_message(request, await self(request), entity)
async def _send_album(self: 'TelegramClient', entity, files, caption='', async def _send_album(self: 'TelegramClient', entity, files, caption='',
formatting_entities=None,
progress_callback=None, reply_to=None, progress_callback=None, reply_to=None,
parse_mode=(), silent=None, schedule=None, parse_mode=(), silent=None, schedule=None,
supports_streaming=None, clear_draft=None, supports_streaming=None, clear_draft=None,
@ -441,16 +467,25 @@ class UploadMethods:
# cache only makes a difference for documents where the user may # cache only makes a difference for documents where the user may
# want the attributes used on them to change. # want the attributes used on them to change.
# #
# In theory documents can be sent inside the albums but they appear # In theory documents can be sent inside the albums, but they appear
# as different messages (not inside the album), and the logic to set # as different messages (not inside the album), and the logic to set
# the attributes/avoid cache is already written in .send_file(). # the attributes/avoid cache is already written in .send_file().
entity = await self.get_input_entity(entity) entity = await self.get_input_entity(entity)
if not utils.is_list_like(caption): if not utils.is_list_like(caption):
caption = (caption,) caption = (caption,)
if not all(isinstance(obj, list) for obj in formatting_entities):
formatting_entities = (formatting_entities,)
captions = [] captions = []
for c in reversed(caption): # Pop from the end (so reverse) # If the formatting_entities argument is provided, we don't use parse_mode
captions.append(await self._parse_message_text(c or '', parse_mode)) if formatting_entities:
# Pop from the end (so reverse)
capt_with_ent = itertools.zip_longest(reversed(caption), reversed(formatting_entities), fillvalue=None)
for msg_caption, msg_entities in capt_with_ent:
captions.append((msg_caption, msg_entities))
else:
for c in reversed(caption): # Pop from the end (so reverse)
captions.append(await self._parse_message_text(c or '', parse_mode))
reply_to = utils.get_message_id(reply_to) reply_to = utils.get_message_id(reply_to)
@ -482,7 +517,7 @@ class UploadMethods:
)) ))
fm = utils.get_input_media( fm = utils.get_input_media(
r.document, supports_streaming=supports_streaming) r.document, supports_streaming=supports_streaming)
if captions: if captions:
caption, msg_entities = captions.pop() caption, msg_entities = captions.pop()
@ -635,7 +670,7 @@ class UploadMethods:
part_count = (file_size + part_size - 1) // part_size part_count = (file_size + part_size - 1) // part_size
self._log[__name__].info('Uploading file of %d bytes in %d chunks of %d', self._log[__name__].info('Uploading file of %d bytes in %d chunks of %d',
file_size, part_count, part_size) file_size, part_count, part_size)
pos = 0 pos = 0
for part_index in range(part_count): for part_index in range(part_count):
@ -712,7 +747,7 @@ class UploadMethods:
# `aiofiles` do not base `io.IOBase` but do have `read`, so we # `aiofiles` do not base `io.IOBase` but do have `read`, so we
# just check for the read attribute to see if it's file-like. # just check for the read attribute to see if it's file-like.
if not isinstance(file, (str, bytes, types.InputFile, types.InputFileBig))\ if not isinstance(file, (str, bytes, types.InputFile, types.InputFileBig)) \
and not hasattr(file, 'read'): and not hasattr(file, 'read'):
# The user may pass a Message containing media (or the media, # The user may pass a Message containing media (or the media,
# or anything similar) that should be treated as a file. Try # or anything similar) that should be treated as a file. Try