Officially remove bot_file_id support

This commit is contained in:
Lonami Exo 2021-09-18 13:04:13 +02:00
parent 3d36bb7b93
commit 9af8ec8cce
6 changed files with 20 additions and 188 deletions

View File

@ -178,6 +178,23 @@ The following modules have been moved inside ``_misc``:
// TODO review telethon/__init__.py isn't exposing more than it should
Support for bot-API style file_id has been removed
--------------------------------------------------
They have been half-broken for a while now, so this is just making an existing reality official.
See `issue #1613 <https://github.com/LonamiWebs/Telethon/issues/1613>`__ for details.
An alternative solution to re-use files may be provided in the future. For the time being, you
should either upload the file as needed, or keep a message with the media somewhere you can
later fetch it (by storing the chat and message identifier).
Additionally, the ``custom.File.id`` property is gone (which used to provide access to this
"bot-API style" file identifier.
// TODO could probably provide an in-memory cache for uploads to temporarily reuse old InputFile.
// this should lessen the impact of the removal of this feature
The custom.Message class and the way it is used has changed
-----------------------------------------------------------

View File

@ -282,9 +282,6 @@ async def download_media(
date = datetime.datetime.now()
media = message
if isinstance(media, str):
media = utils.resolve_bot_file_id(media)
if isinstance(media, _tl.MessageService):
if isinstance(message.action,
_tl.MessageActionChatEditPhoto):

View File

@ -3147,10 +3147,6 @@ class TelegramClient:
send the file as "external" media, and Telegram is the
one that will fetch the media and send it.
* A Bot API-like ``file_id``. You can convert previously
sent media to file IDs for later reusing with
`telethon.utils.pack_bot_file_id`.
* A handle to an existing file (for example, if you sent a
message with media before, you can use its ``message.media``
as a file here).

View File

@ -425,17 +425,13 @@ async def _file_to_media(
media = _tl.InputMediaPhotoExternal(file, ttl_seconds=ttl)
else:
media = _tl.InputMediaDocumentExternal(file, ttl_seconds=ttl)
else:
bot_file = utils.resolve_bot_file_id(file)
if bot_file:
media = utils.get_input_media(bot_file, ttl=ttl)
if media:
pass # Already have media, don't check the rest
elif not file_handle:
raise ValueError(
'Failed to convert {} to media. Not an existing file, '
'an HTTP URL or a valid bot-API-like file ID'.format(file)
'Failed to convert {} to media. Not an existing file or '
'HTTP URL'.format(file)
)
elif as_image:
media = _tl.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl)

View File

@ -856,11 +856,7 @@ def is_image(file):
"""
Returns `True` if the file extension looks like an image file to Telegram.
"""
match = re.match(r'\.(png|jpe?g)', _get_extension(file), re.IGNORECASE)
if match:
return True
else:
return isinstance(resolve_bot_file_id(file), _tl.Photo)
return bool(re.match(r'\.(png|jpe?g)', _get_extension(file), re.IGNORECASE))
def is_gif(file):
@ -1119,161 +1115,6 @@ def _encode_telegram_base64(string):
return None # not valid base64, not valid ascii, not a string
def resolve_bot_file_id(file_id):
"""
Given a Bot API-style `file_id <telethon.tl.custom.file.File.id>`,
returns the media it represents. If the `file_id <telethon.tl.custom.file.File.id>`
is not valid, `None` is returned instead.
Note that the `file_id <telethon.tl.custom.file.File.id>` does not have information
such as image dimensions or file size, so these will be zero if present.
For thumbnails, the photo ID and hash will always be zero.
"""
data = _rle_decode(_decode_telegram_base64(file_id))
if not data:
return None
# This isn't officially documented anywhere, but
# we assume the last byte is some kind of "version".
data, version = data[:-1], data[-1]
if version not in (2, 4):
return None
if (version == 2 and len(data) == 24) or (version == 4 and len(data) == 25):
if version == 2:
file_type, dc_id, media_id, access_hash = struct.unpack('<iiqq', data)
# elif version == 4:
else:
# TODO Figure out what the extra byte means
file_type, dc_id, media_id, access_hash, _ = struct.unpack('<iiqqb', data)
if not (1 <= dc_id <= 5):
# Valid `file_id`'s must have valid DC IDs. Since this method is
# called when sending a file and the user may have entered a path
# they believe is correct but the file doesn't exist, this method
# may detect a path as "valid" bot `file_id` even when it's not.
# By checking the `dc_id`, we greatly reduce the chances of this
# happening.
return None
attributes = []
if file_type == 3 or file_type == 9:
attributes.append(_tl.DocumentAttributeAudio(
duration=0,
voice=file_type == 3
))
elif file_type == 4 or file_type == 13:
attributes.append(_tl.DocumentAttributeVideo(
duration=0,
w=0,
h=0,
round_message=file_type == 13
))
# elif file_type == 5: # other, cannot know which
elif file_type == 8:
attributes.append(_tl.DocumentAttributeSticker(
alt='',
stickerset=_tl.InputStickerSetEmpty()
))
elif file_type == 10:
attributes.append(_tl.DocumentAttributeAnimated())
return _tl.Document(
id=media_id,
access_hash=access_hash,
date=None,
mime_type='',
size=0,
thumbs=None,
dc_id=dc_id,
attributes=attributes,
file_reference=b''
)
elif (version == 2 and len(data) == 44) or (version == 4 and len(data) in (49, 77)):
if version == 2:
(file_type, dc_id, media_id, access_hash,
volume_id, secret, local_id) = struct.unpack('<iiqqqqi', data)
# else version == 4:
elif len(data) == 49:
# TODO Figure out what the extra five bytes mean
(file_type, dc_id, media_id, access_hash,
volume_id, secret, local_id, _) = struct.unpack('<iiqqqqi5s', data)
elif len(data) == 77:
# See #1613.
(file_type, dc_id, _, media_id, access_hash, volume_id, _, local_id, _) = struct.unpack('<ii28sqqq12sib', data)
else:
return None
if not (1 <= dc_id <= 5):
return None
# Thumbnails (small) always have ID 0; otherwise size 'x'
photo_size = 's' if media_id or access_hash else 'x'
return _tl.Photo(
id=media_id,
access_hash=access_hash,
file_reference=b'',
date=None,
sizes=[_tl.PhotoSize(
type=photo_size,
w=0,
h=0,
size=0
)],
dc_id=dc_id,
has_stickers=None
)
def pack_bot_file_id(file):
"""
Inverse operation for `resolve_bot_file_id`.
The only parameters this method will accept are :tl:`Document` and
:tl:`Photo`, and it will return a variable-length ``file_id`` string.
If an invalid parameter is given, it will ``return None``.
"""
if isinstance(file, _tl.MessageMediaDocument):
file = file.document
elif isinstance(file, _tl.MessageMediaPhoto):
file = file.photo
if isinstance(file, _tl.Document):
file_type = 5
for attribute in file.attributes:
if isinstance(attribute, _tl.DocumentAttributeAudio):
file_type = 3 if attribute.voice else 9
elif isinstance(attribute, _tl.DocumentAttributeVideo):
file_type = 13 if attribute.round_message else 4
elif isinstance(attribute, _tl.DocumentAttributeSticker):
file_type = 8
elif isinstance(attribute, _tl.DocumentAttributeAnimated):
file_type = 10
else:
continue
break
return _encode_telegram_base64(_rle_encode(struct.pack(
'<iiqqb', file_type, file.dc_id, file.id, file.access_hash, 2)))
elif isinstance(file, _tl.Photo):
size = next((x for x in reversed(file.sizes) if isinstance(
x, (_tl.PhotoSize, _tl.PhotoCachedSize))), None)
if not size:
return None
size = size.location
return _encode_telegram_base64(_rle_encode(struct.pack(
'<iiqqqqib', 2, file.dc_id, file.id, file.access_hash,
size.volume_id, 0, size.local_id, 2 # 0 = old `secret`
)))
else:
return None
def resolve_invite_link(link):
"""
Resolves the given invite link. Returns a tuple of

View File

@ -18,21 +18,6 @@ class File:
def __init__(self, media):
self.media = media
@property
def id(self):
"""
The bot-API style ``file_id`` representing this file.
.. note::
This file ID may not work under user accounts,
but should still be usable by bot accounts.
You can, however, still use it to identify
a file in for example a database.
"""
return utils.pack_bot_file_id(self.media)
@property
def name(self):
"""