Handle Bytes as File Input (#2233)

* Handle bytes file input

* fix tests

* Docs, Tests & Rearrangements

* Use versioning directives

* fixing type hinting of send_photo

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
This commit is contained in:
Bibo-Joshi 2020-12-18 11:20:03 +01:00 committed by GitHub
parent 2d7a974b8f
commit 80b34811ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 215 additions and 96 deletions

View file

@ -27,7 +27,6 @@ import logging
from datetime import datetime
from typing import (
IO,
TYPE_CHECKING,
Any,
Callable,
@ -565,7 +564,7 @@ class Bot(TelegramObject):
def send_photo(
self,
chat_id: int,
photo: Union[str, PhotoSize, IO],
photo: Union[FileInput, PhotoSize],
caption: str = None,
disable_notification: bool = False,
reply_to_message_id: Union[int, str] = None,
@ -586,12 +585,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
photo (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
photo (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.PhotoSize`): Photo to send.
Pass a file_id as String to send a photo that exists on the Telegram servers
(recommended), pass an HTTP URL as a String for Telegram to get a photo from the
Internet, or upload a new photo using multipart/form-data. Lastly you can pass
an existing :class:`telegram.PhotoSize` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the photo, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -684,12 +686,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
audio (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
audio (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Audio`): Audio file to send.
Pass a file_id as String to send an audio file that exists on the Telegram servers
(recommended), pass an HTTP URL as a String for Telegram to get an audio file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Audio` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the audio, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -715,12 +720,15 @@ class Bot(TelegramObject):
reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
to remove reply keyboard or to force a reply from the user.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file
sent; can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
of the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
@ -794,12 +802,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
document (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
document (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Document`): File to send.
Pass a file_id as String to send a file that exists on the Telegram servers
(recommended), pass an HTTP URL as a String for Telegram to get a file from the
Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Document` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the document, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -822,12 +833,15 @@ class Bot(TelegramObject):
reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
to remove reply keyboard or to force a reply from the user.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file
sent; can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
of the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
@ -888,12 +902,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
sticker (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Sticker`): Sticker to send.
Pass a file_id as String to send a file that exists on the Telegram servers
(recommended), pass an HTTP URL as a String for Telegram to get a .webp file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Sticker` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
receive a notification with no sound.
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
@ -965,12 +982,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
video (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
video (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Video`): Video file to send.
Pass a file_id as String to send an video file that exists on the Telegram servers
(recommended), pass an HTTP URL as a String for Telegram to get an video file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Video` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the video, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -998,12 +1018,15 @@ class Bot(TelegramObject):
reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
to remove reply keyboard or to force a reply from the user.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file
sent; can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
of the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
@ -1078,12 +1101,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
video_note (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
video_note (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.VideoNote`): Video note
to send. Pass a file_id as String to send a video note that exists on the Telegram
servers (recommended) or upload a new video using multipart/form-data. Or you can
pass an existing :class:`telegram.VideoNote` object to send. Sending video notes by
a URL is currently unsupported.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the video note, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -1101,12 +1127,15 @@ class Bot(TelegramObject):
reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A
JSON-serialized object for an inline keyboard, custom reply keyboard,
instructions to remove reply keyboard or to force a reply from the user.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file
sent; can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
of the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
@ -1174,12 +1203,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
animation (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
animation (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Animation`): Animation to
send. Pass a file_id as String to send an animation that exists on the Telegram
servers (recommended), pass an HTTP URL as a String for Telegram to get an
animation from the Internet, or upload a new animation using multipart/form-data.
Lastly you can pass an existing :class:`telegram.Animation` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the animation, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -1188,12 +1220,15 @@ class Bot(TelegramObject):
duration (:obj:`int`, optional): Duration of sent animation in seconds.
width (:obj:`int`, optional): Animation width.
height (:obj:`int`, optional): Animation height.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file
sent; can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
of the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
caption (:obj:`str`, optional): Animation caption (may also be used when resending
animations by file_id), 0-1024 characters after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to
@ -1283,12 +1318,15 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
voice (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
voice (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Voice`): Voice file to send.
Pass a file_id as String to send an voice file that exists on the Telegram servers
(recommended), pass an HTTP URL as a String for Telegram to get an voice file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Voice` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the voice, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -3708,7 +3746,10 @@ class Bot(TelegramObject):
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
photo (`filelike object` | :class:`pathlib.Path`): New chat photo.
photo (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`): New chat photo.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
@ -3998,10 +4039,13 @@ class Bot(TelegramObject):
Args:
user_id (:obj:`int`): User identifier of sticker file owner.
png_sticker (:obj:`str` | `filelike object` | :class:`pathlib.Path`): Png image with
the sticker,
png_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`):
Png image with the sticker,
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
and either width or height must be exactly 512px.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during
creation of the connection pool).
@ -4057,18 +4101,24 @@ class Bot(TelegramObject):
must end in "_by_<bot username>". <bot_username> is case insensitive.
1-64 characters.
title (:obj:`str`): Sticker set title, 1-64 characters.
png_sticker (:obj:`str` | `filelike object` | :class:`pathlib.Path`, optional): Png
image with the sticker,
png_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \
optional): Png image with the sticker,
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
and either width or height must be exactly 512px. Pass a file_id as a String to
send a file that already exists on the Telegram servers, pass an HTTP URL as a
String for Telegram to get a file from the Internet, or upload a new one
using multipart/form-data.
tgs_sticker (:obj:`str` | `filelike object` | :class:`pathlib.Path`, optional): TGS
animation with the sticker,
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
tgs_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \
optional): TGS animation with the sticker,
uploaded using multipart/form-data. See
https://core.telegram.org/animated_stickers#technical-requirements for technical
requirements.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
emojis (:obj:`str`): One or more emoji corresponding to the sticker.
contains_masks (:obj:`bool`, optional): Pass :obj:`True`, if a set of mask stickers
should be created.
@ -4134,18 +4184,24 @@ class Bot(TelegramObject):
Args:
user_id (:obj:`int`): User identifier of created sticker set owner.
name (:obj:`str`): Sticker set name.
png_sticker (:obj:`str` | `filelike object` | :class:`pathlib.Path`, optional): PNG
image with the sticker,
png_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \
optional): PNG image with the sticker,
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
and either width or height must be exactly 512px. Pass a file_id as a String to
send a file that already exists on the Telegram servers, pass an HTTP URL as a
String for Telegram to get a file from the Internet, or upload a new one
using multipart/form-data.
tgs_sticker (:obj:`str` | `filelike object` | :class:`pathlib.Path`, optional): TGS
animation with the sticker,
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
tgs_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \
optional): TGS animation with the sticker,
uploaded using multipart/form-data. See
https://core.telegram.org/animated_stickers#technical-requirements for technical
requirements.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
emojis (:obj:`str`): One or more emoji corresponding to the sticker.
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
should be placed on faces.
@ -4252,8 +4308,8 @@ class Bot(TelegramObject):
Args:
name (:obj:`str`): Sticker set name
user_id (:obj:`int`): User identifier of created sticker set owner.
thumb (:obj:`str` | `filelike object` | :class:`pathlib.Path`, optional): A PNG image
with the thumbnail, must
thumb (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \
optional): A PNG image with the thumbnail, must
be up to 128 kilobytes in size and have width and height exactly 100px, or a TGS
animation with the thumbnail up to 32 kilobytes in size; see
https://core.telegram.org/animated_stickers#technical-requirements for animated
@ -4261,6 +4317,9 @@ class Bot(TelegramObject):
already exists on the Telegram servers, pass an HTTP URL as a String for Telegram
to get a file from the Internet, or upload a new one using multipart/form-data.
Animated sticker set thumbnail can't be uploaded via HTTP URL.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during
creation of the connection pool).

View file

@ -23,7 +23,7 @@ import imghdr
import logging
import mimetypes
import os
from typing import IO, Optional, Tuple
from typing import IO, Optional, Tuple, Union
from uuid import uuid4
DEFAULT_MIME_TYPE = 'application/octet-stream'
@ -39,7 +39,8 @@ class InputFile:
attach (:obj:`str`): Optional. Attach id for sending multiple files.
Args:
obj (:obj:`File handler`): An open file descriptor.
obj (:obj:`File handler` | :obj:`bytes`): An open file descriptor or the files content as
bytes.
filename (:obj:`str`, optional): Filename for this InputFile.
attach (:obj:`bool`, optional): Whether this should be send as one file or is part of a
collection of files.
@ -49,15 +50,18 @@ class InputFile:
"""
def __init__(self, obj: IO, filename: str = None, attach: bool = None):
def __init__(self, obj: Union[IO, bytes], filename: str = None, attach: bool = None):
self.filename = None
self.input_file_content = obj.read()
if isinstance(obj, bytes):
self.input_file_content = obj
else:
self.input_file_content = obj.read()
self.attach = 'attached' + uuid4().hex if attach else None
if filename:
self.filename = filename
elif hasattr(obj, 'name') and not isinstance(obj.name, int):
self.filename = os.path.basename(obj.name)
elif hasattr(obj, 'name') and not isinstance(obj.name, int): # type: ignore[union-attr]
self.filename = os.path.basename(obj.name) # type: ignore[union-attr]
image_mime_type = self.is_image(self.input_file_content)
if image_mime_type:

View file

@ -73,22 +73,28 @@ class InputMediaAnimation(InputMedia):
Args:
media (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Animation`): File to send. Pass a
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
:class:`telegram.Animation` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the animation, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
.. versionadded:: 13.1
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file sent;
can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
caption (:obj:`str`, optional): Caption of the animation to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
@ -155,11 +161,14 @@ class InputMediaPhoto(InputMedia):
entities that appear in the caption.
Args:
media (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.PhotoSize`): File to send. Pass a
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
:class:`telegram.PhotoSize` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the photo, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -209,11 +218,14 @@ class InputMediaVideo(InputMedia):
thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send.
Args:
media (:obj:`str` | `filelike object` | :class:`pathlib.Path` | :class:`telegram.Video`):
File to send. Pass a
media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Video`): File to send. Pass a
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
:class:`telegram.Video` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the video, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -231,13 +243,16 @@ class InputMediaVideo(InputMedia):
duration (:obj:`int`, optional): Video duration.
supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is
suitable for streaming.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file sent;
can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
Note:
* When using a :class:`telegram.Video` for the :attr:`media` attribute. It will take the
width, height and duration from that video, unless otherwise specified with the optional
@ -304,11 +319,15 @@ class InputMediaAudio(InputMedia):
thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send.
Args:
media (:obj:`str` | `filelike object` | :class:`pathlib.Path` | :class:`telegram.Audio`):
media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Audio`):
File to send. Pass a
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
:class:`telegram.Audio` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the audio, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -325,13 +344,16 @@ class InputMediaAudio(InputMedia):
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
tags.
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file sent;
can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
Note:
When using a :class:`telegram.Audio` for the :attr:`media` attribute. It will take the
duration, performer and title from that video, unless otherwise specified with the
@ -391,11 +413,14 @@ class InputMediaDocument(InputMedia):
the document is sent as part of an album.
Args:
media (:obj:`str` | `filelike object` | :class:`pathlib.Path` | \
media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Document`): File to send. Pass a
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
:class:`telegram.Document` object to send.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
filename (:obj:`str`, optional): Custom file name for the document, when uploading a
new file. Convenience parameter, useful e.g. when sending files generated by the
:obj:`tempfile` module.
@ -408,12 +433,15 @@ class InputMediaDocument(InputMedia):
in :class:`telegram.ParseMode` for the available modes.
caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special
entities that appear in the caption, which can be specified instead of parse_mode.
thumb (`filelike object` | :class:`pathlib.Path`, optional): Thumbnail of the file sent;
can be ignored if
thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
the file sent; can be ignored if
thumbnail generation for the file is supported server-side. The thumbnail should be
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
Thumbnails can't be reused and can be only uploaded as a new file.
.. versionchanged:: 13.2
Accept :obj:`bytes` as input.
disable_content_type_detection (:obj:`bool`, optional): Disables automatic server-side
content type detection for files uploaded using multipart/form-data. Always true, if
the document is sent as part of an album.

View file

@ -86,12 +86,13 @@ def parse_file_input(
adds the ``file://`` prefix. If the input is a relative path of a local file, computes the
absolute path and adds the ``file://`` prefix. Returns the input unchanged, otherwise.
* :class:`pathlib.Path` objects are treated the same way as strings.
* For IO input, returns an :class:`telegram.InputFile`.
* For IO and bytes input, returns an :class:`telegram.InputFile`.
* If :attr:`tg_type` is specified and the input is of that type, returns the ``file_id``
attribute.
Args:
file_input (:obj:`str` | `filelike object` | Telegram media object): The input to parse.
file_input (:obj:`str` | :obj:`bytes` | `filelike object` | Telegram media object): The
input to parse.
tg_type (:obj:`type`, optional): The Telegram media type the input can be. E.g.
:class:`telegram.Animation`.
attach (:obj:`bool`, optional): Whether this file should be send as one file or is part of
@ -111,10 +112,12 @@ def parse_file_input(
return file_input
if isinstance(file_input, (str, Path)):
if is_local_file(file_input):
out = f'file://{Path(file_input).absolute()}'
out = Path(file_input).absolute().as_uri()
else:
out = file_input # type: ignore[assignment]
return out
if isinstance(file_input, bytes):
return InputFile(file_input, attach=attach, filename=filename)
if InputFile.is_file(file_input):
file_input = cast(IO, file_input)
return InputFile(file_input, attach=attach, filename=filename)

View file

@ -26,9 +26,9 @@ if TYPE_CHECKING:
FileLike = Union[IO, 'InputFile']
"""Either an open file handler or a :class:`telegram.InputFile`."""
FileInput = Union[str, FileLike, Path]
"""Valid input for passing files to Telegram. Either a file id as string, a file like object or
a local file path as string or :class:`pathlib.Path`."""
FileInput = Union[str, bytes, FileLike, Path]
"""Valid input for passing files to Telegram. Either a file id as string, a file like object,
a local file path as string, :class:`pathlib.Path` or the file contents as :obj:`bytes`."""
JSONDict = Dict[str, Any]
"""Dictionary containing response from Telegram or data to send to the API."""

View file

@ -193,11 +193,12 @@ class TestAnimation:
def test_send_animation_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
print(data.get('animation'), expected)
test_flag = data.get('animation') == expected and data.get('thumb') == expected
monkeypatch.setattr(bot, '_post', make_assertion)

View file

@ -215,7 +215,7 @@ class TestAudio:
def test_send_audio_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -1477,7 +1477,7 @@ class TestBot:
def test_set_chat_photo_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -240,7 +240,7 @@ class TestDocument:
def test_send_document_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -279,17 +279,17 @@ class TestHelpers:
@pytest.mark.parametrize(
'string,expected',
[
('tests/data/game.gif', f"file://{Path.cwd() / 'tests' / 'data' / 'game.gif'}"),
('tests/data/game.gif', (Path.cwd() / 'tests' / 'data' / 'game.gif').as_uri()),
('tests/data', 'tests/data'),
('file://foobar', 'file://foobar'),
(
str(Path.cwd() / 'tests' / 'data' / 'game.gif'),
f"file://{Path.cwd() / 'tests' / 'data' / 'game.gif'}",
(Path.cwd() / 'tests' / 'data' / 'game.gif').as_uri(),
),
(str(Path.cwd() / 'tests' / 'data'), str(Path.cwd() / 'tests' / 'data')),
(
Path.cwd() / 'tests' / 'data' / 'game.gif',
f"file://{Path.cwd() / 'tests' / 'data' / 'game.gif'}",
(Path.cwd() / 'tests' / 'data' / 'game.gif').as_uri(),
),
(Path.cwd() / 'tests' / 'data', Path.cwd() / 'tests' / 'data'),
(
@ -316,6 +316,21 @@ class TestHelpers:
assert parsed.attach
assert parsed.filename == 'test_file'
def test_parse_file_input_bytes(self):
with open('tests/data/text_file.txt', 'rb') as file:
parsed = helpers.parse_file_input(file.read())
assert isinstance(parsed, InputFile)
assert not parsed.attach
assert parsed.filename == 'application.octet-stream'
with open('tests/data/text_file.txt', 'rb') as file:
parsed = helpers.parse_file_input(file.read(), attach=True, filename='test_file')
assert isinstance(parsed, InputFile)
assert parsed.attach
assert parsed.filename == 'test_file'
def test_parse_file_input_tg_object(self):
animation = Animation('file_id', 'unique_id', 1, 1, 1)
assert helpers.parse_file_input(animation, Animation) == 'file_id'

View file

@ -115,3 +115,14 @@ class TestInputFile:
InputFile(MockedFileobject('tests/data/telegram'), filename='blah.jpg').filename
== 'blah.jpg'
)
def test_send_bytes(self, bot, chat_id):
# We test this here and not at the respective test modules because it's not worth
# duplicating the test for the different methods
with open('tests/data/text_file.txt', 'rb') as file:
message = bot.send_document(chat_id, file.read())
out = BytesIO()
assert message.document.get_file().download(out=out)
out.seek(0)
assert out.read().decode('utf-8') == 'PTB Rocks!'

View file

@ -173,8 +173,8 @@ class TestInputMediaVideo:
input_media_video = InputMediaVideo(
'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg'
)
assert input_media_video.media == f"file://{Path.cwd() / 'tests/data/telegram.mp4'}"
assert input_media_video.thumb == f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
assert input_media_video.media == (Path.cwd() / 'tests/data/telegram.mp4/').as_uri()
assert input_media_video.thumb == (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
class TestInputMediaPhoto:
@ -217,7 +217,7 @@ class TestInputMediaPhoto:
def test_with_local_files(self):
input_media_photo = InputMediaPhoto('tests/data/telegram.mp4')
assert input_media_photo.media == f"file://{Path.cwd() / 'tests/data/telegram.mp4'}"
assert input_media_photo.media == (Path.cwd() / 'tests/data/telegram.mp4/').as_uri()
class TestInputMediaAnimation:
@ -269,12 +269,8 @@ class TestInputMediaAnimation:
input_media_animation = InputMediaAnimation(
'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg'
)
assert input_media_animation.media == 'file://' + str(
Path.cwd() / 'tests/data/telegram.mp4'
)
assert input_media_animation.thumb == 'file://' + str(
Path.cwd() / 'tests/data/telegram.jpg'
)
assert input_media_animation.media == (Path.cwd() / 'tests/data/telegram.mp4').as_uri()
assert input_media_animation.thumb == (Path.cwd() / 'tests/data/telegram.jpg').as_uri()
class TestInputMediaAudio:
@ -332,8 +328,8 @@ class TestInputMediaAudio:
input_media_audio = InputMediaAudio(
'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg'
)
assert input_media_audio.media == f"file://{Path.cwd() / 'tests/data/telegram.mp4'}"
assert input_media_audio.thumb == f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
assert input_media_audio.media == (Path.cwd() / 'tests/data/telegram.mp4/').as_uri()
assert input_media_audio.thumb == (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
class TestInputMediaDocument:
@ -388,12 +384,8 @@ class TestInputMediaDocument:
input_media_document = InputMediaDocument(
'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg'
)
assert input_media_document.media == 'file://' + str(
Path.cwd() / 'tests/data/telegram.mp4'
)
assert input_media_document.thumb == 'file://' + str(
Path.cwd() / 'tests/data/telegram.jpg'
)
assert input_media_document.media == (Path.cwd() / 'tests/data/telegram.mp4').as_uri()
assert input_media_document.thumb == (Path.cwd() / 'tests/data/telegram.jpg').as_uri()
@pytest.fixture(scope='function') # noqa: F811
@ -486,16 +478,22 @@ class TestSendMediaGroup:
self, bot, chat_id, video_file, photo_file, animation_file # noqa: F811
): # noqa: F811
def func():
return bot.send_media_group(
chat_id, [InputMediaVideo(video_file), InputMediaPhoto(photo_file)]
)
with open('tests/data/telegram.jpg', 'rb') as file:
return bot.send_media_group(
chat_id,
[
InputMediaVideo(video_file),
InputMediaPhoto(photo_file),
InputMediaPhoto(file.read()),
],
)
messages = expect_bad_request(
func, 'Type of file mismatch', 'Telegram did not accept the file.'
)
assert isinstance(messages, list)
assert len(messages) == 2
assert len(messages) == 3
assert all([isinstance(mes, Message) for mes in messages])
assert all([mes.media_group_id == messages[0].media_group_id for mes in messages])

View file

@ -232,7 +232,7 @@ class TestPhoto:
def test_send_photo_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -207,7 +207,7 @@ class TestSticker:
def test_send_sticker_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):
@ -449,7 +449,7 @@ class TestStickerSet:
def test_upload_sticker_file_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):
@ -463,7 +463,7 @@ class TestStickerSet:
def test_create_new_sticker_set_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):
@ -479,7 +479,7 @@ class TestStickerSet:
def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):
@ -493,7 +493,7 @@ class TestStickerSet:
def test_set_sticker_set_thumb_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -231,7 +231,7 @@ class TestVideo:
def test_send_video_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -164,7 +164,7 @@ class TestVideoNote:
def test_send_video_note_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):

View file

@ -193,7 +193,7 @@ class TestVoice:
def test_send_voice_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
expected = f"file://{Path.cwd() / 'tests/data/telegram.jpg'}"
expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri()
file = 'tests/data/telegram.jpg'
def make_assertion(_, data, *args, **kwargs):