init commit
Some checks failed
continuous-integration/drone Build is failing

This commit is contained in:
2025-09-04 01:51:59 +09:00
commit aca280b64d
1841 changed files with 753304 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Common base class for media objects"""
from typing import TYPE_CHECKING, Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.types import JSONDict, ODVInput
if TYPE_CHECKING:
from telegram import File
class _BaseMedium(TelegramObject):
"""Base class for objects representing the various media file types.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`, optional): File size.
Attributes:
file_id (:obj:`str`): File identifier.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`): Optional. File size.
"""
__slots__ = ("file_id", "file_size", "file_unique_id")
def __init__(
self,
file_id: str,
file_unique_id: str,
file_size: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.file_id: str = str(file_id)
self.file_unique_id: str = str(file_unique_id)
# Optionals
self.file_size: Optional[int] = file_size
self._id_attrs = (self.file_unique_id,)
async def get_file(
self,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> "File":
"""Convenience wrapper over :meth:`telegram.Bot.get_file`
For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.error.TelegramError`
"""
return await self.get_bot().get_file(
file_id=self.file_id,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Common base class for media objects with thumbnails"""
from typing import TYPE_CHECKING, Optional, TypeVar
from telegram._files._basemedium import _BaseMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.argumentparsing import de_json_optional
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
# pylint: disable=invalid-name
ThumbedMT_co = TypeVar("ThumbedMT_co", bound="_BaseThumbedMedium", covariant=True)
class _BaseThumbedMedium(_BaseMedium):
"""
Base class for objects representing the various media file types that may include a thumbnail.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`, optional): File size.
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by the sender.
.. versionadded:: 20.2
Attributes:
file_id (:obj:`str`): File identifier.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`): Optional. File size.
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail as defined by the sender.
.. versionadded:: 20.2
"""
__slots__ = ("thumbnail",)
def __init__(
self,
file_id: str,
file_unique_id: str,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
api_kwargs=api_kwargs,
)
self.thumbnail: Optional[PhotoSize] = thumbnail
@classmethod
def de_json(
cls: type[ThumbedMT_co], data: JSONDict, bot: Optional["Bot"] = None
) -> ThumbedMT_co:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
# In case this wasn't already done by the subclass
if not isinstance(data.get("thumbnail"), PhotoSize):
data["thumbnail"] = de_json_optional(data.get("thumbnail"), PhotoSize, bot)
api_kwargs = {}
# This is a deprecated field that TG still returns for backwards compatibility
# Let's filter it out to speed up the de-json process
if data.get("thumb") is not None:
api_kwargs["thumb"] = data.pop("thumb")
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)

View File

@@ -0,0 +1,166 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains objects that represent paid media in Telegram."""
import datetime as dtm
from typing import Final, Optional, Union
from telegram import constants
from telegram._files.inputfile import InputFile
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.files import parse_file_input
from telegram._utils.types import FileInput, JSONDict
class InputStoryContent(TelegramObject):
"""This object describes the content of a story to post. Currently, it can be one of:
* :class:`telegram.InputStoryContentPhoto`
* :class:`telegram.InputStoryContentVideo`
.. versionadded:: 22.1
Args:
type (:obj:`str`): Type of the content.
Attributes:
type (:obj:`str`): Type of the content.
"""
__slots__ = ("type",)
PHOTO: Final[str] = constants.InputStoryContentType.PHOTO
""":const:`telegram.constants.InputStoryContentType.PHOTO`"""
VIDEO: Final[str] = constants.InputStoryContentType.VIDEO
""":const:`telegram.constants.InputStoryContentType.VIDEO`"""
def __init__(
self,
type: str, # pylint: disable=redefined-builtin
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
super().__init__(api_kwargs=api_kwargs)
self.type: str = enum.get_member(constants.InputStoryContentType, type, type)
self._freeze()
@staticmethod
def _parse_file_input(file_input: FileInput) -> Union[str, InputFile]:
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
return parse_file_input(file_input, attach=True, local_mode=True)
class InputStoryContentPhoto(InputStoryContent):
"""Describes a photo to post as a story.
.. versionadded:: 22.1
Args:
photo (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): The photo to post as a story. The photo must be of the
size :tg-const:`telegram.constants.InputStoryContentLimit.PHOTO_WIDTH`
x :tg-const:`telegram.constants.InputStoryContentLimit.PHOTO_HEIGHT` and must not
exceed :tg-const:`telegram.constants.InputStoryContentLimit.PHOTOSIZE_UPLOAD` MB.
|uploadinputnopath|.
Attributes:
type (:obj:`str`): Type of the content, must be :attr:`~telegram.InputStoryContent.PHOTO`.
photo (:class:`telegram.InputFile`): The photo to post as a story. The photo must be of the
size :tg-const:`telegram.constants.InputStoryContentLimit.PHOTO_WIDTH`
x :tg-const:`telegram.constants.InputStoryContentLimit.PHOTO_HEIGHT` and must not
exceed :tg-const:`telegram.constants.InputStoryContentLimit.PHOTOSIZE_UPLOAD` MB.
"""
__slots__ = ("photo",)
def __init__(
self,
photo: FileInput,
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
super().__init__(type=InputStoryContent.PHOTO, api_kwargs=api_kwargs)
with self._unfrozen():
self.photo: Union[str, InputFile] = self._parse_file_input(photo)
class InputStoryContentVideo(InputStoryContent):
"""
Describes a video to post as a story.
.. versionadded:: 22.1
Args:
video (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): The video to post as a story. The video must be of
the size :tg-const:`telegram.constants.InputStoryContentLimit.VIDEO_WIDTH`
x :tg-const:`telegram.constants.InputStoryContentLimit.VIDEO_HEIGHT`,
streamable, encoded with ``H.265`` codec, with key frames added
each second in the ``MPEG4`` format, and must not exceed
:tg-const:`telegram.constants.InputStoryContentLimit.VIDEOSIZE_UPLOAD` MB.
|uploadinputnopath|.
duration (:class:`datetime.timedelta` | :obj:`int` | :obj:`float`, optional): Precise
duration of the video in seconds;
0-:tg-const:`telegram.constants.InputStoryContentLimit.MAX_VIDEO_DURATION`
cover_frame_timestamp (:class:`datetime.timedelta` | :obj:`int` | :obj:`float`, optional):
Timestamp in seconds of the frame that will be used as the static cover for the story.
Defaults to ``0.0``.
is_animation (:obj:`bool`, optional): Pass :obj:`True` if the video has no sound
Attributes:
type (:obj:`str`): Type of the content, must be :attr:`~telegram.InputStoryContent.VIDEO`.
video (:class:`telegram.InputFile`): The video to post as a story. The video must be of
the size :tg-const:`telegram.constants.InputStoryContentLimit.VIDEO_WIDTH`
x :tg-const:`telegram.constants.InputStoryContentLimit.VIDEO_HEIGHT`,
streamable, encoded with ``H.265`` codec, with key frames added
each second in the ``MPEG4`` format, and must not exceed
:tg-const:`telegram.constants.InputStoryContentLimit.VIDEOSIZE_UPLOAD` MB.
duration (:class:`datetime.timedelta`): Optional. Precise duration of the video in seconds;
0-:tg-const:`telegram.constants.InputStoryContentLimit.MAX_VIDEO_DURATION`
cover_frame_timestamp (:class:`datetime.timedelta`): Optional. Timestamp in seconds of the
frame that will be used as the static cover for the story. Defaults to ``0.0``.
is_animation (:obj:`bool`): Optional. Pass :obj:`True` if the video has no sound
"""
__slots__ = ("cover_frame_timestamp", "duration", "is_animation", "video")
def __init__(
self,
video: FileInput,
duration: Optional[Union[float, dtm.timedelta]] = None,
cover_frame_timestamp: Optional[Union[float, dtm.timedelta]] = None,
is_animation: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
super().__init__(type=InputStoryContent.VIDEO, api_kwargs=api_kwargs)
with self._unfrozen():
self.video: Union[str, InputFile] = self._parse_file_input(video)
self.duration: Optional[dtm.timedelta] = to_timedelta(duration)
self.cover_frame_timestamp: Optional[dtm.timedelta] = to_timedelta(
cover_frame_timestamp
)
self.is_animation: Optional[bool] = is_animation

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Animation."""
import datetime as dtm
from typing import Optional, Union
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.types import JSONDict, TimePeriod
class Animation(_BaseThumbedMedium):
"""This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound).
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by the sender.
height (:obj:`int`): Video height as defined by the sender.
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of the video
in seconds as defined by the sender.
.. versionchanged:: v22.2
|time-period-input|
file_name (:obj:`str`, optional): Original animation filename as defined by the sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
file_size (:obj:`int`, optional): File size in bytes.
thumbnail (:class:`telegram.PhotoSize`, optional): Animation thumbnail as defined by
sender.
.. versionadded:: 20.2
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by the sender.
height (:obj:`int`): Video height as defined by the sender.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the video in seconds
as defined by the sender.
.. deprecated:: v22.2
|time-period-int-deprecated|
file_name (:obj:`str`): Optional. Original animation filename as defined by the sender.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
file_size (:obj:`int`): Optional. File size in bytes.
thumbnail (:class:`telegram.PhotoSize`): Optional. Animation thumbnail as defined by
sender.
.. versionadded:: 20.2
"""
__slots__ = ("_duration", "file_name", "height", "mime_type", "width")
def __init__(
self,
file_id: str,
file_unique_id: str,
width: int,
height: int,
duration: TimePeriod,
file_name: Optional[str] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
api_kwargs=api_kwargs,
thumbnail=thumbnail,
)
with self._unfrozen():
# Required
self.width: int = width
self.height: int = height
self._duration: dtm.timedelta = to_timedelta(duration)
# Optional
self.mime_type: Optional[str] = mime_type
self.file_name: Optional[str] = file_name
@property
def duration(self) -> Union[int, dtm.timedelta]:
return get_timedelta_value( # type: ignore[return-value]
self._duration, attribute="duration"
)

View File

@@ -0,0 +1,121 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Audio."""
import datetime as dtm
from typing import Optional, Union
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.types import JSONDict, TimePeriod
class Audio(_BaseThumbedMedium):
"""This object represents an audio file to be treated as music by the Telegram clients.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
the same over time and for different bots. Can't be used to download or reuse the file.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the audio in
seconds as defined by the sender.
.. versionchanged:: v22.2
|time-period-input|
performer (:obj:`str`, optional): Performer of the audio as defined by the sender or by
audio tags.
title (:obj:`str`, optional): Title of the audio as defined by the sender or by audio tags.
file_name (:obj:`str`, optional): Original filename as defined by the sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
file_size (:obj:`int`, optional): File size in bytes.
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to
which the music file belongs.
.. versionadded:: 20.2
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
the same over time and for different bots. Can't be used to download or reuse the file.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the audio in seconds as
defined by the sender.
.. deprecated:: v22.2
|time-period-int-deprecated|
performer (:obj:`str`): Optional. Performer of the audio as defined by the sender or by
audio tags.
title (:obj:`str`): Optional. Title of the audio as defined by the sender or by audio tags.
file_name (:obj:`str`): Optional. Original filename as defined by the sender.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
file_size (:obj:`int`): Optional. File size in bytes.
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail of the album cover to
which the music file belongs.
.. versionadded:: 20.2
"""
__slots__ = ("_duration", "file_name", "mime_type", "performer", "title")
def __init__(
self,
file_id: str,
file_unique_id: str,
duration: TimePeriod,
performer: Optional[str] = None,
title: Optional[str] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
file_name: Optional[str] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
thumbnail=thumbnail,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Required
self._duration: dtm.timedelta = to_timedelta(duration)
# Optional
self.performer: Optional[str] = performer
self.title: Optional[str] = title
self.mime_type: Optional[str] = mime_type
self.file_name: Optional[str] = file_name
@property
def duration(self) -> Union[int, dtm.timedelta]:
return get_timedelta_value( # type: ignore[return-value]
self._duration, attribute="duration"
)

View File

@@ -0,0 +1,176 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatPhoto."""
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.types import JSONDict, ODVInput
if TYPE_CHECKING:
from telegram import File
class ChatPhoto(TelegramObject):
"""This object represents a chat photo.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`small_file_unique_id` and :attr:`big_file_unique_id` are
equal.
Args:
small_file_id (:obj:`str`): File identifier of small
(:tg-const:`telegram.ChatPhoto.SIZE_SMALL` x :tg-const:`telegram.ChatPhoto.SIZE_SMALL`)
chat photo. This file_id can be used only for photo download and only for as long
as the photo is not changed.
small_file_unique_id (:obj:`str`): Unique file identifier of small
(:tg-const:`telegram.ChatPhoto.SIZE_SMALL` x :tg-const:`telegram.ChatPhoto.SIZE_SMALL`)
chat photo, which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
big_file_id (:obj:`str`): File identifier of big
(:tg-const:`telegram.ChatPhoto.SIZE_BIG` x :tg-const:`telegram.ChatPhoto.SIZE_BIG`)
chat photo. This file_id can be used only for photo download and only for as long as
the photo is not changed.
big_file_unique_id (:obj:`str`): Unique file identifier of big
(:tg-const:`telegram.ChatPhoto.SIZE_BIG` x :tg-const:`telegram.ChatPhoto.SIZE_BIG`)
chat photo, which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
Attributes:
small_file_id (:obj:`str`): File identifier of small
(:tg-const:`telegram.ChatPhoto.SIZE_SMALL` x :tg-const:`telegram.ChatPhoto.SIZE_SMALL`)
chat photo. This file_id can be used only for photo download and only for as long
as the photo is not changed.
small_file_unique_id (:obj:`str`): Unique file identifier of small
(:tg-const:`telegram.ChatPhoto.SIZE_SMALL` x :tg-const:`telegram.ChatPhoto.SIZE_SMALL`)
chat photo, which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
big_file_id (:obj:`str`): File identifier of big
(:tg-const:`telegram.ChatPhoto.SIZE_BIG` x :tg-const:`telegram.ChatPhoto.SIZE_BIG`)
chat photo. This file_id can be used only for photo download and only for as long as
the photo is not changed.
big_file_unique_id (:obj:`str`): Unique file identifier of big
(:tg-const:`telegram.ChatPhoto.SIZE_BIG` x :tg-const:`telegram.ChatPhoto.SIZE_BIG`)
chat photo, which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
"""
__slots__ = (
"big_file_id",
"big_file_unique_id",
"small_file_id",
"small_file_unique_id",
)
def __init__(
self,
small_file_id: str,
small_file_unique_id: str,
big_file_id: str,
big_file_unique_id: str,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.small_file_id: str = small_file_id
self.small_file_unique_id: str = small_file_unique_id
self.big_file_id: str = big_file_id
self.big_file_unique_id: str = big_file_unique_id
self._id_attrs = (
self.small_file_unique_id,
self.big_file_unique_id,
)
self._freeze()
async def get_small_file(
self,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> "File":
"""Convenience wrapper over :meth:`telegram.Bot.get_file` for getting the small
(:tg-const:`telegram.ChatPhoto.SIZE_SMALL` x :tg-const:`telegram.ChatPhoto.SIZE_SMALL`)
chat photo
For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.error.TelegramError`
"""
return await self.get_bot().get_file(
file_id=self.small_file_id,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
async def get_big_file(
self,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> "File":
"""Convenience wrapper over :meth:`telegram.Bot.get_file` for getting the
big (:tg-const:`telegram.ChatPhoto.SIZE_BIG` x :tg-const:`telegram.ChatPhoto.SIZE_BIG`)
chat photo
For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`.
Returns:
:class:`telegram.File`
Raises:
:class:`telegram.error.TelegramError`
"""
return await self.get_bot().get_file(
file_id=self.big_file_id,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
SIZE_SMALL: Final[int] = constants.ChatPhotoSize.SMALL
""":const:`telegram.constants.ChatPhotoSize.SMALL`
.. versionadded:: 20.0
"""
SIZE_BIG: Final[int] = constants.ChatPhotoSize.BIG
""":const:`telegram.constants.ChatPhotoSize.BIG`
.. versionadded:: 20.0
"""

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Contact."""
from typing import Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
class Contact(TelegramObject):
"""This object represents a phone contact.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`phone_number` is equal.
Args:
phone_number (:obj:`str`): Contact's phone number.
first_name (:obj:`str`): Contact's first name.
last_name (:obj:`str`, optional): Contact's last name.
user_id (:obj:`int`, optional): Contact's user identifier in Telegram.
vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard.
Attributes:
phone_number (:obj:`str`): Contact's phone number.
first_name (:obj:`str`): Contact's first name.
last_name (:obj:`str`): Optional. Contact's last name.
user_id (:obj:`int`): Optional. Contact's user identifier in Telegram.
vcard (:obj:`str`): Optional. Additional data about the contact in the form of a vCard.
"""
__slots__ = ("first_name", "last_name", "phone_number", "user_id", "vcard")
def __init__(
self,
phone_number: str,
first_name: str,
last_name: Optional[str] = None,
user_id: Optional[int] = None,
vcard: Optional[str] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.phone_number: str = str(phone_number)
self.first_name: str = first_name
# Optionals
self.last_name: Optional[str] = last_name
self.user_id: Optional[int] = user_id
self.vcard: Optional[str] = vcard
self._id_attrs = (self.phone_number,)
self._freeze()

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Document."""
from typing import Optional
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.types import JSONDict
class Document(_BaseThumbedMedium):
"""This object represents a general file
(as opposed to photos, voice messages and audio files).
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
the same over time and for different bots. Can't be used to download or reuse the file.
file_name (:obj:`str`, optional): Original filename as defined by the sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
file_size (:obj:`int`, optional): File size in bytes.
thumbnail (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by the
sender.
.. versionadded:: 20.2
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
the same over time and for different bots. Can't be used to download or reuse the file.
file_name (:obj:`str`): Optional. Original filename as defined by the sender.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
file_size (:obj:`int`): Optional. File size in bytes.
thumbnail (:class:`telegram.PhotoSize`): Optional. Document thumbnail as defined by the
sender.
.. versionadded:: 20.2
"""
__slots__ = ("file_name", "mime_type")
def __init__(
self,
file_id: str,
file_unique_id: str,
file_name: Optional[str] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
thumbnail=thumbnail,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Optional
self.mime_type: Optional[str] = mime_type
self.file_name: Optional[str] = file_name

View File

@@ -0,0 +1,371 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram File."""
import shutil
import urllib.parse as urllib_parse
from base64 import b64decode
from pathlib import Path
from typing import TYPE_CHECKING, BinaryIO, Optional
from telegram._passport.credentials import decrypt
from telegram._telegramobject import TelegramObject
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.files import is_local_file
from telegram._utils.types import FilePathInput, JSONDict, ODVInput
if TYPE_CHECKING:
from telegram import FileCredentials
class File(TelegramObject):
"""
This object represents a file ready to be downloaded. The file can be e.g. downloaded with
:attr:`download_to_drive`. It is guaranteed that the link will be valid for at least 1 hour.
When the link expires, a new one can be requested by calling :meth:`telegram.Bot.get_file`.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
.. versionchanged:: 20.0
``download`` was split into :meth:`download_to_drive` and :meth:`download_to_memory`.
Note:
* Maximum file size to download is
:tg-const:`telegram.constants.FileSizeLimit.FILESIZE_DOWNLOAD`.
* If you obtain an instance of this class from :attr:`telegram.PassportFile.get_file`,
then it will automatically be decrypted as it downloads when you call e.g.
:meth:`download_to_drive`.
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`, optional): File size in bytes, if known.
file_path (:obj:`str`, optional): File path. Use e.g. :meth:`download_to_drive` to get the
file.
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`): Optional. File size in bytes, if known.
file_path (:obj:`str`): Optional. File path. Use e.g. :meth:`download_to_drive` to get the
file.
"""
__slots__ = (
"_credentials",
"file_id",
"file_path",
"file_size",
"file_unique_id",
)
def __init__(
self,
file_id: str,
file_unique_id: str,
file_size: Optional[int] = None,
file_path: Optional[str] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.file_id: str = str(file_id)
self.file_unique_id: str = str(file_unique_id)
# Optionals
self.file_size: Optional[int] = file_size
self.file_path: Optional[str] = file_path
self._credentials: Optional[FileCredentials] = None
self._id_attrs = (self.file_unique_id,)
self._freeze()
def _get_encoded_url(self) -> str:
"""Convert any UTF-8 char in :obj:`File.file_path` into a url encoded ASCII string."""
sres = urllib_parse.urlsplit(str(self.file_path))
return urllib_parse.urlunsplit(
urllib_parse.SplitResult(
sres.scheme, sres.netloc, urllib_parse.quote(sres.path), sres.query, sres.fragment
)
)
def _prepare_decrypt(self, buf: bytes) -> bytes:
return decrypt(b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf)
async def download_to_drive(
self,
custom_path: Optional[FilePathInput] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
) -> Path:
"""
Download this file. By default, the file is saved in the current working directory with
:attr:`file_path` as file name. If :paramref:`custom_path` is supplied as a :obj:`str` or
:obj:`pathlib.Path`, it will be saved to that path.
Note:
If :paramref:`custom_path` isn't 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.
The only exception to this are encrypted files (e.g. a passport file). For these, a
file with the prefix `decrypted_` will be created in the same directory as the
original file in order to decrypt the file without changing the existing one
in-place.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
.. versionchanged:: 20.0
* :paramref:`custom_path` parameter now also accepts :class:`pathlib.Path` as argument.
* Returns :class:`pathlib.Path` object in cases where previously a :obj:`str` was
returned.
* This method was previously called ``download``. It was split into
:meth:`download_to_drive` and :meth:`download_to_memory`.
.. versionchanged:: 21.7
Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without
a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that
operation.
Args:
custom_path (:class:`pathlib.Path` | :obj:`str` , optional): The path where the file
will be saved to. If not specified, will be saved in the current working directory
with :attr:`file_path` as file name or the :attr:`file_id` if :attr:`file_path`
is not set.
Keyword Args:
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
Returns:
:class:`pathlib.Path`: Returns the Path object the file was downloaded to.
Raises:
RuntimeError: If :attr:`file_path` is not set.
"""
if not self.file_path:
raise RuntimeError("No `file_path` available for this file. Can not download.")
local_file = is_local_file(self.file_path)
url = None if local_file else self._get_encoded_url()
# if _credentials exists we want to decrypt the file
if local_file and self._credentials:
file_to_decrypt = Path(self.file_path)
buf = self._prepare_decrypt(file_to_decrypt.read_bytes())
if custom_path is not None:
path = Path(custom_path)
else:
path = Path(str(file_to_decrypt.parent) + "/decrypted_" + file_to_decrypt.name)
path.write_bytes(buf)
return path
if custom_path is not None and local_file:
shutil.copyfile(self.file_path, str(custom_path))
return Path(custom_path)
if custom_path:
filename = Path(custom_path)
elif local_file:
return Path(self.file_path)
else:
filename = Path(Path(self.file_path).name)
buf = await self.get_bot().request.retrieve(
url,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
)
if self._credentials:
buf = self._prepare_decrypt(buf)
filename.write_bytes(buf)
return filename
async def download_to_memory(
self,
out: BinaryIO,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
) -> None:
"""
Download this file into memory. :paramref:`out` needs to be supplied with a
:obj:`io.BufferedIOBase`, the file contents will be saved to that object using the
:obj:`out.write<io.BufferedIOBase.write>` method.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
Hint:
If you want to immediately read the data from ``out`` after calling this method, you
should call ``out.seek(0)`` first. See also :meth:`io.IOBase.seek`.
.. versionadded:: 20.0
.. versionchanged:: 21.7
Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without
a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that
operation.
Args:
out (:obj:`io.BufferedIOBase`): A file-like object. Must be opened for writing in
binary mode.
Keyword Args:
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
Raises:
RuntimeError: If :attr:`file_path` is not set.
"""
if not self.file_path:
raise RuntimeError("No `file_path` available for this file. Can not download.")
local_file = is_local_file(self.file_path)
url = None if local_file else self._get_encoded_url()
path = Path(self.file_path) if local_file else None
if local_file:
buf = path.read_bytes()
else:
buf = await self.get_bot().request.retrieve(
url,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
)
if self._credentials:
buf = self._prepare_decrypt(buf)
out.write(buf)
async def download_as_bytearray(
self,
buf: Optional[bytearray] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
) -> bytearray:
"""Download this file and return it as a bytearray.
.. versionchanged:: 21.7
Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without
a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that
operation.
Args:
buf (:obj:`bytearray`, optional): Extend the given bytearray with the downloaded data.
Keyword Args:
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. versionadded:: 20.0
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. versionadded:: 20.0
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. versionadded:: 20.0
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. versionadded:: 20.0
Returns:
:obj:`bytearray`: The same object as :paramref:`buf` if it was specified. Otherwise a
newly allocated :obj:`bytearray`.
Raises:
RuntimeError: If :attr:`file_path` is not set.
"""
if not self.file_path:
raise RuntimeError("No `file_path` available for this file. Can not download.")
if buf is None:
buf = bytearray()
if is_local_file(self.file_path):
bytes_data = Path(self.file_path).read_bytes()
else:
bytes_data = await self.get_bot().request.retrieve(
self._get_encoded_url(),
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
)
if self._credentials:
buf.extend(self._prepare_decrypt(bytes_data))
else:
buf.extend(bytes_data)
return buf
def set_credentials(self, credentials: "FileCredentials") -> None:
"""Sets the passport credentials for the file.
Args:
credentials (:class:`telegram.FileCredentials`): The credentials.
"""
self._credentials = credentials

View File

@@ -0,0 +1,142 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InputFile."""
import mimetypes
from typing import IO, Optional, Union
from uuid import uuid4
from telegram._utils.files import guess_file_name, load_file
from telegram._utils.strings import TextEncoding
from telegram._utils.types import FieldTuple
_DEFAULT_MIME_TYPE = "application/octet-stream"
class InputFile:
"""This object represents a Telegram InputFile.
.. versionchanged:: 20.0
* The former attribute ``attach`` was renamed to :attr:`attach_name`.
* Method ``is_image`` was removed. If you pass :obj:`bytes` to :paramref:`obj` and would
like to have the mime type automatically guessed, please pass :paramref:`filename`
in addition.
Args:
obj (:term:`file object` | :obj:`bytes` | :obj:`str`): An open file descriptor or the files
content as bytes or string.
Note:
If :paramref:`obj` is a string, it will be encoded as bytes via
:external:obj:`obj.encode('utf-8') <str.encode>`.
.. versionchanged:: 20.0
Accept string input.
filename (:obj:`str`, optional): Filename for this InputFile.
attach (:obj:`bool`, optional): Pass :obj:`True` if the parameter this file belongs to in
the request to Telegram should point to the multipart data via an ``attach://`` URI.
Defaults to `False`.
read_file_handle (:obj:`bool`, optional): If :obj:`True` and :paramref:`obj` is a file
handle, the data will be read from the file handle on initialization of this object.
If :obj:`False`, the file handle will be passed on to the
:attr:`networking backend <telegram.request.BaseRequest.do_request>` which will have
to handle the reading. Defaults to :obj:`True`.
Tip:
If you upload extremely large files, you may want to set this to :obj:`False` to
avoid reading the complete file into memory. Additionally, this may be supported
better by the networking backend (in particular it is handled better by
the default :class:`~telegram.request.HTTPXRequest`).
Important:
If you set this to :obj:`False`, you have to ensure that the file handle is still
open when the request is made. In particular, the following snippet can *not* work
as expected.
.. code-block:: python
with open('file.txt', 'rb') as file:
input_file = InputFile(file, read_file_handle=False)
# here the file handle is already closed and the upload will fail
await bot.send_document(chat_id, input_file)
.. versionadded:: 21.5
Attributes:
input_file_content (:obj:`bytes` | :class:`IO`): The binary content of the file to send.
attach_name (:obj:`str`): Optional. If present, the parameter this file belongs to in
the request to Telegram should point to the multipart data via a an URI of the form
``attach://<attach_name>`` URI.
filename (:obj:`str`): Filename for the file to be sent.
mimetype (:obj:`str`): The mimetype inferred from the file to be sent.
"""
__slots__ = ("attach_name", "filename", "input_file_content", "mimetype")
def __init__(
self,
obj: Union[IO[bytes], bytes, str],
filename: Optional[str] = None,
attach: bool = False,
read_file_handle: bool = True,
):
if isinstance(obj, bytes):
self.input_file_content: Union[bytes, IO[bytes]] = obj
elif isinstance(obj, str):
self.input_file_content = obj.encode(TextEncoding.UTF_8)
elif read_file_handle:
reported_filename, self.input_file_content = load_file(obj)
filename = filename or reported_filename
else:
self.input_file_content = obj
filename = filename or guess_file_name(obj)
self.attach_name: Optional[str] = "attached" + uuid4().hex if attach else None
if filename:
self.mimetype: str = (
mimetypes.guess_type(filename, strict=False)[0] or _DEFAULT_MIME_TYPE
)
else:
self.mimetype = _DEFAULT_MIME_TYPE
self.filename: str = filename or self.mimetype.replace("/", ".")
@property
def field_tuple(self) -> FieldTuple:
"""Field tuple representing the contents of the file for upload to the Telegram servers.
.. versionchanged:: 21.5
Content may now be a file handle.
Returns:
tuple[:obj:`str`, :obj:`bytes` | :class:`IO`, :obj:`str`]:
"""
return self.filename, self.input_file_content, self.mimetype
@property
def attach_uri(self) -> Optional[str]:
"""URI to insert into the JSON data for uploading the file. Returns :obj:`None`, if
:attr:`attach_name` is :obj:`None`.
"""
return f"attach://{self.attach_name}" if self.attach_name else None

View File

@@ -0,0 +1,915 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram InputMedia Objects."""
import datetime as dtm
from collections.abc import Sequence
from typing import Final, Optional, Union
from telegram import constants
from telegram._files.animation import Animation
from telegram._files.audio import Audio
from telegram._files.document import Document
from telegram._files.inputfile import InputFile
from telegram._files.photosize import PhotoSize
from telegram._files.video import Video
from telegram._messageentity import MessageEntity
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.argumentparsing import parse_sequence_arg, to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.files import parse_file_input
from telegram._utils.types import FileInput, JSONDict, ODVInput, TimePeriod
from telegram.constants import InputMediaType
MediaType = Union[Animation, Audio, Document, PhotoSize, Video]
class InputMedia(TelegramObject):
"""
Base class for Telegram InputMedia Objects.
.. versionchanged:: 20.0
Added arguments and attributes :attr:`type`, :attr:`media`, :attr:`caption`,
:attr:`caption_entities`, :paramref:`parse_mode`.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
Args:
media_type (:obj:`str`): Type of media that the instance represents.
media (:obj:`str` | :class:`~telegram.InputFile`): File to send.
|fileinputnopath|
caption (:obj:`str`, optional): Caption of the media to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities|
.. versionchanged:: 20.0
|sequenceclassargs|
parse_mode (:obj:`str`, optional): |parse_mode|
Attributes:
type (:obj:`str`): Type of the input media.
media (:obj:`str` | :class:`telegram.InputFile`): Media to send.
caption (:obj:`str`): Optional. Caption of the media to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
* |tupleclassattrs|
* |alwaystuple|
"""
__slots__ = ("caption", "caption_entities", "media", "parse_mode", "type")
def __init__(
self,
media_type: str,
media: Union[str, InputFile],
caption: Optional[str] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.type: str = enum.get_member(constants.InputMediaType, media_type, media_type)
self.media: Union[str, InputFile] = media
self.caption: Optional[str] = caption
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.parse_mode: ODVInput[str] = parse_mode
self._freeze()
@staticmethod
def _parse_thumbnail_input(thumbnail: Optional[FileInput]) -> Optional[Union[str, InputFile]]:
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
return (
parse_file_input(thumbnail, attach=True, local_mode=True)
if thumbnail is not None
else thumbnail
)
class InputPaidMedia(TelegramObject):
"""
Base class for Telegram InputPaidMedia Objects. Currently, it can be one of:
* :class:`telegram.InputPaidMediaPhoto`
* :class:`telegram.InputPaidMediaVideo`
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
.. versionadded:: 21.4
Args:
type (:obj:`str`): Type of media that the instance represents.
media (:obj:`str` | :class:`~telegram.InputFile`): File
to send. |fileinputnopath|
Attributes:
type (:obj:`str`): Type of the input media.
media (:obj:`str` | :class:`telegram.InputFile`): Media to send.
"""
PHOTO: Final[str] = constants.InputPaidMediaType.PHOTO
""":const:`telegram.constants.InputPaidMediaType.PHOTO`"""
VIDEO: Final[str] = constants.InputPaidMediaType.VIDEO
""":const:`telegram.constants.InputPaidMediaType.VIDEO`"""
__slots__ = ("media", "type")
def __init__(
self,
type: str, # pylint: disable=redefined-builtin
media: Union[str, InputFile],
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.type: str = enum.get_member(constants.InputPaidMediaType, type, type)
self.media: Union[str, InputFile] = media
self._freeze()
class InputPaidMediaPhoto(InputPaidMedia):
"""The paid media to send is a photo.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
.. versionadded:: 21.4
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.PhotoSize`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
Attributes:
type (:obj:`str`): Type of the media, always
:tg-const:`telegram.constants.InputPaidMediaType.PHOTO`.
media (:obj:`str` | :class:`telegram.InputFile`): Photo to send.
"""
__slots__ = ()
def __init__(
self,
media: Union[FileInput, PhotoSize],
*,
api_kwargs: Optional[JSONDict] = None,
):
media = parse_file_input(media, PhotoSize, attach=True, local_mode=True)
super().__init__(type=InputPaidMedia.PHOTO, media=media, api_kwargs=api_kwargs)
self._freeze()
class InputPaidMediaVideo(InputPaidMedia):
"""
The paid media to send is a video.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
.. versionadded:: 21.4
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
arguments.
* :paramref:`thumbnail` will be ignored for small video files, for which Telegram can
easily generate thumbnails. However, this behaviour is undocumented and might be
changed by Telegram.
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Video`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.Video` object to send.
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): |thumbdocstringnopath|
cover (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): Cover for the video in the message. |fileinputnopath|
.. versionchanged:: 21.11
start_timestamp (:obj:`int`, optional): Start timestamp for the video in the message
.. versionchanged:: 21.11
width (:obj:`int`, optional): Video width.
height (:obj:`int`, optional): Video height.
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Video duration in seconds.
.. versionchanged:: v22.2
|time-period-input|
supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is
suitable for streaming.
Attributes:
type (:obj:`str`): Type of the media, always
:tg-const:`telegram.constants.InputPaidMediaType.VIDEO`.
media (:obj:`str` | :class:`telegram.InputFile`): Video to send.
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
cover (:class:`telegram.InputFile`): Optional. Cover for the video in the message.
|fileinputnopath|
.. versionchanged:: 21.11
start_timestamp (:obj:`int`): Optional. Start timestamp for the video in the message
.. versionchanged:: 21.11
width (:obj:`int`): Optional. Video width.
height (:obj:`int`): Optional. Video height.
duration (:obj:`int` | :class:`datetime.timedelta`): Optional. Video duration in seconds.
.. deprecated:: v22.2
|time-period-int-deprecated|
supports_streaming (:obj:`bool`): Optional. :obj:`True`, if the uploaded video is
suitable for streaming.
"""
__slots__ = (
"_duration",
"cover",
"height",
"start_timestamp",
"supports_streaming",
"thumbnail",
"width",
)
def __init__(
self,
media: Union[FileInput, Video],
thumbnail: Optional[FileInput] = None,
width: Optional[int] = None,
height: Optional[int] = None,
duration: Optional[TimePeriod] = None,
supports_streaming: Optional[bool] = None,
cover: Optional[FileInput] = None,
start_timestamp: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Video):
width = width if width is not None else media.width
height = height if height is not None else media.height
duration = duration if duration is not None else media._duration
media = media.file_id
else:
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
media = parse_file_input(media, attach=True, local_mode=True)
super().__init__(type=InputPaidMedia.VIDEO, media=media, api_kwargs=api_kwargs)
with self._unfrozen():
self.thumbnail: Optional[Union[str, InputFile]] = InputMedia._parse_thumbnail_input(
thumbnail
)
self.width: Optional[int] = width
self.height: Optional[int] = height
self._duration: Optional[dtm.timedelta] = to_timedelta(duration)
self.supports_streaming: Optional[bool] = supports_streaming
self.cover: Optional[Union[InputFile, str]] = (
parse_file_input(cover, attach=True, local_mode=True) if cover else None
)
self.start_timestamp: Optional[int] = start_timestamp
@property
def duration(self) -> Optional[Union[int, dtm.timedelta]]:
return get_timedelta_value(self._duration, attribute="duration")
class InputMediaAnimation(InputMedia):
"""Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent.
Note:
When using a :class:`telegram.Animation` for the :attr:`media` attribute, it will take the
width, height and duration from that animation, unless otherwise specified with the
optional arguments.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Animation`): File to send. |fileinputnopath|
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
caption (:obj:`str`, optional): Caption of the animation to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`, optional): |parse_mode|
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities|
.. versionchanged:: 20.0
|sequenceclassargs|
width (:obj:`int`, optional): Animation width.
height (:obj:`int`, optional): Animation height.
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Animation duration
in seconds.
.. versionchanged:: v22.2
|time-period-input|
has_spoiler (:obj:`bool`, optional): Pass :obj:`True`, if the animation needs to be covered
with a spoiler animation.
.. versionadded:: 20.0
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): |thumbdocstringnopath|
.. versionadded:: 20.2
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
.. versionadded:: 21.3
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.ANIMATION`.
media (:obj:`str` | :class:`telegram.InputFile`): Animation to send.
caption (:obj:`str`): Optional. Caption of the animation to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting.
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
* |tupleclassattrs|
* |alwaystuple|
width (:obj:`int`): Optional. Animation width.
height (:obj:`int`): Optional. Animation height.
duration (:obj:`int` | :class:`datetime.timedelta`): Optional. Animation duration
in seconds.
.. deprecated:: v22.2
|time-period-int-deprecated|
has_spoiler (:obj:`bool`): Optional. :obj:`True`, if the animation is covered with a
spoiler animation.
.. versionadded:: 20.0
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
.. versionadded:: 20.2
show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med|
.. versionadded:: 21.3
"""
__slots__ = (
"_duration",
"has_spoiler",
"height",
"show_caption_above_media",
"thumbnail",
"width",
)
def __init__(
self,
media: Union[FileInput, Animation],
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
width: Optional[int] = None,
height: Optional[int] = None,
duration: Optional[TimePeriod] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
has_spoiler: Optional[bool] = None,
thumbnail: Optional[FileInput] = None,
show_caption_above_media: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Animation):
width = media.width if width is None else width
height = media.height if height is None else height
duration = duration if duration is not None else media._duration
media = media.file_id
else:
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
media = parse_file_input(media, filename=filename, attach=True, local_mode=True)
super().__init__(
InputMediaType.ANIMATION,
media,
caption,
caption_entities,
parse_mode,
api_kwargs=api_kwargs,
)
with self._unfrozen():
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumbnail_input(
thumbnail
)
self.width: Optional[int] = width
self.height: Optional[int] = height
self._duration: Optional[dtm.timedelta] = to_timedelta(duration)
self.has_spoiler: Optional[bool] = has_spoiler
self.show_caption_above_media: Optional[bool] = show_caption_above_media
@property
def duration(self) -> Optional[Union[int, dtm.timedelta]]:
return get_timedelta_value(self._duration, attribute="duration")
class InputMediaPhoto(InputMedia):
"""Represents a photo to be sent.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.PhotoSize`): File to send. |fileinputnopath|
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.
.. versionadded:: 13.1
caption (:obj:`str`, optional ): Caption of the photo to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`, optional): |parse_mode|
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities|
.. versionchanged:: 20.0
|sequenceclassargs|
has_spoiler (:obj:`bool`, optional): Pass :obj:`True`, if the photo needs to be covered
with a spoiler animation.
.. versionadded:: 20.0
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
.. versionadded:: 21.3
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.PHOTO`.
media (:obj:`str` | :class:`telegram.InputFile`): Photo to send.
caption (:obj:`str`): Optional. Caption of the photo to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
* |tupleclassattrs|
* |alwaystuple|
has_spoiler (:obj:`bool`): Optional. :obj:`True`, if the photo is covered with a
spoiler animation.
.. versionadded:: 20.0
show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med|
.. versionadded:: 21.3
"""
__slots__ = (
"has_spoiler",
"show_caption_above_media",
)
def __init__(
self,
media: Union[FileInput, PhotoSize],
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
has_spoiler: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
media = parse_file_input(media, PhotoSize, filename=filename, attach=True, local_mode=True)
super().__init__(
InputMediaType.PHOTO,
media,
caption,
caption_entities,
parse_mode,
api_kwargs=api_kwargs,
)
with self._unfrozen():
self.has_spoiler: Optional[bool] = has_spoiler
self.show_caption_above_media: Optional[bool] = show_caption_above_media
class InputMediaVideo(InputMedia):
"""Represents a video to be sent.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
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
arguments.
* :paramref:`thumbnail` will be ignored for small video files, for which Telegram can
easily generate thumbnails. However, this behaviour is undocumented and might be
changed by Telegram.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Video`): File to send. |fileinputnopath|
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.
.. versionadded:: 13.1
caption (:obj:`str`, optional): Caption of the video to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`, optional): |parse_mode|
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities|
.. versionchanged:: 20.0
|sequenceclassargs|
width (:obj:`int`, optional): Video width.
height (:obj:`int`, optional): Video height.
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Video duration in seconds.
.. versionchanged:: v22.2
|time-period-input|
supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is
suitable for streaming.
has_spoiler (:obj:`bool`, optional): Pass :obj:`True`, if the video needs to be covered
with a spoiler animation.
.. versionadded:: 20.0
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): |thumbdocstringnopath|
.. versionadded:: 20.2
cover (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): Cover for the video in the message. |fileinputnopath|
.. versionchanged:: 21.11
start_timestamp (:obj:`int`, optional): Start timestamp for the video in the message
.. versionchanged:: 21.11
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
.. versionadded:: 21.3
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.VIDEO`.
media (:obj:`str` | :class:`telegram.InputFile`): Video file to send.
caption (:obj:`str`): Optional. Caption of the video to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
* |tupleclassattrs|
* |alwaystuple|
width (:obj:`int`): Optional. Video width.
height (:obj:`int`): Optional. Video height.
duration (:obj:`int` | :class:`datetime.timedelta`): Optional. Video duration in seconds.
.. deprecated:: v22.2
|time-period-int-deprecated|
supports_streaming (:obj:`bool`): Optional. :obj:`True`, if the uploaded video is
suitable for streaming.
has_spoiler (:obj:`bool`): Optional. :obj:`True`, if the video is covered with a
spoiler animation.
.. versionadded:: 20.0
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
.. versionadded:: 20.2
show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med|
.. versionadded:: 21.3
cover (:class:`telegram.InputFile`): Optional. Cover for the video in the message.
|fileinputnopath|
.. versionchanged:: 21.11
start_timestamp (:obj:`int`): Optional. Start timestamp for the video in the message
.. versionchanged:: 21.11
"""
__slots__ = (
"_duration",
"cover",
"has_spoiler",
"height",
"show_caption_above_media",
"start_timestamp",
"supports_streaming",
"thumbnail",
"width",
)
def __init__(
self,
media: Union[FileInput, Video],
caption: Optional[str] = None,
width: Optional[int] = None,
height: Optional[int] = None,
duration: Optional[TimePeriod] = None,
supports_streaming: Optional[bool] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
has_spoiler: Optional[bool] = None,
thumbnail: Optional[FileInput] = None,
show_caption_above_media: Optional[bool] = None,
cover: Optional[FileInput] = None,
start_timestamp: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Video):
width = width if width is not None else media.width
height = height if height is not None else media.height
duration = duration if duration is not None else media._duration
media = media.file_id
else:
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
media = parse_file_input(media, filename=filename, attach=True, local_mode=True)
super().__init__(
InputMediaType.VIDEO,
media,
caption,
caption_entities,
parse_mode,
api_kwargs=api_kwargs,
)
with self._unfrozen():
self.width: Optional[int] = width
self.height: Optional[int] = height
self._duration: Optional[dtm.timedelta] = to_timedelta(duration)
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumbnail_input(
thumbnail
)
self.supports_streaming: Optional[bool] = supports_streaming
self.has_spoiler: Optional[bool] = has_spoiler
self.show_caption_above_media: Optional[bool] = show_caption_above_media
self.cover: Optional[Union[InputFile, str]] = (
parse_file_input(cover, attach=True, local_mode=True) if cover else None
)
self.start_timestamp: Optional[int] = start_timestamp
@property
def duration(self) -> Optional[Union[int, dtm.timedelta]]:
return get_timedelta_value(self._duration, attribute="duration")
class InputMediaAudio(InputMedia):
"""Represents an audio file to be treated as music to be sent.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
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
optional arguments.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Audio`): File to send. |fileinputnopath|
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.
.. versionadded:: 13.1
caption (:obj:`str`, optional): Caption of the audio to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`, optional): |parse_mode|
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities|
.. versionchanged:: 20.0
|sequenceclassargs|
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of the audio
in seconds as defined by the sender.
.. versionchanged:: v22.2
|time-period-input|
performer (:obj:`str`, optional): Performer of the audio as defined by the sender or by
audio tags.
title (:obj:`str`, optional): Title of the audio as defined by the sender or by audio tags.
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): |thumbdocstringnopath|
.. versionadded:: 20.2
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.AUDIO`.
media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send.
caption (:obj:`str`): Optional. Caption of the audio to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
* |tupleclassattrs|
* |alwaystuple|
duration (:obj:`int` | :class:`datetime.timedelta`): Optional. Duration of the audio
in seconds.
.. deprecated:: v22.2
|time-period-int-deprecated|
performer (:obj:`str`): Optional. Performer of the audio as defined by the sender or by
audio tags.
title (:obj:`str`): Optional. Title of the audio as defined by the sender or by audio tags.
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
.. versionadded:: 20.2
"""
__slots__ = ("_duration", "performer", "thumbnail", "title")
def __init__(
self,
media: Union[FileInput, Audio],
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
duration: Optional[TimePeriod] = None,
performer: Optional[str] = None,
title: Optional[str] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
thumbnail: Optional[FileInput] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Audio):
duration = duration if duration is not None else media._duration
performer = media.performer if performer is None else performer
title = media.title if title is None else title
media = media.file_id
else:
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
media = parse_file_input(media, filename=filename, attach=True, local_mode=True)
super().__init__(
InputMediaType.AUDIO,
media,
caption,
caption_entities,
parse_mode,
api_kwargs=api_kwargs,
)
with self._unfrozen():
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumbnail_input(
thumbnail
)
self._duration: Optional[dtm.timedelta] = to_timedelta(duration)
self.title: Optional[str] = title
self.performer: Optional[str] = performer
@property
def duration(self) -> Optional[Union[int, dtm.timedelta]]:
return get_timedelta_value(self._duration, attribute="duration")
class InputMediaDocument(InputMedia):
"""Represents a general file to be sent.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path` | :class:`telegram.Document`): File to send. |fileinputnopath|
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.
.. versionadded:: 13.1
caption (:obj:`str`, optional): Caption of the document to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`, optional): |parse_mode|
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities|
.. versionchanged:: 20.0
|sequenceclassargs|
disable_content_type_detection (:obj:`bool`, optional): Disables automatic server-side
content type detection for files uploaded using multipart/form-data. Always
:obj:`True`, if the document is sent as part of an album.
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): |thumbdocstringnopath|
.. versionadded:: 20.2
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.DOCUMENT`.
media (:obj:`str` | :class:`telegram.InputFile`): File to send.
caption (:obj:`str`): Optional. Caption of the document to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
* |tupleclassattrs|
* |alwaystuple|
disable_content_type_detection (:obj:`bool`): Optional. Disables automatic server-side
content type detection for files uploaded using multipart/form-data. Always
:obj:`True`, if the document is sent as part of an album.
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
.. versionadded:: 20.2
"""
__slots__ = ("disable_content_type_detection", "thumbnail")
def __init__(
self,
media: Union[FileInput, Document],
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_content_type_detection: Optional[bool] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
thumbnail: Optional[FileInput] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
media = parse_file_input(media, Document, filename=filename, attach=True, local_mode=True)
super().__init__(
InputMediaType.DOCUMENT,
media,
caption,
caption_entities,
parse_mode,
api_kwargs=api_kwargs,
)
with self._unfrozen():
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumbnail_input(
thumbnail
)
self.disable_content_type_detection: Optional[bool] = disable_content_type_detection

View File

@@ -0,0 +1,138 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an objects that represents a InputProfilePhoto and subclasses."""
import datetime as dtm
from typing import TYPE_CHECKING, Optional, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.files import parse_file_input
from telegram._utils.types import FileInput, JSONDict
if TYPE_CHECKING:
from telegram import InputFile
class InputProfilePhoto(TelegramObject):
"""This object describes a profile photo to set. Currently, it can be one of
* :class:`InputProfilePhotoStatic`
* :class:`InputProfilePhotoAnimated`
.. versionadded:: 22.1
Args:
type (:obj:`str`): Type of the profile photo.
Attributes:
type (:obj:`str`): Type of the profile photo.
"""
STATIC = constants.InputProfilePhotoType.STATIC
""":obj:`str`: :tg-const:`telegram.constants.InputProfilePhotoType.STATIC`."""
ANIMATED = constants.InputProfilePhotoType.ANIMATED
""":obj:`str`: :tg-const:`telegram.constants.InputProfilePhotoType.ANIMATED`."""
__slots__ = ("type",)
def __init__(
self,
type: str, # pylint: disable=redefined-builtin
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.type: str = enum.get_member(constants.InputProfilePhotoType, type, type)
self._freeze()
class InputProfilePhotoStatic(InputProfilePhoto):
"""A static profile photo in the .JPG format.
.. versionadded:: 22.1
Args:
photo (:term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path`): The static profile photo. |uploadinputnopath|
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputProfilePhotoType.STATIC`.
photo (:class:`telegram.InputFile` | :obj:`str`): The static profile photo.
"""
__slots__ = ("photo",)
def __init__(
self,
photo: FileInput,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(type=constants.InputProfilePhotoType.STATIC, api_kwargs=api_kwargs)
with self._unfrozen():
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
self.photo: Union[str, InputFile] = parse_file_input(
photo, attach=True, local_mode=True
)
class InputProfilePhotoAnimated(InputProfilePhoto):
"""An animated profile photo in the MPEG4 format.
.. versionadded:: 22.1
Args:
animation (:term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path`): The animated profile photo. |uploadinputnopath|
main_frame_timestamp (:class:`datetime.timedelta` | :obj:`int` | :obj:`float`, optional):
Timestamp in seconds of the frame that will be used as the static profile photo.
Defaults to ``0.0``.
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InputProfilePhotoType.ANIMATED`.
animation (:class:`telegram.InputFile` | :obj:`str`): The animated profile photo.
main_frame_timestamp (:class:`datetime.timedelta`): Optional. Timestamp in seconds of the
frame that will be used as the static profile photo. Defaults to ``0.0``.
"""
__slots__ = ("animation", "main_frame_timestamp")
def __init__(
self,
animation: FileInput,
main_frame_timestamp: Union[float, dtm.timedelta, None] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(type=constants.InputProfilePhotoType.ANIMATED, api_kwargs=api_kwargs)
with self._unfrozen():
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
self.animation: Union[str, InputFile] = parse_file_input(
animation, attach=True, local_mode=True
)
self.main_frame_timestamp: Optional[dtm.timedelta] = to_timedelta(main_frame_timestamp)

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InputSticker."""
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional, Union
from telegram._files.sticker import MaskPosition
from telegram._telegramobject import TelegramObject
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.files import parse_file_input
from telegram._utils.types import FileInput, JSONDict
if TYPE_CHECKING:
from telegram._files.inputfile import InputFile
class InputSticker(TelegramObject):
"""
This object describes a sticker to be added to a sticker set.
.. versionadded:: 20.2
.. versionchanged:: 21.1
As of Bot API 7.2, the new argument :paramref:`format` is a required argument, and thus the
order of the arguments has changed.
Args:
sticker (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path`): The
added sticker. |uploadinputnopath| Animated and video stickers can't be uploaded via
HTTP URL.
emoji_list (Sequence[:obj:`str`]): Sequence of
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker.
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask should be
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
keywords (Sequence[:obj:`str`], optional): Sequence of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
for the sticker with the total length of up to
:tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. For
":tg-const:`telegram.constants.StickerType.REGULAR`" and
":tg-const:`telegram.constants.StickerType.CUSTOM_EMOJI`" stickers only.
format (:obj:`str`): Format of the added sticker, must be one of
:tg-const:`telegram.constants.StickerFormat.STATIC` for a
``.WEBP`` or ``.PNG`` image, :tg-const:`telegram.constants.StickerFormat.ANIMATED`
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a
``.WEBM`` video.
.. versionadded:: 21.1
Attributes:
sticker (:obj:`str` | :class:`telegram.InputFile`): The added sticker.
emoji_list (tuple[:obj:`str`]): Tuple of
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker.
mask_position (:class:`telegram.MaskPosition`): Optional. Position where the mask should be
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
keywords (tuple[:obj:`str`]): Optional. Tuple of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
for the sticker with the total length of up to
:tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. For
":tg-const:`telegram.constants.StickerType.REGULAR`" and
":tg-const:`telegram.constants.StickerType.CUSTOM_EMOJI`" stickers only.
":tg-const:`telegram.constants.StickerType.CUSTOM_EMOJI`" stickers only.
format (:obj:`str`): Format of the added sticker, must be one of
:tg-const:`telegram.constants.StickerFormat.STATIC` for a
``.WEBP`` or ``.PNG`` image, :tg-const:`telegram.constants.StickerFormat.ANIMATED`
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a
``.WEBM`` video.
.. versionadded:: 21.1
"""
__slots__ = ("emoji_list", "format", "keywords", "mask_position", "sticker")
def __init__(
self,
sticker: FileInput,
emoji_list: Sequence[str],
format: str, # pylint: disable=redefined-builtin
mask_position: Optional[MaskPosition] = None,
keywords: Optional[Sequence[str]] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
self.sticker: Union[str, InputFile] = parse_file_input(
sticker,
local_mode=True,
attach=True,
)
self.emoji_list: tuple[str, ...] = parse_sequence_arg(emoji_list)
self.format: str = format
self.mask_position: Optional[MaskPosition] = mask_position
self.keywords: tuple[str, ...] = parse_sequence_arg(keywords)
self._freeze()

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Location."""
import datetime as dtm
from typing import Final, Optional, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.types import JSONDict, TimePeriod
class Location(TelegramObject):
"""This object represents a point on the map.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`longitude` and :attr:`latitude` are equal.
Args:
longitude (:obj:`float`): Longitude as defined by the sender.
latitude (:obj:`float`): Latitude as defined by the sender.
horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location,
measured in meters; 0-:tg-const:`telegram.Location.HORIZONTAL_ACCURACY`.
live_period (:obj:`int` | :class:`datetime.timedelta`, optional): Time relative to the
message sending date, during which the location can be updated, in seconds. For active
live locations only.
.. versionchanged:: v22.2
|time-period-input|
heading (:obj:`int`, optional): The direction in which user is moving, in degrees;
:tg-const:`telegram.Location.MIN_HEADING`-:tg-const:`telegram.Location.MAX_HEADING`.
For active live locations only.
proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts about
approaching another chat member, in meters. For sent live locations only.
Attributes:
longitude (:obj:`float`): Longitude as defined by the sender.
latitude (:obj:`float`): Latitude as defined by the sender.
horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location,
measured in meters; 0-:tg-const:`telegram.Location.HORIZONTAL_ACCURACY`.
live_period (:obj:`int` | :class:`datetime.timedelta`): Optional. Time relative to the
message sending date, during which the location can be updated, in seconds. For active
live locations only.
.. deprecated:: v22.2
|time-period-int-deprecated|
heading (:obj:`int`): Optional. The direction in which user is moving, in degrees;
:tg-const:`telegram.Location.MIN_HEADING`-:tg-const:`telegram.Location.MAX_HEADING`.
For active live locations only.
proximity_alert_radius (:obj:`int`): Optional. Maximum distance for proximity alerts about
approaching another chat member, in meters. For sent live locations only.
"""
__slots__ = (
"_live_period",
"heading",
"horizontal_accuracy",
"latitude",
"longitude",
"proximity_alert_radius",
)
def __init__(
self,
longitude: float,
latitude: float,
horizontal_accuracy: Optional[float] = None,
live_period: Optional[TimePeriod] = None,
heading: Optional[int] = None,
proximity_alert_radius: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.longitude: float = longitude
self.latitude: float = latitude
# Optionals
self.horizontal_accuracy: Optional[float] = horizontal_accuracy
self._live_period: Optional[dtm.timedelta] = to_timedelta(live_period)
self.heading: Optional[int] = heading
self.proximity_alert_radius: Optional[int] = (
int(proximity_alert_radius) if proximity_alert_radius else None
)
self._id_attrs = (self.longitude, self.latitude)
self._freeze()
@property
def live_period(self) -> Optional[Union[int, dtm.timedelta]]:
return get_timedelta_value(self._live_period, attribute="live_period")
HORIZONTAL_ACCURACY: Final[int] = constants.LocationLimit.HORIZONTAL_ACCURACY
""":const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY`
.. versionadded:: 20.0
"""
MIN_HEADING: Final[int] = constants.LocationLimit.MIN_HEADING
""":const:`telegram.constants.LocationLimit.MIN_HEADING`
.. versionadded:: 20.0
"""
MAX_HEADING: Final[int] = constants.LocationLimit.MAX_HEADING
""":const:`telegram.constants.LocationLimit.MAX_HEADING`
.. versionadded:: 20.0
"""

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram PhotoSize."""
from typing import Optional
from telegram._files._basemedium import _BaseMedium
from telegram._utils.types import JSONDict
class PhotoSize(_BaseMedium):
"""This object represents one size of a photo or a file/sticker thumbnail.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`, optional): File size in bytes.
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`): Optional. File size in bytes.
"""
__slots__ = ("height", "width")
def __init__(
self,
file_id: str,
file_unique_id: str,
width: int,
height: int,
file_size: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Required
self.width: int = width
self.height: int = height

View File

@@ -0,0 +1,382 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains objects that represent stickers."""
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.file import File
from telegram._files.photosize import PhotoSize
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class Sticker(_BaseThumbedMedium):
"""This object represents a sticker.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
Note:
As of v13.11 :paramref:`is_video` is a required argument and therefore the order of the
arguments had to be changed. Use keyword arguments to make sure that the arguments are
passed correctly.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Sticker width.
height (:obj:`int`): Sticker height.
is_animated (:obj:`bool`): :obj:`True`, if the sticker is animated.
is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker.
.. versionadded:: 13.11
type (:obj:`str`): Type of the sticker. Currently one of :attr:`REGULAR`,
:attr:`MASK`, :attr:`CUSTOM_EMOJI`. The type of the sticker is independent from its
format, which is determined by the fields :attr:`is_animated` and :attr:`is_video`.
.. versionadded:: 20.0
emoji (:obj:`str`, optional): Emoji associated with the sticker
set_name (:obj:`str`, optional): Name of the sticker set to which the sticker belongs.
mask_position (:class:`telegram.MaskPosition`, optional): For mask stickers, the position
where the mask should be placed.
file_size (:obj:`int`, optional): File size in bytes.
premium_animation (:class:`telegram.File`, optional): For premium regular stickers,
premium animation for the sticker.
.. versionadded:: 20.0
custom_emoji_id (:obj:`str`, optional): For custom emoji stickers, unique identifier of the
custom emoji.
.. versionadded:: 20.0
thumbnail (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the ``.WEBP`` or
``.JPG`` format.
.. versionadded:: 20.2
needs_repainting (:obj:`bool`, optional): :obj:`True`, if the sticker must be repainted to
a text color in messages, the color of the Telegram Premium badge in emoji status,
white color on chat photos, or another appropriate color in other places.
.. versionadded:: 20.2
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Sticker width.
height (:obj:`int`): Sticker height.
is_animated (:obj:`bool`): :obj:`True`, if the sticker is animated.
is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker.
.. versionadded:: 13.11
type (:obj:`str`): Type of the sticker. Currently one of :attr:`REGULAR`,
:attr:`MASK`, :attr:`CUSTOM_EMOJI`. The type of the sticker is independent from its
format, which is determined by the fields :attr:`is_animated` and :attr:`is_video`.
.. versionadded:: 20.0
emoji (:obj:`str`): Optional. Emoji associated with the sticker.
set_name (:obj:`str`): Optional. Name of the sticker set to which the sticker belongs.
mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position
where the mask should be placed.
file_size (:obj:`int`): Optional. File size in bytes.
premium_animation (:class:`telegram.File`): Optional. For premium regular stickers,
premium animation for the sticker.
.. versionadded:: 20.0
custom_emoji_id (:obj:`str`): Optional. For custom emoji stickers, unique identifier of the
custom emoji.
.. versionadded:: 20.0
thumbnail (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the ``.WEBP`` or
``.JPG`` format.
.. versionadded:: 20.2
needs_repainting (:obj:`bool`): Optional. :obj:`True`, if the sticker must be repainted to
a text color in messages, the color of the Telegram Premium badge in emoji status,
white color on chat photos, or another appropriate color in other places.
.. versionadded:: 20.2
"""
__slots__ = (
"custom_emoji_id",
"emoji",
"height",
"is_animated",
"is_video",
"mask_position",
"needs_repainting",
"premium_animation",
"set_name",
"type",
"width",
)
def __init__(
self,
file_id: str,
file_unique_id: str,
width: int,
height: int,
is_animated: bool,
is_video: bool,
type: str, # pylint: disable=redefined-builtin
emoji: Optional[str] = None,
file_size: Optional[int] = None,
set_name: Optional[str] = None,
mask_position: Optional["MaskPosition"] = None,
premium_animation: Optional["File"] = None,
custom_emoji_id: Optional[str] = None,
thumbnail: Optional[PhotoSize] = None,
needs_repainting: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
thumbnail=thumbnail,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Required
self.width: int = width
self.height: int = height
self.is_animated: bool = is_animated
self.is_video: bool = is_video
self.type: str = enum.get_member(constants.StickerType, type, type)
# Optional
self.emoji: Optional[str] = emoji
self.set_name: Optional[str] = set_name
self.mask_position: Optional[MaskPosition] = mask_position
self.premium_animation: Optional[File] = premium_animation
self.custom_emoji_id: Optional[str] = custom_emoji_id
self.needs_repainting: Optional[bool] = needs_repainting
REGULAR: Final[str] = constants.StickerType.REGULAR
""":const:`telegram.constants.StickerType.REGULAR`"""
MASK: Final[str] = constants.StickerType.MASK
""":const:`telegram.constants.StickerType.MASK`"""
CUSTOM_EMOJI: Final[str] = constants.StickerType.CUSTOM_EMOJI
""":const:`telegram.constants.StickerType.CUSTOM_EMOJI`"""
@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Sticker":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
data["thumbnail"] = de_json_optional(data.get("thumbnail"), PhotoSize, bot)
data["mask_position"] = de_json_optional(data.get("mask_position"), MaskPosition, bot)
data["premium_animation"] = de_json_optional(data.get("premium_animation"), File, bot)
api_kwargs = {}
# This is a deprecated field that TG still returns for backwards compatibility
# Let's filter it out to speed up the de-json process
if data.get("thumb") is not None:
api_kwargs["thumb"] = data.pop("thumb")
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
class StickerSet(TelegramObject):
"""This object represents a sticker set.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`name` is equal.
Note:
As of v13.11 :paramref:`is_video` is a required argument and therefore the order of the
arguments had to be changed. Use keyword arguments to make sure that the arguments are
passed correctly.
.. versionchanged:: 20.0
The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type` instead.
.. versionchanged:: 21.1
The parameters ``is_video`` and ``is_animated`` are deprecated and now made optional. Thus,
the order of the arguments had to be changed.
.. versionchanged:: 20.5
|removed_thumb_note|
.. versionremoved:: 21.2
Removed the deprecated arguments and attributes ``is_animated`` and ``is_video``.
Args:
name (:obj:`str`): Sticker set name.
title (:obj:`str`): Sticker set title.
stickers (Sequence[:class:`telegram.Sticker`]): List of all set stickers.
.. versionchanged:: 20.0
|sequenceclassargs|
sticker_type (:obj:`str`): Type of stickers in the set, currently one of
:attr:`telegram.Sticker.REGULAR`, :attr:`telegram.Sticker.MASK`,
:attr:`telegram.Sticker.CUSTOM_EMOJI`.
.. versionadded:: 20.0
thumbnail (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the ``.WEBP``,
``.TGS``, or ``.WEBM`` format.
.. versionadded:: 20.2
Attributes:
name (:obj:`str`): Sticker set name.
title (:obj:`str`): Sticker set title.
stickers (tuple[:class:`telegram.Sticker`]): List of all set stickers.
.. versionchanged:: 20.0
|tupleclassattrs|
sticker_type (:obj:`str`): Type of stickers in the set, currently one of
:attr:`telegram.Sticker.REGULAR`, :attr:`telegram.Sticker.MASK`,
:attr:`telegram.Sticker.CUSTOM_EMOJI`.
.. versionadded:: 20.0
thumbnail (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the ``.WEBP``,
``.TGS``, or ``.WEBM`` format.
.. versionadded:: 20.2
"""
__slots__ = (
"name",
"sticker_type",
"stickers",
"thumbnail",
"title",
)
def __init__(
self,
name: str,
title: str,
stickers: Sequence[Sticker],
sticker_type: str,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.name: str = name
self.title: str = title
self.stickers: tuple[Sticker, ...] = parse_sequence_arg(stickers)
self.sticker_type: str = sticker_type
# Optional
self.thumbnail: Optional[PhotoSize] = thumbnail
self._id_attrs = (self.name,)
self._freeze()
@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "StickerSet":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
data["thumbnail"] = de_json_optional(data.get("thumbnail"), PhotoSize, bot)
data["stickers"] = de_list_optional(data.get("stickers"), Sticker, bot)
api_kwargs = {}
# These are deprecated fields that TG still returns for backwards compatibility
# Let's filter them out to speed up the de-json process
for deprecated_field in ("contains_masks", "thumb", "is_animated", "is_video"):
if deprecated_field in data:
api_kwargs[deprecated_field] = data.pop(deprecated_field)
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
class MaskPosition(TelegramObject):
"""This object describes the position on faces where a mask should be placed by default.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`point`, :attr:`x_shift`, :attr:`y_shift` and, :attr:`scale`
are equal.
Args:
point (:obj:`str`): The part of the face relative to which the mask should be placed.
One of :attr:`FOREHEAD`, :attr:`EYES`, :attr:`MOUTH`, or :attr:`CHIN`.
x_shift (:obj:`float`): Shift by X-axis measured in widths of the mask scaled to the face
size, from left to right. For example, choosing ``-1.0`` will place mask just to the
left of the default mask position.
y_shift (:obj:`float`): Shift by Y-axis measured in heights of the mask scaled to the face
size, from top to bottom. For example, ``1.0`` will place the mask just below the
default mask position.
scale (:obj:`float`): Mask scaling coefficient. For example, ``2.0`` means double size.
Attributes:
point (:obj:`str`): The part of the face relative to which the mask should be placed.
One of :attr:`FOREHEAD`, :attr:`EYES`, :attr:`MOUTH`, or :attr:`CHIN`.
x_shift (:obj:`float`): Shift by X-axis measured in widths of the mask scaled to the face
size, from left to right. For example, choosing ``-1.0`` will place mask just to the
left of the default mask position.
y_shift (:obj:`float`): Shift by Y-axis measured in heights of the mask scaled to the face
size, from top to bottom. For example, ``1.0`` will place the mask just below the
default mask position.
scale (:obj:`float`): Mask scaling coefficient. For example, ``2.0`` means double size.
"""
__slots__ = ("point", "scale", "x_shift", "y_shift")
FOREHEAD: Final[str] = constants.MaskPosition.FOREHEAD
""":const:`telegram.constants.MaskPosition.FOREHEAD`"""
EYES: Final[str] = constants.MaskPosition.EYES
""":const:`telegram.constants.MaskPosition.EYES`"""
MOUTH: Final[str] = constants.MaskPosition.MOUTH
""":const:`telegram.constants.MaskPosition.MOUTH`"""
CHIN: Final[str] = constants.MaskPosition.CHIN
""":const:`telegram.constants.MaskPosition.CHIN`"""
def __init__(
self,
point: str,
x_shift: float,
y_shift: float,
scale: float,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.point: str = point
self.x_shift: float = x_shift
self.y_shift: float = y_shift
self.scale: float = scale
self._id_attrs = (self.point, self.x_shift, self.y_shift, self.scale)
self._freeze()

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Venue."""
from typing import TYPE_CHECKING, Optional
from telegram._files.location import Location
from telegram._telegramobject import TelegramObject
from telegram._utils.argumentparsing import de_json_optional
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class Venue(TelegramObject):
"""This object represents a venue.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`location` and :attr:`title` are equal.
Note:
Foursquare details and Google Place details are mutually exclusive. However, this
behaviour is undocumented and might be changed by Telegram.
Args:
location (:class:`telegram.Location`): Venue location.
title (:obj:`str`): Name of the venue.
address (:obj:`str`): Address of the venue.
foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue.
foursquare_type (:obj:`str`, optional): Foursquare type of the venue. (For example,
"arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".)
google_place_id (:obj:`str`, optional): Google Places identifier of the venue.
google_place_type (:obj:`str`, optional): Google Places type of the venue. (See
`supported types <https://developers.google.com/maps/documentation/places/web-service\
/supported_types>`_.)
Attributes:
location (:class:`telegram.Location`): Venue location.
title (:obj:`str`): Name of the venue.
address (:obj:`str`): Address of the venue.
foursquare_id (:obj:`str`): Optional. Foursquare identifier of the venue.
foursquare_type (:obj:`str`): Optional. Foursquare type of the venue. (For example,
"arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".)
google_place_id (:obj:`str`): Optional. Google Places identifier of the venue.
google_place_type (:obj:`str`): Optional. Google Places type of the venue. (See
`supported types <https://developers.google.com/maps/documentation/places/web-service\
/supported_types>`_.)
"""
__slots__ = (
"address",
"foursquare_id",
"foursquare_type",
"google_place_id",
"google_place_type",
"location",
"title",
)
def __init__(
self,
location: Location,
title: str,
address: str,
foursquare_id: Optional[str] = None,
foursquare_type: Optional[str] = None,
google_place_id: Optional[str] = None,
google_place_type: Optional[str] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.location: Location = location
self.title: str = title
self.address: str = address
# Optionals
self.foursquare_id: Optional[str] = foursquare_id
self.foursquare_type: Optional[str] = foursquare_type
self.google_place_id: Optional[str] = google_place_id
self.google_place_type: Optional[str] = google_place_type
self._id_attrs = (self.location, self.title)
self._freeze()
@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Venue":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
data["location"] = de_json_optional(data.get("location"), Location, bot)
return super().de_json(data=data, bot=bot)

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Video."""
import datetime as dtm
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional, Union
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.argumentparsing import de_list_optional, parse_sequence_arg, to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.types import JSONDict, TimePeriod
if TYPE_CHECKING:
from telegram import Bot
class Video(_BaseThumbedMedium):
"""This object represents a video file.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by the sender.
height (:obj:`int`): Video height as defined by the sender.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the video
in seconds as defined by the sender.
.. versionchanged:: v22.2
|time-period-input|
file_name (:obj:`str`, optional): Original filename as defined by the sender.
mime_type (:obj:`str`, optional): MIME type of a file as defined by the sender.
file_size (:obj:`int`, optional): File size in bytes.
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
.. versionadded:: 20.2
cover (Sequence[:class:`telegram.PhotoSize`], optional): Available sizes of the cover of
the video in the message.
.. versionadded:: 21.11
start_timestamp (:obj:`int` | :class:`datetime.timedelta`, optional): Timestamp in seconds
from which the video will play in the message
.. versionadded:: 21.11
.. versionchanged:: v22.2
|time-period-input|
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by the sender.
height (:obj:`int`): Video height as defined by the sender.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the video in seconds
as defined by the sender.
.. deprecated:: v22.2
|time-period-int-deprecated|
file_name (:obj:`str`): Optional. Original filename as defined by the sender.
mime_type (:obj:`str`): Optional. MIME type of a file as defined by the sender.
file_size (:obj:`int`): Optional. File size in bytes.
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
.. versionadded:: 20.2
cover (tuple[:class:`telegram.PhotoSize`]): Optional, Available sizes of the cover of
the video in the message.
.. versionadded:: 21.11
start_timestamp (:obj:`int` | :class:`datetime.timedelta`): Optional. Timestamp in seconds
from which the video will play in the message
.. versionadded:: 21.11
.. deprecated:: v22.2
|time-period-int-deprecated|
"""
__slots__ = (
"_duration",
"_start_timestamp",
"cover",
"file_name",
"height",
"mime_type",
"width",
)
def __init__(
self,
file_id: str,
file_unique_id: str,
width: int,
height: int,
duration: TimePeriod,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
file_name: Optional[str] = None,
thumbnail: Optional[PhotoSize] = None,
cover: Optional[Sequence[PhotoSize]] = None,
start_timestamp: Optional[TimePeriod] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
thumbnail=thumbnail,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Required
self.width: int = width
self.height: int = height
self._duration: dtm.timedelta = to_timedelta(duration)
# Optional
self.mime_type: Optional[str] = mime_type
self.file_name: Optional[str] = file_name
self.cover: Optional[Sequence[PhotoSize]] = parse_sequence_arg(cover)
self._start_timestamp: Optional[dtm.timedelta] = to_timedelta(start_timestamp)
@property
def duration(self) -> Union[int, dtm.timedelta]:
return get_timedelta_value( # type: ignore[return-value]
self._duration, attribute="duration"
)
@property
def start_timestamp(self) -> Optional[Union[int, dtm.timedelta]]:
return get_timedelta_value(self._start_timestamp, attribute="start_timestamp")
@classmethod
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Video":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
data["cover"] = de_list_optional(data.get("cover"), PhotoSize, bot)
return super().de_json(data=data, bot=bot)

View File

@@ -0,0 +1,107 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram VideoNote."""
import datetime as dtm
from typing import Optional, Union
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.types import JSONDict, TimePeriod
class VideoNote(_BaseThumbedMedium):
"""This object represents a video message (available in Telegram apps as of v.4.0).
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
.. versionchanged:: 20.5
|removed_thumb_note|
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
length (:obj:`int`): Video width and height (diameter of the video message) as defined
by sender.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the video in
seconds as defined by the sender.
.. versionchanged:: v22.2
|time-period-input|
file_size (:obj:`int`, optional): File size in bytes.
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
.. versionadded:: 20.2
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
length (:obj:`int`): Video width and height (diameter of the video message) as defined
by sender.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the video in seconds as
defined by the sender.
.. deprecated:: v22.2
|time-period-int-deprecated|
file_size (:obj:`int`): Optional. File size in bytes.
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
.. versionadded:: 20.2
"""
__slots__ = ("_duration", "length")
def __init__(
self,
file_id: str,
file_unique_id: str,
length: int,
duration: TimePeriod,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
thumbnail=thumbnail,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Required
self.length: int = length
self._duration: dtm.timedelta = to_timedelta(duration)
@property
def duration(self) -> Union[int, dtm.timedelta]:
return get_timedelta_value( # type: ignore[return-value]
self._duration, attribute="duration"
)

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Voice."""
import datetime as dtm
from typing import Optional, Union
from telegram._files._basemedium import _BaseMedium
from telegram._utils.argumentparsing import to_timedelta
from telegram._utils.datetime import get_timedelta_value
from telegram._utils.types import JSONDict, TimePeriod
class Voice(_BaseMedium):
"""This object represents a voice note.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`file_unique_id` is equal.
Args:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the audio in
seconds as defined by the sender.
.. versionchanged:: v22.2
|time-period-input|
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
file_size (:obj:`int`, optional): File size in bytes.
Attributes:
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
duration (:obj:`int` | :class:`datetime.timedelta`): Duration of the audio in seconds as
defined by the sender.
.. deprecated:: v22.2
|time-period-int-deprecated|
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
file_size (:obj:`int`): Optional. File size in bytes.
"""
__slots__ = ("_duration", "mime_type")
def __init__(
self,
file_id: str,
file_unique_id: str,
duration: TimePeriod,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
file_unique_id=file_unique_id,
file_size=file_size,
api_kwargs=api_kwargs,
)
with self._unfrozen():
# Required
self._duration: dtm.timedelta = to_timedelta(duration)
# Optional
self.mime_type: Optional[str] = mime_type
@property
def duration(self) -> Union[int, dtm.timedelta]:
return get_timedelta_value( # type: ignore[return-value]
self._duration, attribute="duration"
)