mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-20 08:05:27 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8f1164b0c | |||
| da11561f87 | |||
| 354a8e0854 | |||
| bc68488c14 | |||
| 19d7939355 | |||
| 3495ce3aeb | |||
| dd9af64a5c | |||
| da3bc6974a | |||
| b1fc0596b9 | |||
| 6d2334c88b | |||
| a0c81ec3d4 | |||
| c8d9898eaa | |||
| 616b0b55ef | |||
| c71612ffae | |||
| 4143d99f56 | |||
| cbe808e471 | |||
| 300ec920a1 | |||
| 075f517458 | |||
| c82a0808d1 | |||
| ea7e5a69aa | |||
| f67e8c0804 | |||
| af130ef5e7 |
@@ -11,7 +11,7 @@ jobs:
|
||||
pull-requests: write # for srvaroa/labeler to add labels in PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: srvaroa/labeler@v1.6.1
|
||||
- uses: srvaroa/labeler@v1.7.0
|
||||
# Config file at .github/labeler.yml
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
+12
-12
@@ -6,7 +6,7 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.9.1
|
||||
rev: 23.10.1
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
@@ -17,7 +17,7 @@ repos:
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: v3.0.0
|
||||
rev: v3.0.1
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(telegram|examples)/.*\.py$
|
||||
@@ -28,14 +28,14 @@ repos:
|
||||
- --jobs=0
|
||||
|
||||
additional_dependencies:
|
||||
- httpx~=0.24.1
|
||||
- httpx~=0.25.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- cachetools~=5.3.2
|
||||
- aiolimiter~=1.1.0
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.5.1
|
||||
rev: v1.6.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: mypy-ptb
|
||||
@@ -44,10 +44,10 @@ repos:
|
||||
- types-pytz
|
||||
- types-cryptography
|
||||
- types-cachetools
|
||||
- httpx~=0.24.1
|
||||
- httpx~=0.25.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- cachetools~=5.3.2
|
||||
- aiolimiter~=1.1.0
|
||||
- . # this basically does `pip install -e .`
|
||||
- id: mypy
|
||||
@@ -59,10 +59,10 @@ repos:
|
||||
additional_dependencies:
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- cachetools~=5.3.2
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.13.0
|
||||
rev: v3.15.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
files: ^(telegram|examples|tests|docs)/.*\.py$
|
||||
@@ -77,14 +77,14 @@ repos:
|
||||
- --diff
|
||||
- --check
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 'v0.0.292'
|
||||
rev: 'v0.1.5'
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: ruff
|
||||
files: ^(telegram|examples|tests)/.*\.py$
|
||||
additional_dependencies:
|
||||
- httpx~=0.24.1
|
||||
- httpx~=0.25.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- cachetools~=5.3.2
|
||||
- aiolimiter~=1.1.0
|
||||
|
||||
+49
@@ -4,6 +4,55 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 20.6
|
||||
============
|
||||
|
||||
*Released 2023-11-27*
|
||||
|
||||
This is the technical changelog for version 20.6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`__.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
||||
- Add ``JobQueue.scheduler_configuration`` and Corresponding Warnings (:pr:`3913` closes :issue:`3837`)
|
||||
- Add Parameter ``socket_options`` to ``HTTPXRequest`` (:pr:`3935` closes :issue:`2965`)
|
||||
- Add ``ApplicationBuilder.(get_updates_)socket_options`` (:pr:`3943`)
|
||||
- Improve ``write_timeout`` Handling for Media Methods (:pr:`3952`)
|
||||
- Add ``filters.Mention`` (:pr:`3941` closes :issue:`3799`)
|
||||
- Rename ``proxy_url`` to ``proxy`` and Allow ``httpx.{Proxy, URL}`` as Input (:pr:`3939` closes :issue:`3844`)
|
||||
|
||||
Bug Fixes & Changes
|
||||
-------------------
|
||||
|
||||
- Adjust ``read_timeout`` Behavior for ``Bot.get_updates`` (:pr:`3963` closes :issue:`3893`)
|
||||
- Improve ``BaseHandler.__repr__`` for Callbacks without ``__qualname__`` (:pr:`3934`)
|
||||
- Fix Persistency Issue with Ended Non-Blocking Conversations (:pr:`3962`)
|
||||
- Improve Type Hinting for Arguments with Default Values in ``Bot`` (:pr:`3942`)
|
||||
|
||||
Documentation Improvements
|
||||
--------------------------
|
||||
|
||||
- Add Documentation for ``__aenter__`` and ``__aexit__`` Methods (:pr:`3907` closes :issue:`3886`)
|
||||
- Improve Insertion of Kwargs into ``Bot`` Methods (:pr:`3965`)
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Adjust Tests to New Error Messages (:pr:`3970`)
|
||||
|
||||
Dependency Updates
|
||||
------------------
|
||||
|
||||
- Bump ``pytest-xdist`` from 3.3.1 to 3.4.0 (:pr:`3975`)
|
||||
- ``pre-commit`` autoupdate (:pr:`3967`)
|
||||
- Update ``httpx`` requirement from ~=0.25.1 to ~=0.25.2 (:pr:`3983`)
|
||||
- Bump ``pytest-xdist`` from 3.4.0 to 3.5.0 (:pr:`3982`)
|
||||
- Update ``httpx`` requirement from ~=0.25.0 to ~=0.25.1 (:pr:`3961`)
|
||||
- Bump ``srvaroa/labeler`` from 1.6.1 to 1.7.0 (:pr:`3958`)
|
||||
- Update ``cachetools`` requirement from ~=5.3.1 to ~=5.3.2 (:pr:`3954`)
|
||||
- Bump ``pytest`` from 7.4.2 to 7.4.3 (:pr:`3953`)
|
||||
|
||||
|
||||
Version 20.6
|
||||
============
|
||||
|
||||
|
||||
+2
-2
@@ -135,7 +135,7 @@ As these features are *optional*, the corresponding 3rd party dependencies are n
|
||||
Instead, they are listed as optional dependencies.
|
||||
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
|
||||
|
||||
The only required dependency is `httpx ~= 0.24.1 <https://www.python-httpx.org>`_ for
|
||||
The only required dependency is `httpx ~= 0.25.2 <https://www.python-httpx.org>`_ for
|
||||
``telegram.request.HTTPXRequest``, the default networking backend.
|
||||
|
||||
``python-telegram-bot`` is most useful when used along with additional libraries.
|
||||
@@ -153,7 +153,7 @@ PTB can be installed with optional dependencies:
|
||||
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
|
||||
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
|
||||
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.3.3 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
|
||||
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools~=5.3.1 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
|
||||
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools~=5.3.2 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
|
||||
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``.
|
||||
|
||||
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ As these features are *optional*, the corresponding 3rd party dependencies are n
|
||||
Instead, they are listed as optional dependencies.
|
||||
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
|
||||
|
||||
The only required dependency is `httpx ~= 0.24.1 <https://www.python-httpx.org>`_ for
|
||||
The only required dependency is `httpx ~= 0.25.2 <https://www.python-httpx.org>`_ for
|
||||
``telegram.request.HTTPXRequest``, the default networking backend.
|
||||
|
||||
``python-telegram-bot`` is most useful when used along with additional libraries.
|
||||
|
||||
@@ -18,67 +18,80 @@
|
||||
import inspect
|
||||
|
||||
keyword_args = [
|
||||
"Keyword Arguments:",
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.read_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to {read_timeout}."
|
||||
),
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.read_timeout: {read_timeout_type}, optional",
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.write_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to {write_timeout}."
|
||||
" read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`. "
|
||||
),
|
||||
(
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.write_timeout: :obj:`float` |"
|
||||
" :obj:`None`, optional"
|
||||
" write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
),
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.connect_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to "
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
" connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
),
|
||||
(
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.connect_timeout: :obj:`float` | "
|
||||
":obj:`None`, optional"
|
||||
" pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
),
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.pool_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to "
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
" api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments"
|
||||
" to be passed to the Telegram API."
|
||||
),
|
||||
(
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.pool_timeout: :obj:`float` |"
|
||||
" :obj:`None`, optional"
|
||||
),
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.api_kwargs: Arbitrary keyword arguments"
|
||||
" to be passed to the Telegram API."
|
||||
),
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.api_kwargs: :obj:`dict`, optional",
|
||||
"",
|
||||
]
|
||||
write_timeout_sub = [":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`", "``20``"]
|
||||
read_timeout_sub = [
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`",
|
||||
"``2``. :paramref:`timeout` will be added to this value",
|
||||
|
||||
media_write_timeout_deprecation_methods = [
|
||||
"send_photo",
|
||||
"send_audio",
|
||||
"send_document",
|
||||
"send_sticker",
|
||||
"send_video",
|
||||
"send_video_note",
|
||||
"send_animation",
|
||||
"send_voice",
|
||||
"send_media_group",
|
||||
"set_chat_photo",
|
||||
"upload_sticker_file",
|
||||
"add_sticker_to_set",
|
||||
"create_new_sticker_set",
|
||||
]
|
||||
media_write_timeout_deprecation = [
|
||||
" write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.write_timeout`. By default, ``20`` "
|
||||
" seconds are used as write timeout."
|
||||
"",
|
||||
"",
|
||||
" .. deprecated:: 20.7",
|
||||
" In future versions, the default value will be changed to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
get_updates_read_timeout_addition = [
|
||||
" :paramref:`timeout` will be added to this value.",
|
||||
"",
|
||||
"",
|
||||
" .. versionchanged:: 20.7",
|
||||
" Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE` instead of ",
|
||||
" ``2``.",
|
||||
]
|
||||
read_timeout_type = [":obj:`float` | :obj:`None`", ":obj:`float`"]
|
||||
|
||||
|
||||
def find_insert_pos_for_kwargs(lines: list[str]) -> int:
|
||||
"""Finds the correct position to insert the keyword arguments and returns the index."""
|
||||
for idx, value in reversed(list(enumerate(lines))): # reversed since :returns: is at the end
|
||||
if value.startswith(":returns:"):
|
||||
if value.startswith("Returns"):
|
||||
return idx
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def is_write_timeout_20(obj: object) -> int:
|
||||
"""inspects the default value of write_timeout parameter of the bot method."""
|
||||
sig = inspect.signature(obj)
|
||||
return 1 if (sig.parameters["write_timeout"].default == 20) else 0
|
||||
|
||||
|
||||
def check_timeout_and_api_kwargs_presence(obj: object) -> int:
|
||||
"""Checks if the method has timeout and api_kwargs keyword only parameters."""
|
||||
sig = inspect.signature(obj)
|
||||
|
||||
+19
-15
@@ -29,11 +29,10 @@ from docs.auxil.admonition_inserter import AdmonitionInserter
|
||||
from docs.auxil.kwargs_insertion import (
|
||||
check_timeout_and_api_kwargs_presence,
|
||||
find_insert_pos_for_kwargs,
|
||||
is_write_timeout_20,
|
||||
get_updates_read_timeout_addition,
|
||||
keyword_args,
|
||||
read_timeout_sub,
|
||||
read_timeout_type,
|
||||
write_timeout_sub,
|
||||
media_write_timeout_deprecation,
|
||||
media_write_timeout_deprecation_methods,
|
||||
)
|
||||
from docs.auxil.link_code import LINE_NUMBERS
|
||||
|
||||
@@ -107,19 +106,24 @@ def autodoc_process_docstring(
|
||||
f"Couldn't find the correct position to insert the keyword args for {obj}."
|
||||
)
|
||||
|
||||
long_write_timeout = is_write_timeout_20(obj)
|
||||
get_updates_sub = 1 if (method_name == "get_updates") else 0
|
||||
get_updates: bool = method_name == "get_updates"
|
||||
# The below can be done in 1 line with itertools.chain, but this must be modified in-place
|
||||
insert_idx = insert_index
|
||||
for i in range(insert_index, insert_index + len(keyword_args)):
|
||||
lines.insert(
|
||||
i,
|
||||
keyword_args[i - insert_index].format(
|
||||
method=method_name,
|
||||
write_timeout=write_timeout_sub[long_write_timeout],
|
||||
read_timeout=read_timeout_sub[get_updates_sub],
|
||||
read_timeout_type=read_timeout_type[get_updates_sub],
|
||||
),
|
||||
)
|
||||
to_insert = keyword_args[i - insert_index]
|
||||
|
||||
if (
|
||||
"post.write_timeout`. Defaults to" in to_insert
|
||||
and method_name in media_write_timeout_deprecation_methods
|
||||
):
|
||||
effective_insert: list[str] = media_write_timeout_deprecation
|
||||
elif get_updates and to_insert.lstrip().startswith("read_timeout"):
|
||||
effective_insert = [to_insert] + get_updates_read_timeout_addition
|
||||
else:
|
||||
effective_insert = [to_insert]
|
||||
|
||||
lines[insert_idx:insert_idx] = effective_insert
|
||||
insert_idx += len(effective_insert)
|
||||
|
||||
ADMONITION_INSERTER.insert_admonitions(
|
||||
obj=typing.cast(collections.abc.Callable, obj),
|
||||
|
||||
+12
-3
@@ -21,9 +21,9 @@ author = "Leandro Toledo"
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = "20.6" # telegram.__version__[:3]
|
||||
version = "20.7" # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = "20.6" # telegram.__version__
|
||||
release = "20.7" # telegram.__version__
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = "6.1.3"
|
||||
@@ -77,6 +77,12 @@ napoleon_use_admonition_for_examples = True
|
||||
# and we document the types anyway
|
||||
autodoc_typehints = "none"
|
||||
|
||||
# Show docstring for special members
|
||||
autodoc_default_options = {
|
||||
"special-members": True,
|
||||
"exclude-members": "__init__",
|
||||
}
|
||||
|
||||
# Fail on warnings & unresolved references etc
|
||||
nitpicky = True
|
||||
|
||||
@@ -314,5 +320,8 @@ from docs.auxil.tg_const_role import CONSTANTS_ROLE, TGConstXRefRole
|
||||
def setup(app: Sphinx):
|
||||
app.connect("autodoc-skip-member", autodoc_skip_member)
|
||||
app.connect("autodoc-process-bases", autodoc_process_bases)
|
||||
app.connect("autodoc-process-docstring", autodoc_process_docstring)
|
||||
# The default priority is 500. We want our function to run before napoleon doc-conversion
|
||||
# and sphinx-paramlinks do, b/c otherwise the inserted kwargs in the bot methods won't show
|
||||
# up in the objects.inv file that Sphinx generates (i.e. not in the search).
|
||||
app.connect("autodoc-process-docstring", autodoc_process_docstring, priority=100)
|
||||
app.add_role_to_domain("py", CONSTANTS_ROLE, TGConstXRefRole())
|
||||
|
||||
@@ -61,3 +61,5 @@
|
||||
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``.
|
||||
|
||||
.. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``.
|
||||
|
||||
.. |async_context_manager| replace:: Asynchronous context manager which
|
||||
@@ -1,9 +1,9 @@
|
||||
pre-commit # needed for pre-commit hooks in the git commit command
|
||||
|
||||
# For the test suite
|
||||
pytest==7.4.2
|
||||
pytest==7.4.3
|
||||
pytest-asyncio==0.21.1 # needed because pytest doesn't come with native support for coroutines as tests
|
||||
pytest-xdist==3.3.1 # xdist runs tests in parallel
|
||||
pytest-xdist==3.5.0 # xdist runs tests in parallel
|
||||
flaky # Used for flaky tests (flaky decorator)
|
||||
beautifulsoup4 # used in test_official for parsing tg docs
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ tornado~=6.3.3 # webhooks!ext
|
||||
|
||||
# Cachetools and APS don't have a strict stability policy.
|
||||
# Let's be cautious for now.
|
||||
cachetools~=5.3.1 # callback-data!ext
|
||||
cachetools~=5.3.2 # callback-data!ext
|
||||
APScheduler~=3.10.4 # job-queue!ext
|
||||
|
||||
# pytz is required by APS and just needs the lower bound due to #2120
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@
|
||||
# versions and only increase the lower bound if necessary
|
||||
|
||||
# httpx has no stable release yet, so let's be cautious for now
|
||||
httpx ~= 0.25.0
|
||||
httpx ~= 0.25.2
|
||||
|
||||
+63
-41
@@ -93,14 +93,7 @@ from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.files import is_local_file, parse_file_input
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
)
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram._webhookinfo import WebhookInfo
|
||||
from telegram.constants import InlineQueryLimit
|
||||
@@ -108,7 +101,7 @@ from telegram.error import InvalidToken
|
||||
from telegram.request import BaseRequest, RequestData
|
||||
from telegram.request._httpxrequest import HTTPXRequest
|
||||
from telegram.request._requestparameter import RequestParameter
|
||||
from telegram.warnings import PTBUserWarning
|
||||
from telegram.warnings import PTBDeprecationWarning, PTBUserWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
@@ -149,6 +142,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
finally:
|
||||
await bot.shutdown()
|
||||
|
||||
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
|
||||
|
||||
Note:
|
||||
* Most bot methods have the argument ``api_kwargs`` which allows passing arbitrary keywords
|
||||
to the Telegram API. This can be used to access new features of the API before they are
|
||||
@@ -310,6 +305,16 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
self._freeze()
|
||||
|
||||
async def __aenter__(self: BT) -> BT:
|
||||
"""
|
||||
|async_context_manager| :meth:`initializes <initialize>` the Bot.
|
||||
|
||||
Returns:
|
||||
The initialized Bot instance.
|
||||
|
||||
Raises:
|
||||
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
|
||||
is called in this case.
|
||||
"""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
@@ -323,6 +328,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""|async_context_manager| :meth:`shuts down <shutdown>` the Bot."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
@@ -785,7 +791,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -920,7 +926,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
chat_id: Union[int, str],
|
||||
from_chat_id: Union[str, int],
|
||||
message_id: int,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
@@ -986,7 +992,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
chat_id: Union[int, str],
|
||||
photo: Union[FileInput, "PhotoSize"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -998,7 +1004,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1100,7 +1106,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1112,7 +1118,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1224,7 +1230,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
chat_id: Union[int, str],
|
||||
document: Union[FileInput, "Document"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1237,7 +1243,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1343,7 +1349,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
sticker: Union[FileInput, "Sticker"],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1352,7 +1358,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
emoji: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1431,7 +1437,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
width: Optional[int] = None,
|
||||
@@ -1447,7 +1453,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1570,7 +1576,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1580,7 +1586,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1685,7 +1691,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1697,7 +1703,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1813,7 +1819,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1824,7 +1830,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1936,7 +1942,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2057,7 +2063,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
chat_id: Union[int, str],
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
@@ -2318,7 +2324,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
title: Optional[str] = None,
|
||||
address: Optional[str] = None,
|
||||
foursquare_id: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
foursquare_type: Optional[str] = None,
|
||||
@@ -2442,7 +2448,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
phone_number: Optional[str] = None,
|
||||
first_name: Optional[str] = None,
|
||||
last_name: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
vcard: Optional[str] = None,
|
||||
@@ -2543,7 +2549,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: int,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -3490,7 +3496,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
timeout: Optional[int] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
*,
|
||||
read_timeout: float = 2,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3552,6 +3558,22 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
"allowed_updates": allowed_updates,
|
||||
}
|
||||
|
||||
# The "or 0" is needed for the case where read_timeout is None.
|
||||
if not isinstance(read_timeout, DefaultValue):
|
||||
arg_read_timeout: float = read_timeout or 0
|
||||
else:
|
||||
try:
|
||||
arg_read_timeout = self._request[0].read_timeout or 0
|
||||
except NotImplementedError:
|
||||
arg_read_timeout = 2
|
||||
self._warn(
|
||||
f"The class {self._request[0].__class__.__name__} does not override "
|
||||
"the property `read_timeout`. Overriding this property will be mandatory in "
|
||||
"future versions. Using 2 seconds as fallback.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
# Ideally we'd use an aggressive read timeout for the polling. However,
|
||||
# * Short polling should return within 2 seconds.
|
||||
# * Long polling poses a different problem: the connection might have been dropped while
|
||||
@@ -3562,7 +3584,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
await self._post(
|
||||
"getUpdates",
|
||||
data,
|
||||
read_timeout=read_timeout + timeout if timeout else read_timeout,
|
||||
read_timeout=arg_read_timeout + timeout if timeout else arg_read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
@@ -4174,7 +4196,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
need_email: Optional[bool] = None,
|
||||
need_shipping_address: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
@@ -5141,7 +5163,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
photo: FileInput,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -5521,7 +5543,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
sticker_format: Optional[str],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -5581,7 +5603,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
sticker: Optional["InputSticker"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -5683,7 +5705,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
needs_repainting: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -6747,9 +6769,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
||||
@@ -26,7 +26,7 @@ from telegram._message import Message
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import DVInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.types import JSONDict, ODVInput, ReplyMarkup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
@@ -725,9 +725,9 @@ class CallbackQuery(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
||||
+31
-38
@@ -33,14 +33,7 @@ from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
)
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram.helpers import escape_markdown
|
||||
from telegram.helpers import mention_html as helpers_mention_html
|
||||
from telegram.helpers import mention_markdown as helpers_mention_markdown
|
||||
@@ -1095,7 +1088,7 @@ class Chat(TelegramObject):
|
||||
photo: FileInput,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1326,7 +1319,7 @@ class Chat(TelegramObject):
|
||||
text: str,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1381,7 +1374,7 @@ class Chat(TelegramObject):
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1457,7 +1450,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
photo: Union[FileInput, "PhotoSize"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1469,7 +1462,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1510,7 +1503,7 @@ class Chat(TelegramObject):
|
||||
phone_number: Optional[str] = None,
|
||||
first_name: Optional[str] = None,
|
||||
last_name: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
vcard: Optional[str] = None,
|
||||
@@ -1562,7 +1555,7 @@ class Chat(TelegramObject):
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1574,7 +1567,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1617,7 +1610,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
document: Union[FileInput, "Document"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1630,7 +1623,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1712,7 +1705,7 @@ class Chat(TelegramObject):
|
||||
async def send_game(
|
||||
self,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1769,7 +1762,7 @@ class Chat(TelegramObject):
|
||||
need_email: Optional[bool] = None,
|
||||
need_shipping_address: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
@@ -1847,7 +1840,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
@@ -1905,7 +1898,7 @@ class Chat(TelegramObject):
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1917,7 +1910,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1960,7 +1953,7 @@ class Chat(TelegramObject):
|
||||
async def send_sticker(
|
||||
self,
|
||||
sticker: Union[FileInput, "Sticker"],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1969,7 +1962,7 @@ class Chat(TelegramObject):
|
||||
emoji: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2008,7 +2001,7 @@ class Chat(TelegramObject):
|
||||
title: Optional[str] = None,
|
||||
address: Optional[str] = None,
|
||||
foursquare_id: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
foursquare_type: Optional[str] = None,
|
||||
@@ -2064,7 +2057,7 @@ class Chat(TelegramObject):
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
width: Optional[int] = None,
|
||||
@@ -2080,7 +2073,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2126,7 +2119,7 @@ class Chat(TelegramObject):
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2136,7 +2129,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2176,7 +2169,7 @@ class Chat(TelegramObject):
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -2187,7 +2180,7 @@ class Chat(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2294,9 +2287,9 @@ class Chat(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -2344,9 +2337,9 @@ class Chat(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -2391,7 +2384,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
from_chat_id: Union[str, int],
|
||||
message_id: int,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
@@ -2433,7 +2426,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
|
||||
@@ -55,7 +55,10 @@ class MenuButton(TelegramObject):
|
||||
__slots__ = ("type",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, *, api_kwargs: Optional[JSONDict] = None # skipcq: PYL-W0622
|
||||
self,
|
||||
type: str, # skipcq: PYL-W0622
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
): # pylint: disable=redefined-builtin
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = type
|
||||
|
||||
+31
-32
@@ -61,7 +61,6 @@ from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestam
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
MarkdownVersion,
|
||||
@@ -1061,7 +1060,7 @@ class Message(TelegramObject):
|
||||
text: str,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1115,7 +1114,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
text: str,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1179,7 +1178,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
text: str,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1239,7 +1238,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
text: str,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1308,7 +1307,7 @@ class Message(TelegramObject):
|
||||
*,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1357,7 +1356,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
photo: Union[FileInput, "PhotoSize"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1370,7 +1369,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1419,7 +1418,7 @@ class Message(TelegramObject):
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1432,7 +1431,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1481,7 +1480,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
document: Union[FileInput, "Document"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1495,7 +1494,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1546,7 +1545,7 @@ class Message(TelegramObject):
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1559,7 +1558,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1609,7 +1608,7 @@ class Message(TelegramObject):
|
||||
async def reply_sticker(
|
||||
self,
|
||||
sticker: Union[FileInput, "Sticker"],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1619,7 +1618,7 @@ class Message(TelegramObject):
|
||||
*,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1662,7 +1661,7 @@ class Message(TelegramObject):
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
width: Optional[int] = None,
|
||||
@@ -1679,7 +1678,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1731,7 +1730,7 @@ class Message(TelegramObject):
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1742,7 +1741,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1789,7 +1788,7 @@ class Message(TelegramObject):
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1801,7 +1800,7 @@ class Message(TelegramObject):
|
||||
filename: Optional[str] = None,
|
||||
quote: Optional[bool] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1848,7 +1847,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
@@ -1912,7 +1911,7 @@ class Message(TelegramObject):
|
||||
title: Optional[str] = None,
|
||||
address: Optional[str] = None,
|
||||
foursquare_id: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
foursquare_type: Optional[str] = None,
|
||||
@@ -1975,7 +1974,7 @@ class Message(TelegramObject):
|
||||
phone_number: Optional[str] = None,
|
||||
first_name: Optional[str] = None,
|
||||
last_name: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
vcard: Optional[str] = None,
|
||||
@@ -2184,7 +2183,7 @@ class Message(TelegramObject):
|
||||
async def reply_game(
|
||||
self,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2250,7 +2249,7 @@ class Message(TelegramObject):
|
||||
need_email: Optional[bool] = None,
|
||||
need_shipping_address: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
@@ -2336,7 +2335,7 @@ class Message(TelegramObject):
|
||||
async def forward(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
@@ -2389,9 +2388,9 @@ class Message(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -2445,9 +2444,9 @@ class Message(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
||||
@@ -471,7 +471,11 @@ class _CredentialsBase(TelegramObject):
|
||||
__slots__ = ("hash", "secret", "file_hash", "data_hash")
|
||||
|
||||
def __init__(
|
||||
self, hash: str, secret: str, *, api_kwargs: Optional[JSONDict] = None # skipcq: PYL-W0622
|
||||
self,
|
||||
hash: str, # skipcq: PYL-W0622
|
||||
secret: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
|
||||
+28
-35
@@ -25,14 +25,7 @@ from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from telegram._menubutton import MenuButton
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
)
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram.helpers import mention_html as helpers_mention_html
|
||||
from telegram.helpers import mention_markdown as helpers_mention_markdown
|
||||
|
||||
@@ -391,7 +384,7 @@ class User(TelegramObject):
|
||||
text: str,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -441,7 +434,7 @@ class User(TelegramObject):
|
||||
self,
|
||||
photo: Union[FileInput, "PhotoSize"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -453,7 +446,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -504,7 +497,7 @@ class User(TelegramObject):
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -551,7 +544,7 @@ class User(TelegramObject):
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -563,7 +556,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -648,7 +641,7 @@ class User(TelegramObject):
|
||||
phone_number: Optional[str] = None,
|
||||
first_name: Optional[str] = None,
|
||||
last_name: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
vcard: Optional[str] = None,
|
||||
@@ -745,7 +738,7 @@ class User(TelegramObject):
|
||||
self,
|
||||
document: Union[FileInput, "Document"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -758,7 +751,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -801,7 +794,7 @@ class User(TelegramObject):
|
||||
async def send_game(
|
||||
self,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -861,7 +854,7 @@ class User(TelegramObject):
|
||||
need_email: Optional[bool] = None,
|
||||
need_shipping_address: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
@@ -942,7 +935,7 @@ class User(TelegramObject):
|
||||
self,
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
@@ -1003,7 +996,7 @@ class User(TelegramObject):
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1015,7 +1008,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1061,7 +1054,7 @@ class User(TelegramObject):
|
||||
async def send_sticker(
|
||||
self,
|
||||
sticker: Union[FileInput, "Sticker"],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1070,7 +1063,7 @@ class User(TelegramObject):
|
||||
emoji: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1110,7 +1103,7 @@ class User(TelegramObject):
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
width: Optional[int] = None,
|
||||
@@ -1126,7 +1119,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1177,7 +1170,7 @@ class User(TelegramObject):
|
||||
title: Optional[str] = None,
|
||||
address: Optional[str] = None,
|
||||
foursquare_id: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
foursquare_type: Optional[str] = None,
|
||||
@@ -1236,7 +1229,7 @@ class User(TelegramObject):
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1246,7 +1239,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1289,7 +1282,7 @@ class User(TelegramObject):
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1300,7 +1293,7 @@ class User(TelegramObject):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1413,9 +1406,9 @@ class User(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -1466,9 +1459,9 @@ class User(TelegramObject):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
||||
@@ -95,3 +95,9 @@ HTTPVersion = Literal["1.1", "2.0", "2"]
|
||||
CorrectOptionID = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
MarkdownVersion = Literal[1, 2]
|
||||
|
||||
SocketOpt = Union[
|
||||
Tuple[int, int, int],
|
||||
Tuple[int, int, Union[bytes, bytearray]],
|
||||
Tuple[int, int, None, int],
|
||||
]
|
||||
|
||||
@@ -51,7 +51,7 @@ class Version(NamedTuple):
|
||||
|
||||
|
||||
__version_info__: Final[Version] = Version(
|
||||
major=20, minor=6, micro=0, releaselevel="final", serial=0
|
||||
major=20, minor=7, micro=0, releaselevel="final", serial=0
|
||||
)
|
||||
__version__: Final[str] = str(__version_info__)
|
||||
|
||||
|
||||
@@ -149,6 +149,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
finally:
|
||||
await application.shutdown()
|
||||
|
||||
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
|
||||
|
||||
Examples:
|
||||
:any:`Echo Bot <examples.echobot>`
|
||||
|
||||
@@ -345,7 +347,15 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
self.__create_task_tasks: Set[asyncio.Task] = set() # Used for awaiting tasks upon exit
|
||||
|
||||
async def __aenter__(self: _AppType) -> _AppType: # noqa: PYI019
|
||||
"""Simple context manager which initializes the App."""
|
||||
"""|async_context_manager| :meth:`initializes <initialize>` the App.
|
||||
|
||||
Returns:
|
||||
The initialized App instance.
|
||||
|
||||
Raises:
|
||||
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
|
||||
is called in this case.
|
||||
"""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
@@ -359,7 +369,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the App from the context manager."""
|
||||
"""|async_context_manager| :meth:`shuts down <shutdown>` the App."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
@@ -690,7 +700,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
poll_interval: float = 0.0,
|
||||
timeout: int = 10,
|
||||
bootstrap_retries: int = -1,
|
||||
read_timeout: float = 2,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -735,16 +745,37 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
* > 0 - retry up to X times
|
||||
|
||||
read_timeout (:obj:`float`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to ``2``.
|
||||
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. versionchanged:: 20.7
|
||||
Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE` instead of
|
||||
``2``.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_read_timeout`.
|
||||
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.write_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_write_timeout`.
|
||||
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.connect_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_connect_timeout`.
|
||||
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.pool_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout`.
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
allowed_updates (List[:obj:`str`], optional): Passed to
|
||||
@@ -773,6 +804,14 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
"Application.run_polling is only available if the application has an Updater."
|
||||
)
|
||||
|
||||
if (read_timeout, write_timeout, connect_timeout, pool_timeout) != ((DEFAULT_NONE,) * 4):
|
||||
warn(
|
||||
"Setting timeouts via `Application.run_polling` is deprecated. "
|
||||
"Please use `ApplicationBuilder.get_updates_*_timeout` instead.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
def error_callback(exc: TelegramError) -> None:
|
||||
self.create_task(self.process_error(error=exc, update=None))
|
||||
|
||||
@@ -1076,7 +1115,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
return await asyncio.create_task(coroutine)
|
||||
# If user uses generator in python 3.12+, Exception will happen and we cannot do
|
||||
# anything about it. (hence the type ignore if mypy is run on python 3.12-)
|
||||
return await coroutine # type: ignore
|
||||
return await coroutine # type: ignore[misc]
|
||||
except Exception as exception:
|
||||
if isinstance(exception, ApplicationHandlerStop):
|
||||
warn(
|
||||
@@ -1357,7 +1396,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
chat_id (:obj:`int`): The chat id to delete. The entry will be deleted even if it is
|
||||
not empty.
|
||||
"""
|
||||
self._chat_data.pop(chat_id, None) # type: ignore[arg-type]
|
||||
self._chat_data.pop(chat_id, None)
|
||||
self._chat_ids_to_be_deleted_in_persistence.add(chat_id)
|
||||
|
||||
def drop_user_data(self, user_id: int) -> None:
|
||||
@@ -1376,7 +1415,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
user_id (:obj:`int`): The user id to delete. The entry will be deleted even if it is
|
||||
not empty.
|
||||
"""
|
||||
self._user_data.pop(user_id, None) # type: ignore[arg-type]
|
||||
self._user_data.pop(user_id, None)
|
||||
self._user_ids_to_be_deleted_in_persistence.add(user_id)
|
||||
|
||||
def migrate_chat_data(
|
||||
|
||||
@@ -23,6 +23,7 @@ from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Collection,
|
||||
Coroutine,
|
||||
Dict,
|
||||
Generic,
|
||||
@@ -32,9 +33,12 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
import httpx
|
||||
|
||||
from telegram._bot import Bot
|
||||
from telegram._utils.defaultvalue import DEFAULT_FALSE, DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.types import DVInput, DVType, FilePathInput, HTTPVersion, ODVInput
|
||||
from telegram._utils.types import DVInput, DVType, FilePathInput, HTTPVersion, ODVInput, SocketOpt
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.ext._application import Application
|
||||
from telegram.ext._baseupdateprocessor import BaseUpdateProcessor, SimpleUpdateProcessor
|
||||
from telegram.ext._contexttypes import ContextTypes
|
||||
@@ -44,8 +48,10 @@ from telegram.ext._updater import Updater
|
||||
from telegram.ext._utils.types import BD, BT, CCT, CD, JQ, UD
|
||||
from telegram.request import BaseRequest
|
||||
from telegram.request._httpxrequest import HTTPXRequest
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Update
|
||||
from telegram.ext import BasePersistence, BaseRateLimiter, CallbackContext, Defaults
|
||||
from telegram.ext._utils.types import RLARGS
|
||||
|
||||
@@ -66,14 +72,16 @@ _BOT_CHECKS = [
|
||||
("request", "request instance"),
|
||||
("get_updates_request", "get_updates_request instance"),
|
||||
("connection_pool_size", "connection_pool_size"),
|
||||
("proxy_url", "proxy_url"),
|
||||
("proxy", "proxy"),
|
||||
("socket_options", "socket_options"),
|
||||
("pool_timeout", "pool_timeout"),
|
||||
("connect_timeout", "connect_timeout"),
|
||||
("read_timeout", "read_timeout"),
|
||||
("write_timeout", "write_timeout"),
|
||||
("http_version", "http_version"),
|
||||
("get_updates_connection_pool_size", "get_updates_connection_pool_size"),
|
||||
("get_updates_proxy_url", "get_updates_proxy_url"),
|
||||
("get_updates_proxy", "get_updates_proxy"),
|
||||
("get_updates_socket_options", "get_updates_socket_options"),
|
||||
("get_updates_pool_timeout", "get_updates_pool_timeout"),
|
||||
("get_updates_connect_timeout", "get_updates_connect_timeout"),
|
||||
("get_updates_read_timeout", "get_updates_read_timeout"),
|
||||
@@ -136,9 +144,10 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
"_get_updates_connect_timeout",
|
||||
"_get_updates_connection_pool_size",
|
||||
"_get_updates_pool_timeout",
|
||||
"_get_updates_proxy_url",
|
||||
"_get_updates_proxy",
|
||||
"_get_updates_read_timeout",
|
||||
"_get_updates_request",
|
||||
"_get_updates_socket_options",
|
||||
"_get_updates_write_timeout",
|
||||
"_get_updates_http_version",
|
||||
"_job_queue",
|
||||
@@ -149,10 +158,11 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
"_post_stop",
|
||||
"_private_key",
|
||||
"_private_key_password",
|
||||
"_proxy_url",
|
||||
"_proxy",
|
||||
"_rate_limiter",
|
||||
"_read_timeout",
|
||||
"_request",
|
||||
"_socket_options",
|
||||
"_token",
|
||||
"_update_queue",
|
||||
"_updater",
|
||||
@@ -166,14 +176,16 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
self._base_url: DVType[str] = DefaultValue("https://api.telegram.org/bot")
|
||||
self._base_file_url: DVType[str] = DefaultValue("https://api.telegram.org/file/bot")
|
||||
self._connection_pool_size: DVInput[int] = DEFAULT_NONE
|
||||
self._proxy_url: DVInput[str] = DEFAULT_NONE
|
||||
self._proxy: DVInput[Union[str, httpx.Proxy, httpx.URL]] = DEFAULT_NONE
|
||||
self._socket_options: DVInput[Collection[SocketOpt]] = DEFAULT_NONE
|
||||
self._connect_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._read_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._write_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._pool_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._request: DVInput[BaseRequest] = DEFAULT_NONE
|
||||
self._get_updates_connection_pool_size: DVInput[int] = DEFAULT_NONE
|
||||
self._get_updates_proxy_url: DVInput[str] = DEFAULT_NONE
|
||||
self._get_updates_proxy: DVInput[Union[str, httpx.Proxy, httpx.URL]] = DEFAULT_NONE
|
||||
self._get_updates_socket_options: DVInput[Collection[SocketOpt]] = DEFAULT_NONE
|
||||
self._get_updates_connect_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._get_updates_read_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._get_updates_write_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
@@ -186,7 +198,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
self._arbitrary_callback_data: Union[DefaultValue[bool], int] = DEFAULT_FALSE
|
||||
self._local_mode: DVType[bool] = DEFAULT_FALSE
|
||||
self._bot: DVInput[Bot] = DEFAULT_NONE
|
||||
self._update_queue: DVType[Queue] = DefaultValue(Queue())
|
||||
self._update_queue: DVType[Queue[Union[Update, object]]] = DefaultValue(Queue())
|
||||
|
||||
try:
|
||||
self._job_queue: ODVInput[JobQueue] = DefaultValue(JobQueue())
|
||||
@@ -214,7 +226,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
if not isinstance(getattr(self, f"{prefix}request"), DefaultValue):
|
||||
return getattr(self, f"{prefix}request")
|
||||
|
||||
proxy_url = DefaultValue.get_value(getattr(self, f"{prefix}proxy_url"))
|
||||
proxy = DefaultValue.get_value(getattr(self, f"{prefix}proxy"))
|
||||
socket_options = DefaultValue.get_value(getattr(self, f"{prefix}socket_options"))
|
||||
if get_updates:
|
||||
connection_pool_size = (
|
||||
DefaultValue.get_value(getattr(self, f"{prefix}connection_pool_size")) or 1
|
||||
@@ -239,8 +252,9 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
|
||||
return HTTPXRequest(
|
||||
connection_pool_size=connection_pool_size,
|
||||
proxy_url=proxy_url,
|
||||
proxy=proxy,
|
||||
http_version=http_version, # type: ignore[arg-type]
|
||||
socket_options=socket_options,
|
||||
**effective_timeouts,
|
||||
)
|
||||
|
||||
@@ -258,7 +272,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
arbitrary_callback_data=DefaultValue.get_value(self._arbitrary_callback_data),
|
||||
request=self._build_request(get_updates=False),
|
||||
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), # type: ignore[arg-type]
|
||||
local_mode=DefaultValue.get_value(self._local_mode),
|
||||
)
|
||||
|
||||
@@ -303,7 +317,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
|
||||
application: Application[
|
||||
BT, CCT, UD, CD, BD, JQ
|
||||
] = DefaultValue.get_value( # pylint: disable=not-callable
|
||||
] = DefaultValue.get_value( # type: ignore[operator] # pylint: disable=not-callable
|
||||
self._application_class
|
||||
)(
|
||||
bot=bot,
|
||||
@@ -311,7 +325,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
updater=updater,
|
||||
update_processor=self._update_processor,
|
||||
job_queue=job_queue,
|
||||
persistence=persistence,
|
||||
persistence=persistence, # type: ignore[arg-type]
|
||||
context_types=DefaultValue.get_value(self._context_types),
|
||||
post_init=self._post_init,
|
||||
post_shutdown=self._post_shutdown,
|
||||
@@ -320,12 +334,12 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
)
|
||||
|
||||
if job_queue is not None:
|
||||
job_queue.set_application(application) # type: ignore[arg-type]
|
||||
job_queue.set_application(application) # type: ignore[arg-type, union-attr]
|
||||
|
||||
if persistence is not None:
|
||||
# This raises an exception if persistence.store_data.callback_data is True
|
||||
# but self.bot is not an instance of ExtBot - so no need to check that later on
|
||||
persistence.set_bot(bot)
|
||||
persistence.set_bot(bot) # type: ignore[union-attr]
|
||||
|
||||
return application
|
||||
|
||||
@@ -419,8 +433,11 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
if not isinstance(getattr(self, f"_{prefix}connection_pool_size"), DefaultValue):
|
||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "connection_pool_size"))
|
||||
|
||||
if not isinstance(getattr(self, f"_{prefix}proxy_url"), DefaultValue):
|
||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "proxy_url"))
|
||||
if not isinstance(getattr(self, f"_{prefix}proxy"), DefaultValue):
|
||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "proxy"))
|
||||
|
||||
if not isinstance(getattr(self, f"_{prefix}socket_options"), DefaultValue):
|
||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "socket_options"))
|
||||
|
||||
if not isinstance(getattr(self, f"_{prefix}http_version"), DefaultValue):
|
||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, "http_version"))
|
||||
@@ -486,21 +503,64 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
return self
|
||||
|
||||
def proxy_url(self: BuilderType, proxy_url: str) -> BuilderType:
|
||||
"""Sets the proxy for the :paramref:`~telegram.request.HTTPXRequest.proxy_url`
|
||||
parameter of :attr:`telegram.Bot.request`. Defaults to :obj:`None`.
|
||||
"""Legacy name for :meth:`proxy`, kept for backward compatibility.
|
||||
|
||||
.. seealso:: :meth:`get_updates_proxy`
|
||||
|
||||
.. seealso:: :meth:`get_updates_proxy_url`
|
||||
.. deprecated:: 20.7
|
||||
|
||||
Args:
|
||||
proxy_url (:obj:`str`): The URL to the proxy server. See
|
||||
:paramref:`telegram.request.HTTPXRequest.proxy_url` for more information.
|
||||
proxy_url (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): See
|
||||
:paramref:`telegram.ext.ApplicationBuilder.proxy.proxy`.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
"""
|
||||
self._request_param_check(name="proxy_url", get_updates=False)
|
||||
self._proxy_url = proxy_url
|
||||
warn(
|
||||
"`ApplicationBuilder.proxy_url` is deprecated since version "
|
||||
"20.7. Use `ApplicationBuilder.proxy` instead.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.proxy(proxy_url)
|
||||
|
||||
def proxy(self: BuilderType, proxy: Union[str, httpx.Proxy, httpx.URL]) -> BuilderType:
|
||||
"""Sets the proxy for the :paramref:`~telegram.request.HTTPXRequest.proxy`
|
||||
parameter of :attr:`telegram.Bot.request`. Defaults to :obj:`None`.
|
||||
|
||||
.. seealso:: :meth:`get_updates_proxy`
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Args:
|
||||
proxy (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): The URL to a proxy
|
||||
server, a ``httpx.Proxy`` object or a ``httpx.URL`` object. See
|
||||
:paramref:`telegram.request.HTTPXRequest.proxy` for more information.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
"""
|
||||
self._request_param_check(name="proxy", get_updates=False)
|
||||
self._proxy = proxy
|
||||
return self
|
||||
|
||||
def socket_options(self: BuilderType, socket_options: Collection[SocketOpt]) -> BuilderType:
|
||||
"""Sets the options for the :paramref:`~telegram.request.HTTPXRequest.socket_options`
|
||||
parameter of :attr:`telegram.Bot.request`. Defaults to :obj:`None`.
|
||||
|
||||
.. seealso:: :meth:`get_updates_socket_options`
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Args:
|
||||
socket_options (Collection[:obj:`tuple`], optional): Socket options. See
|
||||
:paramref:`telegram.request.HTTPXRequest.socket_options` for more information.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
"""
|
||||
self._request_param_check(name="socket_options", get_updates=False)
|
||||
self._socket_options = socket_options
|
||||
return self
|
||||
|
||||
def connect_timeout(self: BuilderType, connect_timeout: Optional[float]) -> BuilderType:
|
||||
@@ -655,21 +715,68 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
return self
|
||||
|
||||
def get_updates_proxy_url(self: BuilderType, get_updates_proxy_url: str) -> BuilderType:
|
||||
"""Sets the proxy for the :paramref:`telegram.request.HTTPXRequest.proxy_url`
|
||||
parameter which is used for :meth:`telegram.Bot.get_updates`. Defaults to :obj:`None`.
|
||||
"""Legacy name for :meth:`get_updates_proxy`, kept for backward compatibility.
|
||||
|
||||
.. seealso:: :meth:`proxy`
|
||||
|
||||
.. seealso:: :meth:`proxy_url`
|
||||
.. deprecated:: 20.7
|
||||
|
||||
Args:
|
||||
get_updates_proxy_url (:obj:`str`): The URL to the proxy server. See
|
||||
:paramref:`telegram.request.HTTPXRequest.proxy_url` for more information.
|
||||
get_updates_proxy_url (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): See
|
||||
:paramref:`telegram.ext.ApplicationBuilder.get_updates_proxy.get_updates_proxy`.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
"""
|
||||
self._request_param_check(name="proxy_url", get_updates=True)
|
||||
self._get_updates_proxy_url = get_updates_proxy_url
|
||||
warn(
|
||||
"`ApplicationBuilder.get_updates_proxy_url` is deprecated since version "
|
||||
"20.7. Use `ApplicationBuilder.get_updates_proxy` instead.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.get_updates_proxy(get_updates_proxy_url)
|
||||
|
||||
def get_updates_proxy(
|
||||
self: BuilderType, get_updates_proxy: Union[str, httpx.Proxy, httpx.URL]
|
||||
) -> BuilderType:
|
||||
"""Sets the proxy for the :paramref:`telegram.request.HTTPXRequest.proxy`
|
||||
parameter which is used for :meth:`telegram.Bot.get_updates`. Defaults to :obj:`None`.
|
||||
|
||||
.. seealso:: :meth:`proxy`
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Args:
|
||||
proxy (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): The URL to a proxy server,
|
||||
a ``httpx.Proxy`` object or a ``httpx.URL`` object. See
|
||||
:paramref:`telegram.request.HTTPXRequest.proxy` for more information.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
"""
|
||||
self._request_param_check(name="proxy", get_updates=True)
|
||||
self._get_updates_proxy = get_updates_proxy
|
||||
return self
|
||||
|
||||
def get_updates_socket_options(
|
||||
self: BuilderType, get_updates_socket_options: Collection[SocketOpt]
|
||||
) -> BuilderType:
|
||||
"""Sets the options for the :paramref:`~telegram.request.HTTPXRequest.socket_options`
|
||||
parameter of :paramref:`telegram.Bot.get_updates_request`. Defaults to :obj:`None`.
|
||||
|
||||
.. seealso:: :meth:`socket_options`
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Args:
|
||||
get_updates_socket_options (Collection[:obj:`tuple`], optional): Socket options. See
|
||||
:paramref:`telegram.request.HTTPXRequest.socket_options` for more information.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
"""
|
||||
self._request_param_check(name="socket_options", get_updates=True)
|
||||
self._get_updates_socket_options = get_updates_socket_options
|
||||
return self
|
||||
|
||||
def get_updates_connect_timeout(
|
||||
|
||||
@@ -105,7 +105,11 @@ class BaseHandler(Generic[UT, CCT], ABC):
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, callback=self.callback.__qualname__)
|
||||
try:
|
||||
callback_name = self.callback.__qualname__
|
||||
except AttributeError:
|
||||
callback_name = repr(self.callback)
|
||||
return build_repr_with_selected_attrs(self, callback=callback_name)
|
||||
|
||||
@abstractmethod
|
||||
def check_update(self, update: object) -> Optional[Union[bool, object]]:
|
||||
|
||||
@@ -27,6 +27,25 @@ class BaseUpdateProcessor(ABC):
|
||||
"""An abstract base class for update processors. You can use this class to implement
|
||||
your own update processor.
|
||||
|
||||
Instances of this class can be used as asyncio context managers, where
|
||||
|
||||
.. code:: python
|
||||
|
||||
async with processor:
|
||||
# code
|
||||
|
||||
is roughly equivalent to
|
||||
|
||||
.. code:: python
|
||||
|
||||
try:
|
||||
await processor.initialize()
|
||||
# code
|
||||
finally:
|
||||
await processor.shutdown()
|
||||
|
||||
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
|
||||
|
||||
.. seealso:: :wiki:`Concurrency`
|
||||
|
||||
.. versionadded:: 20.4
|
||||
@@ -49,7 +68,15 @@ class BaseUpdateProcessor(ABC):
|
||||
self._semaphore = BoundedSemaphore(self.max_concurrent_updates)
|
||||
|
||||
async def __aenter__(self) -> "BaseUpdateProcessor":
|
||||
"""Simple context manager which initializes the Processor."""
|
||||
"""|async_context_manager| :meth:`initializes <initialize>` the Processor.
|
||||
|
||||
Returns:
|
||||
The initialized Processor instance.
|
||||
|
||||
Raises:
|
||||
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
|
||||
is called in this case.
|
||||
"""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
@@ -63,7 +90,7 @@ class BaseUpdateProcessor(ABC):
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Simple context manager which shuts down the Processor."""
|
||||
"""|async_context_manager| :meth:`shuts down <shutdown>` the Processor."""
|
||||
await self.shutdown()
|
||||
|
||||
@property
|
||||
|
||||
@@ -236,11 +236,13 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
|
||||
await self.application.persistence.refresh_bot_data(self.bot_data)
|
||||
if self.application.persistence.store_data.chat_data and self._chat_id is not None:
|
||||
await self.application.persistence.refresh_chat_data(
|
||||
chat_id=self._chat_id, chat_data=self.chat_data # type: ignore[arg-type]
|
||||
chat_id=self._chat_id,
|
||||
chat_data=self.chat_data, # type: ignore[arg-type]
|
||||
)
|
||||
if self.application.persistence.store_data.user_data and self._user_id is not None:
|
||||
await self.application.persistence.refresh_user_data(
|
||||
user_id=self._user_id, user_data=self.user_data # type: ignore[arg-type]
|
||||
user_id=self._user_id,
|
||||
user_data=self.user_data, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
def drop_callback_data(self, callback_query: CallbackQuery) -> None:
|
||||
|
||||
@@ -621,9 +621,16 @@ class ConversationHandler(BaseHandler[Update, CCT]):
|
||||
self._conversations.update(current_conversations)
|
||||
# above might be partly overridden but that's okay since we warn about that in
|
||||
# add_handler
|
||||
self._conversations.update_no_track(
|
||||
await application.persistence.get_conversations(self.name)
|
||||
)
|
||||
stored_data = await application.persistence.get_conversations(self.name)
|
||||
self._conversations.update_no_track(stored_data)
|
||||
|
||||
# Since CH.END is stored as normal state, we need to properly parse it here in order to
|
||||
# actually end the conversation, i.e. delete the key from the _conversations dict
|
||||
# This also makes sure that these entries are deleted from the persisted data on the next
|
||||
# run of Application.update_persistence
|
||||
for key, state in stored_data.items():
|
||||
if state == self.END:
|
||||
self._update_state(new_state=self.END, key=key)
|
||||
|
||||
out = {self.name: self._conversations}
|
||||
|
||||
|
||||
+32
-39
@@ -86,14 +86,7 @@ from telegram._utils.datetime import to_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
)
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram.ext._callbackdatacache import CallbackDataCache
|
||||
from telegram.ext._utils.types import RLARGS
|
||||
from telegram.request import BaseRequest
|
||||
@@ -556,7 +549,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
timeout: Optional[int] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
*,
|
||||
read_timeout: float = 2,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -686,9 +679,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -750,7 +743,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
sticker: Optional["InputSticker"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1068,7 +1061,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
needs_repainting: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -1518,7 +1511,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
chat_id: Union[int, str],
|
||||
from_chat_id: Union[str, int],
|
||||
message_id: int,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
@@ -2174,7 +2167,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2186,7 +2179,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2225,7 +2218,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -2237,7 +2230,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2297,7 +2290,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
phone_number: Optional[str] = None,
|
||||
first_name: Optional[str] = None,
|
||||
last_name: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
vcard: Optional[str] = None,
|
||||
@@ -2372,7 +2365,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
chat_id: Union[int, str],
|
||||
document: Union[FileInput, "Document"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -2385,7 +2378,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2417,7 +2410,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
self,
|
||||
chat_id: int,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2466,7 +2459,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
need_email: Optional[bool] = None,
|
||||
need_shipping_address: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
@@ -2526,7 +2519,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
chat_id: Union[int, str],
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
@@ -2580,7 +2573,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
message_thread_id: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2614,7 +2607,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2652,7 +2645,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
chat_id: Union[int, str],
|
||||
photo: Union[FileInput, "PhotoSize"],
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -2664,7 +2657,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2751,7 +2744,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
sticker: Union[FileInput, "Sticker"],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2760,7 +2753,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
emoji: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2791,7 +2784,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
title: Optional[str] = None,
|
||||
address: Optional[str] = None,
|
||||
foursquare_id: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
foursquare_type: Optional[str] = None,
|
||||
@@ -2839,7 +2832,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
width: Optional[int] = None,
|
||||
@@ -2855,7 +2848,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2893,7 +2886,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2903,7 +2896,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -2935,7 +2928,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -2946,7 +2939,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
*,
|
||||
filename: Optional[str] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -3071,7 +3064,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
photo: FileInput,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -3472,7 +3465,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
sticker_format: Optional[str],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
||||
@@ -76,6 +76,17 @@ class JobQueue(Generic[CCT]):
|
||||
Attributes:
|
||||
scheduler (:class:`apscheduler.schedulers.asyncio.AsyncIOScheduler`): The scheduler.
|
||||
|
||||
Warning:
|
||||
This scheduler is configured by :meth:`set_application`. Additional configuration
|
||||
settings can be made by users. However, calling
|
||||
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` will delete any
|
||||
previous configuration settings. Therefore, please make sure to pass the values
|
||||
returned by :attr:`scheduler_configuration` to the method call in addition to your
|
||||
custom values.
|
||||
Alternatively, you can also use methods like
|
||||
:meth:`~apscheduler.schedulers.base.BaseScheduler.add_jobstore` to avoid using
|
||||
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` altogether.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Uses :class:`~apscheduler.schedulers.asyncio.AsyncIOScheduler` instead of
|
||||
:class:`~apscheduler.schedulers.background.BackgroundScheduler`
|
||||
@@ -94,9 +105,7 @@ class JobQueue(Generic[CCT]):
|
||||
|
||||
self._application: Optional[weakref.ReferenceType[Application]] = None
|
||||
self._executor = AsyncIOExecutor()
|
||||
self.scheduler: AsyncIOScheduler = AsyncIOScheduler(
|
||||
timezone=pytz.utc, executors={"default": self._executor}
|
||||
)
|
||||
self.scheduler: AsyncIOScheduler = AsyncIOScheduler(**self.scheduler_configuration)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the JobQueue in the form ``JobQueue[application=...]``.
|
||||
@@ -119,6 +128,43 @@ class JobQueue(Generic[CCT]):
|
||||
return application
|
||||
raise RuntimeError("The application instance is no longer alive.")
|
||||
|
||||
@property
|
||||
def scheduler_configuration(self) -> JSONDict:
|
||||
"""Provides configuration values that are used by :class:`JobQueue` for :attr:`scheduler`.
|
||||
|
||||
Tip:
|
||||
Since calling
|
||||
:meth:`scheduler.configure() <apscheduler.schedulers.base.BaseScheduler.configure>`
|
||||
deletes any previous setting, please make sure to pass these values to the method call
|
||||
in addition to your custom values:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
scheduler.configure(..., **job_queue.scheduler_configuration)
|
||||
|
||||
Alternatively, you can also use methods like
|
||||
:meth:`~apscheduler.schedulers.base.BaseScheduler.add_jobstore` to avoid using
|
||||
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` altogether.
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Returns:
|
||||
Dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary.
|
||||
|
||||
"""
|
||||
timezone: object = pytz.utc
|
||||
if (
|
||||
self._application
|
||||
and isinstance(self.application.bot, ExtBot)
|
||||
and self.application.bot.defaults
|
||||
):
|
||||
timezone = self.application.bot.defaults.tzinfo or pytz.utc
|
||||
|
||||
return {
|
||||
"timezone": timezone,
|
||||
"executors": {"default": self._executor},
|
||||
}
|
||||
|
||||
def _tz_now(self) -> datetime.datetime:
|
||||
return datetime.datetime.now(self.scheduler.timezone)
|
||||
|
||||
@@ -166,11 +212,7 @@ class JobQueue(Generic[CCT]):
|
||||
|
||||
"""
|
||||
self._application = weakref.ref(application)
|
||||
if isinstance(application.bot, ExtBot) and application.bot.defaults:
|
||||
self.scheduler.configure(
|
||||
timezone=application.bot.defaults.tzinfo or pytz.utc,
|
||||
executors={"default": self._executor},
|
||||
)
|
||||
self.scheduler.configure(**self.scheduler_configuration)
|
||||
|
||||
@staticmethod
|
||||
async def job_callback(job_queue: "JobQueue[CCT]", job: "Job[CCT]") -> None:
|
||||
|
||||
@@ -492,7 +492,7 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
|
||||
"""
|
||||
if self.chat_data is None:
|
||||
return
|
||||
self.chat_data.pop(chat_id, None) # type: ignore[arg-type]
|
||||
self.chat_data.pop(chat_id, None)
|
||||
|
||||
if not self.on_flush:
|
||||
if not self.single_file:
|
||||
@@ -511,7 +511,7 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
|
||||
"""
|
||||
if self.user_data is None:
|
||||
return
|
||||
self.user_data.pop(user_id, None) # type: ignore[arg-type]
|
||||
self.user_data.pop(user_id, None)
|
||||
|
||||
if not self.on_flush:
|
||||
if not self.single_file:
|
||||
|
||||
+63
-15
@@ -78,6 +78,8 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
finally:
|
||||
await updater.shutdown()
|
||||
|
||||
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
|
||||
|
||||
.. seealso:: :wiki:`Architecture Overview <Architecture>`,
|
||||
:wiki:`Builder Pattern <Builder-Pattern>`
|
||||
|
||||
@@ -126,7 +128,16 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
self.__polling_cleanup_cb: Optional[Callable[[], Coroutine[Any, Any, None]]] = None
|
||||
|
||||
async def __aenter__(self: _UpdaterType) -> _UpdaterType: # noqa: PYI019
|
||||
"""Simple context manager which initializes the Updater."""
|
||||
"""
|
||||
|async_context_manager| :meth:`initializes <initialize>` the Updater.
|
||||
|
||||
Returns:
|
||||
The initialized Updater instance.
|
||||
|
||||
Raises:
|
||||
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
|
||||
is called in this case.
|
||||
"""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
@@ -140,7 +151,7 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the Updater from the context manager."""
|
||||
"""|async_context_manager| :meth:`shuts down <shutdown>` the Updater."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
@@ -200,7 +211,7 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
poll_interval: float = 0.0,
|
||||
timeout: int = 10,
|
||||
bootstrap_retries: int = -1,
|
||||
read_timeout: float = 2,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -225,16 +236,40 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
* 0 - no retries
|
||||
* > 0 - retry up to X times
|
||||
read_timeout (:obj:`float`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to ``2``.
|
||||
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. versionchanged:: 20.7
|
||||
Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE` instead of
|
||||
``2``.
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_read_timeout` or
|
||||
:paramref:`telegram.Bot.get_updates_request`.
|
||||
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.write_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_write_timeout` or
|
||||
:paramref:`telegram.Bot.get_updates_request`.
|
||||
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.connect_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_connect_timeout` or
|
||||
:paramref:`telegram.Bot.get_updates_request`.
|
||||
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.pool_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
|
||||
.. deprecated:: 20.7
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout` or
|
||||
:paramref:`telegram.Bot.get_updates_request`.
|
||||
allowed_updates (List[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
@@ -260,6 +295,10 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
:exc:`RuntimeError`: If the updater is already running or was not initialized.
|
||||
|
||||
"""
|
||||
# We refrain from issuing deprecation warnings for the timeout parameters here, as we
|
||||
# already issue them in `Application`. This means that there are no warnings when using
|
||||
# `Updater` without `Application`, but this is a rather special use case.
|
||||
|
||||
if error_callback and asyncio.iscoroutinefunction(error_callback):
|
||||
raise TypeError(
|
||||
"The `error_callback` must not be a coroutine function! Use an ordinary function "
|
||||
@@ -305,7 +344,7 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
self,
|
||||
poll_interval: float,
|
||||
timeout: int,
|
||||
read_timeout: float,
|
||||
read_timeout: ODVInput[float],
|
||||
write_timeout: ODVInput[float],
|
||||
connect_timeout: ODVInput[float],
|
||||
pool_timeout: ODVInput[float],
|
||||
@@ -390,16 +429,25 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
_LOGGER.debug(
|
||||
"Calling `get_updates` one more time to mark all fetched updates as read."
|
||||
)
|
||||
await self.bot.get_updates(
|
||||
offset=self._last_update_id,
|
||||
# We don't want to do long polling here!
|
||||
timeout=0,
|
||||
read_timeout=read_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
write_timeout=write_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
allowed_updates=allowed_updates,
|
||||
)
|
||||
try:
|
||||
await self.bot.get_updates(
|
||||
offset=self._last_update_id,
|
||||
# We don't want to do long polling here!
|
||||
timeout=0,
|
||||
read_timeout=read_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
write_timeout=write_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
allowed_updates=allowed_updates,
|
||||
)
|
||||
except TelegramError as exc:
|
||||
_LOGGER.error(
|
||||
"Error while calling `get_updates` one more time to mark all fetched updates "
|
||||
"as read: %s. Suppressing error to ensure graceful shutdown. When polling for "
|
||||
"updates is restarted, updates may be fetched again. Please adjust timeouts "
|
||||
"via `ApplicationBuilder` or the parameter `get_updates_request` of `Bot`.",
|
||||
exc_info=exc,
|
||||
)
|
||||
|
||||
self.__polling_cleanup_cb = _get_updates_cleanup
|
||||
|
||||
|
||||
@@ -99,7 +99,9 @@ class TrackingDict(UserDict, Generic[_KT, _VT]):
|
||||
# Mypy seems a bit inconsistent about what it wants as types for `default` and return value
|
||||
# so we just ignore a bit
|
||||
def pop( # type: ignore[override]
|
||||
self, key: _KT, default: _VT = DEFAULT_NONE # type: ignore[assignment]
|
||||
self,
|
||||
key: _KT,
|
||||
default: _VT = DEFAULT_NONE, # type: ignore[assignment]
|
||||
) -> _VT:
|
||||
if key in self:
|
||||
self.__track_write(key)
|
||||
|
||||
+69
-1
@@ -66,6 +66,7 @@ __all__ = (
|
||||
"LOCATION",
|
||||
"Language",
|
||||
"MessageFilter",
|
||||
"Mention",
|
||||
"PASSPORT_DATA",
|
||||
"PHOTO",
|
||||
"POLL",
|
||||
@@ -91,7 +92,6 @@ __all__ = (
|
||||
"VOICE",
|
||||
"ViaBot",
|
||||
)
|
||||
|
||||
import mimetypes
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -99,6 +99,7 @@ from typing import (
|
||||
Collection,
|
||||
Dict,
|
||||
FrozenSet,
|
||||
Iterable,
|
||||
List,
|
||||
Match,
|
||||
NoReturn,
|
||||
@@ -1521,6 +1522,73 @@ LOCATION = _Location(name="filters.LOCATION")
|
||||
"""Messages that contain :attr:`telegram.Message.location`."""
|
||||
|
||||
|
||||
class Mention(MessageFilter):
|
||||
"""Messages containing mentions of specified users or chats.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
MessageHandler(filters.Mention("username"), callback)
|
||||
MessageHandler(filters.Mention(["@username", 123456]), callback)
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Args:
|
||||
mentions (:obj:`int` | :obj:`str` | :class:`telegram.User` | Collection[:obj:`int` | \
|
||||
:obj:`str` | :class:`telegram.User`]):
|
||||
Specifies the users and chats to filter for. Messages that do not mention at least one
|
||||
of the specified users or chats will not be handled. Leading ``'@'`` s in usernames
|
||||
will be discarded.
|
||||
"""
|
||||
|
||||
__slots__ = ("_mentions",)
|
||||
|
||||
def __init__(self, mentions: SCT[Union[int, str, TGUser]]):
|
||||
super().__init__(name=f"filters.Mention({mentions})")
|
||||
if isinstance(mentions, Iterable) and not isinstance(mentions, str):
|
||||
self._mentions = {self._fix_mention_username(mention) for mention in mentions}
|
||||
else:
|
||||
self._mentions = {self._fix_mention_username(mentions)}
|
||||
|
||||
@staticmethod
|
||||
def _fix_mention_username(mention: Union[int, str, TGUser]) -> Union[int, str, TGUser]:
|
||||
if not isinstance(mention, str):
|
||||
return mention
|
||||
return mention.lstrip("@")
|
||||
|
||||
@classmethod
|
||||
def _check_mention(cls, message: Message, mention: Union[int, str, TGUser]) -> bool:
|
||||
if not message.entities:
|
||||
return False
|
||||
|
||||
entity_texts = message.parse_entities(
|
||||
types=[MessageEntity.MENTION, MessageEntity.TEXT_MENTION]
|
||||
)
|
||||
|
||||
if isinstance(mention, TGUser):
|
||||
return any(
|
||||
mention.id == entity.user.id
|
||||
or mention.username == entity.user.username
|
||||
or mention.username == cls._fix_mention_username(entity_texts[entity])
|
||||
for entity in message.entities
|
||||
if entity.user
|
||||
) or any(
|
||||
mention.username == cls._fix_mention_username(entity_text)
|
||||
for entity_text in entity_texts.values()
|
||||
)
|
||||
if isinstance(mention, int):
|
||||
return bool(
|
||||
any(mention == entity.user.id for entity in message.entities if entity.user)
|
||||
)
|
||||
return any(
|
||||
mention == cls._fix_mention_username(entity_text)
|
||||
for entity_text in entity_texts.values()
|
||||
)
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return any(self._check_mention(message, mention) for mention in self._mentions)
|
||||
|
||||
|
||||
class _PassportData(MessageFilter):
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from telegram._utils.defaultvalue import DEFAULT_NONE as _DEFAULT_NONE
|
||||
from telegram._utils.defaultvalue import DefaultValue
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram._version import __version__ as ptb_ver
|
||||
from telegram.error import (
|
||||
BadRequest,
|
||||
@@ -39,6 +40,7 @@ from telegram.error import (
|
||||
TelegramError,
|
||||
)
|
||||
from telegram.request._requestdata import RequestData
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
RT = TypeVar("RT", bound="BaseRequest")
|
||||
|
||||
@@ -70,6 +72,8 @@ class BaseRequest(
|
||||
finally:
|
||||
await request_object.shutdown()
|
||||
|
||||
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
|
||||
|
||||
Tip:
|
||||
JSON encoding and decoding is done with the standard library's :mod:`json` by default.
|
||||
To use a custom library for this, you can override :meth:`parse_json_payload` and implement
|
||||
@@ -99,6 +103,15 @@ class BaseRequest(
|
||||
"""
|
||||
|
||||
async def __aenter__(self: RT) -> RT:
|
||||
"""|async_context_manager| :meth:`initializes <initialize>` the Request.
|
||||
|
||||
Returns:
|
||||
The initialized Request instance.
|
||||
|
||||
Raises:
|
||||
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
|
||||
is called in this case.
|
||||
"""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
@@ -112,10 +125,29 @@ class BaseRequest(
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""|async_context_manager| :meth:`shuts down <shutdown>` the Request."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
@property
|
||||
def read_timeout(self) -> Optional[float]:
|
||||
"""This property must return the default read timeout in seconds used by this class.
|
||||
More precisely, the returned value should be the one used when
|
||||
:paramref:`post.read_timeout` of :meth:post` is not passed/equal to :attr:`DEFAULT_NONE`.
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
Warning:
|
||||
For now this property does not need to be implemented by subclasses and will raise
|
||||
:exc:`NotImplementedError` if accessed without being overridden. However, in future
|
||||
versions, this property will be abstract and must be implemented by subclasses.
|
||||
|
||||
Returns:
|
||||
:obj:`float` | :obj:`None`: The read timeout in seconds.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
async def initialize(self) -> None:
|
||||
"""Initialize resources used by this class. Must be implemented by a subclass."""
|
||||
@@ -271,8 +303,28 @@ class BaseRequest(
|
||||
TelegramError
|
||||
|
||||
"""
|
||||
# TGs response also has the fields 'ok' and 'error_code'.
|
||||
# However, we rather rely on the HTTP status code for now.
|
||||
# Import needs to be here since HTTPXRequest is a subclass of BaseRequest
|
||||
from telegram.request import HTTPXRequest # pylint: disable=import-outside-toplevel
|
||||
|
||||
# 20 is the documented default value for all the media related bot methods and custom
|
||||
# implementations of BaseRequest may explicitly rely on that. Hence, we follow the
|
||||
# standard deprecation policy and deprecate starting with version 20.7.
|
||||
# For our own implementation HTTPXRequest, we can handle that ourselves, so we skip the
|
||||
# warning in that case.
|
||||
has_files = request_data and request_data.multipart_data
|
||||
if (
|
||||
has_files
|
||||
and not isinstance(self, HTTPXRequest)
|
||||
and isinstance(write_timeout, DefaultValue)
|
||||
):
|
||||
warn(
|
||||
f"The `write_timeout` parameter passed to {self.__class__.__name__}.do_request "
|
||||
"will default to `BaseRequest.DEFAULT_NONE` instead of 20 in future versions "
|
||||
"for *all* methods of the `Bot` class, including methods sending media.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
write_timeout = 20
|
||||
|
||||
try:
|
||||
code, payload = await self.do_request(
|
||||
@@ -301,6 +353,8 @@ class BaseRequest(
|
||||
# In some special cases, we can raise more informative exceptions:
|
||||
# see https://core.telegram.org/bots/api#responseparameters and
|
||||
# https://core.telegram.org/bots/api#making-requests
|
||||
# TGs response also has the fields 'ok' and 'error_code'.
|
||||
# However, we rather rely on the HTTP status code for now.
|
||||
parameters = response_data.get("parameters")
|
||||
if parameters:
|
||||
migrate_to_chat_id = parameters.get("migrate_to_chat_id")
|
||||
|
||||
@@ -17,16 +17,18 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains methods to make POST and GET requests using the httpx library."""
|
||||
from typing import Optional, Tuple
|
||||
from typing import Collection, Optional, Tuple, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from telegram._utils.defaultvalue import DefaultValue
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.types import HTTPVersion, ODVInput
|
||||
from telegram._utils.types import HTTPVersion, ODVInput, SocketOpt
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.error import NetworkError, TimedOut
|
||||
from telegram.request._baserequest import BaseRequest
|
||||
from telegram.request._requestdata import RequestData
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
# Note to future devs:
|
||||
# Proxies are currently only tested manually. The httpx development docs have a nice guide on that:
|
||||
@@ -49,17 +51,10 @@ class HTTPXRequest(BaseRequest):
|
||||
Note:
|
||||
Independent of the value, one additional connection will be reserved for
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
proxy_url (:obj:`str`, optional): The URL to the proxy server. For example
|
||||
``'http://127.0.0.1:3128'`` or ``'socks5://127.0.0.1:3128'``. Defaults to :obj:`None`.
|
||||
proxy_url (:obj:`str`, optional): Legacy name for :paramref:`proxy`, kept for backward
|
||||
compatibility. Defaults to :obj:`None`.
|
||||
|
||||
Note:
|
||||
* The proxy URL can also be set via the environment variables ``HTTPS_PROXY`` or
|
||||
``ALL_PROXY``. See `the docs of httpx`_ for more info.
|
||||
* For Socks5 support, additional dependencies are required. Make sure to install
|
||||
PTB via :command:`pip install "python-telegram-bot[socks]"` in this case.
|
||||
* Socks5 proxies can not be set via environment variables.
|
||||
|
||||
.. _the docs of httpx: https://www.python-httpx.org/environment_variables/#proxies
|
||||
.. deprecated:: 20.7
|
||||
read_timeout (:obj:`float` | :obj:`None`, optional): If passed, specifies the maximum
|
||||
amount of time (in seconds) to wait for a response from Telegram's server.
|
||||
This value is used unless a different value is passed to :meth:`do_request`.
|
||||
@@ -91,6 +86,32 @@ class HTTPXRequest(BaseRequest):
|
||||
|
||||
.. versionchanged:: 20.5
|
||||
Accept ``"2"`` as a valid value.
|
||||
socket_options (Collection[:obj:`tuple`], optional): Socket options to be passed to the
|
||||
underlying `library \
|
||||
<https://www.encode.io/httpcore/async/#httpcore.AsyncConnectionPool.__init__>`_.
|
||||
|
||||
Note:
|
||||
The values accepted by this parameter depend on the operating system.
|
||||
This is a low-level parameter and should only be used if you are familiar with
|
||||
these concepts.
|
||||
|
||||
.. versionadded:: 20.7
|
||||
proxy (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``, optional): The URL to a proxy server,
|
||||
a ``httpx.Proxy`` object or a ``httpx.URL`` object. For example
|
||||
``'http://127.0.0.1:3128'`` or ``'socks5://127.0.0.1:3128'``. Defaults to :obj:`None`.
|
||||
|
||||
Note:
|
||||
* The proxy URL can also be set via the environment variables ``HTTPS_PROXY`` or
|
||||
``ALL_PROXY``. See `the docs of httpx`_ for more info.
|
||||
* HTTPS proxies can be configured by passing a ``httpx.Proxy`` object with
|
||||
a corresponding ``ssl_context``.
|
||||
* For Socks5 support, additional dependencies are required. Make sure to install
|
||||
PTB via :command:`pip install "python-telegram-bot[socks]"` in this case.
|
||||
* Socks5 proxies can not be set via environment variables.
|
||||
|
||||
.. _the docs of httpx: https://www.python-httpx.org/environment_variables/#proxies
|
||||
|
||||
.. versionadded:: 20.7
|
||||
|
||||
"""
|
||||
|
||||
@@ -99,13 +120,27 @@ class HTTPXRequest(BaseRequest):
|
||||
def __init__(
|
||||
self,
|
||||
connection_pool_size: int = 1,
|
||||
proxy_url: Optional[str] = None,
|
||||
proxy_url: Optional[Union[str, httpx.Proxy, httpx.URL]] = None,
|
||||
read_timeout: Optional[float] = 5.0,
|
||||
write_timeout: Optional[float] = 5.0,
|
||||
connect_timeout: Optional[float] = 5.0,
|
||||
pool_timeout: Optional[float] = 1.0,
|
||||
http_version: HTTPVersion = "1.1",
|
||||
socket_options: Optional[Collection[SocketOpt]] = None,
|
||||
proxy: Optional[Union[str, httpx.Proxy, httpx.URL]] = None,
|
||||
):
|
||||
if proxy_url is not None and proxy is not None:
|
||||
raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.")
|
||||
|
||||
if proxy_url is not None:
|
||||
proxy = proxy_url
|
||||
warn(
|
||||
"The parameter `proxy_url` is deprecated since version 20.7. Use `proxy` "
|
||||
"instead.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
self._http_version = http_version
|
||||
timeout = httpx.Timeout(
|
||||
connect=connect_timeout,
|
||||
@@ -122,16 +157,21 @@ class HTTPXRequest(BaseRequest):
|
||||
raise ValueError("`http_version` must be either '1.1', '2.0' or '2'.")
|
||||
|
||||
http1 = http_version == "1.1"
|
||||
|
||||
# See https://github.com/python-telegram-bot/python-telegram-bot/pull/3542
|
||||
# for why we need to use `dict()` here.
|
||||
self._client_kwargs = dict( # pylint: disable=use-dict-literal # noqa: C408
|
||||
timeout=timeout,
|
||||
proxies=proxy_url,
|
||||
limits=limits,
|
||||
http1=http1,
|
||||
http2=not http1,
|
||||
http_kwargs = {"http1": http1, "http2": not http1}
|
||||
transport = (
|
||||
httpx.AsyncHTTPTransport(
|
||||
socket_options=socket_options,
|
||||
)
|
||||
if socket_options
|
||||
else None
|
||||
)
|
||||
self._client_kwargs = {
|
||||
"timeout": timeout,
|
||||
"proxies": proxy,
|
||||
"limits": limits,
|
||||
"transport": transport,
|
||||
**http_kwargs,
|
||||
}
|
||||
|
||||
try:
|
||||
self._client = self._build_client()
|
||||
@@ -158,6 +198,16 @@ class HTTPXRequest(BaseRequest):
|
||||
"""
|
||||
return self._http_version
|
||||
|
||||
@property
|
||||
def read_timeout(self) -> Optional[float]:
|
||||
"""See :attr:`BaseRequest.read_timeout`.
|
||||
|
||||
Returns:
|
||||
:obj:`float` | :obj:`None`: The default read timeout in seconds as passed to
|
||||
:paramref:`HTTPXRequest.read_timeout`.
|
||||
"""
|
||||
return self._client.timeout.read
|
||||
|
||||
def _build_client(self) -> httpx.AsyncClient:
|
||||
return httpx.AsyncClient(**self._client_kwargs) # type: ignore[arg-type]
|
||||
|
||||
@@ -188,17 +238,25 @@ class HTTPXRequest(BaseRequest):
|
||||
if self._client.is_closed:
|
||||
raise RuntimeError("This HTTPXRequest is not initialized!")
|
||||
|
||||
files = request_data.multipart_data if request_data else None
|
||||
data = request_data.json_parameters if request_data else None
|
||||
|
||||
# If user did not specify timeouts (for e.g. in a bot method), use the default ones when we
|
||||
# created this instance.
|
||||
if isinstance(read_timeout, DefaultValue):
|
||||
read_timeout = self._client.timeout.read
|
||||
if isinstance(write_timeout, DefaultValue):
|
||||
write_timeout = self._client.timeout.write
|
||||
if isinstance(connect_timeout, DefaultValue):
|
||||
connect_timeout = self._client.timeout.connect
|
||||
if isinstance(pool_timeout, DefaultValue):
|
||||
pool_timeout = self._client.timeout.pool
|
||||
|
||||
if isinstance(write_timeout, DefaultValue):
|
||||
# Making the networking backend decide on the proper timeout values instead of doing
|
||||
# it via the default values of the Bot methods was introduced in version 20.7.
|
||||
# We hard-code the value here for now until we add additional parameters to this
|
||||
# class to control the media_write_timeout separately.
|
||||
write_timeout = self._client.timeout.write if not files else 20
|
||||
|
||||
timeout = httpx.Timeout(
|
||||
connect=connect_timeout,
|
||||
read=read_timeout,
|
||||
@@ -206,15 +264,6 @@ class HTTPXRequest(BaseRequest):
|
||||
pool=pool_timeout,
|
||||
)
|
||||
|
||||
# TODO p0: On Linux, use setsockopt to properly set socket level keepalive.
|
||||
# (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 120)
|
||||
# (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)
|
||||
# (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 8)
|
||||
# TODO p4: Support setsockopt on lesser platforms than Linux.
|
||||
|
||||
files = request_data.multipart_data if request_data else None
|
||||
data = request_data.json_parameters if request_data else None
|
||||
|
||||
try:
|
||||
res = await self._client.request(
|
||||
method=method,
|
||||
|
||||
@@ -322,7 +322,7 @@ class TestAnimationWithRequest(TestAnimationBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_animation(
|
||||
chat_id, animation, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -156,7 +156,7 @@ class TestContactWithRequest(TestContactBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_contact(
|
||||
chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -335,7 +335,7 @@ class TestDocumentWithRequest(TestDocumentBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_document(
|
||||
chat_id, document, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -822,7 +822,7 @@ class TestSendMediaGroupWithRequest:
|
||||
)
|
||||
assert [m.reply_to_message is None for m in messages]
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_media_group(
|
||||
chat_id, media_group, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -189,7 +189,7 @@ class TestLocationWithRequest:
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_location(
|
||||
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -357,7 +357,7 @@ class TestPhotoWithRequest(TestPhotoBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_photo(
|
||||
chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -399,7 +399,7 @@ class TestStickerWithRequest(TestStickerBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_sticker(
|
||||
chat_id, sticker, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -171,7 +171,7 @@ class TestVenueWithRequest(TestVenueBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_venue(
|
||||
chat_id, venue=venue, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -359,7 +359,7 @@ class TestVideoWithRequest(TestVideoBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_video(
|
||||
chat_id, video, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -258,7 +258,7 @@ class TestVideoNoteWithRequest(TestVideoNoteBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_video_note(
|
||||
chat_id, video_note, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -308,7 +308,7 @@ class TestVoiceWithRequest(TestVoiceBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_voice(
|
||||
chat_id, voice, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -276,7 +276,7 @@ class TestInvoiceWithRequest(TestInvoiceBase):
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_invoice(
|
||||
chat_id,
|
||||
self.title,
|
||||
|
||||
@@ -1498,6 +1498,54 @@ class TestApplication:
|
||||
found_log = True
|
||||
assert found_log
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"timeout_name",
|
||||
["read_timeout", "connect_timeout", "write_timeout", "pool_timeout", "poll_interval"],
|
||||
)
|
||||
@pytest.mark.skipif(
|
||||
platform.system() == "Windows",
|
||||
reason="Can't send signals without stopping whole process on windows",
|
||||
)
|
||||
def test_run_polling_timeout_deprecation_warnings(
|
||||
self, timeout_name, monkeypatch, recwarn, app
|
||||
):
|
||||
async def get_updates(*args, **kwargs):
|
||||
# This makes sure that other coroutines have a chance of running as well
|
||||
await asyncio.sleep(0)
|
||||
return []
|
||||
|
||||
def thread_target():
|
||||
waited = 0
|
||||
while not app.running:
|
||||
time.sleep(0.05)
|
||||
waited += 0.05
|
||||
if waited > 5:
|
||||
pytest.fail("App apparently won't start")
|
||||
|
||||
time.sleep(0.05)
|
||||
|
||||
os.kill(os.getpid(), signal.SIGINT)
|
||||
|
||||
monkeypatch.setattr(app.bot, "get_updates", get_updates)
|
||||
|
||||
thread = Thread(target=thread_target)
|
||||
thread.start()
|
||||
|
||||
kwargs = {timeout_name: 42}
|
||||
app.run_polling(drop_pending_updates=True, close_loop=False, **kwargs)
|
||||
thread.join()
|
||||
|
||||
if timeout_name == "poll_interval":
|
||||
assert len(recwarn) == 0
|
||||
return
|
||||
|
||||
assert len(recwarn) == 1
|
||||
assert "Setting timeouts via `Application.run_polling` is deprecated." in str(
|
||||
recwarn[0].message
|
||||
)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__, "wrong stacklevel"
|
||||
|
||||
@pytest.mark.skipif(
|
||||
platform.system() == "Windows",
|
||||
reason="Can't send signals without stopping whole process on windows",
|
||||
@@ -2210,7 +2258,8 @@ class TestApplication:
|
||||
else:
|
||||
app.run_webhook(close_loop=False, stop_signals=None)
|
||||
|
||||
assert len(recwarn) == 0
|
||||
for record in recwarn:
|
||||
assert not str(record.message).startswith("Could not add signal handlers for the stop")
|
||||
|
||||
@pytest.mark.flaky(3, 1) # loop.call_later will error the test when a flood error is received
|
||||
def test_signal_handlers(self, app, monkeypatch):
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import inspect
|
||||
from dataclasses import dataclass
|
||||
from http import HTTPStatus
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from telegram import Bot
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram.ext import (
|
||||
AIORateLimiter,
|
||||
Application,
|
||||
@@ -37,6 +41,7 @@ from telegram.ext import (
|
||||
from telegram.ext._applicationbuilder import _BOT_CHECKS
|
||||
from telegram.ext._baseupdateprocessor import SimpleUpdateProcessor
|
||||
from telegram.request import HTTPXRequest
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
from tests.auxil.constants import PRIVATE_KEY
|
||||
from tests.auxil.envvars import TEST_WITH_OPT_DEPS
|
||||
from tests.auxil.files import data_file
|
||||
@@ -64,6 +69,34 @@ class TestApplicationBuilder:
|
||||
assert getattr(builder, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(builder)) == len(set(mro_slots(builder))), "duplicate slot"
|
||||
|
||||
@pytest.mark.parametrize("get_updates", [True, False])
|
||||
def test_all_methods_request(self, builder, get_updates):
|
||||
arguments = inspect.signature(HTTPXRequest.__init__).parameters.keys()
|
||||
prefix = "get_updates_" if get_updates else ""
|
||||
for argument in arguments:
|
||||
if argument == "self":
|
||||
continue
|
||||
assert hasattr(builder, prefix + argument), f"missing method {prefix}{argument}"
|
||||
|
||||
@pytest.mark.parametrize("bot_class", [Bot, ExtBot])
|
||||
def test_all_methods_bot(self, builder, bot_class):
|
||||
arguments = inspect.signature(bot_class.__init__).parameters.keys()
|
||||
for argument in arguments:
|
||||
if argument == "self":
|
||||
continue
|
||||
if argument == "private_key_password":
|
||||
argument = "private_key" # noqa: PLW2901
|
||||
assert hasattr(builder, argument), f"missing method {argument}"
|
||||
|
||||
def test_all_methods_application(self, builder):
|
||||
arguments = inspect.signature(Application.__init__).parameters.keys()
|
||||
for argument in arguments:
|
||||
if argument == "self":
|
||||
continue
|
||||
if argument == "update_processor":
|
||||
argument = "concurrent_updates" # noqa: PLW2901
|
||||
assert hasattr(builder, argument), f"missing method {argument}"
|
||||
|
||||
def test_job_queue_init_exception(self, monkeypatch):
|
||||
def init_raises_runtime_error(*args, **kwargs):
|
||||
raise RuntimeError("RuntimeError")
|
||||
@@ -91,6 +124,7 @@ class TestApplicationBuilder:
|
||||
limits: object
|
||||
http1: object
|
||||
http2: object
|
||||
transport: object = None
|
||||
|
||||
monkeypatch.setattr(httpx, "AsyncClient", Client)
|
||||
|
||||
@@ -168,7 +202,9 @@ class TestApplicationBuilder:
|
||||
"pool_timeout",
|
||||
"read_timeout",
|
||||
"write_timeout",
|
||||
"proxy",
|
||||
"proxy_url",
|
||||
"socket_options",
|
||||
"bot",
|
||||
"updater",
|
||||
"http_version",
|
||||
@@ -177,8 +213,9 @@ class TestApplicationBuilder:
|
||||
def test_mutually_exclusive_for_request(self, builder, method):
|
||||
builder.request(1)
|
||||
|
||||
method_name = method.replace("proxy_url", "proxy")
|
||||
with pytest.raises(
|
||||
RuntimeError, match=f"`{method}` may only be set, if no request instance"
|
||||
RuntimeError, match=f"`{method_name}` may only be set, if no request instance"
|
||||
):
|
||||
getattr(builder, method)(data_file("private.key"))
|
||||
|
||||
@@ -195,7 +232,9 @@ class TestApplicationBuilder:
|
||||
"get_updates_pool_timeout",
|
||||
"get_updates_read_timeout",
|
||||
"get_updates_write_timeout",
|
||||
"get_updates_proxy",
|
||||
"get_updates_proxy_url",
|
||||
"get_updates_socket_options",
|
||||
"get_updates_http_version",
|
||||
"bot",
|
||||
"updater",
|
||||
@@ -204,9 +243,10 @@ class TestApplicationBuilder:
|
||||
def test_mutually_exclusive_for_get_updates_request(self, builder, method):
|
||||
builder.get_updates_request(1)
|
||||
|
||||
method_name = method.replace("proxy_url", "proxy")
|
||||
with pytest.raises(
|
||||
RuntimeError,
|
||||
match=f"`{method}` may only be set, if no get_updates_request instance",
|
||||
match=f"`{method_name}` may only be set, if no get_updates_request instance",
|
||||
):
|
||||
getattr(builder, method)(data_file("private.key"))
|
||||
|
||||
@@ -224,13 +264,17 @@ class TestApplicationBuilder:
|
||||
"get_updates_read_timeout",
|
||||
"get_updates_write_timeout",
|
||||
"get_updates_proxy_url",
|
||||
"get_updates_proxy",
|
||||
"get_updates_socket_options",
|
||||
"get_updates_http_version",
|
||||
"connection_pool_size",
|
||||
"connect_timeout",
|
||||
"pool_timeout",
|
||||
"read_timeout",
|
||||
"write_timeout",
|
||||
"proxy",
|
||||
"proxy_url",
|
||||
"socket_options",
|
||||
"http_version",
|
||||
"bot",
|
||||
"update_queue",
|
||||
@@ -241,14 +285,17 @@ class TestApplicationBuilder:
|
||||
def test_mutually_exclusive_for_updater(self, builder, method):
|
||||
builder.updater(1)
|
||||
|
||||
method_name = method.replace("proxy_url", "proxy")
|
||||
with pytest.raises(
|
||||
RuntimeError,
|
||||
match=f"`{method}` may only be set, if no updater",
|
||||
match=f"`{method_name}` may only be set, if no updater",
|
||||
):
|
||||
getattr(builder, method)(data_file("private.key"))
|
||||
|
||||
builder = ApplicationBuilder()
|
||||
getattr(builder, method)(data_file("private.key"))
|
||||
|
||||
method = method.replace("proxy_url", "proxy")
|
||||
with pytest.raises(RuntimeError, match=f"`updater` may only be set, if no {method}"):
|
||||
builder.updater(1)
|
||||
|
||||
@@ -260,14 +307,18 @@ class TestApplicationBuilder:
|
||||
"get_updates_pool_timeout",
|
||||
"get_updates_read_timeout",
|
||||
"get_updates_write_timeout",
|
||||
"get_updates_proxy",
|
||||
"get_updates_proxy_url",
|
||||
"get_updates_socket_options",
|
||||
"get_updates_http_version",
|
||||
"connection_pool_size",
|
||||
"connect_timeout",
|
||||
"pool_timeout",
|
||||
"read_timeout",
|
||||
"write_timeout",
|
||||
"proxy",
|
||||
"proxy_url",
|
||||
"socket_options",
|
||||
"bot",
|
||||
"http_version",
|
||||
]
|
||||
@@ -284,7 +335,16 @@ class TestApplicationBuilder:
|
||||
getattr(builder, method)(data_file("private.key"))
|
||||
builder.updater(None)
|
||||
|
||||
def test_all_bot_args_custom(self, builder, bot, monkeypatch):
|
||||
# We test with bot the new & legacy version to ensure that the legacy version still works
|
||||
@pytest.mark.parametrize(
|
||||
("proxy_method", "get_updates_proxy_method"),
|
||||
[("proxy", "get_updates_proxy"), ("proxy_url", "get_updates_proxy_url")],
|
||||
ids=["new", "legacy"],
|
||||
)
|
||||
def test_all_bot_args_custom(
|
||||
self, builder, bot, monkeypatch, proxy_method, get_updates_proxy_method
|
||||
):
|
||||
# Only socket_options is tested in a standalone test, since that's easier
|
||||
defaults = Defaults()
|
||||
request = HTTPXRequest()
|
||||
get_updates_request = HTTPXRequest()
|
||||
@@ -322,19 +382,21 @@ class TestApplicationBuilder:
|
||||
limits: object
|
||||
http1: object
|
||||
http2: object
|
||||
transport: object = None
|
||||
|
||||
monkeypatch.setattr(httpx, "AsyncClient", Client)
|
||||
|
||||
builder = ApplicationBuilder().token(bot.token)
|
||||
builder.connection_pool_size(1).connect_timeout(2).pool_timeout(3).read_timeout(
|
||||
4
|
||||
).write_timeout(5).proxy_url("proxy_url").http_version("1.1")
|
||||
).write_timeout(5).http_version("1.1")
|
||||
getattr(builder, proxy_method)("proxy")
|
||||
app = builder.build()
|
||||
client = app.bot.request._client
|
||||
|
||||
assert client.timeout == httpx.Timeout(pool=3, connect=2, read=4, write=5)
|
||||
assert client.limits == httpx.Limits(max_connections=1, max_keepalive_connections=1)
|
||||
assert client.proxies == "proxy_url"
|
||||
assert client.proxies == "proxy"
|
||||
assert client.http1 is True
|
||||
assert client.http2 is False
|
||||
|
||||
@@ -343,20 +405,49 @@ class TestApplicationBuilder:
|
||||
2
|
||||
).get_updates_pool_timeout(3).get_updates_read_timeout(4).get_updates_write_timeout(
|
||||
5
|
||||
).get_updates_proxy_url(
|
||||
"proxy_url"
|
||||
).get_updates_http_version(
|
||||
"1.1"
|
||||
)
|
||||
getattr(builder, get_updates_proxy_method)("get_updates_proxy")
|
||||
app = builder.build()
|
||||
client = app.bot._request[0]._client
|
||||
|
||||
assert client.timeout == httpx.Timeout(pool=3, connect=2, read=4, write=5)
|
||||
assert client.limits == httpx.Limits(max_connections=1, max_keepalive_connections=1)
|
||||
assert client.proxies == "proxy_url"
|
||||
assert client.proxies == "get_updates_proxy"
|
||||
assert client.http1 is True
|
||||
assert client.http2 is False
|
||||
|
||||
def test_custom_socket_options(self, builder, monkeypatch, bot):
|
||||
httpx_request_kwargs = []
|
||||
httpx_request_init = HTTPXRequest.__init__
|
||||
|
||||
def init_transport(*args, **kwargs):
|
||||
nonlocal httpx_request_kwargs
|
||||
# This is called once for request and once for get_updates_request, so we make
|
||||
# it a list
|
||||
httpx_request_kwargs.append(kwargs.copy())
|
||||
httpx_request_init(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(HTTPXRequest, "__init__", init_transport)
|
||||
|
||||
builder.token(bot.token).build()
|
||||
assert httpx_request_kwargs[0].get("socket_options") is None
|
||||
assert httpx_request_kwargs[1].get("socket_options") is None
|
||||
|
||||
httpx_request_kwargs = []
|
||||
ApplicationBuilder().token(bot.token).socket_options(((1, 2, 3),)).connection_pool_size(
|
||||
"request"
|
||||
).get_updates_socket_options(((4, 5, 6),)).get_updates_connection_pool_size(
|
||||
"get_updates"
|
||||
).build()
|
||||
|
||||
for kwargs in httpx_request_kwargs:
|
||||
if kwargs.get("connection_pool_size") == "request":
|
||||
assert kwargs.get("socket_options") == ((1, 2, 3),)
|
||||
else:
|
||||
assert kwargs.get("socket_options") == ((4, 5, 6),)
|
||||
|
||||
def test_custom_application_class(self, bot, builder):
|
||||
class CustomApplication(Application):
|
||||
def __init__(self, arg, **kwargs):
|
||||
@@ -476,3 +567,48 @@ class TestApplicationBuilder:
|
||||
assert app.job_queue is None
|
||||
assert isinstance(app.update_queue, asyncio.Queue)
|
||||
assert isinstance(app.updater, Updater)
|
||||
|
||||
def test_proxy_url_deprecation_warning(self, bot, builder, recwarn):
|
||||
builder.token(bot.token).proxy_url("proxy_url")
|
||||
assert len(recwarn) == 1
|
||||
assert "`ApplicationBuilder.proxy_url` is deprecated" in str(recwarn[0].message)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__, "wrong stacklevel"
|
||||
|
||||
def test_get_updates_proxy_url_deprecation_warning(self, bot, builder, recwarn):
|
||||
builder.token(bot.token).get_updates_proxy_url("get_updates_proxy_url")
|
||||
assert len(recwarn) == 1
|
||||
assert "`ApplicationBuilder.get_updates_proxy_url` is deprecated" in str(
|
||||
recwarn[0].message
|
||||
)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__, "wrong stacklevel"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("read_timeout", "timeout", "expected"),
|
||||
[
|
||||
(None, None, 0),
|
||||
(1, None, 1),
|
||||
(None, 1, 1),
|
||||
(DEFAULT_NONE, None, 10),
|
||||
(DEFAULT_NONE, 1, 11),
|
||||
(1, 2, 3),
|
||||
],
|
||||
)
|
||||
async def test_get_updates_read_timeout_value_passing(
|
||||
self, bot, read_timeout, timeout, expected, monkeypatch, builder
|
||||
):
|
||||
# This test is a double check that ApplicationBuilder respects the changes of #3963 just
|
||||
# like `Bot` does - see also the corresponding test in test_bot.py (same name)
|
||||
caught_read_timeout = None
|
||||
|
||||
async def catch_timeouts(*args, **kwargs):
|
||||
nonlocal caught_read_timeout
|
||||
caught_read_timeout = kwargs.get("read_timeout")
|
||||
return HTTPStatus.OK, b'{"ok": "True", "result": {}}'
|
||||
|
||||
monkeypatch.setattr(HTTPXRequest, "do_request", catch_timeouts)
|
||||
|
||||
bot = builder.get_updates_read_timeout(10).token(bot.token).build().bot
|
||||
await bot.get_updates(read_timeout=read_timeout, timeout=timeout)
|
||||
assert caught_read_timeout == expected
|
||||
|
||||
@@ -52,3 +52,23 @@ class TestHandler:
|
||||
|
||||
sh = SubclassHandler()
|
||||
assert repr(sh) == "SubclassHandler[callback=TestHandler.test_repr.<locals>.some_func]"
|
||||
|
||||
def test_repr_no_qualname(self):
|
||||
class ClassBasedCallback:
|
||||
async def __call__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return "Repr of ClassBasedCallback"
|
||||
|
||||
class SubclassHandler(BaseHandler):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(callback=ClassBasedCallback())
|
||||
|
||||
def check_update(self, update: object):
|
||||
pass
|
||||
|
||||
sh = SubclassHandler()
|
||||
assert repr(sh) == "SubclassHandler[callback=Repr of ClassBasedCallback]"
|
||||
|
||||
@@ -1442,6 +1442,59 @@ class TestBasePersistence:
|
||||
# This is the important part: the persistence is updated with `None` when the conv ends
|
||||
assert papp.persistence.conversations == {"conv_1": {(1, 1): None}}
|
||||
|
||||
async def test_non_blocking_conversation_ends(self, bot):
|
||||
papp = build_papp(token=bot.token, update_interval=100)
|
||||
event = asyncio.Event()
|
||||
|
||||
async def callback(_, __):
|
||||
await event.wait()
|
||||
return HandlerStates.END
|
||||
|
||||
conversation = ConversationHandler(
|
||||
entry_points=[
|
||||
TrackingConversationHandler.build_handler(HandlerStates.END, callback=callback)
|
||||
],
|
||||
states={},
|
||||
fallbacks=[],
|
||||
persistent=True,
|
||||
name="conv",
|
||||
block=False,
|
||||
)
|
||||
papp.add_handler(conversation)
|
||||
|
||||
async with papp:
|
||||
await papp.start()
|
||||
assert papp.persistence.updated_conversations == {}
|
||||
|
||||
await papp.process_update(
|
||||
TrackingConversationHandler.build_update(HandlerStates.END, 1)
|
||||
)
|
||||
assert papp.persistence.updated_conversations == {}
|
||||
|
||||
papp.persistence.reset_tracking()
|
||||
event.set()
|
||||
await asyncio.sleep(0.01)
|
||||
await papp.update_persistence()
|
||||
|
||||
# On shutdown, persisted data should include the END state b/c that's what the
|
||||
# pending state is being resolved to
|
||||
assert papp.persistence.updated_conversations == {"conv": {(1, 1): 1}}
|
||||
assert papp.persistence.conversations == {"conv": {(1, 1): HandlerStates.END}}
|
||||
|
||||
await papp.stop()
|
||||
|
||||
async with papp:
|
||||
# On the next restart/persistence loading the ConversationHandler should resolve
|
||||
# the stored END state to None …
|
||||
assert papp.persistence.conversations == {"conv": {(1, 1): HandlerStates.END}}
|
||||
# … and the update should be accepted by the entry point again
|
||||
assert conversation.check_update(
|
||||
TrackingConversationHandler.build_update(HandlerStates.END, 1)
|
||||
)
|
||||
|
||||
await papp.update_persistence()
|
||||
assert papp.persistence.conversations == {"conv": {(1, 1): None}}
|
||||
|
||||
async def test_conversation_timeout(self, bot):
|
||||
# high update_interval so that we can instead manually call it
|
||||
papp = build_papp(token=bot.token, update_interval=150)
|
||||
|
||||
@@ -2423,3 +2423,65 @@ class TestFilters:
|
||||
),
|
||||
)
|
||||
assert filters.ATTACHMENT.check_update(up)
|
||||
|
||||
def test_filters_mention_no_entities(self, update):
|
||||
update.message.text = "test"
|
||||
assert not filters.Mention("@test").check_update(update)
|
||||
assert not filters.Mention(123456).check_update(update)
|
||||
assert not filters.Mention("123456").check_update(update)
|
||||
assert not filters.Mention(User(1, "first_name", False)).check_update(update)
|
||||
assert not filters.Mention(
|
||||
["@test", 123456, "123456", User(1, "first_name", False)]
|
||||
).check_update(update)
|
||||
|
||||
def test_filters_mention_type_mention(self, update):
|
||||
update.message.text = "@test1 @test2 user"
|
||||
update.message.entities = [
|
||||
MessageEntity(MessageEntity.MENTION, 0, 6),
|
||||
MessageEntity(MessageEntity.MENTION, 7, 6),
|
||||
]
|
||||
|
||||
user_no_username = User(123456, "first_name", False)
|
||||
user_wrong_username = User(123456, "first_name", False, username="wrong")
|
||||
user_1 = User(111, "first_name", False, username="test1")
|
||||
user_2 = User(222, "first_name", False, username="test2")
|
||||
|
||||
for username in ("@test1", "@test2"):
|
||||
assert filters.Mention(username).check_update(update)
|
||||
assert filters.Mention({username}).check_update(update)
|
||||
|
||||
for user in (user_1, user_2):
|
||||
assert filters.Mention(user).check_update(update)
|
||||
assert filters.Mention({user}).check_update(update)
|
||||
|
||||
assert not filters.Mention(
|
||||
["@test3", 123, user_no_username, user_wrong_username]
|
||||
).check_update(update)
|
||||
|
||||
def test_filters_mention_type_text_mention(self, update):
|
||||
user_1 = User(111, "first_name", False, username="test1")
|
||||
user_2 = User(222, "first_name", False, username="test2")
|
||||
user_no_username = User(123456, "first_name", False)
|
||||
user_wrong_username = User(123456, "first_name", False, username="wrong")
|
||||
|
||||
update.message.text = "test1 test2 user"
|
||||
update.message.entities = [
|
||||
MessageEntity(MessageEntity.TEXT_MENTION, 0, 5, user=user_1),
|
||||
MessageEntity(MessageEntity.TEXT_MENTION, 6, 5, user=user_2),
|
||||
]
|
||||
|
||||
for username in ("@test1", "@test2"):
|
||||
assert filters.Mention(username).check_update(update)
|
||||
assert filters.Mention({username}).check_update(update)
|
||||
|
||||
for user in (user_1, user_2):
|
||||
assert filters.Mention(user).check_update(update)
|
||||
assert filters.Mention({user}).check_update(update)
|
||||
|
||||
for user_id in (111, 222):
|
||||
assert filters.Mention(user_id).check_update(update)
|
||||
assert filters.Mention({user_id}).check_update(update)
|
||||
|
||||
assert not filters.Mention(
|
||||
["@test3", 123, user_no_username, user_wrong_username]
|
||||
).check_update(update)
|
||||
|
||||
@@ -25,7 +25,7 @@ import time
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Job, JobQueue
|
||||
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Defaults, Job, JobQueue
|
||||
from telegram.warnings import PTBUserWarning
|
||||
from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS
|
||||
from tests.auxil.pytest_classes import make_bot
|
||||
@@ -106,6 +106,15 @@ class TestJobQueue:
|
||||
self.job_time = 0
|
||||
self.received_error = None
|
||||
|
||||
def test_scheduler_configuration(self, job_queue, timezone, bot):
|
||||
# Unfortunately, we can't really test the executor setting explicitly without relying
|
||||
# on protected attributes. However, this should be tested enough implicitly via all the
|
||||
# other tests in here
|
||||
assert job_queue.scheduler_configuration["timezone"] is UTC
|
||||
|
||||
tz_app = ApplicationBuilder().defaults(Defaults(tzinfo=timezone)).token(bot.token).build()
|
||||
assert tz_app.job_queue.scheduler_configuration["timezone"] is timezone
|
||||
|
||||
async def job_run_once(self, context):
|
||||
if (
|
||||
isinstance(context, CallbackContext)
|
||||
@@ -578,7 +587,7 @@ class TestJobQueue:
|
||||
assert rec.name == "telegram.ext.Application"
|
||||
assert "No error handlers are registered" in rec.getMessage()
|
||||
|
||||
async def test_custom_context(self, bot, job_queue):
|
||||
async def test_custom_context(self, bot):
|
||||
application = (
|
||||
ApplicationBuilder()
|
||||
.token(bot.token)
|
||||
@@ -589,6 +598,7 @@ class TestJobQueue:
|
||||
)
|
||||
.build()
|
||||
)
|
||||
job_queue = JobQueue()
|
||||
job_queue.set_application(application)
|
||||
|
||||
async def callback(context):
|
||||
@@ -599,9 +609,11 @@ class TestJobQueue:
|
||||
type(context.bot_data),
|
||||
)
|
||||
|
||||
await job_queue.start()
|
||||
job_queue.run_once(callback, 0.1)
|
||||
await asyncio.sleep(0.15)
|
||||
assert self.result == (CustomContext, None, None, int)
|
||||
await job_queue.stop()
|
||||
|
||||
async def test_attribute_error(self):
|
||||
job = Job(self.job_run_once)
|
||||
|
||||
@@ -331,6 +331,39 @@ class TestUpdater:
|
||||
|
||||
assert log_found
|
||||
|
||||
async def test_polling_mark_updates_as_read_timeout(self, monkeypatch, updater, caplog):
|
||||
timeout_event = asyncio.Event()
|
||||
|
||||
async def get_updates(*args, **kwargs):
|
||||
await asyncio.sleep(0)
|
||||
if timeout_event.is_set():
|
||||
raise TimedOut("TestMessage")
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(updater.bot, "get_updates", get_updates)
|
||||
|
||||
async with updater:
|
||||
await updater.start_polling()
|
||||
with caplog.at_level(logging.ERROR):
|
||||
timeout_event.set()
|
||||
await updater.stop()
|
||||
|
||||
assert len(caplog.records) >= 1
|
||||
log_found = False
|
||||
for record in caplog.records:
|
||||
if not record.getMessage().startswith(
|
||||
"Error while calling `get_updates` one more time"
|
||||
):
|
||||
continue
|
||||
|
||||
assert record.name == "telegram.ext.Updater"
|
||||
assert record.exc_info[0] is TimedOut
|
||||
assert str(record.exc_info[1]) == "TestMessage"
|
||||
log_found = True
|
||||
break
|
||||
|
||||
assert log_found
|
||||
|
||||
async def test_polling_mark_updates_as_read_failure(self, monkeypatch, updater, caplog):
|
||||
async def get_updates(*args, **kwargs):
|
||||
await asyncio.sleep(0)
|
||||
@@ -376,7 +409,7 @@ class TestUpdater:
|
||||
|
||||
expected = {
|
||||
"timeout": 10,
|
||||
"read_timeout": 2,
|
||||
"read_timeout": DEFAULT_NONE,
|
||||
"write_timeout": DEFAULT_NONE,
|
||||
"connect_timeout": DEFAULT_NONE,
|
||||
"pool_timeout": DEFAULT_NONE,
|
||||
|
||||
+153
-14
@@ -28,6 +28,7 @@ from typing import Any, Callable, Coroutine, Tuple
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from httpx import AsyncHTTPTransport
|
||||
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram.error import (
|
||||
@@ -41,7 +42,10 @@ from telegram.error import (
|
||||
TelegramError,
|
||||
TimedOut,
|
||||
)
|
||||
from telegram.request import BaseRequest, RequestData
|
||||
from telegram.request._httpxrequest import HTTPXRequest
|
||||
from telegram.request._requestparameter import RequestParameter
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
from tests.auxil.envvars import TEST_WITH_OPT_DEPS
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
@@ -78,7 +82,7 @@ async def httpx_request():
|
||||
class TestNoSocksHTTP2WithoutRequest:
|
||||
async def test_init(self, bot):
|
||||
with pytest.raises(RuntimeError, match=r"python-telegram-bot\[socks\]"):
|
||||
HTTPXRequest(proxy_url="socks5://foo")
|
||||
HTTPXRequest(proxy="socks5://foo")
|
||||
with pytest.raises(RuntimeError, match=r"python-telegram-bot\[http2\]"):
|
||||
HTTPXRequest(http_version="2")
|
||||
|
||||
@@ -117,7 +121,7 @@ class TestRequestWithoutRequest:
|
||||
|
||||
# Make sure that other exceptions are forwarded
|
||||
with pytest.raises(ImportError, match=r"Other Error Message"):
|
||||
HTTPXRequest(proxy_url="socks5://foo")
|
||||
HTTPXRequest(proxy="socks5://foo")
|
||||
|
||||
def test_slot_behaviour(self):
|
||||
inst = HTTPXRequest()
|
||||
@@ -329,7 +333,7 @@ class TestRequestWithoutRequest:
|
||||
|
||||
assert await httpx_request.retrieve(None, None) == server_response
|
||||
|
||||
async def test_timeout_propagation(self, monkeypatch, httpx_request):
|
||||
async def test_timeout_propagation_to_do_request(self, monkeypatch, httpx_request):
|
||||
async def make_assertion(*args, **kwargs):
|
||||
self.test_flag = (
|
||||
kwargs.get("read_timeout"),
|
||||
@@ -341,7 +345,7 @@ class TestRequestWithoutRequest:
|
||||
|
||||
monkeypatch.setattr(httpx_request, "do_request", make_assertion)
|
||||
|
||||
await httpx_request.post("url", "method")
|
||||
await httpx_request.post("url", None)
|
||||
assert self.test_flag == (DEFAULT_NONE, DEFAULT_NONE, DEFAULT_NONE, DEFAULT_NONE)
|
||||
|
||||
await httpx_request.post(
|
||||
@@ -349,6 +353,73 @@ class TestRequestWithoutRequest:
|
||||
)
|
||||
assert self.test_flag == (1, 2, 3, 4)
|
||||
|
||||
def test_read_timeout_not_implemented(self):
|
||||
class SimpleRequest(BaseRequest):
|
||||
async def do_request(self, *args, **kwargs):
|
||||
raise httpx.ReadTimeout("read timeout")
|
||||
|
||||
async def initialize(self) -> None:
|
||||
pass
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
pass
|
||||
|
||||
with pytest.raises(NotImplementedError):
|
||||
SimpleRequest().read_timeout
|
||||
|
||||
@pytest.mark.parametrize("media", [True, False])
|
||||
async def test_timeout_propagation_write_timeout(
|
||||
self, monkeypatch, media, input_media_photo, recwarn # noqa: F811
|
||||
):
|
||||
class CustomRequest(BaseRequest):
|
||||
async def initialize(self_) -> None:
|
||||
pass
|
||||
|
||||
async def shutdown(self_) -> None:
|
||||
pass
|
||||
|
||||
async def do_request(self_, *args, **kwargs) -> Tuple[int, bytes]:
|
||||
self.test_flag = (
|
||||
kwargs.get("read_timeout"),
|
||||
kwargs.get("connect_timeout"),
|
||||
kwargs.get("write_timeout"),
|
||||
kwargs.get("pool_timeout"),
|
||||
)
|
||||
return HTTPStatus.OK, b'{"ok": "True", "result": {}}'
|
||||
|
||||
custom_request = CustomRequest()
|
||||
data = {"string": "string", "int": 1, "float": 1.0}
|
||||
if media:
|
||||
data["media"] = input_media_photo
|
||||
request_data = RequestData(
|
||||
parameters=[RequestParameter.from_input(key, value) for key, value in data.items()],
|
||||
)
|
||||
|
||||
# First make sure that custom timeouts are always respected
|
||||
await custom_request.post(
|
||||
"url", request_data, read_timeout=1, connect_timeout=2, write_timeout=3, pool_timeout=4
|
||||
)
|
||||
assert self.test_flag == (1, 2, 3, 4)
|
||||
|
||||
# Now also ensure that the default timeout for media requests is 20 seconds
|
||||
await custom_request.post("url", request_data)
|
||||
assert self.test_flag == (
|
||||
DEFAULT_NONE,
|
||||
DEFAULT_NONE,
|
||||
20 if media else DEFAULT_NONE,
|
||||
DEFAULT_NONE,
|
||||
)
|
||||
|
||||
if media:
|
||||
assert len(recwarn) == 1
|
||||
assert "will default to `BaseRequest.DEFAULT_NONE` instead of 20" in str(
|
||||
recwarn[0].message
|
||||
)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__
|
||||
else:
|
||||
assert len(recwarn) == 0
|
||||
|
||||
|
||||
@pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="No need to run this twice")
|
||||
class TestHTTPXRequestWithoutRequest:
|
||||
@@ -358,7 +429,9 @@ class TestHTTPXRequestWithoutRequest:
|
||||
def _reset(self):
|
||||
self.test_flag = None
|
||||
|
||||
def test_init(self, monkeypatch):
|
||||
# We parametrize this to make sure that the legacy `proxy_url` argument is still supported
|
||||
@pytest.mark.parametrize("proxy_argument", ["proxy", "proxy_url"])
|
||||
def test_init(self, monkeypatch, proxy_argument):
|
||||
@dataclass
|
||||
class Client:
|
||||
timeout: object
|
||||
@@ -366,6 +439,7 @@ class TestHTTPXRequestWithoutRequest:
|
||||
limits: object
|
||||
http1: object
|
||||
http2: object
|
||||
transport: object = None
|
||||
|
||||
monkeypatch.setattr(httpx, "AsyncClient", Client)
|
||||
|
||||
@@ -378,20 +452,32 @@ class TestHTTPXRequestWithoutRequest:
|
||||
assert request._client.http1 is True
|
||||
assert not request._client.http2
|
||||
|
||||
request = HTTPXRequest(
|
||||
connection_pool_size=42,
|
||||
proxy_url="proxy_url",
|
||||
connect_timeout=43,
|
||||
read_timeout=44,
|
||||
write_timeout=45,
|
||||
pool_timeout=46,
|
||||
)
|
||||
assert request._client.proxies == "proxy_url"
|
||||
kwargs = {
|
||||
"connection_pool_size": 42,
|
||||
proxy_argument: "proxy",
|
||||
"connect_timeout": 43,
|
||||
"read_timeout": 44,
|
||||
"write_timeout": 45,
|
||||
"pool_timeout": 46,
|
||||
}
|
||||
request = HTTPXRequest(**kwargs)
|
||||
assert request._client.proxies == "proxy"
|
||||
assert request._client.limits == httpx.Limits(
|
||||
max_connections=42, max_keepalive_connections=42
|
||||
)
|
||||
assert request._client.timeout == httpx.Timeout(connect=43, read=44, write=45, pool=46)
|
||||
|
||||
def test_proxy_mutually_exclusive(self):
|
||||
with pytest.raises(ValueError, match="mutually exclusive"):
|
||||
HTTPXRequest(proxy="proxy", proxy_url="proxy_url")
|
||||
|
||||
def test_proxy_url_deprecation_warning(self, recwarn):
|
||||
HTTPXRequest(proxy_url="http://127.0.0.1:3128")
|
||||
assert len(recwarn) == 1
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert "`proxy_url` is deprecated" in str(recwarn[0].message)
|
||||
assert recwarn[0].filename == __file__, "incorrect stacklevel"
|
||||
|
||||
async def test_multiple_inits_and_shutdowns(self, monkeypatch):
|
||||
self.test_flag = defaultdict(int)
|
||||
|
||||
@@ -618,6 +704,59 @@ class TestHTTPXRequestWithoutRequest:
|
||||
|
||||
assert exc_info.value.__cause__ is pool_timeout
|
||||
|
||||
@pytest.mark.parametrize("media", [True, False])
|
||||
async def test_do_request_write_timeout(
|
||||
self, monkeypatch, media, httpx_request, input_media_photo, recwarn # noqa: F811
|
||||
):
|
||||
async def request(_, **kwargs):
|
||||
self.test_flag = kwargs.get("timeout")
|
||||
return httpx.Response(HTTPStatus.OK, content=b'{"ok": "True", "result": {}}')
|
||||
|
||||
monkeypatch.setattr(httpx.AsyncClient, "request", request)
|
||||
|
||||
data = {"string": "string", "int": 1, "float": 1.0}
|
||||
if media:
|
||||
data["media"] = input_media_photo
|
||||
request_data = RequestData(
|
||||
parameters=[RequestParameter.from_input(key, value) for key, value in data.items()],
|
||||
)
|
||||
|
||||
# First make sure that custom timeouts are always respected
|
||||
await httpx_request.post(
|
||||
"url", request_data, read_timeout=1, connect_timeout=2, write_timeout=3, pool_timeout=4
|
||||
)
|
||||
assert self.test_flag == httpx.Timeout(read=1, connect=2, write=3, pool=4)
|
||||
|
||||
# Now also ensure that the default timeout for media requests is 20 seconds
|
||||
await httpx_request.post("url", request_data)
|
||||
assert self.test_flag == httpx.Timeout(read=5, connect=5, write=20 if media else 5, pool=1)
|
||||
|
||||
# Just for double-checking, since warnings are issued for implementations of BaseRequest
|
||||
# other than HTTPXRequest
|
||||
assert len(recwarn) == 0
|
||||
|
||||
async def test_socket_opts(self, monkeypatch):
|
||||
transport_kwargs = {}
|
||||
transport_init = AsyncHTTPTransport.__init__
|
||||
|
||||
def init_transport(*args, **kwargs):
|
||||
nonlocal transport_kwargs
|
||||
transport_kwargs = kwargs.copy()
|
||||
transport_init(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(AsyncHTTPTransport, "__init__", init_transport)
|
||||
|
||||
HTTPXRequest()
|
||||
assert "socket_options" not in transport_kwargs
|
||||
|
||||
transport_kwargs = {}
|
||||
HTTPXRequest(socket_options=((1, 2, 3),))
|
||||
assert transport_kwargs["socket_options"] == ((1, 2, 3),)
|
||||
|
||||
@pytest.mark.parametrize("read_timeout", [None, 1, 2, 3])
|
||||
async def test_read_timeout_property(self, read_timeout):
|
||||
assert HTTPXRequest(read_timeout=read_timeout).read_timeout == read_timeout
|
||||
|
||||
|
||||
@pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="No need to run this twice")
|
||||
class TestHTTPXRequestWithRequest:
|
||||
|
||||
+68
-9
@@ -26,7 +26,9 @@ import re
|
||||
import socket
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from http import HTTPStatus
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
@@ -78,7 +80,7 @@ from telegram.error import BadRequest, InvalidToken, NetworkError
|
||||
from telegram.ext import ExtBot, InvalidCallbackData
|
||||
from telegram.helpers import escape_markdown
|
||||
from telegram.request import BaseRequest, HTTPXRequest, RequestData
|
||||
from telegram.warnings import PTBUserWarning
|
||||
from telegram.warnings import PTBDeprecationWarning, PTBUserWarning
|
||||
from tests.auxil.bot_method_checks import check_defaults_handling
|
||||
from tests.auxil.ci_bots import FALLBACKS
|
||||
from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS
|
||||
@@ -1362,14 +1364,14 @@ class TestBotWithoutRequest:
|
||||
class OkException(BaseException):
|
||||
pass
|
||||
|
||||
async def do_request(*args, **kwargs):
|
||||
obj = kwargs.get("write_timeout")
|
||||
if obj == 20:
|
||||
async def request(*args, **kwargs):
|
||||
timeout = kwargs.get("timeout")
|
||||
if timeout.write == 20:
|
||||
raise OkException
|
||||
|
||||
return 200, b'{"ok": true, "result": []}'
|
||||
|
||||
monkeypatch.setattr(bot.request, "do_request", do_request)
|
||||
monkeypatch.setattr(httpx.AsyncClient, "request", request)
|
||||
|
||||
# Test file uploading
|
||||
with pytest.raises(OkException):
|
||||
@@ -2180,7 +2182,7 @@ class TestBotWithRequest:
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_poll(
|
||||
chat_id,
|
||||
question=question,
|
||||
@@ -2237,7 +2239,7 @@ class TestBotWithRequest:
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_dice(
|
||||
chat_id, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
@@ -2446,6 +2448,63 @@ class TestBotWithRequest:
|
||||
if updates:
|
||||
assert isinstance(updates[0], Update)
|
||||
|
||||
@pytest.mark.parametrize("bot_class", [Bot, ExtBot])
|
||||
async def test_get_updates_read_timeout_deprecation_warning(
|
||||
self, bot, recwarn, monkeypatch, bot_class
|
||||
):
|
||||
# Using the normal HTTPXRequest should not issue any warnings
|
||||
await bot.get_updates()
|
||||
assert len(recwarn) == 0
|
||||
|
||||
# Now let's test deprecation warning when using get_updates for other BaseRequest
|
||||
# subclasses (we just monkeypatch the existing HTTPXRequest for this)
|
||||
read_timeout = None
|
||||
|
||||
async def catch_timeouts(*args, **kwargs):
|
||||
nonlocal read_timeout
|
||||
read_timeout = kwargs.get("read_timeout")
|
||||
return HTTPStatus.OK, b'{"ok": "True", "result": {}}'
|
||||
|
||||
monkeypatch.setattr(HTTPXRequest, "read_timeout", BaseRequest.read_timeout)
|
||||
monkeypatch.setattr(HTTPXRequest, "do_request", catch_timeouts)
|
||||
|
||||
bot = bot_class(get_updates_request=HTTPXRequest(), token=bot.token)
|
||||
await bot.get_updates()
|
||||
|
||||
assert len(recwarn) == 1
|
||||
assert "does not override the property `read_timeout`" in str(recwarn[0].message)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__, "wrong stacklevel"
|
||||
|
||||
assert read_timeout == 2
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("read_timeout", "timeout", "expected"),
|
||||
[
|
||||
(None, None, 0),
|
||||
(1, None, 1),
|
||||
(None, 1, 1),
|
||||
(DEFAULT_NONE, None, 10),
|
||||
(DEFAULT_NONE, 1, 11),
|
||||
(1, 2, 3),
|
||||
],
|
||||
)
|
||||
async def test_get_updates_read_timeout_value_passing(
|
||||
self, bot, read_timeout, timeout, expected, monkeypatch
|
||||
):
|
||||
caught_read_timeout = None
|
||||
|
||||
async def catch_timeouts(*args, **kwargs):
|
||||
nonlocal caught_read_timeout
|
||||
caught_read_timeout = kwargs.get("read_timeout")
|
||||
return HTTPStatus.OK, b'{"ok": "True", "result": {}}'
|
||||
|
||||
monkeypatch.setattr(HTTPXRequest, "do_request", catch_timeouts)
|
||||
|
||||
bot = Bot(get_updates_request=HTTPXRequest(read_timeout=10), token=bot.token)
|
||||
await bot.get_updates(read_timeout=read_timeout, timeout=timeout)
|
||||
assert caught_read_timeout == expected
|
||||
|
||||
@pytest.mark.xdist_group("getUpdates_and_webhook")
|
||||
@pytest.mark.parametrize("use_ip", [True, False])
|
||||
# local file path as file_input is tested below in test_set_webhook_params
|
||||
@@ -2570,7 +2629,7 @@ class TestBotWithRequest:
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_game(
|
||||
chat_id, game_short_name, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
@@ -3053,7 +3112,7 @@ class TestBotWithRequest:
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
with pytest.raises(BadRequest, match="Message to reply not found"):
|
||||
await default_bot.send_message(
|
||||
chat_id, "test", reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ from bs4 import BeautifulSoup, PageElement, Tag
|
||||
|
||||
import telegram
|
||||
from telegram._utils.defaultvalue import DefaultValue
|
||||
from telegram._utils.types import DVInput, FileInput, ODVInput
|
||||
from telegram._utils.types import FileInput, ODVInput
|
||||
from telegram.ext import Defaults
|
||||
from tests.auxil.envvars import RUN_TEST_OFFICIAL
|
||||
|
||||
@@ -433,11 +433,10 @@ def check_param_type(
|
||||
# Special case for when the parameter is a default value parameter
|
||||
for name, _ in inspect.getmembers(Defaults, lambda x: isinstance(x, property)):
|
||||
if name in ptb_param.name: # no strict == since we have a param: `explanation_parse_mode`
|
||||
# Check if it's DVInput or ODVInput
|
||||
for param_type in [DVInput, ODVInput]:
|
||||
parsed = param_type[mapped_type]
|
||||
if ptb_annotation == parsed:
|
||||
return True
|
||||
# Check if it's ODVInput
|
||||
parsed = ODVInput[mapped_type]
|
||||
if (ptb_annotation | None) == parsed: # We have to add back None in our annotation
|
||||
return True
|
||||
return False
|
||||
|
||||
# Special case for send_* methods where we accept more types than the official API:
|
||||
|
||||
Reference in New Issue
Block a user