Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 101 additions & 157 deletions telegram/bot.py

Large diffs are not rendered by default.

45 changes: 34 additions & 11 deletions telegram/files/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram File."""
import os
import shutil
import urllib.parse as urllib_parse
from base64 import b64decode
from os.path import basename
from typing import IO, TYPE_CHECKING, Any, Optional, Union

from telegram import TelegramObject
from telegram.passport.credentials import decrypt
from telegram.utils.helpers import is_local_file

if TYPE_CHECKING:
from telegram import Bot, FileCredentials
Expand Down Expand Up @@ -98,7 +100,10 @@ def download(
the ``out.write`` method.

Note:
:attr:`custom_path` and :attr:`out` are mutually exclusive.
* :attr:`custom_path` and :attr:`out` are mutually exclusive.
* If neither :attr:`custom_path` nor :attr:`out` is provided and :attr:`file_path` is
the path of a local file (which is the case when a Bot API Server is running in
local mode), this method will just return the path.

Args:
custom_path (:obj:`str`, optional): Custom path.
Expand All @@ -110,7 +115,7 @@ def download(

Returns:
:obj:`str` | :obj:`io.BufferedWriter`: The same object as :attr:`out` if specified.
Otherwise, returns the filename downloaded to.
Otherwise, returns the filename downloaded to or the file path of the local file.

Raises:
ValueError: If both :attr:`custom_path` and :attr:`out` are passed.
Expand All @@ -119,20 +124,35 @@ def download(
if custom_path is not None and out is not None:
raise ValueError('custom_path and out are mutually exclusive')

# Convert any UTF-8 char into a url encoded ASCII string.
url = self._get_encoded_url()
local_file = is_local_file(self.file_path)

if local_file:
url = self.file_path
else:
# Convert any UTF-8 char into a url encoded ASCII string.
url = self._get_encoded_url()

if out:
buf = self.bot.request.retrieve(url)
if self._credentials:
buf = decrypt(
b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf
)
if local_file:
with open(url, 'rb') as file:
buf = file.read()
else:
buf = self.bot.request.retrieve(url)
if self._credentials:
buf = decrypt(
b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf
)
out.write(buf)
return out

if custom_path and local_file:
shutil.copyfile(self.file_path, custom_path)
return custom_path

if custom_path:
filename = custom_path
elif local_file:
return self.file_path
elif self.file_path:
filename = basename(self.file_path)
else:
Expand Down Expand Up @@ -169,8 +189,11 @@ def download_as_bytearray(self, buf: bytearray = None) -> bytes:
"""
if buf is None:
buf = bytearray()

buf.extend(self.bot.request.retrieve(self._get_encoded_url()))
if is_local_file(self.file_path):
with open(self.file_path, "rb") as file:
buf.extend(file.read())
else:
buf.extend(self.bot.request.retrieve(self._get_encoded_url()))
return buf

def set_credentials(self, credentials: 'FileCredentials') -> None:
Expand Down
108 changes: 39 additions & 69 deletions telegram/files/inputmedia.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram InputMedia Objects."""

from typing import IO, Union, cast, List, Tuple
from typing import Union, List, Tuple

from telegram import (
Animation,
Expand All @@ -30,8 +30,8 @@
Video,
MessageEntity,
)
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue
from telegram.utils.types import FileLike, JSONDict
from telegram.utils.helpers import DEFAULT_NONE, DefaultValue, parse_file_input
from telegram.utils.types import FileInput, JSONDict


class InputMedia(TelegramObject):
Expand Down Expand Up @@ -73,11 +73,13 @@ class InputMediaAnimation(InputMedia):


Args:
media (:obj:`str` | `filelike object` | :class:`telegram.Animation`): File to send. Pass a
media (:obj:`str` | `filelike object` | :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.
thumb (`filelike object`, optional): Thumbnail of the file sent; can be ignored if
thumb (`filelike object` | :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.
Expand All @@ -101,8 +103,8 @@ class InputMediaAnimation(InputMedia):

def __init__(
self,
media: Union[str, FileLike, Animation],
thumb: FileLike = None,
media: Union[FileInput, Animation],
thumb: FileInput = None,
caption: str = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
width: int = None,
Expand All @@ -117,18 +119,11 @@ def __init__(
self.width = media.width
self.height = media.height
self.duration = media.duration
elif InputFile.is_file(media):
media = cast(IO, media)
self.media = InputFile(media, attach=True)
else:
self.media = media # type: ignore[assignment]
self.media = parse_file_input(media, attach=True)

if thumb:
if InputFile.is_file(thumb):
thumb = cast(IO, thumb)
self.thumb = InputFile(thumb, attach=True)
else:
self.thumb = thumb # type: ignore[assignment]
self.thumb = parse_file_input(thumb, attach=True)

if caption:
self.caption = caption
Expand All @@ -154,7 +149,8 @@ class InputMediaPhoto(InputMedia):
entities that appear in the caption.

Args:
media (:obj:`str` | `filelike object` | :class:`telegram.PhotoSize`): File to send. Pass a
media (:obj:`str` | `filelike object` | :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.
Expand All @@ -169,20 +165,13 @@ class InputMediaPhoto(InputMedia):

def __init__(
self,
media: Union[str, FileLike, PhotoSize],
media: Union[FileInput, PhotoSize],
caption: str = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
):
self.type = 'photo'

if isinstance(media, PhotoSize):
self.media: Union[str, InputFile] = media.file_id
elif InputFile.is_file(media):
media = cast(IO, media)
self.media = InputFile(media, attach=True)
else:
self.media = media # type: ignore[assignment]
self.media = parse_file_input(media, PhotoSize, attach=True)

if caption:
self.caption = caption
Expand All @@ -208,7 +197,8 @@ class InputMediaVideo(InputMedia):
thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send.

Args:
media (:obj:`str` | `filelike object` | :class:`telegram.Video`): File to send. Pass a
media (:obj:`str` | `filelike object` | :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.
Expand All @@ -224,7 +214,8 @@ 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`, optional): Thumbnail of the file sent; can be ignored if
thumb (`filelike object` | :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.
Expand All @@ -241,14 +232,14 @@ class InputMediaVideo(InputMedia):

def __init__(
self,
media: Union[str, FileLike, Video],
media: Union[FileInput, Video],
caption: str = None,
width: int = None,
height: int = None,
duration: int = None,
supports_streaming: bool = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
thumb: FileLike = None,
thumb: FileInput = None,
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
):
self.type = 'video'
Expand All @@ -258,18 +249,11 @@ def __init__(
self.width = media.width
self.height = media.height
self.duration = media.duration
elif InputFile.is_file(media):
media = cast(IO, media)
self.media = InputFile(media, attach=True)
else:
self.media = media # type: ignore[assignment]
self.media = parse_file_input(media, attach=True)

if thumb:
if InputFile.is_file(thumb):
thumb = cast(IO, thumb)
self.thumb = InputFile(thumb, attach=True)
else:
self.thumb = thumb # type: ignore[assignment]
self.thumb = parse_file_input(thumb, attach=True)

if caption:
self.caption = caption
Expand Down Expand Up @@ -302,7 +286,8 @@ class InputMediaAudio(InputMedia):
thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send.

Args:
media (:obj:`str` | `filelike object` | :class:`telegram.Audio`): File to send. Pass a
media (:obj:`str` | `filelike object` | :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.
Expand All @@ -317,7 +302,8 @@ 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`, optional): Thumbnail of the file sent; can be ignored if
thumb (`filelike object` | :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.
Expand All @@ -331,8 +317,8 @@ class InputMediaAudio(InputMedia):

def __init__(
self,
media: Union[str, FileLike, Audio],
thumb: FileLike = None,
media: Union[FileInput, Audio],
thumb: FileInput = None,
caption: str = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
duration: int = None,
Expand All @@ -347,18 +333,11 @@ def __init__(
self.duration = media.duration
self.performer = media.performer
self.title = media.title
elif InputFile.is_file(media):
media = cast(IO, media)
self.media = InputFile(media, attach=True)
else:
self.media = media # type: ignore[assignment]
self.media = parse_file_input(media, attach=True)

if thumb:
if InputFile.is_file(thumb):
thumb = cast(IO, thumb)
self.thumb = InputFile(thumb, attach=True)
else:
self.thumb = thumb # type: ignore[assignment]
self.thumb = parse_file_input(thumb, attach=True)

if caption:
self.caption = caption
Expand Down Expand Up @@ -388,7 +367,8 @@ class InputMediaDocument(InputMedia):
the document is sent as part of an album.

Args:
media (:obj:`str` | `filelike object` | :class:`telegram.Document`): File to send. Pass a
media (:obj:`str` | `filelike object` | :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.
Expand All @@ -399,7 +379,8 @@ 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`, optional): Thumbnail of the file sent; can be ignored if
thumb (`filelike object` | :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.
Expand All @@ -411,29 +392,18 @@ class InputMediaDocument(InputMedia):

def __init__(
self,
media: Union[str, FileLike, Document],
thumb: FileLike = None,
media: Union[FileInput, Document],
thumb: FileInput = None,
caption: str = None,
parse_mode: Union[str, DefaultValue] = DEFAULT_NONE,
disable_content_type_detection: bool = None,
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
):
self.type = 'document'

if isinstance(media, Document):
self.media: Union[str, InputFile] = media.file_id
elif InputFile.is_file(media):
media = cast(IO, media)
self.media = InputFile(media, attach=True)
else:
self.media = media # type: ignore[assignment]
self.media = parse_file_input(media, Document, attach=True)

if thumb:
if InputFile.is_file(thumb):
thumb = cast(IO, thumb)
self.thumb = InputFile(thumb, attach=True)
else:
self.thumb = thumb # type: ignore[assignment]
self.thumb = parse_file_input(thumb, attach=True)

if caption:
self.caption = caption
Expand Down
Loading