mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
Explicit local_mode
Setting (#3154)
This commit is contained in:
parent
aed8e68fca
commit
fdfbcdf51e
21 changed files with 801 additions and 485 deletions
|
@ -64,6 +64,9 @@ source_suffix = ".rst"
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = "index"
|
master_doc = "index"
|
||||||
|
|
||||||
|
# Global substitutions
|
||||||
|
rst_prolog = (Path.cwd() / "../substitutions/global.rst").read_text(encoding="utf-8")
|
||||||
|
|
||||||
# -- Extension settings ------------------------------------------------
|
# -- Extension settings ------------------------------------------------
|
||||||
napoleon_use_admonition_for_examples = True
|
napoleon_use_admonition_for_examples = True
|
||||||
|
|
||||||
|
|
|
@ -304,6 +304,8 @@
|
||||||
- The first name of the bot
|
- The first name of the bot
|
||||||
* - :attr:`~telegram.Bot.last_name`
|
* - :attr:`~telegram.Bot.last_name`
|
||||||
- The last name of the bot
|
- The last name of the bot
|
||||||
|
* - :attr:`~telegram.Bot.local_mode`
|
||||||
|
- Whether the bot is running in local mode
|
||||||
* - :attr:`~telegram.Bot.username`
|
* - :attr:`~telegram.Bot.username`
|
||||||
- The username of the bot, without leading ``@``
|
- The username of the bot, without leading ``@``
|
||||||
* - :attr:`~telegram.Bot.link`
|
* - :attr:`~telegram.Bot.link`
|
||||||
|
|
17
docs/substitutions/global.rst
Normal file
17
docs/substitutions/global.rst
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.. |uploadinput| replace:: To upload a file, you can either pass a :term:`file object` (e.g. ``open("filename", "rb")``), the file contents as bytes or the path of the file (as string or :class:`pathlib.Path` object). In the latter case, the file contents will either be read as bytes or the file path will be passed to Telegram, depending on the :paramref:`~telegram.Bot.local_mode` setting.
|
||||||
|
|
||||||
|
.. |uploadinputnopath| replace:: To upload a file, you can either pass a :term:`file object` (e.g. ``open("filename", "rb")``) or the file contents as bytes. If the bot is running in :paramref:`~telegram.Bot.local_mode`, passing the path of the file (as string or :class:`pathlib.Path` object) is supported as well.
|
||||||
|
|
||||||
|
.. |fileinputbase| replace:: Pass a ``file_id`` as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one.
|
||||||
|
|
||||||
|
.. |fileinput| replace:: |fileinputbase| |uploadinput|
|
||||||
|
|
||||||
|
.. |fileinputnopath| replace:: |fileinputbase| |uploadinputnopath|
|
||||||
|
|
||||||
|
.. |thumbdocstringbase| replace:: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file.
|
||||||
|
|
||||||
|
.. |thumbdocstring| replace:: |thumbdocstringbase| |uploadinput|
|
||||||
|
|
||||||
|
.. |thumbdocstringnopath| replace:: |thumbdocstringbase| |uploadinputnopath|
|
||||||
|
|
||||||
|
.. |editreplymarkup| replace:: It is currently only possible to edit messages without :attr:`telegram.Message.reply_markup` or with inline keyboards.
|
418
telegram/_bot.py
418
telegram/_bot.py
|
@ -99,6 +99,7 @@ from telegram.request._requestparameter import RequestParameter
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from telegram import (
|
from telegram import (
|
||||||
InlineQueryResult,
|
InlineQueryResult,
|
||||||
|
InputFile,
|
||||||
InputMediaAudio,
|
InputMediaAudio,
|
||||||
InputMediaDocument,
|
InputMediaDocument,
|
||||||
InputMediaPhoto,
|
InputMediaPhoto,
|
||||||
|
@ -160,6 +161,10 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
``location``, ``filename``, ``venue``, ``contact``,
|
``location``, ``filename``, ``venue``, ``contact``,
|
||||||
``{read, write, connect, pool}_timeout``, ``api_kwargs``. Use a named argument for those,
|
``{read, write, connect, pool}_timeout``, ``api_kwargs``. Use a named argument for those,
|
||||||
and notice that some positional arguments changed position as a result.
|
and notice that some positional arguments changed position as a result.
|
||||||
|
* For uploading files, file paths are now always accepted. If :paramref:`local_mode` is
|
||||||
|
:obj:`False`, the file contents will be read in binary mode and uploaded. Otherwise,
|
||||||
|
the file path will be passed in the
|
||||||
|
`file URI scheme <https://en.wikipedia.org/wiki/File_URI_scheme>`_.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
token (:obj:`str`): Bot's unique authentication token.
|
token (:obj:`str`): Bot's unique authentication token.
|
||||||
|
@ -175,6 +180,14 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`telegram.request.HTTPXRequest` will be used.
|
:class:`telegram.request.HTTPXRequest` will be used.
|
||||||
private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data.
|
private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data.
|
||||||
private_key_password (:obj:`bytes`, optional): Password for above private key.
|
private_key_password (:obj:`bytes`, optional): Password for above private key.
|
||||||
|
local_mode (:obj:`bool`, optional): Set to :obj:`True`, if the :paramref:`base_url` is
|
||||||
|
the URI of a `Local Bot API Server <https://core.telegram.org/bots/api#using-a-local\
|
||||||
|
-bot-api-server>`_ that runs with the ``--local`` flag. Currently, the only effect of
|
||||||
|
this is that files are uploaded using their local path in the
|
||||||
|
`file URI scheme <https://en.wikipedia.org/wiki/File_URI_scheme>`_.
|
||||||
|
Defaults to :obj:`False`.
|
||||||
|
|
||||||
|
.. versionadded:: 20.0.
|
||||||
|
|
||||||
.. include:: inclusions/bot_methods.rst
|
.. include:: inclusions/bot_methods.rst
|
||||||
|
|
||||||
|
@ -189,6 +202,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"_request",
|
"_request",
|
||||||
"_logger",
|
"_logger",
|
||||||
"_initialized",
|
"_initialized",
|
||||||
|
"_local_mode",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -200,6 +214,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
get_updates_request: BaseRequest = None,
|
get_updates_request: BaseRequest = None,
|
||||||
private_key: bytes = None,
|
private_key: bytes = None,
|
||||||
private_key_password: bytes = None,
|
private_key_password: bytes = None,
|
||||||
|
local_mode: bool = False,
|
||||||
):
|
):
|
||||||
if not token:
|
if not token:
|
||||||
raise InvalidToken("You must pass the token you received from https://t.me/Botfather!")
|
raise InvalidToken("You must pass the token you received from https://t.me/Botfather!")
|
||||||
|
@ -207,6 +222,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
|
|
||||||
self._base_url = base_url + self._token
|
self._base_url = base_url + self._token
|
||||||
self._base_file_url = base_file_url + self._token
|
self._base_file_url = base_file_url + self._token
|
||||||
|
self._local_mode = local_mode
|
||||||
self._bot_user: Optional[User] = None
|
self._bot_user: Optional[User] = None
|
||||||
self._private_key = None
|
self._private_key = None
|
||||||
self._logger = logging.getLogger(__name__)
|
self._logger = logging.getLogger(__name__)
|
||||||
|
@ -253,6 +269,14 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
return self._base_file_url
|
return self._base_file_url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def local_mode(self) -> bool:
|
||||||
|
""":obj:`bool`: Whether this bot is running in local mode.
|
||||||
|
|
||||||
|
.. versionadded:: 20.0
|
||||||
|
"""
|
||||||
|
return self._local_mode
|
||||||
|
|
||||||
# Proper type hints are difficult because:
|
# Proper type hints are difficult because:
|
||||||
# 1. cryptography doesn't have a nice base class, so it would get lengthy
|
# 1. cryptography doesn't have a nice base class, so it would get lengthy
|
||||||
# 2. we can't import cryptography if it's not installed
|
# 2. we can't import cryptography if it's not installed
|
||||||
|
@ -283,6 +307,21 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
def _parse_file_input(
|
||||||
|
self,
|
||||||
|
file_input: Union[FileInput, "TelegramObject"],
|
||||||
|
tg_type: Type["TelegramObject"] = None,
|
||||||
|
filename: str = None,
|
||||||
|
attach: bool = False,
|
||||||
|
) -> Union[str, "InputFile", Any]:
|
||||||
|
return parse_file_input(
|
||||||
|
file_input=file_input,
|
||||||
|
tg_type=tg_type,
|
||||||
|
filename=filename,
|
||||||
|
attach=attach,
|
||||||
|
local_mode=self._local_mode,
|
||||||
|
)
|
||||||
|
|
||||||
def _insert_defaults(self, data: Dict[str, object]) -> None: # skipcq: PYL-R0201
|
def _insert_defaults(self, data: Dict[str, object]) -> None: # skipcq: PYL-R0201
|
||||||
"""This method is here to make ext.Defaults work. Because we need to be able to tell
|
"""This method is here to make ext.Defaults work. Because we need to be able to tell
|
||||||
e.g. `send_message(chat_id, text)` from `send_message(chat_id, text, parse_mode=None)`, the
|
e.g. `send_message(chat_id, text)` from `send_message(chat_id, text, parse_mode=None)`, the
|
||||||
|
@ -888,10 +927,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
) -> Message:
|
) -> Message:
|
||||||
"""Use this method to send photos.
|
"""Use this method to send photos.
|
||||||
|
|
||||||
Note:
|
|
||||||
The photo argument can be either a file_id, an URL or a file from disk
|
|
||||||
``open(filename, 'rb')``
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_photo`, :attr:`telegram.Chat.send_photo`,
|
.. seealso:: :attr:`telegram.Message.reply_photo`, :attr:`telegram.Chat.send_photo`,
|
||||||
:attr:`telegram.User.send_photo`
|
:attr:`telegram.User.send_photo`
|
||||||
|
|
||||||
|
@ -900,13 +935,15 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
photo (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
photo (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.PhotoSize`): Photo to send.
|
:class:`telegram.PhotoSize`): Photo to send.
|
||||||
Pass a file_id as String to send a photo that exists on the Telegram servers
|
|fileinput|
|
||||||
(recommended), pass an HTTP URL as a String for Telegram to get a photo from the
|
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
|
||||||
Internet, or upload a new photo using multipart/form-data. Lastly you can pass
|
|
||||||
an existing :class:`telegram.PhotoSize` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
caption (:obj:`str`, optional): Photo caption (may also be used when resending photos
|
caption (:obj:`str`, optional): Photo caption (may also be used when resending photos
|
||||||
by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH`
|
by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH`
|
||||||
characters after entities parsing.
|
characters after entities parsing.
|
||||||
|
@ -961,7 +998,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"photo": parse_file_input(photo, PhotoSize, filename=filename),
|
"photo": self._parse_file_input(photo, PhotoSize, filename=filename),
|
||||||
"parse_mode": parse_mode,
|
"parse_mode": parse_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,10 +1058,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
|
|
||||||
For sending voice messages, use the :meth:`send_voice` method instead.
|
For sending voice messages, use the :meth:`send_voice` method instead.
|
||||||
|
|
||||||
Note:
|
|
||||||
The audio argument can be either a file_id, an URL or a file from disk
|
|
||||||
``open(filename, 'rb')``
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_audio`, :attr:`telegram.Chat.send_audio`,
|
.. seealso:: :attr:`telegram.Message.reply_audio`, :attr:`telegram.Chat.send_audio`,
|
||||||
:attr:`telegram.User.send_audio`
|
:attr:`telegram.User.send_audio`
|
||||||
|
|
||||||
|
@ -1033,13 +1066,15 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
audio (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
audio (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Audio`): Audio file to send.
|
:class:`telegram.Audio`): Audio file to send.
|
||||||
Pass a file_id as String to send an audio file that exists on the Telegram servers
|
|fileinput|
|
||||||
(recommended), pass an HTTP URL as a String for Telegram to get an audio file from
|
Lastly you can pass an existing :class:`telegram.Audio` object to send.
|
||||||
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
|
|
||||||
an existing :class:`telegram.Audio` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
caption (:obj:`str`, optional): Audio caption,
|
caption (:obj:`str`, optional): Audio caption,
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
entities parsing.
|
entities parsing.
|
||||||
|
@ -1067,16 +1102,16 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
||||||
Additional interface options. An object for an inline keyboard, custom reply
|
Additional interface options. An object for an inline keyboard, custom reply
|
||||||
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
of the file sent; can be ignored if
|
optional): |thumbdocstring|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
|
|
||||||
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
filename (:obj:`str`, optional): Custom file name for the audio, when uploading a
|
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
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
|
@ -1106,7 +1141,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"audio": parse_file_input(audio, Audio, filename=filename),
|
"audio": self._parse_file_input(audio, Audio, filename=filename),
|
||||||
"parse_mode": parse_mode,
|
"parse_mode": parse_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1122,7 +1157,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
if caption_entities:
|
if caption_entities:
|
||||||
data["caption_entities"] = caption_entities
|
data["caption_entities"] = caption_entities
|
||||||
if thumb:
|
if thumb:
|
||||||
data["thumb"] = parse_file_input(thumb, attach=True)
|
data["thumb"] = self._parse_file_input(thumb, attach=True)
|
||||||
|
|
||||||
return await self._send_message( # type: ignore[return-value]
|
return await self._send_message( # type: ignore[return-value]
|
||||||
"sendAudio",
|
"sendAudio",
|
||||||
|
@ -1169,12 +1204,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, this limit may be
|
:tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, this limit may be
|
||||||
changed in the future.
|
changed in the future.
|
||||||
|
|
||||||
Note:
|
|
||||||
* The document argument can be either a file_id, an URL or a file from disk
|
|
||||||
``open(filename, 'rb')``.
|
|
||||||
|
|
||||||
* Sending by URL will currently only work ``GIF``, ``PDF`` & ``ZIP`` files.
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_document`, :attr:`telegram.Chat.send_document`,
|
.. seealso:: :attr:`telegram.Message.reply_document`, :attr:`telegram.Chat.send_document`,
|
||||||
:attr:`telegram.User.send_document`
|
:attr:`telegram.User.send_document`
|
||||||
|
|
||||||
|
@ -1183,13 +1212,18 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
document (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
document (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Document`): File to send.
|
:class:`telegram.Document`): File to send.
|
||||||
Pass a file_id as String to send a file that exists on the Telegram servers
|
|fileinput|
|
||||||
(recommended), pass an HTTP URL as a String for Telegram to get a file from the
|
Lastly you can pass an existing :class:`telegram.Document` object to send.
|
||||||
Internet, or upload a new one using multipart/form-data. Lastly you can pass
|
|
||||||
an existing :class:`telegram.Document` object to send.
|
Note:
|
||||||
|
Sending by URL will currently only work ``GIF``, ``PDF`` & ``ZIP`` files.
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
caption (:obj:`str`, optional): Document caption (may also be used when resending
|
caption (:obj:`str`, optional): Document caption (may also be used when resending
|
||||||
documents by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH`
|
documents by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH`
|
||||||
characters after entities parsing.
|
characters after entities parsing.
|
||||||
|
@ -1216,16 +1250,16 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
||||||
Additional interface options. An object for an inline keyboard, custom reply
|
Additional interface options. An object for an inline keyboard, custom reply
|
||||||
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
of the file sent; can be ignored if
|
optional): |thumbdocstring|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
|
|
||||||
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
filename (:obj:`str`, optional): Custom file name for the document, when uploading a
|
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
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
|
@ -1253,7 +1287,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"document": parse_file_input(document, Document, filename=filename),
|
"document": self._parse_file_input(document, Document, filename=filename),
|
||||||
"parse_mode": parse_mode,
|
"parse_mode": parse_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1265,7 +1299,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
if disable_content_type_detection is not None:
|
if disable_content_type_detection is not None:
|
||||||
data["disable_content_type_detection"] = disable_content_type_detection
|
data["disable_content_type_detection"] = disable_content_type_detection
|
||||||
if thumb:
|
if thumb:
|
||||||
data["thumb"] = parse_file_input(thumb, attach=True)
|
data["thumb"] = self._parse_file_input(thumb, attach=True)
|
||||||
|
|
||||||
return await self._send_message( # type: ignore[return-value]
|
return await self._send_message( # type: ignore[return-value]
|
||||||
"sendDocument",
|
"sendDocument",
|
||||||
|
@ -1302,10 +1336,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
Use this method to send static ``.WEBP``, animated ``.TGS``, or video ``.WEBM`` stickers.
|
Use this method to send static ``.WEBP``, animated ``.TGS``, or video ``.WEBM`` stickers.
|
||||||
|
|
||||||
Note:
|
|
||||||
The :paramref:`sticker` argument can be either a file_id, an URL or a file from disk
|
|
||||||
``open(filename, 'rb')``
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_sticker`, :attr:`telegram.Chat.send_sticker`,
|
.. seealso:: :attr:`telegram.Message.reply_sticker`, :attr:`telegram.Chat.send_sticker`,
|
||||||
:attr:`telegram.User.send_sticker`
|
:attr:`telegram.User.send_sticker`
|
||||||
|
|
||||||
|
@ -1314,13 +1344,15 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Sticker`): Sticker to send.
|
:class:`telegram.Sticker`): Sticker to send.
|
||||||
Pass a file_id as String to send a file that exists on the Telegram servers
|
|fileinput|
|
||||||
(recommended), pass an HTTP URL as a String for Telegram to get a .webp file from
|
Lastly you can pass an existing :class:`telegram.Sticker` object to send.
|
||||||
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
|
|
||||||
an existing :class:`telegram.Sticker` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
|
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
|
||||||
receive a notification with no sound.
|
receive a notification with no sound.
|
||||||
protect_content (:obj:`bool`, optional): Protects the contents of the sent message from
|
protect_content (:obj:`bool`, optional): Protects the contents of the sent message from
|
||||||
|
@ -1359,7 +1391,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`telegram.error.TelegramError`
|
:class:`telegram.error.TelegramError`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {"chat_id": chat_id, "sticker": parse_file_input(sticker, Sticker)}
|
data: JSONDict = {"chat_id": chat_id, "sticker": self._parse_file_input(sticker, Sticker)}
|
||||||
return await self._send_message( # type: ignore[return-value]
|
return await self._send_message( # type: ignore[return-value]
|
||||||
"sendSticker",
|
"sendSticker",
|
||||||
data,
|
data,
|
||||||
|
@ -1410,11 +1442,9 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
changed in the future.
|
changed in the future.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
* The :paramref:`video` argument can be either a file_id, an URL or a file from disk
|
:paramref:`thumb` will be ignored for small video files, for which Telegram can
|
||||||
``open(filename, 'rb')``
|
easily generate thumbnails. However, this behaviour is undocumented and might be
|
||||||
* :paramref:`thumb` will be ignored for small video files, for which Telegram can
|
changed by Telegram.
|
||||||
easily generate thumbnails. However, this behaviour is undocumented and might be
|
|
||||||
changed by Telegram.
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_video`, :attr:`telegram.Chat.send_video`,
|
.. seealso:: :attr:`telegram.Message.reply_video`, :attr:`telegram.Chat.send_video`,
|
||||||
:attr:`telegram.User.send_video`
|
:attr:`telegram.User.send_video`
|
||||||
|
@ -1424,13 +1454,15 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
video (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
video (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Video`): Video file to send.
|
:class:`telegram.Video`): Video file to send.
|
||||||
Pass a file_id as String to send an video file that exists on the Telegram servers
|
|fileinput|
|
||||||
(recommended), pass an HTTP URL as a String for Telegram to get an video file from
|
Lastly you can pass an existing :class:`telegram.Video` object to send.
|
||||||
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
|
|
||||||
an existing :class:`telegram.Video` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
duration (:obj:`int`, optional): Duration of sent video in seconds.
|
duration (:obj:`int`, optional): Duration of sent video in seconds.
|
||||||
width (:obj:`int`, optional): Video width.
|
width (:obj:`int`, optional): Video width.
|
||||||
height (:obj:`int`, optional): Video height.
|
height (:obj:`int`, optional): Video height.
|
||||||
|
@ -1460,16 +1492,16 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
||||||
Additional interface options. An object for an inline keyboard, custom reply
|
Additional interface options. An object for an inline keyboard, custom reply
|
||||||
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
of the file sent; can be ignored if
|
optional): |thumbdocstring|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
|
|
||||||
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
filename (:obj:`str`, optional): Custom file name for the video, when uploading a
|
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
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
|
@ -1499,7 +1531,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"video": parse_file_input(video, Video, filename=filename),
|
"video": self._parse_file_input(video, Video, filename=filename),
|
||||||
"parse_mode": parse_mode,
|
"parse_mode": parse_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1516,7 +1548,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
if height:
|
if height:
|
||||||
data["height"] = height
|
data["height"] = height
|
||||||
if thumb:
|
if thumb:
|
||||||
data["thumb"] = parse_file_input(thumb, attach=True)
|
data["thumb"] = self._parse_file_input(thumb, attach=True)
|
||||||
|
|
||||||
return await self._send_message( # type: ignore[return-value]
|
return await self._send_message( # type: ignore[return-value]
|
||||||
"sendVideo",
|
"sendVideo",
|
||||||
|
@ -1559,11 +1591,9 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
Use this method to send video messages.
|
Use this method to send video messages.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
* The :paramref:`video_note` argument can be either a file_id or a file from disk
|
:paramref:`thumb` will be ignored for small video files, for which Telegram can
|
||||||
``open(filename, 'rb')``
|
easily generate thumbnails. However, this behaviour is undocumented and might be
|
||||||
* :paramref:`thumb` will be ignored for small video files, for which Telegram can
|
changed by Telegram.
|
||||||
easily generate thumbnails. However, this behaviour is undocumented and might be
|
|
||||||
changed by Telegram.
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_video_note`,
|
.. seealso:: :attr:`telegram.Message.reply_video_note`,
|
||||||
:attr:`telegram.Chat.send_video_note`,
|
:attr:`telegram.Chat.send_video_note`,
|
||||||
|
@ -1573,14 +1603,19 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
video_note (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
video_note (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.VideoNote`): Video note
|
:class:`telegram.VideoNote`): Video note to send.
|
||||||
to send. Pass a file_id as String to send a video note that exists on the Telegram
|
Pass a file_id as String to send a video note that exists on the Telegram
|
||||||
servers (recommended) or upload a new video using multipart/form-data. Or you can
|
servers (recommended) or upload a new video using multipart/form-data.
|
||||||
pass an existing :class:`telegram.VideoNote` object to send. Sending video notes by
|
|uploadinput|
|
||||||
a URL is currently unsupported.
|
Lastly you can pass an existing :class:`telegram.VideoNote` object to send.
|
||||||
|
Sending video notes by a URL is currently unsupported.
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
duration (:obj:`int`, optional): Duration of sent video in seconds.
|
duration (:obj:`int`, optional): Duration of sent video in seconds.
|
||||||
length (:obj:`int`, optional): Video width and height, i.e. diameter of the video
|
length (:obj:`int`, optional): Video width and height, i.e. diameter of the video
|
||||||
message.
|
message.
|
||||||
|
@ -1599,16 +1634,16 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
||||||
Additional interface options. An object for an inline keyboard, custom reply
|
Additional interface options. An object for an inline keyboard, custom reply
|
||||||
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
of the file sent; can be ignored if
|
optional): |thumbdocstring|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
|
|
||||||
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
filename (:obj:`str`, optional): Custom file name for the video note, when uploading a
|
filename (:obj:`str`, optional): Custom file name for the video note, when uploading a
|
||||||
new file. Convenience parameter, useful e.g. when sending files generated by the
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
|
@ -1638,7 +1673,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"video_note": parse_file_input(video_note, VideoNote, filename=filename),
|
"video_note": self._parse_file_input(video_note, VideoNote, filename=filename),
|
||||||
}
|
}
|
||||||
|
|
||||||
if duration is not None:
|
if duration is not None:
|
||||||
|
@ -1646,7 +1681,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
if length is not None:
|
if length is not None:
|
||||||
data["length"] = length
|
data["length"] = length
|
||||||
if thumb:
|
if thumb:
|
||||||
data["thumb"] = parse_file_input(thumb, attach=True)
|
data["thumb"] = self._parse_file_input(thumb, attach=True)
|
||||||
|
|
||||||
return await self._send_message( # type: ignore[return-value]
|
return await self._send_message( # type: ignore[return-value]
|
||||||
"sendVideoNote",
|
"sendVideoNote",
|
||||||
|
@ -1696,7 +1731,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:paramref:`thumb` will be ignored for small files, for which Telegram can easily
|
:paramref:`thumb` will be ignored for small files, for which Telegram can easily
|
||||||
generate thumb nails. However, this behaviour is undocumented and might be changed
|
generate thumbnails. However, this behaviour is undocumented and might be changed
|
||||||
by Telegram.
|
by Telegram.
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_animation`,
|
.. seealso:: :attr:`telegram.Message.reply_animation`,
|
||||||
|
@ -1707,10 +1742,8 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
animation (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
animation (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Animation`): Animation to
|
:class:`telegram.Animation`): Animation to send.
|
||||||
send. Pass a file_id as String to send an animation that exists on the Telegram
|
|fileinput|
|
||||||
servers (recommended), pass an HTTP URL as a String for Telegram to get an
|
|
||||||
animation from the Internet, or upload a new animation using multipart/form-data.
|
|
||||||
Lastly you can pass an existing :class:`telegram.Animation` object to send.
|
Lastly you can pass an existing :class:`telegram.Animation` object to send.
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
|
@ -1718,15 +1751,16 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
duration (:obj:`int`, optional): Duration of sent animation in seconds.
|
duration (:obj:`int`, optional): Duration of sent animation in seconds.
|
||||||
width (:obj:`int`, optional): Animation width.
|
width (:obj:`int`, optional): Animation width.
|
||||||
height (:obj:`int`, optional): Animation height.
|
height (:obj:`int`, optional): Animation height.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
of the file sent; can be ignored if
|
optional): |thumbdocstring|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than 200 kB in size. A thumbnail's width and height should
|
|
||||||
not exceed 320. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
caption (:obj:`str`, optional): Animation caption (may also be used when resending
|
caption (:obj:`str`, optional): Animation caption (may also be used when resending
|
||||||
animations by file_id),
|
animations by file_id),
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
|
@ -1782,7 +1816,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"animation": parse_file_input(animation, Animation, filename=filename),
|
"animation": self._parse_file_input(animation, Animation, filename=filename),
|
||||||
"parse_mode": parse_mode,
|
"parse_mode": parse_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1793,7 +1827,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
if height:
|
if height:
|
||||||
data["height"] = height
|
data["height"] = height
|
||||||
if thumb:
|
if thumb:
|
||||||
data["thumb"] = parse_file_input(thumb, attach=True)
|
data["thumb"] = self._parse_file_input(thumb, attach=True)
|
||||||
if caption:
|
if caption:
|
||||||
data["caption"] = caption
|
data["caption"] = caption
|
||||||
if caption_entities:
|
if caption_entities:
|
||||||
|
@ -1844,11 +1878,8 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
in size, this limit may be changed in the future.
|
in size, this limit may be changed in the future.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
* The :paramref:`voice` argument can be either a file_id, an URL or a file from disk
|
To use this method, the file must have the type :mimetype:`audio/ogg` and be no more
|
||||||
``open(filename, 'rb')``.
|
than ``1MB`` in size. ``1-20MB`` voice notes will be sent as files.
|
||||||
|
|
||||||
* To use this method, the file must have the type :mimetype:`audio/ogg` and be no more
|
|
||||||
than ``1MB`` in size. ``1-20MB`` voice notes will be sent as files.
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.reply_voice`, :attr:`telegram.Chat.send_voice`,
|
.. seealso:: :attr:`telegram.Message.reply_voice`, :attr:`telegram.Chat.send_voice`,
|
||||||
:attr:`telegram.User.send_voice`
|
:attr:`telegram.User.send_voice`
|
||||||
|
@ -1858,13 +1889,15 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
voice (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
voice (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Voice`): Voice file to send.
|
:class:`telegram.Voice`): Voice file to send.
|
||||||
Pass a file_id as String to send an voice file that exists on the Telegram servers
|
|fileinput|
|
||||||
(recommended), pass an HTTP URL as a String for Telegram to get an voice file from
|
Lastly you can pass an existing :class:`telegram.Voice` object to send.
|
||||||
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
|
|
||||||
an existing :class:`telegram.Voice` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
caption (:obj:`str`, optional): Voice message caption,
|
caption (:obj:`str`, optional): Voice message caption,
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
entities parsing.
|
entities parsing.
|
||||||
|
@ -1920,7 +1953,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {
|
data: JSONDict = {
|
||||||
"chat_id": chat_id,
|
"chat_id": chat_id,
|
||||||
"voice": parse_file_input(voice, Voice, filename=filename),
|
"voice": self._parse_file_input(voice, Voice, filename=filename),
|
||||||
"parse_mode": parse_mode,
|
"parse_mode": parse_mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3502,8 +3535,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
Use this method to edit text and game messages.
|
Use this method to edit text and game messages.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
It is currently only possible to edit messages without
|
|editreplymarkup|.
|
||||||
:attr:`telegram.Message.reply_markup` or with inline keyboards.
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.edit_text`,
|
.. seealso:: :attr:`telegram.Message.edit_text`,
|
||||||
:attr:`telegram.CallbackQuery.edit_message_text`
|
:attr:`telegram.CallbackQuery.edit_message_text`
|
||||||
|
@ -3601,8 +3633,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
Use this method to edit captions of messages.
|
Use this method to edit captions of messages.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
It is currently only possible to edit messages without
|
|editreplymarkup|
|
||||||
:attr:`telegram.Message.reply_markup` or with inline keyboards
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.edit_caption`,
|
.. seealso:: :attr:`telegram.Message.edit_caption`,
|
||||||
:attr:`telegram.CallbackQuery.edit_message_caption`
|
:attr:`telegram.CallbackQuery.edit_message_caption`
|
||||||
|
@ -3698,8 +3729,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:attr:`~telegram.File.file_id` or specify a URL.
|
:attr:`~telegram.File.file_id` or specify a URL.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
It is currently only possible to edit messages without
|
|editreplymarkup|
|
||||||
:attr:`telegram.Message.reply_markup` or with inline keyboards
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.edit_media`,
|
.. seealso:: :attr:`telegram.Message.edit_media`,
|
||||||
:attr:`telegram.CallbackQuery.edit_message_media`
|
:attr:`telegram.CallbackQuery.edit_message_media`
|
||||||
|
@ -3779,8 +3809,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
(for inline bots).
|
(for inline bots).
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
It is currently only possible to edit messages without
|
|editreplymarkup|
|
||||||
:attr:`telegram.Message.reply_markup` or with inline keyboards
|
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Message.edit_reply_markup`,
|
.. seealso:: :attr:`telegram.Message.edit_reply_markup`,
|
||||||
:attr:`telegram.CallbackQuery.edit_message_reply_markup`
|
:attr:`telegram.CallbackQuery.edit_message_reply_markup`
|
||||||
|
@ -3856,6 +3885,12 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
) -> List[Update]:
|
) -> List[Update]:
|
||||||
"""Use this method to receive incoming updates using long polling.
|
"""Use this method to receive incoming updates using long polling.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
1. This method will not work if an outgoing webhook is set up.
|
||||||
|
2. In order to avoid getting duplicate updates, recalculate offset after each
|
||||||
|
server response.
|
||||||
|
3. To take full advantage of this library take a look at :class:`telegram.ext.Updater`
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
offset (:obj:`int`, optional): Identifier of the first update to be returned. Must be
|
offset (:obj:`int`, optional): Identifier of the first update to be returned. Must be
|
||||||
greater by one than the highest among the identifiers of previously received
|
greater by one than the highest among the identifiers of previously received
|
||||||
|
@ -3895,12 +3930,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||||
Telegram API.
|
Telegram API.
|
||||||
|
|
||||||
Note:
|
|
||||||
1. This method will not work if an outgoing webhook is set up.
|
|
||||||
2. In order to avoid getting duplicate updates, recalculate offset after each
|
|
||||||
server response.
|
|
||||||
3. To take full advantage of this library take a look at :class:`telegram.ext.Updater`
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[:class:`telegram.Update`]
|
List[:class:`telegram.Update`]
|
||||||
|
|
||||||
|
@ -3970,15 +3999,25 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
``X-Telegram-Bot-Api-Secret-Token`` with the secret token as content.
|
``X-Telegram-Bot-Api-Secret-Token`` with the secret token as content.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
The certificate argument should be a file from disk ``open(filename, 'rb')``.
|
1. You will not be able to receive updates using :meth:`get_updates` for long as an
|
||||||
|
outgoing webhook is set up.
|
||||||
|
2. To use a self-signed certificate, you need to upload your public key certificate
|
||||||
|
using :paramref:`certificate` parameter. Please upload as
|
||||||
|
:class:`~telegram.InputFile`, sending a String will not work.
|
||||||
|
3. Ports currently supported for Webhooks:
|
||||||
|
:attr:`telegram.constants.SUPPORTED_WEBHOOK_PORTS`.
|
||||||
|
|
||||||
|
If you're having any trouble setting up webhooks, please check out this `guide to
|
||||||
|
Webhooks`_.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
url (:obj:`str`): HTTPS url to send updates to. Use an empty string to remove webhook
|
url (:obj:`str`): HTTPS url to send updates to. Use an empty string to remove webhook
|
||||||
integration.
|
integration.
|
||||||
certificate (:term:`file object`): Upload your public key certificate so that the root
|
certificate (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`):
|
||||||
certificate in use can be checked. See our self-signed guide for details.
|
Upload your public key certificate so that the root
|
||||||
(https://github.com/python-telegram-bot/python-telegram-bot/wiki/Webhooks#\
|
certificate in use can be checked. See our `self-signed guide <https://github.com/\
|
||||||
creating-a-self-signed-certificate-using-openssl)
|
python-telegram-bot/python-telegram-bot/wiki/Webhooks#creating-a-self-signed-\
|
||||||
|
certificate-using-openssl>`_ for details. |uploadinputnopath|
|
||||||
ip_address (:obj:`str`, optional): The fixed IP address which will be used to send
|
ip_address (:obj:`str`, optional): The fixed IP address which will be used to send
|
||||||
webhook requests instead of the IP address resolved through DNS.
|
webhook requests instead of the IP address resolved through DNS.
|
||||||
max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS
|
max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS
|
||||||
|
@ -4021,18 +4060,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||||
Telegram API.
|
Telegram API.
|
||||||
|
|
||||||
Note:
|
|
||||||
1. You will not be able to receive updates using :meth:`get_updates` for long as an
|
|
||||||
outgoing webhook is set up.
|
|
||||||
2. To use a self-signed certificate, you need to upload your public key certificate
|
|
||||||
using certificate parameter. Please upload as InputFile, sending a String will not
|
|
||||||
work.
|
|
||||||
3. Ports currently supported for Webhooks:
|
|
||||||
:attr:`telegram.constants.SUPPORTED_WEBHOOK_PORTS`.
|
|
||||||
|
|
||||||
If you're having any trouble setting up webhooks, please check out this `guide to
|
|
||||||
Webhooks`_.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`bool` On success, :obj:`True` is returned.
|
:obj:`bool` On success, :obj:`True` is returned.
|
||||||
|
|
||||||
|
@ -4045,7 +4072,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
data: JSONDict = {"url": url}
|
data: JSONDict = {"url": url}
|
||||||
|
|
||||||
if certificate:
|
if certificate:
|
||||||
data["certificate"] = parse_file_input(certificate)
|
data["certificate"] = self._parse_file_input(certificate)
|
||||||
if max_connections is not None:
|
if max_connections is not None:
|
||||||
data["max_connections"] = max_connections
|
data["max_connections"] = max_connections
|
||||||
if allowed_updates is not None:
|
if allowed_updates is not None:
|
||||||
|
@ -5492,6 +5519,13 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
link is revoked. The bot must be an administrator in the chat for this to work and must
|
link is revoked. The bot must be an administrator in the chat for this to work and must
|
||||||
have the appropriate admin rights.
|
have the appropriate admin rights.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Each administrator in a chat generates their own invite links. Bots can't use invite
|
||||||
|
links generated by other administrators. If you want your bot to work with invite
|
||||||
|
links, it will need to generate its own link using :meth:`export_chat_invite_link` or
|
||||||
|
by calling the :meth:`get_chat` method. If your bot needs to generate a new primary
|
||||||
|
invite link replacing its previous one, use :attr:`export_chat_invite_link` again.
|
||||||
|
|
||||||
.. seealso:: :attr:`telegram.Chat.export_invite_link`
|
.. seealso:: :attr:`telegram.Chat.export_invite_link`
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -5514,13 +5548,6 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||||
Telegram API.
|
Telegram API.
|
||||||
|
|
||||||
Note:
|
|
||||||
Each administrator in a chat generates their own invite links. Bots can't use invite
|
|
||||||
links generated by other administrators. If you want your bot to work with invite
|
|
||||||
links, it will need to generate its own link using :meth:`export_chat_invite_link` or
|
|
||||||
by calling the :meth:`get_chat` method. If your bot needs to generate a new primary
|
|
||||||
invite link replacing its previous one, use :attr:`export_chat_invite_link` again.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`str`: New invite link on success.
|
:obj:`str`: New invite link on success.
|
||||||
|
|
||||||
|
@ -5955,10 +5982,15 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||||
of the target channel (in the format ``@channelusername``).
|
of the target channel (in the format ``@channelusername``).
|
||||||
photo (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`): New chat photo.
|
photo (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`): New chat photo.
|
||||||
|
|uploadinput|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||||
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
|
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
|
||||||
|
@ -5982,7 +6014,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager):
|
||||||
:class:`telegram.error.TelegramError`
|
:class:`telegram.error.TelegramError`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {"chat_id": chat_id, "photo": parse_file_input(photo)}
|
data: JSONDict = {"chat_id": chat_id, "photo": self._parse_file_input(photo)}
|
||||||
result = await self._post(
|
result = await self._post(
|
||||||
"setChatPhoto",
|
"setChatPhoto",
|
||||||
data,
|
data,
|
||||||
|
@ -6488,19 +6520,20 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
:meth:`create_new_sticker_set` and :meth:`add_sticker_to_set` methods (can be used multiple
|
:meth:`create_new_sticker_set` and :meth:`add_sticker_to_set` methods (can be used multiple
|
||||||
times).
|
times).
|
||||||
|
|
||||||
Note:
|
|
||||||
The :paramref:`png_sticker` argument can be either a file_id, an URL or a file from
|
|
||||||
disk ``open(filename, 'rb')``
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id (:obj:`int`): User identifier of sticker file owner.
|
user_id (:obj:`int`): User identifier of sticker file owner.
|
||||||
png_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`):
|
png_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`):
|
||||||
**PNG** image with the sticker, must be up to 512 kilobytes in size,
|
**PNG** image with the sticker, must be up to 512 kilobytes in size,
|
||||||
dimensions must not exceed 512px, and either width or height must be exactly 512px.
|
dimensions must not exceed 512px, and either width or height must be exactly 512px.
|
||||||
|
|uploadinput|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||||
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
|
:paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to
|
||||||
|
@ -6524,7 +6557,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
:class:`telegram.error.TelegramError`
|
:class:`telegram.error.TelegramError`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {"user_id": user_id, "png_sticker": parse_file_input(png_sticker)}
|
data: JSONDict = {"user_id": user_id, "png_sticker": self._parse_file_input(png_sticker)}
|
||||||
result = await self._post(
|
result = await self._post(
|
||||||
"uploadStickerFile",
|
"uploadStickerFile",
|
||||||
data,
|
data,
|
||||||
|
@ -6566,10 +6599,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
of the arguments had to be changed. Use keyword arguments to make sure that the
|
of the arguments had to be changed. Use keyword arguments to make sure that the
|
||||||
arguments are passed correctly.
|
arguments are passed correctly.
|
||||||
|
|
||||||
Note:
|
|
||||||
The :paramref:`png_sticker` and :paramref:`tgs_sticker` argument can be either a
|
|
||||||
file_id, an URL or a file from disk ``open(filename, 'rb')``
|
|
||||||
|
|
||||||
.. versionchanged:: 20.0
|
.. versionchanged:: 20.0
|
||||||
The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type`
|
The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type`
|
||||||
instead.
|
instead.
|
||||||
|
@ -6585,27 +6614,37 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
png_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
png_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
||||||
optional): **PNG** image with the sticker,
|
optional): **PNG** image with the sticker,
|
||||||
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
|
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
|
||||||
and either width or height must be exactly 512px. Pass a file_id as a String to
|
and either width or height must be exactly 512px.
|
||||||
send a file that already exists on the Telegram servers, pass an HTTP URL as a
|
|fileinput|
|
||||||
String for Telegram to get a file from the Internet, or upload a new one
|
|
||||||
using multipart/form-data.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
tgs_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
tgs_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
||||||
optional): **TGS** animation with the sticker, uploaded using multipart/form-data.
|
optional): **TGS** animation with the sticker. |uploadinput|
|
||||||
See https://core.telegram.org/stickers#animated-sticker-requirements for technical
|
See https://core.telegram.org/stickers#animated-sticker-requirements for technical
|
||||||
requirements.
|
requirements.
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
webm_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`,\
|
webm_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`,\
|
||||||
optional): **WEBM** video with the sticker, uploaded using multipart/form-data.
|
optional): **WEBM** video with the sticker. |uploadinput|
|
||||||
See https://core.telegram.org/stickers#video-sticker-requirements for
|
See https://core.telegram.org/stickers#video-sticker-requirements for
|
||||||
technical requirements.
|
technical requirements.
|
||||||
|
|
||||||
.. versionadded:: 13.11
|
.. versionadded:: 13.11
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
|
|
||||||
emojis (:obj:`str`): One or more emoji corresponding to the sticker.
|
emojis (:obj:`str`): One or more emoji corresponding to the sticker.
|
||||||
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
|
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
|
||||||
should be placed on faces.
|
should be placed on faces.
|
||||||
|
@ -6642,11 +6681,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
data: JSONDict = {"user_id": user_id, "name": name, "title": title, "emojis": emojis}
|
data: JSONDict = {"user_id": user_id, "name": name, "title": title, "emojis": emojis}
|
||||||
|
|
||||||
if png_sticker is not None:
|
if png_sticker is not None:
|
||||||
data["png_sticker"] = parse_file_input(png_sticker)
|
data["png_sticker"] = self._parse_file_input(png_sticker)
|
||||||
if tgs_sticker is not None:
|
if tgs_sticker is not None:
|
||||||
data["tgs_sticker"] = parse_file_input(tgs_sticker)
|
data["tgs_sticker"] = self._parse_file_input(tgs_sticker)
|
||||||
if webm_sticker is not None:
|
if webm_sticker is not None:
|
||||||
data["webm_sticker"] = parse_file_input(webm_sticker)
|
data["webm_sticker"] = self._parse_file_input(webm_sticker)
|
||||||
if mask_position is not None:
|
if mask_position is not None:
|
||||||
data["mask_position"] = mask_position
|
data["mask_position"] = mask_position
|
||||||
if sticker_type is not None:
|
if sticker_type is not None:
|
||||||
|
@ -6693,10 +6732,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
of the arguments had to be changed. Use keyword arguments to make sure that the
|
of the arguments had to be changed. Use keyword arguments to make sure that the
|
||||||
arguments are passed correctly.
|
arguments are passed correctly.
|
||||||
|
|
||||||
Note:
|
|
||||||
The :paramref:`png_sticker` and :paramref:`tgs_sticker` argument can be either a
|
|
||||||
file_id, an URL or a file from disk ``open(filename, 'rb')``
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id (:obj:`int`): User identifier of created sticker set owner.
|
user_id (:obj:`int`): User identifier of created sticker set owner.
|
||||||
|
|
||||||
|
@ -6704,26 +6739,36 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
png_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
png_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
||||||
optional): **PNG** image with the sticker,
|
optional): **PNG** image with the sticker,
|
||||||
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
|
must be up to 512 kilobytes in size, dimensions must not exceed 512px,
|
||||||
and either width or height must be exactly 512px. Pass a file_id as a String to
|
and either width or height must be exactly 512px.
|
||||||
send a file that already exists on the Telegram servers, pass an HTTP URL as a
|
|fileinput|
|
||||||
String for Telegram to get a file from the Internet, or upload a new one
|
|
||||||
using multipart/form-data.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
tgs_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
tgs_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
|
||||||
optional): **TGS** animation with the sticker, uploaded using multipart/form-data.
|
optional): **TGS** animation with the sticker. |uploadinput|
|
||||||
See https://core.telegram.org/stickers#animated-sticker-requirements for technical
|
See https://core.telegram.org/stickers#animated-sticker-requirements for technical
|
||||||
requirements.
|
requirements.
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
webm_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`,\
|
webm_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`,\
|
||||||
optional): **WEBM** video with the sticker, uploaded using multipart/form-data.
|
optional): **WEBM** video with the sticker. |uploadinput|
|
||||||
See https://core.telegram.org/stickers#video-sticker-requirements for
|
See https://core.telegram.org/stickers#video-sticker-requirements for
|
||||||
technical requirements.
|
technical requirements.
|
||||||
|
|
||||||
.. versionadded:: 13.11
|
.. versionadded:: 13.11
|
||||||
|
|
||||||
|
.. versionchanged:: 20.0
|
||||||
|
File paths as input is also accepted for bots *not* running in
|
||||||
|
:paramref:`~telegram.Bot.local_mode`.
|
||||||
emojis (:obj:`str`): One or more emoji corresponding to the sticker.
|
emojis (:obj:`str`): One or more emoji corresponding to the sticker.
|
||||||
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
|
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
|
||||||
should be placed on faces.
|
should be placed on faces.
|
||||||
|
@ -6754,11 +6799,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
data: JSONDict = {"user_id": user_id, "name": name, "emojis": emojis}
|
data: JSONDict = {"user_id": user_id, "name": name, "emojis": emojis}
|
||||||
|
|
||||||
if png_sticker is not None:
|
if png_sticker is not None:
|
||||||
data["png_sticker"] = parse_file_input(png_sticker)
|
data["png_sticker"] = self._parse_file_input(png_sticker)
|
||||||
if tgs_sticker is not None:
|
if tgs_sticker is not None:
|
||||||
data["tgs_sticker"] = parse_file_input(tgs_sticker)
|
data["tgs_sticker"] = self._parse_file_input(tgs_sticker)
|
||||||
if webm_sticker is not None:
|
if webm_sticker is not None:
|
||||||
data["webm_sticker"] = parse_file_input(webm_sticker)
|
data["webm_sticker"] = self._parse_file_input(webm_sticker)
|
||||||
if mask_position is not None:
|
if mask_position is not None:
|
||||||
data["mask_position"] = mask_position
|
data["mask_position"] = mask_position
|
||||||
|
|
||||||
|
@ -6895,10 +6940,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
for animated sticker sets only. Video thumbnails can be set only for video sticker sets
|
for animated sticker sets only. Video thumbnails can be set only for video sticker sets
|
||||||
only.
|
only.
|
||||||
|
|
||||||
Note:
|
|
||||||
The :paramref:`thumb` can be either a file_id, an URL or a file from disk
|
|
||||||
``open(filename, 'rb')``
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name (:obj:`str`): Sticker set name
|
name (:obj:`str`): Sticker set name
|
||||||
user_id (:obj:`int`): User identifier of created sticker set owner.
|
user_id (:obj:`int`): User identifier of created sticker set owner.
|
||||||
|
@ -6910,9 +6951,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
sticker technical requirements, or a **WEBM** video with the thumbnail up to 32
|
sticker technical requirements, or a **WEBM** video with the thumbnail up to 32
|
||||||
kilobytes in size; see
|
kilobytes in size; see
|
||||||
https://core.telegram.org/stickers#video-sticker-requirements for video sticker
|
https://core.telegram.org/stickers#video-sticker-requirements for video sticker
|
||||||
technical requirements. Pass a file_id as a String to send a file that
|
technical requirements.
|
||||||
already exists on the Telegram servers, pass an HTTP URL as a String for Telegram
|
|fileinput|
|
||||||
to get a file from the Internet, or upload a new one using multipart/form-data.
|
|
||||||
Animated sticker set thumbnails can't be uploaded via HTTP URL.
|
Animated sticker set thumbnails can't be uploaded via HTTP URL.
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
|
@ -6943,7 +6983,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
"""
|
"""
|
||||||
data: JSONDict = {"name": name, "user_id": user_id}
|
data: JSONDict = {"name": name, "user_id": user_id}
|
||||||
if thumb is not None:
|
if thumb is not None:
|
||||||
data["thumb"] = parse_file_input(thumb)
|
data["thumb"] = self._parse_file_input(thumb)
|
||||||
|
|
||||||
result = await self._post(
|
result = await self._post(
|
||||||
"setStickerSetThumb",
|
"setStickerSetThumb",
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from pathlib import Path
|
|
||||||
from typing import IO, Optional, Union
|
from typing import IO, Optional, Union
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from telegram._utils.files import load_file
|
||||||
from telegram._utils.types import FieldTuple
|
from telegram._utils.types import FieldTuple
|
||||||
|
|
||||||
_DEFAULT_MIME_TYPE = "application/octet-stream"
|
_DEFAULT_MIME_TYPE = "application/octet-stream"
|
||||||
|
@ -75,15 +75,10 @@ class InputFile:
|
||||||
elif isinstance(obj, str):
|
elif isinstance(obj, str):
|
||||||
self.input_file_content = obj.encode("utf-8")
|
self.input_file_content = obj.encode("utf-8")
|
||||||
else:
|
else:
|
||||||
self.input_file_content = obj.read()
|
reported_filename, self.input_file_content = load_file(obj)
|
||||||
self.attach_name: Optional[str] = "attached" + uuid4().hex if attach else None
|
filename = filename or reported_filename
|
||||||
|
|
||||||
if (
|
self.attach_name: Optional[str] = "attached" + uuid4().hex if attach else None
|
||||||
not filename
|
|
||||||
and hasattr(obj, "name")
|
|
||||||
and not isinstance(obj.name, int) # type: ignore[union-attr]
|
|
||||||
):
|
|
||||||
filename = Path(obj.name).name # type: ignore[union-attr]
|
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
self.mimetype = mimetypes.guess_type(filename, strict=False)[0] or _DEFAULT_MIME_TYPE
|
self.mimetype = mimetypes.guess_type(filename, strict=False)[0] or _DEFAULT_MIME_TYPE
|
||||||
|
|
|
@ -48,9 +48,8 @@ class InputMedia(TelegramObject):
|
||||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Animation` | :class:`telegram.Audio` | \
|
:class:`telegram.Animation` | :class:`telegram.Audio` | \
|
||||||
:class:`telegram.Document` | :class:`telegram.PhotoSize` | \
|
:class:`telegram.Document` | :class:`telegram.PhotoSize` | \
|
||||||
:class:`telegram.Video`):
|
:class:`telegram.Video`): File to send.
|
||||||
File to send. Pass a file_id to send a file that exists on the Telegram servers
|
|fileinputnopath|
|
||||||
(recommended), pass an HTTP URL for Telegram to get a file from the Internet.
|
|
||||||
Lastly you can pass an existing telegram media object of the corresponding type
|
Lastly you can pass an existing telegram media object of the corresponding type
|
||||||
to send.
|
to send.
|
||||||
caption (:obj:`str`, optional): Caption of the media to be sent,
|
caption (:obj:`str`, optional): Caption of the media to be sent,
|
||||||
|
@ -99,7 +98,11 @@ class InputMedia(TelegramObject):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_thumb_input(thumb: Optional[FileInput]) -> Optional[Union[str, InputFile]]:
|
def _parse_thumb_input(thumb: Optional[FileInput]) -> Optional[Union[str, InputFile]]:
|
||||||
return parse_file_input(thumb, attach=True) if thumb is not None else thumb
|
# 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(thumb, attach=True, local_mode=True) if thumb is not None else thumb
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InputMediaAnimation(InputMedia):
|
class InputMediaAnimation(InputMedia):
|
||||||
|
@ -112,10 +115,8 @@ class InputMediaAnimation(InputMedia):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Animation`): File to send. Pass a
|
:class:`telegram.Animation`): File to send. |fileinputnopath|
|
||||||
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
|
Lastly you can pass an existing :class:`telegram.Animation` object to send.
|
||||||
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
|
|
||||||
:class:`telegram.Animation` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -123,13 +124,9 @@ class InputMediaAnimation(InputMedia):
|
||||||
new file. Convenience parameter, useful e.g. when sending files generated by the
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
:obj:`tempfile` module.
|
:obj:`tempfile` module.
|
||||||
|
|
||||||
.. versionadded:: 13.1
|
.. versionadded:: 13.1
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
the file sent; can be ignored if
|
optional): |thumbdocstringnopath|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than ``200`` kB in size. A thumbnail's width and height should
|
|
||||||
not exceed ``320``. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -180,7 +177,9 @@ class InputMediaAnimation(InputMedia):
|
||||||
duration = media.duration if duration is None else duration
|
duration = media.duration if duration is None else duration
|
||||||
media = media.file_id
|
media = media.file_id
|
||||||
else:
|
else:
|
||||||
media = parse_file_input(media, filename=filename, attach=True)
|
# 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)
|
super().__init__(InputMediaType.ANIMATION, media, caption, caption_entities, parse_mode)
|
||||||
self.thumb = self._parse_thumb_input(thumb)
|
self.thumb = self._parse_thumb_input(thumb)
|
||||||
|
@ -194,10 +193,8 @@ class InputMediaPhoto(InputMedia):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.PhotoSize`): File to send. Pass a
|
:class:`telegram.PhotoSize`): File to send. |fileinputnopath|
|
||||||
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
|
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
|
||||||
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
|
|
||||||
:class:`telegram.PhotoSize` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -205,7 +202,7 @@ class InputMediaPhoto(InputMedia):
|
||||||
new file. Convenience parameter, useful e.g. when sending files generated by the
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
:obj:`tempfile` module.
|
:obj:`tempfile` module.
|
||||||
|
|
||||||
.. versionadded:: 13.1
|
.. versionadded:: 13.1
|
||||||
caption (:obj:`str`, optional ): Caption of the photo to be sent,
|
caption (:obj:`str`, optional ): Caption of the photo to be sent,
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
entities parsing.
|
entities parsing.
|
||||||
|
@ -236,7 +233,9 @@ class InputMediaPhoto(InputMedia):
|
||||||
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
|
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
|
||||||
filename: str = None,
|
filename: str = None,
|
||||||
):
|
):
|
||||||
media = parse_file_input(media, PhotoSize, filename=filename, attach=True)
|
# 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)
|
super().__init__(InputMediaType.PHOTO, media, caption, caption_entities, parse_mode)
|
||||||
|
|
||||||
|
|
||||||
|
@ -253,10 +252,8 @@ class InputMediaVideo(InputMedia):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Video`): File to send. Pass a
|
:class:`telegram.Video`): File to send. |fileinputnopath|
|
||||||
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
|
Lastly you can pass an existing :class:`telegram.Video` object to send.
|
||||||
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
|
|
||||||
:class:`telegram.Video` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -264,7 +261,7 @@ class InputMediaVideo(InputMedia):
|
||||||
new file. Convenience parameter, useful e.g. when sending files generated by the
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
:obj:`tempfile` module.
|
:obj:`tempfile` module.
|
||||||
|
|
||||||
.. versionadded:: 13.1
|
.. versionadded:: 13.1
|
||||||
caption (:obj:`str`, optional): Caption of the video to be sent,
|
caption (:obj:`str`, optional): Caption of the video to be sent,
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
entities parsing.
|
entities parsing.
|
||||||
|
@ -279,12 +276,8 @@ class InputMediaVideo(InputMedia):
|
||||||
duration (:obj:`int`, optional): Video duration in seconds.
|
duration (:obj:`int`, optional): Video duration in seconds.
|
||||||
supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is
|
supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is
|
||||||
suitable for streaming.
|
suitable for streaming.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
the file sent; can be ignored if
|
optional): |thumbdocstringnopath|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than ``200`` kB in size. A thumbnail's width and height should
|
|
||||||
not exceed ``320``. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -327,7 +320,9 @@ class InputMediaVideo(InputMedia):
|
||||||
duration = duration if duration is not None else media.duration
|
duration = duration if duration is not None else media.duration
|
||||||
media = media.file_id
|
media = media.file_id
|
||||||
else:
|
else:
|
||||||
media = parse_file_input(media, filename=filename, attach=True)
|
# 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)
|
super().__init__(InputMediaType.VIDEO, media, caption, caption_entities, parse_mode)
|
||||||
self.width = width
|
self.width = width
|
||||||
|
@ -347,11 +342,8 @@ class InputMediaAudio(InputMedia):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Audio`):
|
:class:`telegram.Audio`): File to send. |fileinputnopath|
|
||||||
File to send. Pass a
|
Lastly you can pass an existing :class:`telegram.Audio` object to send.
|
||||||
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
|
|
||||||
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
|
|
||||||
:class:`telegram.Audio` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -359,7 +351,7 @@ class InputMediaAudio(InputMedia):
|
||||||
new file. Convenience parameter, useful e.g. when sending files generated by the
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
:obj:`tempfile` module.
|
:obj:`tempfile` module.
|
||||||
|
|
||||||
.. versionadded:: 13.1
|
.. versionadded:: 13.1
|
||||||
caption (:obj:`str`, optional): Caption of the audio to be sent,
|
caption (:obj:`str`, optional): Caption of the audio to be sent,
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
entities parsing.
|
entities parsing.
|
||||||
|
@ -373,12 +365,8 @@ class InputMediaAudio(InputMedia):
|
||||||
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
|
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
|
||||||
tags.
|
tags.
|
||||||
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
|
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
the file sent; can be ignored if
|
optional): |thumbdocstringnopath|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than ``200`` kB in size. A thumbnail's width and height should
|
|
||||||
not exceed ``320``. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -418,7 +406,9 @@ class InputMediaAudio(InputMedia):
|
||||||
title = media.title if title is None else title
|
title = media.title if title is None else title
|
||||||
media = media.file_id
|
media = media.file_id
|
||||||
else:
|
else:
|
||||||
media = parse_file_input(media, filename=filename, attach=True)
|
# 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)
|
super().__init__(InputMediaType.AUDIO, media, caption, caption_entities, parse_mode)
|
||||||
self.thumb = self._parse_thumb_input(thumb)
|
self.thumb = self._parse_thumb_input(thumb)
|
||||||
|
@ -432,10 +422,8 @@ class InputMediaDocument(InputMedia):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||||
:class:`telegram.Document`): File to send. Pass a
|
:class:`telegram.Document`): File to send. |fileinputnopath|
|
||||||
file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP
|
Lastly you can pass an existing :class:`telegram.Document` object to send.
|
||||||
URL for Telegram to get a file from the Internet. Lastly you can pass an existing
|
|
||||||
:class:`telegram.Document` object to send.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -443,7 +431,7 @@ class InputMediaDocument(InputMedia):
|
||||||
new file. Convenience parameter, useful e.g. when sending files generated by the
|
new file. Convenience parameter, useful e.g. when sending files generated by the
|
||||||
:obj:`tempfile` module.
|
:obj:`tempfile` module.
|
||||||
|
|
||||||
.. versionadded:: 13.1
|
.. versionadded:: 13.1
|
||||||
caption (:obj:`str`, optional): Caption of the document to be sent,
|
caption (:obj:`str`, optional): Caption of the document to be sent,
|
||||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||||
entities parsing.
|
entities parsing.
|
||||||
|
@ -453,12 +441,8 @@ class InputMediaDocument(InputMedia):
|
||||||
caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special
|
caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special
|
||||||
entities that appear in the caption, which can be specified instead of
|
entities that appear in the caption, which can be specified instead of
|
||||||
:paramref:`parse_mode`.
|
:paramref:`parse_mode`.
|
||||||
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of
|
thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||||
the file sent; can be ignored if
|
optional): |thumbdocstringnopath|
|
||||||
thumbnail generation for the file is supported server-side. The thumbnail should be
|
|
||||||
in JPEG format and less than ``200`` kB in size. A thumbnail's width and height should
|
|
||||||
not exceed ``320``. Ignored if the file is not uploaded using multipart/form-data.
|
|
||||||
Thumbnails can't be reused and can be only uploaded as a new file.
|
|
||||||
|
|
||||||
.. versionchanged:: 13.2
|
.. versionchanged:: 13.2
|
||||||
Accept :obj:`bytes` as input.
|
Accept :obj:`bytes` as input.
|
||||||
|
@ -492,7 +476,9 @@ class InputMediaDocument(InputMedia):
|
||||||
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
|
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None,
|
||||||
filename: str = None,
|
filename: str = None,
|
||||||
):
|
):
|
||||||
media = parse_file_input(media, Document, filename=filename, attach=True)
|
# 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)
|
super().__init__(InputMediaType.DOCUMENT, media, caption, caption_entities, parse_mode)
|
||||||
self.thumb = self._parse_thumb_input(thumb)
|
self.thumb = self._parse_thumb_input(thumb)
|
||||||
self.disable_content_type_detection = disable_content_type_detection
|
self.disable_content_type_detection = disable_content_type_detection
|
||||||
|
|
|
@ -29,13 +29,47 @@ Warning:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import IO, TYPE_CHECKING, Any, Optional, Type, Union, cast
|
from typing import IO, TYPE_CHECKING, Any, Optional, Tuple, Type, TypeVar, Union, cast, overload
|
||||||
|
|
||||||
from telegram._utils.types import FileInput, FilePathInput
|
from telegram._utils.types import FileInput, FilePathInput
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from telegram import InputFile, TelegramObject
|
from telegram import InputFile, TelegramObject
|
||||||
|
|
||||||
|
_T = TypeVar("_T", bound=Union[bytes, "InputFile", str, Path, None])
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def load_file(obj: IO[bytes]) -> Tuple[Optional[str], bytes]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def load_file(obj: _T) -> Tuple[None, _T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def load_file(
|
||||||
|
obj: Optional[FileInput],
|
||||||
|
) -> Tuple[Optional[str], Union[bytes, "InputFile", str, Path, None]]:
|
||||||
|
"""If the input is a file handle, read the data and name and return it. Otherwise, return
|
||||||
|
the input unchanged.
|
||||||
|
"""
|
||||||
|
if obj is None:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
try:
|
||||||
|
contents = obj.read() # type: ignore[union-attr]
|
||||||
|
except AttributeError:
|
||||||
|
return None, cast(Union[bytes, "InputFile", str, Path], obj)
|
||||||
|
|
||||||
|
if hasattr(obj, "name") and not isinstance(obj.name, int): # type: ignore[union-attr]
|
||||||
|
filename = Path(obj.name).name # type: ignore[union-attr]
|
||||||
|
else:
|
||||||
|
filename = None
|
||||||
|
|
||||||
|
return filename, contents
|
||||||
|
|
||||||
|
|
||||||
def is_local_file(obj: Optional[FilePathInput]) -> bool:
|
def is_local_file(obj: Optional[FilePathInput]) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -54,18 +88,24 @@ def is_local_file(obj: Optional[FilePathInput]) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def parse_file_input(
|
def parse_file_input( # pylint: disable=too-many-return-statements
|
||||||
file_input: Union[FileInput, "TelegramObject"],
|
file_input: Union[FileInput, "TelegramObject"],
|
||||||
tg_type: Type["TelegramObject"] = None,
|
tg_type: Type["TelegramObject"] = None,
|
||||||
filename: str = None,
|
filename: str = None,
|
||||||
attach: bool = False,
|
attach: bool = False,
|
||||||
|
local_mode: bool = False,
|
||||||
) -> Union[str, "InputFile", Any]:
|
) -> Union[str, "InputFile", Any]:
|
||||||
"""
|
"""
|
||||||
Parses input for sending files:
|
Parses input for sending files:
|
||||||
|
|
||||||
* For string input, if the input is an absolute path of a local file,
|
* For string input, if the input is an absolute path of a local file:
|
||||||
adds the ``file://`` prefix. If the input is a relative path of a local file, computes the
|
|
||||||
absolute path and adds the ``file://`` prefix. Returns the input unchanged, otherwise.
|
* if ``local_mode`` is ``True``, adds the ``file://`` prefix. If the input is a relative
|
||||||
|
path of a local file, computes the absolute path and adds the ``file://`` prefix.
|
||||||
|
* if ``local_mode`` is ``False``, loads the file as binary data and builds an
|
||||||
|
:class:`InputFile` from that
|
||||||
|
|
||||||
|
Returns the input unchanged, otherwise.
|
||||||
* :class:`pathlib.Path` objects are treated the same way as strings.
|
* :class:`pathlib.Path` objects are treated the same way as strings.
|
||||||
* For IO and bytes input, returns an :class:`telegram.InputFile`.
|
* For IO and bytes input, returns an :class:`telegram.InputFile`.
|
||||||
* If :attr:`tg_type` is specified and the input is of that type, returns the ``file_id``
|
* If :attr:`tg_type` is specified and the input is of that type, returns the ``file_id``
|
||||||
|
@ -81,6 +121,8 @@ def parse_file_input(
|
||||||
attach (:obj:`bool`, optional): Pass :obj:`True` if the parameter this file belongs to in
|
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.
|
the request to Telegram should point to the multipart data via an ``attach://`` URI.
|
||||||
Defaults to `False`. Only relevant if an :class:`telegram.InputFile` is returned.
|
Defaults to `False`. Only relevant if an :class:`telegram.InputFile` is returned.
|
||||||
|
local_mode (:obj:`bool`, optional): Pass :obj:`True` if the bot is running an api server
|
||||||
|
in ``--local`` mode.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`str` | :class:`telegram.InputFile` | :obj:`object`: The parsed input or the untouched
|
:obj:`str` | :class:`telegram.InputFile` | :obj:`object`: The parsed input or the untouched
|
||||||
|
@ -90,13 +132,17 @@ def parse_file_input(
|
||||||
from telegram import InputFile # pylint: disable=import-outside-toplevel
|
from telegram import InputFile # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
if isinstance(file_input, str) and file_input.startswith("file://"):
|
if isinstance(file_input, str) and file_input.startswith("file://"):
|
||||||
|
if not local_mode:
|
||||||
|
raise ValueError("Specified file input is a file URI, but local mode is not enabled.")
|
||||||
return file_input
|
return file_input
|
||||||
if isinstance(file_input, (str, Path)):
|
if isinstance(file_input, (str, Path)):
|
||||||
if is_local_file(file_input):
|
if is_local_file(file_input):
|
||||||
out = Path(file_input).absolute().as_uri()
|
path = Path(file_input)
|
||||||
else:
|
if local_mode:
|
||||||
out = file_input # type: ignore[assignment]
|
return path.absolute().as_uri()
|
||||||
return out
|
return InputFile(path.open(mode="rb"), filename=filename, attach=attach)
|
||||||
|
|
||||||
|
return file_input
|
||||||
if isinstance(file_input, bytes):
|
if isinstance(file_input, bytes):
|
||||||
return InputFile(file_input, filename=filename, attach=attach)
|
return InputFile(file_input, filename=filename, attach=attach)
|
||||||
if hasattr(file_input, "read"):
|
if hasattr(file_input, "read"):
|
||||||
|
|
|
@ -44,8 +44,7 @@ FilePathInput = Union[str, Path]
|
||||||
|
|
||||||
FileInput = Union[FilePathInput, FileLike, bytes, str]
|
FileInput = Union[FilePathInput, FileLike, bytes, str]
|
||||||
"""Valid input for passing files to Telegram. Either a file id as string, a file like object,
|
"""Valid input for passing files to Telegram. Either a file id as string, a file like object,
|
||||||
a local file path as string, :class:`pathlib.Path` or the file contents as :obj:`bytes` or
|
a local file path as string, :class:`pathlib.Path` or the file contents as :obj:`bytes`."""
|
||||||
:obj:`str`."""
|
|
||||||
|
|
||||||
JSONDict = Dict[str, Any]
|
JSONDict = Dict[str, Any]
|
||||||
"""Dictionary containing response from Telegram or data to send to the API."""
|
"""Dictionary containing response from Telegram or data to send to the API."""
|
||||||
|
|
|
@ -83,6 +83,7 @@ _BOT_CHECKS = [
|
||||||
("arbitrary_callback_data", "arbitrary_callback_data"),
|
("arbitrary_callback_data", "arbitrary_callback_data"),
|
||||||
("private_key", "private_key"),
|
("private_key", "private_key"),
|
||||||
("rate_limiter", "rate_limiter instance"),
|
("rate_limiter", "rate_limiter instance"),
|
||||||
|
("local_mode", "local_mode setting"),
|
||||||
]
|
]
|
||||||
|
|
||||||
_TWO_ARGS_REQ = "The parameter `{}` may only be set, if no {} was set."
|
_TWO_ARGS_REQ = "The parameter `{}` may only be set, if no {} was set."
|
||||||
|
@ -148,6 +149,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
"_update_queue",
|
"_update_queue",
|
||||||
"_updater",
|
"_updater",
|
||||||
"_write_timeout",
|
"_write_timeout",
|
||||||
|
"_local_mode",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self: "InitApplicationBuilder"):
|
def __init__(self: "InitApplicationBuilder"):
|
||||||
|
@ -172,6 +174,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
self._private_key_password: ODVInput[bytes] = DEFAULT_NONE
|
self._private_key_password: ODVInput[bytes] = DEFAULT_NONE
|
||||||
self._defaults: ODVInput["Defaults"] = DEFAULT_NONE
|
self._defaults: ODVInput["Defaults"] = DEFAULT_NONE
|
||||||
self._arbitrary_callback_data: DVInput[Union[bool, int]] = DEFAULT_FALSE
|
self._arbitrary_callback_data: DVInput[Union[bool, int]] = DEFAULT_FALSE
|
||||||
|
self._local_mode: DVInput[bool] = DEFAULT_FALSE
|
||||||
self._bot: DVInput[Bot] = DEFAULT_NONE
|
self._bot: DVInput[Bot] = DEFAULT_NONE
|
||||||
self._update_queue: DVInput[Queue] = DefaultValue(Queue())
|
self._update_queue: DVInput[Queue] = DefaultValue(Queue())
|
||||||
self._job_queue: ODVInput["JobQueue"] = DefaultValue(JobQueue())
|
self._job_queue: ODVInput["JobQueue"] = DefaultValue(JobQueue())
|
||||||
|
@ -232,8 +235,17 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
request=self._build_request(get_updates=False),
|
request=self._build_request(get_updates=False),
|
||||||
get_updates_request=self._build_request(get_updates=True),
|
get_updates_request=self._build_request(get_updates=True),
|
||||||
rate_limiter=DefaultValue.get_value(self._rate_limiter),
|
rate_limiter=DefaultValue.get_value(self._rate_limiter),
|
||||||
|
local_mode=DefaultValue.get_value(self._local_mode),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _bot_check(self, name: str) -> None:
|
||||||
|
if self._bot is not DEFAULT_NONE:
|
||||||
|
raise RuntimeError(_TWO_ARGS_REQ.format(name, "bot instance"))
|
||||||
|
|
||||||
|
def _updater_check(self, name: str) -> None:
|
||||||
|
if self._updater not in (DEFAULT_NONE, None):
|
||||||
|
raise RuntimeError(_TWO_ARGS_REQ.format(name, "updater"))
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self: "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]",
|
self: "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]",
|
||||||
) -> Application[BT, CCT, UD, CD, BD, JQ]:
|
) -> Application[BT, CCT, UD, CD, BD, JQ]:
|
||||||
|
@ -326,10 +338,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("token")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("token", "bot instance"))
|
self._updater_check("token")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("token", "updater"))
|
|
||||||
self._token = token
|
self._token = token
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -347,10 +357,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("base_url")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("base_url", "bot instance"))
|
self._updater_check("base_url")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("base_url", "updater"))
|
|
||||||
self._base_url = base_url
|
self._base_url = base_url
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -368,10 +376,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("base_file_url")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("base_file_url", "bot instance"))
|
self._updater_check("base_file_url")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("base_file_url", "updater"))
|
|
||||||
self._base_file_url = base_file_url
|
self._base_file_url = base_file_url
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -388,8 +394,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "connection_pool_size"))
|
raise RuntimeError(_TWO_ARGS_REQ.format(name, "connection_pool_size"))
|
||||||
if not isinstance(getattr(self, f"_{prefix}proxy_url"), DefaultValue):
|
if not isinstance(getattr(self, f"_{prefix}proxy_url"), DefaultValue):
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "proxy_url"))
|
raise RuntimeError(_TWO_ARGS_REQ.format(name, "proxy_url"))
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check(name)
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "bot instance"))
|
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
if self._updater not in (DEFAULT_NONE, None):
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "updater instance"))
|
raise RuntimeError(_TWO_ARGS_REQ.format(name, "updater instance"))
|
||||||
|
|
||||||
|
@ -670,10 +675,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("private_key")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("private_key", "bot instance"))
|
self._updater_check("private_key")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("private_key", "updater"))
|
|
||||||
|
|
||||||
self._private_key = (
|
self._private_key = (
|
||||||
private_key if isinstance(private_key, bytes) else Path(private_key).read_bytes()
|
private_key if isinstance(private_key, bytes) else Path(private_key).read_bytes()
|
||||||
|
@ -698,10 +701,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("defaults")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("defaults", "bot instance"))
|
self._updater_check("defaults")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("defaults", "updater"))
|
|
||||||
self._defaults = defaults
|
self._defaults = defaults
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -725,13 +726,30 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("arbitrary_callback_data")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("arbitrary_callback_data", "bot instance"))
|
self._updater_check("arbitrary_callback_data")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("arbitrary_callback_data", "updater"))
|
|
||||||
self._arbitrary_callback_data = arbitrary_callback_data
|
self._arbitrary_callback_data = arbitrary_callback_data
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def local_mode(self: BuilderType, local_mode: bool) -> BuilderType:
|
||||||
|
"""Specifies the value for :paramref:`~telegram.Bot.local_mode` for the
|
||||||
|
:attr:`telegram.ext.Application.bot`.
|
||||||
|
If not called, will default to :obj:`False`.
|
||||||
|
|
||||||
|
.. seealso:: `Local Bot API Server <https://github.com/python-telegram-bot/\
|
||||||
|
python-telegram-bot/wiki/Local-Bot-API-Server>`_,
|
||||||
|
|
||||||
|
Args:
|
||||||
|
local_mode (:obj:`bool`): Whether the bot should run in local mode.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
|
"""
|
||||||
|
self._bot_check("local_mode")
|
||||||
|
self._updater_check("local_mode")
|
||||||
|
self._local_mode = local_mode
|
||||||
|
return self
|
||||||
|
|
||||||
def bot(
|
def bot(
|
||||||
self: "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]",
|
self: "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]",
|
||||||
bot: InBT,
|
bot: InBT,
|
||||||
|
@ -746,8 +764,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
self._updater_check("bot")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("bot", "updater"))
|
|
||||||
for attr, error in _BOT_CHECKS:
|
for attr, error in _BOT_CHECKS:
|
||||||
if not isinstance(getattr(self, f"_{attr}"), DefaultValue):
|
if not isinstance(getattr(self, f"_{attr}"), DefaultValue):
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("bot", error))
|
raise RuntimeError(_TWO_ARGS_REQ.format("bot", error))
|
||||||
|
@ -992,10 +1009,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
Returns:
|
Returns:
|
||||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||||
"""
|
"""
|
||||||
if self._bot is not DEFAULT_NONE:
|
self._bot_check("rate_limiter")
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("rate_limiter", "bot instance"))
|
self._updater_check("rate_limiter")
|
||||||
if self._updater not in (DEFAULT_NONE, None):
|
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format("rate_limiter", "updater"))
|
|
||||||
self._rate_limiter = rate_limiter
|
self._rate_limiter = rate_limiter
|
||||||
return self # type: ignore[return-value]
|
return self # type: ignore[return-value]
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||||
private_key_password: bytes = None,
|
private_key_password: bytes = None,
|
||||||
defaults: "Defaults" = None,
|
defaults: "Defaults" = None,
|
||||||
arbitrary_callback_data: Union[bool, int] = False,
|
arbitrary_callback_data: Union[bool, int] = False,
|
||||||
|
local_mode: bool = False,
|
||||||
):
|
):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -178,6 +179,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||||
private_key_password: bytes = None,
|
private_key_password: bytes = None,
|
||||||
defaults: "Defaults" = None,
|
defaults: "Defaults" = None,
|
||||||
arbitrary_callback_data: Union[bool, int] = False,
|
arbitrary_callback_data: Union[bool, int] = False,
|
||||||
|
local_mode: bool = False,
|
||||||
rate_limiter: "BaseRateLimiter[RLARGS]" = None,
|
rate_limiter: "BaseRateLimiter[RLARGS]" = None,
|
||||||
):
|
):
|
||||||
...
|
...
|
||||||
|
@ -193,6 +195,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||||
private_key_password: bytes = None,
|
private_key_password: bytes = None,
|
||||||
defaults: "Defaults" = None,
|
defaults: "Defaults" = None,
|
||||||
arbitrary_callback_data: Union[bool, int] = False,
|
arbitrary_callback_data: Union[bool, int] = False,
|
||||||
|
local_mode: bool = False,
|
||||||
rate_limiter: "BaseRateLimiter" = None,
|
rate_limiter: "BaseRateLimiter" = None,
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
|
@ -203,6 +206,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||||
get_updates_request=get_updates_request,
|
get_updates_request=get_updates_request,
|
||||||
private_key=private_key,
|
private_key=private_key,
|
||||||
private_key_password=private_key_password,
|
private_key_password=private_key_password,
|
||||||
|
local_mode=local_mode,
|
||||||
)
|
)
|
||||||
self._defaults = defaults
|
self._defaults = defaults
|
||||||
self._rate_limiter = rate_limiter
|
self._rate_limiter = rate_limiter
|
||||||
|
|
|
@ -22,7 +22,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Animation, Bot, MessageEntity, PhotoSize, Voice
|
from telegram import Animation, Bot, InputFile, MessageEntity, PhotoSize, Voice
|
||||||
from telegram.error import BadRequest, TelegramError
|
from telegram.error import BadRequest, TelegramError
|
||||||
from telegram.helpers import escape_markdown
|
from telegram.helpers import escape_markdown
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
@ -202,20 +202,30 @@ class TestAnimation:
|
||||||
assert message.caption == test_markdown_string
|
assert message.caption == test_markdown_string
|
||||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||||
|
|
||||||
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("animation") == expected and data.get("thumb") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("animation") == expected and data.get("thumb") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("animation"), InputFile) and isinstance(
|
||||||
|
data.get("thumb"), InputFile
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_animation(chat_id, file, thumb=file)
|
await bot.send_animation(chat_id, file, thumb=file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
monkeypatch.delattr(bot, "_post")
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -84,6 +84,7 @@ class TestApplicationBuilder:
|
||||||
assert app.bot.arbitrary_callback_data is False
|
assert app.bot.arbitrary_callback_data is False
|
||||||
assert app.bot.defaults is None
|
assert app.bot.defaults is None
|
||||||
assert app.bot.rate_limiter is None
|
assert app.bot.rate_limiter is None
|
||||||
|
assert app.bot.local_mode is False
|
||||||
|
|
||||||
get_updates_client = app.bot._request[0]._client
|
get_updates_client = app.bot._request[0]._client
|
||||||
assert get_updates_client.limits == httpx.Limits(
|
assert get_updates_client.limits == httpx.Limits(
|
||||||
|
@ -257,6 +258,8 @@ class TestApplicationBuilder:
|
||||||
get_updates_request
|
get_updates_request
|
||||||
).rate_limiter(
|
).rate_limiter(
|
||||||
rate_limiter
|
rate_limiter
|
||||||
|
).local_mode(
|
||||||
|
True
|
||||||
)
|
)
|
||||||
built_bot = builder.build().bot
|
built_bot = builder.build().bot
|
||||||
|
|
||||||
|
@ -273,6 +276,7 @@ class TestApplicationBuilder:
|
||||||
assert built_bot.callback_data_cache.maxsize == 42
|
assert built_bot.callback_data_cache.maxsize == 42
|
||||||
assert built_bot.private_key
|
assert built_bot.private_key
|
||||||
assert built_bot.rate_limiter is rate_limiter
|
assert built_bot.rate_limiter is rate_limiter
|
||||||
|
assert built_bot.local_mode is True
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Client:
|
class Client:
|
||||||
|
|
|
@ -22,7 +22,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Audio, Bot, MessageEntity, Voice
|
from telegram import Audio, Bot, InputFile, MessageEntity, Voice
|
||||||
from telegram.error import TelegramError
|
from telegram.error import TelegramError
|
||||||
from telegram.helpers import escape_markdown
|
from telegram.helpers import escape_markdown
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
@ -234,20 +234,30 @@ class TestAudio:
|
||||||
unprotected = await default_bot.send_audio(chat_id, audio, protect_content=False)
|
unprotected = await default_bot.send_audio(chat_id, audio, protect_content=False)
|
||||||
assert not unprotected.has_protected_content
|
assert not unprotected.has_protected_content
|
||||||
|
|
||||||
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("audio") == expected and data.get("thumb") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("audio") == expected and data.get("thumb") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("audio"), InputFile) and isinstance(
|
||||||
|
data.get("thumb"), InputFile
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_audio(chat_id, file, thumb=file)
|
await bot.send_audio(chat_id, file, thumb=file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
monkeypatch.delattr(bot, "_post")
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
def test_de_json(self, bot, audio):
|
def test_de_json(self, bot, audio):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
|
|
|
@ -44,6 +44,7 @@ from telegram import (
|
||||||
InlineQueryResultArticle,
|
InlineQueryResultArticle,
|
||||||
InlineQueryResultDocument,
|
InlineQueryResultDocument,
|
||||||
InlineQueryResultVoice,
|
InlineQueryResultVoice,
|
||||||
|
InputFile,
|
||||||
InputMedia,
|
InputMedia,
|
||||||
InputTextMessageContent,
|
InputTextMessageContent,
|
||||||
LabeledPrice,
|
LabeledPrice,
|
||||||
|
@ -1578,18 +1579,25 @@ class TestBot:
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize("use_ip", [True, False])
|
@pytest.mark.parametrize("use_ip", [True, False])
|
||||||
async def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot, use_ip):
|
# local file path as file_input is tested below in test_set_webhook_params
|
||||||
|
@pytest.mark.parametrize("file_input", ["bytes", "file_handle"])
|
||||||
|
async def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot, use_ip, file_input):
|
||||||
url = "https://python-telegram-bot.org/test/webhook"
|
url = "https://python-telegram-bot.org/test/webhook"
|
||||||
# Get the ip address of the website - dynamically just in case it ever changes
|
# Get the ip address of the website - dynamically just in case it ever changes
|
||||||
ip = socket.gethostbyname("python-telegram-bot.org")
|
ip = socket.gethostbyname("python-telegram-bot.org")
|
||||||
max_connections = 7
|
max_connections = 7
|
||||||
allowed_updates = ["message"]
|
allowed_updates = ["message"]
|
||||||
|
file_input = (
|
||||||
|
data_file("sslcert.pem").read_bytes()
|
||||||
|
if file_input == "bytes"
|
||||||
|
else data_file("sslcert.pem").open("rb")
|
||||||
|
)
|
||||||
await bot.set_webhook(
|
await bot.set_webhook(
|
||||||
url,
|
url,
|
||||||
max_connections=max_connections,
|
max_connections=max_connections,
|
||||||
allowed_updates=allowed_updates,
|
allowed_updates=allowed_updates,
|
||||||
ip_address=ip if use_ip else None,
|
ip_address=ip if use_ip else None,
|
||||||
certificate=data_file("sslcert.pem").read_bytes() if use_ip else None,
|
certificate=file_input if use_ip else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
@ -1620,16 +1628,25 @@ class TestBot:
|
||||||
assert await bot.set_webhook("", drop_pending_updates=drop_pending_updates)
|
assert await bot.set_webhook("", drop_pending_updates=drop_pending_updates)
|
||||||
assert await bot.delete_webhook(drop_pending_updates=drop_pending_updates)
|
assert await bot.delete_webhook(drop_pending_updates=drop_pending_updates)
|
||||||
|
|
||||||
async def test_set_webhook_params(self, bot, monkeypatch):
|
@pytest.mark.parametrize("local_file", ["str", "Path", False])
|
||||||
|
async def test_set_webhook_params(self, bot, monkeypatch, local_file):
|
||||||
# actually making calls to TG is done in
|
# actually making calls to TG is done in
|
||||||
# test_set_webhook_get_webhook_info_and_delete_webhook. Sadly secret_token can't be tested
|
# test_set_webhook_get_webhook_info_and_delete_webhook. Sadly secret_token can't be tested
|
||||||
# there so we have this function \o/
|
# there so we have this function \o/
|
||||||
async def make_assertion(*args, **_):
|
async def make_assertion(*args, **_):
|
||||||
kwargs = args[1]
|
kwargs = args[1]
|
||||||
|
|
||||||
|
if local_file is False:
|
||||||
|
cert_assertion = (
|
||||||
|
kwargs["certificate"].input_file_content
|
||||||
|
== data_file("sslcert.pem").read_bytes()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
cert_assertion = data_file("sslcert.pem").as_uri()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
kwargs["url"] == "example.com"
|
kwargs["url"] == "example.com"
|
||||||
and kwargs["certificate"].input_file_content
|
and cert_assertion
|
||||||
== data_file("sslcert.pem").read_bytes()
|
|
||||||
and kwargs["max_connections"] == 7
|
and kwargs["max_connections"] == 7
|
||||||
and kwargs["allowed_updates"] == ["messages"]
|
and kwargs["allowed_updates"] == ["messages"]
|
||||||
and kwargs["ip_address"] == "127.0.0.1"
|
and kwargs["ip_address"] == "127.0.0.1"
|
||||||
|
@ -1639,9 +1656,17 @@ class TestBot:
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
|
|
||||||
|
cert_path = data_file("sslcert.pem")
|
||||||
|
if local_file == "str":
|
||||||
|
certificate = str(cert_path)
|
||||||
|
elif local_file == "Path":
|
||||||
|
certificate = cert_path
|
||||||
|
else:
|
||||||
|
certificate = cert_path.read_bytes()
|
||||||
|
|
||||||
assert await bot.set_webhook(
|
assert await bot.set_webhook(
|
||||||
"example.com",
|
"example.com",
|
||||||
data_file("sslcert.pem").read_bytes(),
|
certificate,
|
||||||
7,
|
7,
|
||||||
["messages"],
|
["messages"],
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
|
@ -2163,19 +2188,27 @@ class TestBot:
|
||||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||||
)
|
)
|
||||||
|
|
||||||
async def test_set_chat_photo_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_set_chat_photo_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("photo") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("photo") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("photo"), InputFile)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.set_chat_photo(chat_id, file)
|
await bot.set_chat_photo(chat_id, file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
async def test_delete_chat_photo(self, bot, channel_id):
|
async def test_delete_chat_photo(self, bot, channel_id):
|
||||||
|
|
|
@ -22,7 +22,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Bot, Document, MessageEntity, PhotoSize, Voice
|
from telegram import Bot, Document, InputFile, MessageEntity, PhotoSize, Voice
|
||||||
from telegram.error import BadRequest, TelegramError
|
from telegram.error import BadRequest, TelegramError
|
||||||
from telegram.helpers import escape_markdown
|
from telegram.helpers import escape_markdown
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
@ -255,19 +255,29 @@ class TestDocument:
|
||||||
unprotected = await default_bot.send_document(chat_id, document, protect_content=False)
|
unprotected = await default_bot.send_document(chat_id, document, protect_content=False)
|
||||||
assert not unprotected.has_protected_content
|
assert not unprotected.has_protected_content
|
||||||
|
|
||||||
async def test_send_document_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_document_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("document") == expected and data.get("thumb") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("document") == expected and data.get("thumb") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("document"), InputFile) and isinstance(
|
||||||
|
data.get("thumb"), InputFile
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_document(chat_id, file, thumb=file)
|
await bot.send_document(chat_id, file, thumb=file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
def test_de_json(self, bot, document):
|
def test_de_json(self, bot, document):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -43,23 +46,43 @@ class TestFiles:
|
||||||
assert telegram._utils.files.is_local_file(string) == expected
|
assert telegram._utils.files.is_local_file(string) == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"string,expected",
|
"string,expected_local,expected_non_local",
|
||||||
[
|
[
|
||||||
(data_file("game.gif"), data_file("game.gif").as_uri()),
|
(data_file("game.gif"), data_file("game.gif").as_uri(), InputFile),
|
||||||
(TEST_DATA_PATH, TEST_DATA_PATH),
|
(TEST_DATA_PATH, TEST_DATA_PATH, TEST_DATA_PATH),
|
||||||
("file://foobar", "file://foobar"),
|
("file://foobar", "file://foobar", ValueError),
|
||||||
(str(data_file("game.gif")), data_file("game.gif").as_uri()),
|
(str(data_file("game.gif")), data_file("game.gif").as_uri(), InputFile),
|
||||||
(str(TEST_DATA_PATH), str(TEST_DATA_PATH)),
|
(str(TEST_DATA_PATH), str(TEST_DATA_PATH), str(TEST_DATA_PATH)),
|
||||||
(data_file("game.gif"), data_file("game.gif").as_uri()),
|
|
||||||
(TEST_DATA_PATH, TEST_DATA_PATH),
|
|
||||||
(
|
(
|
||||||
"https:/api.org/file/botTOKEN/document/file_3",
|
"https:/api.org/file/botTOKEN/document/file_3",
|
||||||
"https:/api.org/file/botTOKEN/document/file_3",
|
"https:/api.org/file/botTOKEN/document/file_3",
|
||||||
|
"https:/api.org/file/botTOKEN/document/file_3",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
ids=[
|
||||||
|
"Path(local_file)",
|
||||||
|
"Path(directory)",
|
||||||
|
"file_uri",
|
||||||
|
"str-path local file",
|
||||||
|
"str-path directory",
|
||||||
|
"URL",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
def test_parse_file_input_string(self, string, expected):
|
def test_parse_file_input_string(self, string, expected_local, expected_non_local):
|
||||||
assert telegram._utils.files.parse_file_input(string) == expected
|
assert telegram._utils.files.parse_file_input(string, local_mode=True) == expected_local
|
||||||
|
|
||||||
|
if expected_non_local is InputFile:
|
||||||
|
assert isinstance(
|
||||||
|
telegram._utils.files.parse_file_input(string, local_mode=False), InputFile
|
||||||
|
)
|
||||||
|
elif expected_non_local is ValueError:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
telegram._utils.files.parse_file_input(string, local_mode=False)
|
||||||
|
else:
|
||||||
|
assert (
|
||||||
|
telegram._utils.files.parse_file_input(string, local_mode=False)
|
||||||
|
== expected_non_local
|
||||||
|
)
|
||||||
|
|
||||||
def test_parse_file_input_file_like(self):
|
def test_parse_file_input_file_like(self):
|
||||||
source_file = data_file("game.gif")
|
source_file = data_file("game.gif")
|
||||||
|
@ -105,3 +128,34 @@ class TestFiles:
|
||||||
|
|
||||||
assert isinstance(parsed, InputFile)
|
assert isinstance(parsed, InputFile)
|
||||||
assert bool(parsed.attach_name) is attach
|
assert bool(parsed.attach_name) is attach
|
||||||
|
|
||||||
|
def test_load_file_none(self):
|
||||||
|
assert telegram._utils.files.load_file(None) == (None, None)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("arg", [b"bytes", "string", InputFile(b"content"), Path("file/path")])
|
||||||
|
def test_load_file_no_file(self, arg):
|
||||||
|
out = telegram._utils.files.load_file(arg)
|
||||||
|
assert out[0] is None
|
||||||
|
assert out[1] is arg
|
||||||
|
|
||||||
|
def test_load_file_file_handle(self):
|
||||||
|
out = telegram._utils.files.load_file(data_file("telegram.gif").open("rb"))
|
||||||
|
assert out[0] == "telegram.gif"
|
||||||
|
assert out[1] == data_file("telegram.gif").read_bytes()
|
||||||
|
|
||||||
|
def test_load_file_subprocess_pipe(self):
|
||||||
|
png_file = data_file("telegram.png")
|
||||||
|
cmd_str = "type" if sys.platform == "win32" else "cat"
|
||||||
|
cmd = [cmd_str, str(png_file)]
|
||||||
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=(sys.platform == "win32"))
|
||||||
|
out = telegram._utils.files.load_file(proc.stdout)
|
||||||
|
|
||||||
|
assert out[0] is None
|
||||||
|
assert out[1] == png_file.read_bytes()
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc.kill()
|
||||||
|
except ProcessLookupError:
|
||||||
|
# This exception may be thrown if the process has finished before we had the chance
|
||||||
|
# to kill it.
|
||||||
|
pass
|
||||||
|
|
|
@ -236,19 +236,27 @@ class TestPhoto:
|
||||||
unprotected = await default_bot.send_photo(chat_id, photo, protect_content=False)
|
unprotected = await default_bot.send_photo(chat_id, photo, protect_content=False)
|
||||||
assert not unprotected.has_protected_content
|
assert not unprotected.has_protected_content
|
||||||
|
|
||||||
async def test_send_photo_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_photo_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("photo") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("photo") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("photo"), InputFile)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_photo(chat_id, file)
|
await bot.send_photo(chat_id, file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -23,7 +23,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Audio, Bot, File, MaskPosition, PhotoSize, Sticker, StickerSet
|
from telegram import Audio, Bot, File, InputFile, MaskPosition, PhotoSize, Sticker, StickerSet
|
||||||
from telegram.error import BadRequest, TelegramError
|
from telegram.error import BadRequest, TelegramError
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
from tests.conftest import (
|
from tests.conftest import (
|
||||||
|
@ -250,20 +250,28 @@ class TestSticker:
|
||||||
message = await bot.send_sticker(sticker=sticker, chat_id=chat_id)
|
message = await bot.send_sticker(sticker=sticker, chat_id=chat_id)
|
||||||
assert message
|
assert message
|
||||||
|
|
||||||
async def test_send_sticker_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_sticker_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("sticker") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("sticker") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("sticker"), InputFile)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_sticker(chat_id, file)
|
await bot.send_sticker(chat_id, file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
monkeypatch.delattr(bot, "_post")
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -645,47 +653,67 @@ class TestStickerSet:
|
||||||
file_id = video_sticker_set.stickers[-1].file_id
|
file_id = video_sticker_set.stickers[-1].file_id
|
||||||
assert await bot.delete_sticker_from_set(file_id)
|
assert await bot.delete_sticker_from_set(file_id)
|
||||||
|
|
||||||
async def test_upload_sticker_file_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_upload_sticker_file_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("png_sticker") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("png_sticker") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("png_sticker"), InputFile)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.upload_sticker_file(chat_id, file)
|
await bot.upload_sticker_file(chat_id, file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
monkeypatch.delattr(bot, "_post")
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
async def test_create_new_sticker_set_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_create_new_sticker_set_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = (
|
if local_mode:
|
||||||
data.get("png_sticker") == expected
|
test_flag = (
|
||||||
and data.get("tgs_sticker") == expected
|
data.get("png_sticker") == expected
|
||||||
and data.get("webm_sticker") == expected
|
and data.get("tgs_sticker") == expected
|
||||||
|
and data.get("webm_sticker") == expected
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
test_flag = (
|
||||||
|
isinstance(data.get("png_sticker"), InputFile)
|
||||||
|
and isinstance(data.get("tgs_sticker"), InputFile)
|
||||||
|
and isinstance(data.get("webm_sticker"), InputFile)
|
||||||
|
)
|
||||||
|
|
||||||
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
|
await bot.create_new_sticker_set(
|
||||||
|
chat_id,
|
||||||
|
"name",
|
||||||
|
"title",
|
||||||
|
"emoji",
|
||||||
|
png_sticker=file,
|
||||||
|
tgs_sticker=file,
|
||||||
|
webm_sticker=file,
|
||||||
)
|
)
|
||||||
|
assert test_flag
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.delattr(bot, "_post")
|
||||||
await bot.create_new_sticker_set(
|
finally:
|
||||||
chat_id,
|
bot._local_mode = False
|
||||||
"name",
|
|
||||||
"title",
|
|
||||||
"emoji",
|
|
||||||
png_sticker=file,
|
|
||||||
tgs_sticker=file,
|
|
||||||
webm_sticker=file,
|
|
||||||
)
|
|
||||||
assert test_flag
|
|
||||||
monkeypatch.delattr(bot, "_post")
|
|
||||||
|
|
||||||
async def test_create_new_sticker_all_params(self, monkeypatch, bot, chat_id, mask_position):
|
async def test_create_new_sticker_all_params(self, monkeypatch, bot, chat_id, mask_position):
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
|
@ -713,35 +741,57 @@ class TestStickerSet:
|
||||||
)
|
)
|
||||||
monkeypatch.delattr(bot, "_post")
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
|
||||||
async def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("png_sticker") == expected and data.get("tgs_sticker") == expected
|
if local_mode:
|
||||||
|
test_flag = (
|
||||||
|
data.get("png_sticker") == expected and data.get("tgs_sticker") == expected
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("png_sticker"), InputFile) and isinstance(
|
||||||
|
data.get("tgs_sticker"), InputFile
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.add_sticker_to_set(chat_id, "name", "emoji", png_sticker=file, tgs_sticker=file)
|
await bot.add_sticker_to_set(
|
||||||
assert test_flag
|
chat_id, "name", "emoji", png_sticker=file, tgs_sticker=file
|
||||||
monkeypatch.delattr(bot, "_post")
|
)
|
||||||
|
assert test_flag
|
||||||
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
async def test_set_sticker_set_thumb_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_set_sticker_set_thumb_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("thumb") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("thumb") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("thumb"), InputFile)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.set_sticker_set_thumb("name", chat_id, thumb=file)
|
await bot.set_sticker_set_thumb("name", chat_id, thumb=file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
monkeypatch.delattr(bot, "_post")
|
monkeypatch.delattr(bot, "_post")
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
async def test_get_file_instance_method(self, monkeypatch, sticker):
|
async def test_get_file_instance_method(self, monkeypatch, sticker):
|
||||||
async def make_assertion(*_, **kwargs):
|
async def make_assertion(*_, **kwargs):
|
||||||
|
|
|
@ -22,7 +22,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Bot, MessageEntity, PhotoSize, Video, Voice
|
from telegram import Bot, InputFile, MessageEntity, PhotoSize, Video, Voice
|
||||||
from telegram.error import BadRequest, TelegramError
|
from telegram.error import BadRequest, TelegramError
|
||||||
from telegram.helpers import escape_markdown
|
from telegram.helpers import escape_markdown
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
@ -247,19 +247,29 @@ class TestVideo:
|
||||||
unprotected = await default_bot.send_video(chat_id, video, protect_content=False)
|
unprotected = await default_bot.send_video(chat_id, video, protect_content=False)
|
||||||
assert not unprotected.has_protected_content
|
assert not unprotected.has_protected_content
|
||||||
|
|
||||||
async def test_send_video_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_video_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("video") == expected and data.get("thumb") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("video") == expected and data.get("thumb") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("video"), InputFile) and isinstance(
|
||||||
|
data.get("thumb"), InputFile
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_video(chat_id, file, thumb=file)
|
await bot.send_video(chat_id, file, thumb=file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -22,7 +22,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Bot, PhotoSize, VideoNote, Voice
|
from telegram import Bot, InputFile, PhotoSize, VideoNote, Voice
|
||||||
from telegram.error import BadRequest, TelegramError
|
from telegram.error import BadRequest, TelegramError
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
from tests.conftest import (
|
from tests.conftest import (
|
||||||
|
@ -177,19 +177,31 @@ class TestVideoNote:
|
||||||
assert video_note_dict["duration"] == video_note.duration
|
assert video_note_dict["duration"] == video_note.duration
|
||||||
assert video_note_dict["file_size"] == video_note.file_size
|
assert video_note_dict["file_size"] == video_note.file_size
|
||||||
|
|
||||||
async def test_send_video_note_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_video_note_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("video_note") == expected and data.get("thumb") == expected
|
if local_mode:
|
||||||
|
test_flag = (
|
||||||
|
data.get("video_note") == expected and data.get("thumb") == expected
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("video_note"), InputFile) and isinstance(
|
||||||
|
data.get("thumb"), InputFile
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_video_note(chat_id, file, thumb=file)
|
await bot.send_video_note(chat_id, file, thumb=file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -22,7 +22,7 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from flaky import flaky
|
from flaky import flaky
|
||||||
|
|
||||||
from telegram import Audio, Bot, MessageEntity, Voice
|
from telegram import Audio, Bot, InputFile, MessageEntity, Voice
|
||||||
from telegram.error import BadRequest, TelegramError
|
from telegram.error import BadRequest, TelegramError
|
||||||
from telegram.helpers import escape_markdown
|
from telegram.helpers import escape_markdown
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
@ -207,19 +207,27 @@ class TestVoice:
|
||||||
unprotected = await default_bot.send_voice(chat_id, voice, protect_content=False)
|
unprotected = await default_bot.send_voice(chat_id, voice, protect_content=False)
|
||||||
assert not unprotected.has_protected_content
|
assert not unprotected.has_protected_content
|
||||||
|
|
||||||
async def test_send_voice_local_files(self, monkeypatch, bot, chat_id):
|
@pytest.mark.parametrize("local_mode", [True, False])
|
||||||
# For just test that the correct paths are passed as we have no local bot API set up
|
async def test_send_voice_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||||
test_flag = False
|
try:
|
||||||
file = data_file("telegram.jpg")
|
bot._local_mode = local_mode
|
||||||
expected = file.as_uri()
|
# For just test that the correct paths are passed as we have no local bot API set up
|
||||||
|
test_flag = False
|
||||||
|
file = data_file("telegram.jpg")
|
||||||
|
expected = file.as_uri()
|
||||||
|
|
||||||
async def make_assertion(_, data, *args, **kwargs):
|
async def make_assertion(_, data, *args, **kwargs):
|
||||||
nonlocal test_flag
|
nonlocal test_flag
|
||||||
test_flag = data.get("voice") == expected
|
if local_mode:
|
||||||
|
test_flag = data.get("voice") == expected
|
||||||
|
else:
|
||||||
|
test_flag = isinstance(data.get("voice"), InputFile)
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
await bot.send_voice(chat_id, file)
|
await bot.send_voice(chat_id, file)
|
||||||
assert test_flag
|
assert test_flag
|
||||||
|
finally:
|
||||||
|
bot._local_mode = False
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
Loading…
Reference in a new issue