mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-02-18 04:20:57 +03:00
Officially remove bot_file_id support
This commit is contained in:
parent
3d36bb7b93
commit
9af8ec8cce
|
@ -178,6 +178,23 @@ The following modules have been moved inside ``_misc``:
|
||||||
// TODO review telethon/__init__.py isn't exposing more than it should
|
// 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
|
The custom.Message class and the way it is used has changed
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -282,9 +282,6 @@ async def download_media(
|
||||||
date = datetime.datetime.now()
|
date = datetime.datetime.now()
|
||||||
media = message
|
media = message
|
||||||
|
|
||||||
if isinstance(media, str):
|
|
||||||
media = utils.resolve_bot_file_id(media)
|
|
||||||
|
|
||||||
if isinstance(media, _tl.MessageService):
|
if isinstance(media, _tl.MessageService):
|
||||||
if isinstance(message.action,
|
if isinstance(message.action,
|
||||||
_tl.MessageActionChatEditPhoto):
|
_tl.MessageActionChatEditPhoto):
|
||||||
|
|
|
@ -3147,10 +3147,6 @@ class TelegramClient:
|
||||||
send the file as "external" media, and Telegram is the
|
send the file as "external" media, and Telegram is the
|
||||||
one that will fetch the media and send it.
|
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
|
* A handle to an existing file (for example, if you sent a
|
||||||
message with media before, you can use its ``message.media``
|
message with media before, you can use its ``message.media``
|
||||||
as a file here).
|
as a file here).
|
||||||
|
|
|
@ -425,17 +425,13 @@ async def _file_to_media(
|
||||||
media = _tl.InputMediaPhotoExternal(file, ttl_seconds=ttl)
|
media = _tl.InputMediaPhotoExternal(file, ttl_seconds=ttl)
|
||||||
else:
|
else:
|
||||||
media = _tl.InputMediaDocumentExternal(file, ttl_seconds=ttl)
|
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:
|
if media:
|
||||||
pass # Already have media, don't check the rest
|
pass # Already have media, don't check the rest
|
||||||
elif not file_handle:
|
elif not file_handle:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Failed to convert {} to media. Not an existing file, '
|
'Failed to convert {} to media. Not an existing file or '
|
||||||
'an HTTP URL or a valid bot-API-like file ID'.format(file)
|
'HTTP URL'.format(file)
|
||||||
)
|
)
|
||||||
elif as_image:
|
elif as_image:
|
||||||
media = _tl.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl)
|
media = _tl.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl)
|
||||||
|
|
|
@ -856,11 +856,7 @@ def is_image(file):
|
||||||
"""
|
"""
|
||||||
Returns `True` if the file extension looks like an image file to Telegram.
|
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)
|
return bool(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)
|
|
||||||
|
|
||||||
|
|
||||||
def is_gif(file):
|
def is_gif(file):
|
||||||
|
@ -1119,161 +1115,6 @@ def _encode_telegram_base64(string):
|
||||||
return None # not valid base64, not valid ascii, not a 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):
|
def resolve_invite_link(link):
|
||||||
"""
|
"""
|
||||||
Resolves the given invite link. Returns a tuple of
|
Resolves the given invite link. Returns a tuple of
|
||||||
|
|
|
@ -18,21 +18,6 @@ class File:
|
||||||
def __init__(self, media):
|
def __init__(self, media):
|
||||||
self.media = 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
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user