From 5c7782643da0faf8b3ea0fcb0d4a45a01c7d8975 Mon Sep 17 00:00:00 2001 From: Nick80835 Date: Fri, 21 Apr 2023 21:39:43 -0400 Subject: [PATCH] Implement media spoilers in friendly methods May need a bit more testing, I can't test everything, apologies --- telethon/client/messages.py | 25 +++++++++++++---- telethon/client/uploads.py | 55 +++++++++++++++++++++++++++++-------- telethon/utils.py | 29 +++++++++++++------ 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/telethon/client/messages.py b/telethon/client/messages.py index abe435da..4648daa8 100644 --- a/telethon/client/messages.py +++ b/telethon/client/messages.py @@ -642,6 +642,7 @@ class MessageMethods: schedule: 'hints.DateLike' = None, comment_to: 'typing.Union[int, types.Message]' = None, nosound_video: bool = None, + spoiler: typing.Union[bool, typing.Sequence[bool]] = None ) -> 'types.Message': """ Sends a message to the specified user, chat or channel. @@ -764,6 +765,11 @@ class MessageMethods: on non-video files. This is set to ``True`` for albums, as gifs cannot be sent in albums. + spoiler (`bool`, optional): + Whether or not to spoiler the media in the sent message. When sending an + album, this may be a list of booleans, which will be + assigned to the media pairwise. + Returns The sent `custom.Message `. @@ -832,7 +838,7 @@ class MessageMethods: schedule=schedule, supports_streaming=supports_streaming, formatting_entities=formatting_entities, comment_to=comment_to, background=background, - nosound_video=nosound_video, + nosound_video=nosound_video, spoiler=spoiler ) entity = await self.get_input_entity(entity) @@ -1061,7 +1067,8 @@ class MessageMethods: force_document: bool = False, buttons: typing.Optional['hints.MarkupLike'] = None, supports_streaming: bool = False, - schedule: 'hints.DateLike' = None + schedule: 'hints.DateLike' = None, + spoiler: bool = None ) -> 'types.Message': """ Edits the given message to change its text or media. @@ -1145,6 +1152,9 @@ class MessageMethods: Note that this parameter will have no effect if you are trying to edit a message that was sent via inline bots. + spoiler (`bool`, optional): + Whether or not to spoiler the media in the sent message. + Returns The edited `Message `, unless `entity` was a :tl:`InputBotInlineMessageID` or :tl:`InputBotInlineMessageID64` in which @@ -1183,11 +1193,14 @@ class MessageMethods: if formatting_entities is None: text, formatting_entities = await self._parse_message_text(text, parse_mode) + file_handle, media, image = await self._file_to_media(file, - supports_streaming=supports_streaming, - thumb=thumb, - attributes=attributes, - force_document=force_document) + supports_streaming=supports_streaming, + thumb=thumb, + attributes=attributes, + force_document=force_document, + spoiler=spoiler + ) if isinstance(entity, (types.InputBotInlineMessageID, types.InputBotInlineMessageID64)): request = functions.messages.EditInlineBotMessageRequest( diff --git a/telethon/client/uploads.py b/telethon/client/uploads.py index 59228a72..30f7dac1 100644 --- a/telethon/client/uploads.py +++ b/telethon/client/uploads.py @@ -118,6 +118,7 @@ class UploadMethods: comment_to: 'typing.Union[int, types.Message]' = None, ttl: int = None, nosound_video: bool = None, + spoiler: typing.Union[bool, typing.Sequence[bool]] = None, **kwargs) -> 'types.Message': """ Sends message with the given file to the specified entity. @@ -296,6 +297,11 @@ class UploadMethods: on non-video files. This is set to ``True`` for albums, as gifs cannot be sent in albums. + spoiler (`bool`, optional): + Whether or not to spoiler the media in the sent message. When sending an + album, this may be a list of booleans, which will be + assigned to the media pairwise. + Returns The `Message ` (or messages) containing the sent file, or messages if a list of them was passed. @@ -372,6 +378,11 @@ class UploadMethods: else: captions = [caption] + if utils.is_list_like(spoiler): + spoilers = spoiler + else: + spoilers = [spoiler] + result = [] while file: result += await self._send_album( @@ -380,9 +391,11 @@ class UploadMethods: parse_mode=parse_mode, silent=silent, schedule=schedule, supports_streaming=supports_streaming, clear_draft=clear_draft, force_document=force_document, background=background, + spoiler=spoilers[:10] ) file = file[10:] captions = captions[10:] + spoilers = spoilers[10:] sent_count += 10 return result @@ -400,7 +413,7 @@ class UploadMethods: attributes=attributes, allow_cache=allow_cache, thumb=thumb, voice_note=voice_note, video_note=video_note, supports_streaming=supports_streaming, ttl=ttl, - nosound_video=nosound_video, + nosound_video=nosound_video, spoiler=spoiler ) # e.g. invalid cast from :tl:`MessageMediaWebPage` @@ -420,7 +433,8 @@ class UploadMethods: progress_callback=None, reply_to=None, parse_mode=(), silent=None, schedule=None, supports_streaming=None, clear_draft=None, - force_document=False, background=None, ttl=None): + force_document=False, background=None, ttl=None, + spoiler=None): """Specialized version of .send_file for albums""" # We don't care if the user wants to avoid cache, we will use it # anyway. Why? The cached version will be exactly the same thing @@ -439,6 +453,14 @@ class UploadMethods: for c in reversed(caption): # Pop from the end (so reverse) captions.append(await self._parse_message_text(c or '', parse_mode)) + if utils.is_list_like(spoiler): + spoilers = spoiler + else: + spoilers = [spoiler] + + while len(spoilers) < len(files): + spoilers.append(None) + reply_to = utils.get_message_id(reply_to) used_callback = None if not progress_callback else ( @@ -456,20 +478,27 @@ class UploadMethods: fh, fm, _ = await self._file_to_media( file, supports_streaming=supports_streaming, force_document=force_document, ttl=ttl, - progress_callback=used_callback, nosound_video=True) + progress_callback=used_callback, nosound_video=True, + spoiler=spoilers[sent_count]) if isinstance(fm, (types.InputMediaUploadedPhoto, types.InputMediaPhotoExternal)): r = await self(functions.messages.UploadMediaRequest( entity, media=fm )) - fm = utils.get_input_media(r.photo) + fm = utils.get_input_media( + r.photo, + spoiler=spoilers[sent_count] + ) elif isinstance(fm, types.InputMediaUploadedDocument): r = await self(functions.messages.UploadMediaRequest( entity, media=fm )) fm = utils.get_input_media( - r.document, supports_streaming=supports_streaming) + r.document, + supports_streaming=supports_streaming, + spoiler=spoilers[sent_count] + ) if captions: caption, msg_entities = captions.pop() @@ -686,7 +715,7 @@ class UploadMethods: progress_callback=None, attributes=None, thumb=None, allow_cache=True, voice_note=False, video_note=False, supports_streaming=False, mime_type=None, as_image=None, - ttl=None, nosound_video=None): + ttl=None, nosound_video=None, spoiler=None): if not file: return None, None, None @@ -716,7 +745,8 @@ class UploadMethods: voice_note=voice_note, video_note=video_note, supports_streaming=supports_streaming, - ttl=ttl + ttl=ttl, + spoiler=spoiler ), as_image) except TypeError: # Can't turn whatever was given into media @@ -735,13 +765,13 @@ class UploadMethods: ) elif re.match('https?://', file): if as_image: - media = types.InputMediaPhotoExternal(file, ttl_seconds=ttl) + media = types.InputMediaPhotoExternal(file, ttl_seconds=ttl, spoiler=spoiler) else: - media = types.InputMediaDocumentExternal(file, ttl_seconds=ttl) + media = types.InputMediaDocumentExternal(file, ttl_seconds=ttl, spoiler=spoiler) else: bot_file = utils.resolve_bot_file_id(file) if bot_file: - media = utils.get_input_media(bot_file, ttl=ttl) + media = utils.get_input_media(bot_file, ttl=ttl, spoiler=spoiler) if media: pass # Already have media, don't check the rest @@ -751,7 +781,7 @@ class UploadMethods: 'an HTTP URL or a valid bot-API-like file ID'.format(file) ) elif as_image: - media = types.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl) + media = types.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl, spoiler=spoiler) else: attributes, mime_type = utils.get_attributes( file, @@ -782,7 +812,8 @@ class UploadMethods: thumb=thumb, force_file=force_document and not is_image, ttl_seconds=ttl, - nosound_video=nosound_video + nosound_video=nosound_video, + spoiler=spoiler ) return file_handle, media, as_image diff --git a/telethon/utils.py b/telethon/utils.py index f7b830e7..87611102 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -430,7 +430,7 @@ def get_input_media( media, *, is_photo=False, attributes=None, force_document=False, voice_note=False, video_note=False, supports_streaming=False, - ttl=None + ttl=None, spoiler=None ): """ Similar to :meth:`get_input_peer`, but for media. @@ -452,30 +452,38 @@ def get_input_media( if isinstance(media, types.MessageMediaPhoto): return types.InputMediaPhoto( id=get_input_photo(media.photo), - ttl_seconds=ttl or media.ttl_seconds + ttl_seconds=ttl or media.ttl_seconds, + spoiler=spoiler or media.spoiler ) if isinstance(media, (types.Photo, types.photos.Photo, types.PhotoEmpty)): return types.InputMediaPhoto( id=get_input_photo(media), - ttl_seconds=ttl + ttl_seconds=ttl, + spoiler=spoiler ) if isinstance(media, types.MessageMediaDocument): return types.InputMediaDocument( id=get_input_document(media.document), - ttl_seconds=ttl or media.ttl_seconds + ttl_seconds=ttl or media.ttl_seconds, + spoiler=spoiler or media.spoiler ) if isinstance(media, (types.Document, types.DocumentEmpty)): return types.InputMediaDocument( id=get_input_document(media), - ttl_seconds=ttl + ttl_seconds=ttl, + spoiler=spoiler ) if isinstance(media, (types.InputFile, types.InputFileBig)): if is_photo: - return types.InputMediaUploadedPhoto(file=media, ttl_seconds=ttl) + return types.InputMediaUploadedPhoto( + file=media, + ttl_seconds=ttl, + spoiler=spoiler + ) else: attrs, mime = get_attributes( media, @@ -486,8 +494,13 @@ def get_input_media( supports_streaming=supports_streaming ) return types.InputMediaUploadedDocument( - file=media, mime_type=mime, attributes=attrs, force_file=force_document, - ttl_seconds=ttl) + file=media, + mime_type=mime, + attributes=attrs, + force_file=force_document, + ttl_seconds=ttl, + spoiler=spoiler + ) if isinstance(media, types.MessageMediaGame): return types.InputMediaGame(id=types.InputGameID(