mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-20 16:15:28 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4aedb33d37 | |||
| 4ddc36c625 | |||
| 311f88a716 | |||
| 179cb49dbc | |||
| 512e390738 | |||
| 33b677aeb4 | |||
| 800598ced4 | |||
| ec20f27a82 | |||
| cd59c1c075 | |||
| 52b0f2c3c9 | |||
| 598f44f4c2 | |||
| 22d0dd1301 | |||
| c6b6b0a370 | |||
| ee6c8a5995 | |||
| d5a1a48145 | |||
| 963edbf191 | |||
| 43c3c8f568 |
@@ -84,35 +84,13 @@ Here's how to make a one-off code change.
|
||||
|
||||
- In addition, PTB uses some formatting/styling and linting tools in the pre-commit setup. Some of those tools also have command line tools that can help to run these tools outside of the pre-commit step. If you'd like to leverage that, please have a look at the `pre-commit config file`_ for an overview of which tools (and which versions of them) are used. For example, we use `Black`_ for code formatting. Plugins for Black exist for some `popular editors`_. You can use those instead of manually formatting everything.
|
||||
|
||||
- Please ensure that the code you write is well-tested.
|
||||
- Please ensure that the code you write is well-tested and that all automated tests still pass. We
|
||||
have dedicated an `testing page`_ to help you with that.
|
||||
|
||||
- In addition to that, we provide the `dev` marker for pytest. If you write one or multiple tests and want to run only those, you can decorate them via `@pytest.mark.dev` and then run it with minimal overhead with `pytest ./path/to/test_file.py -m dev`.
|
||||
|
||||
- Don’t break backward compatibility.
|
||||
- Don't break backward compatibility.
|
||||
|
||||
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
|
||||
|
||||
- Before making a commit ensure that all automated tests still pass:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest -v
|
||||
|
||||
Since the tests can take a while to run, you can speed things up by running them in parallel
|
||||
using `pytest-xdist`_ (note that this may effect the result of the test in some rare cases):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest -v -n auto --dist=loadfile
|
||||
|
||||
To run ``test_official`` (particularly useful if you made API changes), run
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export TEST_OFFICIAL=true
|
||||
|
||||
prior to running the tests.
|
||||
|
||||
- If you want run style & type checks before committing run
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -205,7 +183,7 @@ or, if you don't have ``make`` available (e.g. on Windows):
|
||||
|
||||
Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser.
|
||||
|
||||
- Add ``.. versionadded:: version``, ``.. versionchanged:: version`` or ``.. deprecated:: version`` to the associated documentation of your changes, depending on what kind of change you made. This only applies if the change you made is visible to an end user. The directives should be added to class/method descriptions if their general behaviour changed and to the description of all arguments & attributes that changed.
|
||||
- Add ``.. versionadded:: NEXT.VERSION``, ``.. versionchanged:: NEXT.VERSION`` or ``.. deprecated:: NEXT.VERSION`` to the associated documentation of your changes, depending on what kind of change you made. This only applies if the change you made is visible to an end user. The directives should be added to class/method descriptions if their general behaviour changed and to the description of all arguments & attributes that changed.
|
||||
|
||||
Dev facing documentation
|
||||
------------------------
|
||||
@@ -287,4 +265,4 @@ break the API classes. For example:
|
||||
.. _`RTD build`: https://docs.python-telegram-bot.org/en/doc-fixes
|
||||
.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html
|
||||
.. _`section`: #documenting
|
||||
.. _`pytest-xdist`: https://github.com/pytest-dev/pytest-xdist
|
||||
.. _`testing page`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/tests/README.rst
|
||||
|
||||
+12
-30
@@ -44,42 +44,24 @@ jobs:
|
||||
# The first & second one are achieved by mocking the corresponding import
|
||||
# See test_helpers.py & test_no_passport.py for details
|
||||
run: |
|
||||
# Test without passport
|
||||
pytest -v --cov -k test_no_passport.py
|
||||
# We test without optional dependencies first. This includes:
|
||||
# - without pytz
|
||||
# - without jobqueue
|
||||
# - without ratelimiter
|
||||
# - without webhooks
|
||||
# - without arbitrary callback data
|
||||
# - without socks support
|
||||
# - without http2 support
|
||||
TO_TEST="test_no_passport.py or test_datetime.py or test_defaults.py or test_jobqueue.py or test_applicationbuilder.py or test_ratelimiter.py or test_updater.py or test_callbackdatacache.py or test_request.py"
|
||||
pytest -v --cov -k "${TO_TEST}"
|
||||
status=$?
|
||||
|
||||
# test without pytz
|
||||
pytest -v --cov --cov-append -k test_datetime.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
pytest -v --cov --cov-append -k test_defaults.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
|
||||
# test without pytz & jobqueue
|
||||
pytest -v --cov --cov-append -k test_jobqueue.py
|
||||
pytest -v --cov --cov-append -k test_applicationbuilder.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
|
||||
# Test without ratelimiter
|
||||
pytest -v --cov --cov-append -k test_ratelimiter.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
|
||||
# Test without webhooks
|
||||
pytest -v --cov --cov-append -k test_updater.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
|
||||
# Test without callback-data
|
||||
pytest -v --cov --cov-append -k test_callbackdatacache.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
|
||||
# Test without socks
|
||||
pytest -v --cov --cov-append -k test_request.py
|
||||
status=$(( $? > status ? $? : status))
|
||||
|
||||
# Test the rest
|
||||
export TEST_WITH_OPT_DEPS='true'
|
||||
pip install -r requirements-opts.txt
|
||||
# `-n auto --dist loadfile` uses pytest-xdist to run each test file on a different CPU
|
||||
# worker
|
||||
# worker. Increasing number of workers has little effect on test duration, but it seems
|
||||
# to increase flakyness, specially on python 3.7 with --dist=loadgroup.
|
||||
pytest -v --cov --cov-append -n auto --dist loadfile
|
||||
status=$(( $? > status ? $? : status))
|
||||
exit ${status}
|
||||
|
||||
@@ -23,21 +23,22 @@ jobs:
|
||||
- name: Install Pyright
|
||||
run: |
|
||||
python -W ignore -m pip install pyright~=1.1.291
|
||||
- name: Get PR Completeness
|
||||
# Must run before base completeness, as base completeness will checkout the base branch
|
||||
# And we can't go back to the PR branch after that in case the PR is coming from a fork
|
||||
run: |
|
||||
pip install . -U
|
||||
pyright --verifytypes telegram --ignoreexternal --outputjson > pr.json || true
|
||||
pyright --verifytypes telegram --ignoreexternal > pr.readable || true
|
||||
- name: Get Base Completeness
|
||||
run: |
|
||||
git checkout ${{ github.base_ref }}
|
||||
pip install . -U
|
||||
pyright --verifytypes telegram --ignoreexternal --outputjson > base.json || true
|
||||
- name: Get PR Completeness
|
||||
run: |
|
||||
git checkout ${{ github.head_ref }}
|
||||
pip install . -U
|
||||
pyright --verifytypes telegram --ignoreexternal --outputjson > pr.json || true
|
||||
pyright --verifytypes telegram --ignoreexternal > pr.readable || true
|
||||
- name: Compare Completeness
|
||||
uses: jannekem/run-python-script-action@v1
|
||||
with:
|
||||
script: |
|
||||
script: |
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -51,6 +51,8 @@ nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.coveralls.yml
|
||||
.testmondata
|
||||
.testmondata-journal
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
@@ -20,7 +20,7 @@ repos:
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: v2.16.1
|
||||
rev: v2.16.4
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(telegram|examples)/.*\.py$
|
||||
@@ -33,12 +33,12 @@ repos:
|
||||
additional_dependencies:
|
||||
- httpx~=0.23.3
|
||||
- tornado~=6.2
|
||||
- APScheduler~=3.10.0
|
||||
- APScheduler~=3.10.1
|
||||
- cachetools~=5.3.0
|
||||
- aiolimiter~=1.0.0
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.991
|
||||
rev: v1.0.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: mypy-ptb
|
||||
@@ -49,7 +49,7 @@ repos:
|
||||
- types-cachetools
|
||||
- httpx~=0.23.3
|
||||
- tornado~=6.2
|
||||
- APScheduler~=3.10.0
|
||||
- APScheduler~=3.10.1
|
||||
- cachetools~=5.3.0
|
||||
- aiolimiter~=1.0.0
|
||||
- . # this basically does `pip install -e .`
|
||||
@@ -61,7 +61,7 @@ repos:
|
||||
- --follow-imports=silent
|
||||
additional_dependencies:
|
||||
- tornado~=6.2
|
||||
- APScheduler~=3.10.0
|
||||
- APScheduler~=3.10.1
|
||||
- cachetools~=5.3.0
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
@@ -80,7 +80,7 @@ repos:
|
||||
- --diff
|
||||
- --check
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.0.243'
|
||||
rev: 'v0.0.254'
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: ruff
|
||||
@@ -88,6 +88,6 @@ repos:
|
||||
additional_dependencies:
|
||||
- httpx~=0.23.3
|
||||
- tornado~=6.2
|
||||
- APScheduler~=3.10.0
|
||||
- APScheduler~=3.10.1
|
||||
- cachetools~=5.3.0
|
||||
- aiolimiter~=1.0.0
|
||||
|
||||
@@ -100,6 +100,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Riko Naka <https://github.com/rikonaka>`_
|
||||
- `Rizlas <https://github.com/rizlas>`_
|
||||
- `Sahil Sharma <https://github.com/sahilsharma811>`_
|
||||
- `Sam Mosleh <https://github.com/sam-mosleh>`_
|
||||
- `Sascha <https://github.com/saschalalala>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `Shivam Saini <https://github.com/shivamsn97>`_
|
||||
|
||||
+50
-1
@@ -2,6 +2,55 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 20.2
|
||||
============
|
||||
*Released 2023-03-25*
|
||||
|
||||
This is the technical changelog for version 20.2. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
- Full Support for API 6.6 (`#3584`_)
|
||||
- Revert to HTTP/1.1 as Default and make HTTP/2 an Optional Dependency (`#3576`_)
|
||||
|
||||
Minor Changes, Documentation Improvements and CI
|
||||
------------------------------------------------
|
||||
- Documentation Improvements (`#3565`_, `#3600`_)
|
||||
- Handle Symbolic Links in ``was_called_by`` (`#3552`_)
|
||||
- Tidy Up Tests Directory (`#3553`_)
|
||||
- Enhance ``Application.create_task`` (`#3543`_)
|
||||
- Make Type Completeness Workflow Usable for ``PRs`` from Forks (`#3551`_)
|
||||
- Refactor and Overhaul the Test Suite (`#3426`_)
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
- Bump ``pytest-asyncio`` from 0.20.3 to 0.21.0 (`#3624`_)
|
||||
- Bump ``furo`` from 2022.12.7 to 2023.3.23 (`#3625`_)
|
||||
- Bump ``pytest-xdist`` from 3.2.0 to 3.2.1 (`#3606`_)
|
||||
- ``pre-commit`` autoupdate (`#3577`_)
|
||||
- Update ``apscheduler`` requirement from ~=3.10.0 to ~=3.10.1 (`#3572`_)
|
||||
- Bump ``pytest`` from 7.2.1 to 7.2.2 (`#3573`_)
|
||||
- Bump ``pytest-xdist`` from 3.1.0 to 3.2.0 (`#3550`_)
|
||||
- Bump ``sphinxcontrib-mermaid`` from 0.7.1 to 0.8 (`#3549`_)
|
||||
|
||||
.. _`#3584`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3584
|
||||
.. _`#3576`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3576
|
||||
.. _`#3565`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3565
|
||||
.. _`#3600`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3600
|
||||
.. _`#3552`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3552
|
||||
.. _`#3553`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3553
|
||||
.. _`#3543`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3543
|
||||
.. _`#3551`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3551
|
||||
.. _`#3426`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3426
|
||||
.. _`#3624`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3624
|
||||
.. _`#3625`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3625
|
||||
.. _`#3606`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3606
|
||||
.. _`#3577`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3577
|
||||
.. _`#3572`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3572
|
||||
.. _`#3573`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3573
|
||||
.. _`#3550`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3550
|
||||
.. _`#3549`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3549
|
||||
|
||||
Version 20.1
|
||||
============
|
||||
*Released 2023-02-09*
|
||||
@@ -2108,7 +2157,7 @@ Changes
|
||||
- Lots of small improvements to our tests and documentation.
|
||||
|
||||
|
||||
.. _`see docs`: https://docs.python-telegram-bot.org/en/stable/telegram.ext.dispatcher.html#telegram.ext.Dispatcher.add_handler
|
||||
.. _`see docs`: https://docs.python-telegram-bot.org/en/v13.11/telegram.ext.dispatcher.html?highlight=Dispatcher.add_handler#telegram.ext.Dispatcher.add_handler
|
||||
.. _`#777`: https://github.com/python-telegram-bot/python-telegram-bot/pull/777
|
||||
.. _`#806`: https://github.com/python-telegram-bot/python-telegram-bot/pull/806
|
||||
.. _`#766`: https://github.com/python-telegram-bot/python-telegram-bot/pull/766
|
||||
|
||||
+6
-6
@@ -14,7 +14,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.5-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.6-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -93,7 +93,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **6.5** are supported.
|
||||
All types and methods of the Telegram Bot API **6.6** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
@@ -135,9 +135,8 @@ 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[http2] ~= 0.23.3 <https://www.python-httpx.org>`_ for
|
||||
``telegram.request.HTTPXRequest``, the default networking backend. By default, HTTP/2 is used, as it
|
||||
provides greater performance and stability, specially for concurrent requests.
|
||||
The only required dependency is `httpx ~= 0.23.3 <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.
|
||||
To minimize dependency conflicts, we try to be liberal in terms of version requirements on the (optional) dependencies.
|
||||
@@ -151,10 +150,11 @@ PTB can be installed with optional dependencies:
|
||||
|
||||
* ``pip install python-telegram-bot[passport]`` installs the `cryptography>=39.0.1 <https://cryptography.io/en/stable>`_ library. Use this, if you want to use Telegram Passport related functionality.
|
||||
* ``pip install python-telegram-bot[socks]`` installs `httpx[socks] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to work behind a Socks5 server.
|
||||
* ``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.0.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.2 <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.0 <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.0 <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``.
|
||||
* ``pip install python-telegram-bot[job-queue]`` installs the `APScheduler~=3.10.1 <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]``.
|
||||
|
||||
|
||||
+5
-5
@@ -14,7 +14,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot-raw/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.5-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.6-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -89,7 +89,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **6.5** are supported.
|
||||
All types and methods of the Telegram Bot API **6.6** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
@@ -136,9 +136,8 @@ 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[http2] ~= 0.23.3 <https://www.python-httpx.org>`_ for
|
||||
``telegram.request.HTTPXRequest``, the default networking backend. By default, HTTP/2 is used, as
|
||||
it provides greater performance and stability, specially for concurrent requests.
|
||||
The only required dependency is `httpx ~= 0.23.3 <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.
|
||||
To minimize dependency conflicts, we try to be liberal in terms of version requirements on the (optional) dependencies.
|
||||
@@ -152,6 +151,7 @@ PTB can be installed with optional dependencies:
|
||||
|
||||
* ``pip install python-telegram-bot-raw[passport]`` installs the `cryptography>=39.0.1 <https://cryptography.io/en/stable>`_ library. Use this, if you want to use Telegram Passport related functionality.
|
||||
* ``pip install python-telegram-bot-raw[socks]`` installs `httpx[socks] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to work behind a Socks5 server.
|
||||
* ``pip install python-telegram-bot[http2]`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
|
||||
|
||||
To install multiple optional dependencies, separate them by commas, e.g. ``pip install python-telegram-bot-raw[passport,socks]``.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
sphinx==6.1.3
|
||||
sphinx-pypi-upload
|
||||
furo==2022.12.7
|
||||
furo==2023.3.23
|
||||
git+https://github.com/harshil21/furo-sphinx-search@01efc7be422d7dc02390aab9be68d6f5ce1a5618#egg=furo-sphinx-search
|
||||
sphinx-paramlinks==0.5.4
|
||||
sphinxcontrib-mermaid==0.7.1
|
||||
sphinxcontrib-mermaid==0.8.1
|
||||
sphinx-copybutton==0.5.1
|
||||
|
||||
+2
-2
@@ -21,9 +21,9 @@ author = "Leandro Toledo"
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = "20.1" # telegram.__version__[:3]
|
||||
version = "20.2" # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = "20.1" # telegram.__version__
|
||||
release = "20.2" # telegram.__version__
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = "6.1.3"
|
||||
|
||||
@@ -157,6 +157,23 @@
|
||||
- Used for getting the number of members in a chat
|
||||
* - :meth:`~telegram.Bot.get_chat_member`
|
||||
- Used for getting a member of a chat
|
||||
* - :meth:`~telegram.Bot.leave_chat`
|
||||
- Used for leaving a chat
|
||||
|
||||
.. raw:: html
|
||||
|
||||
</details>
|
||||
<br>
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<details>
|
||||
<summary>Bot settings</summary>
|
||||
|
||||
.. list-table::
|
||||
:align: left
|
||||
:widths: 1 4
|
||||
|
||||
* - :meth:`~telegram.Bot.set_my_commands`
|
||||
- Used for setting the list of commands
|
||||
* - :meth:`~telegram.Bot.delete_my_commands`
|
||||
@@ -171,8 +188,14 @@
|
||||
- Used for obtaining the menu button of a private chat or the default menu button
|
||||
* - :meth:`~telegram.Bot.set_chat_menu_button`
|
||||
- Used for setting the menu button of a private chat or the default menu button
|
||||
* - :meth:`~telegram.Bot.leave_chat`
|
||||
- Used for leaving a chat
|
||||
* - :meth:`~telegram.Bot.set_my_description`
|
||||
- Used for setting the description of the bot
|
||||
* - :meth:`~telegram.Bot.get_my_description`
|
||||
- Used for obtaining the description of the bot
|
||||
* - :meth:`~telegram.Bot.set_my_short_description`
|
||||
- Used for setting the short description of the bot
|
||||
* - :meth:`~telegram.Bot.get_my_short_description`
|
||||
- Used for obtaining the short description of the bot
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -194,14 +217,26 @@
|
||||
- Used for deleting a sticker from a set
|
||||
* - :meth:`~telegram.Bot.create_new_sticker_set`
|
||||
- Used for creating a new sticker set
|
||||
* - :meth:`~telegram.Bot.delete_sticker_set`
|
||||
- Used for deleting a sticker set made by a bot
|
||||
* - :meth:`~telegram.Bot.set_chat_sticker_set`
|
||||
- Used for setting a sticker set
|
||||
- Used for setting a sticker set of a chat
|
||||
* - :meth:`~telegram.Bot.delete_chat_sticker_set`
|
||||
- Used for deleting the set sticker set
|
||||
- Used for deleting the set sticker set of a chat
|
||||
* - :meth:`~telegram.Bot.set_sticker_position_in_set`
|
||||
- Used for moving a sticker's position in the set
|
||||
* - :meth:`~telegram.Bot.set_sticker_set_title`
|
||||
- Used for setting the title of a sticker set
|
||||
* - :meth:`~telegram.Bot.set_sticker_emoji_list`
|
||||
- Used for setting the emoji list of a sticker
|
||||
* - :meth:`~telegram.Bot.set_sticker_keywords`
|
||||
- Used for setting the keywords of a sticker
|
||||
* - :meth:`~telegram.Bot.set_sticker_mask_position`
|
||||
- Used for setting the mask position of a mask sticker
|
||||
* - :meth:`~telegram.Bot.set_sticker_set_thumb`
|
||||
- Used for setting the thumbnail of a sticker set
|
||||
* - :meth:`~telegram.Bot.set_custom_emoji_sticker_set_thumbnail`
|
||||
- Used for setting the thumbnail of a custom emoji sticker set
|
||||
* - :meth:`~telegram.Bot.get_sticker_set`
|
||||
- Used for getting a sticker set
|
||||
* - :meth:`~telegram.Bot.upload_sticker_file`
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
GitHub Repository <https://github.com/python-telegram-bot/python-telegram-bot/>
|
||||
Telegram Channel <https://t.me/pythontelegrambotchannel/>
|
||||
Telegram User Group <https://t.me/pythontelegrambotgroup/>
|
||||
contributing
|
||||
coc
|
||||
|
||||
|
||||
contributing
|
||||
testing
|
||||
|
||||
@@ -15,6 +15,8 @@ Available Types
|
||||
telegram.botcommandscopechatadministrators
|
||||
telegram.botcommandscopechatmember
|
||||
telegram.botcommandscopedefault
|
||||
telegram.botdescription
|
||||
telegram.botshortdescription
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chatadministratorrights
|
||||
@@ -53,6 +55,7 @@ Available Types
|
||||
telegram.inputmediadocument
|
||||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.inputsticker
|
||||
telegram.keyboardbutton
|
||||
telegram.keyboardbuttonpolltype
|
||||
telegram.keyboardbuttonrequestchat
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
BotDescription
|
||||
==============
|
||||
|
||||
.. autoclass:: telegram.BotDescription
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
BotShortDescription
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.BotShortDescription
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
InputSticker
|
||||
============
|
||||
|
||||
.. autoclass:: telegram.InputSticker
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1 @@
|
||||
.. include:: ../../tests/README.rst
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
.. |thumbdocstringnopath| replace:: |thumbdocstringbase| |uploadinputnopath|
|
||||
|
||||
.. |thumbargumentdeprecation| replace:: As of Bot API 6.6 this argument is deprecated in favor of
|
||||
|
||||
.. |thumbattributedeprecation| replace:: As of Bot API 6.6 this attribute is deprecated in favor of
|
||||
|
||||
.. |editreplymarkup| replace:: It is currently only possible to edit messages without :attr:`telegram.Message.reply_markup` or with inline keyboards.
|
||||
|
||||
.. |toapikwargsbase| replace:: These arguments are also considered by :meth:`~telegram.TelegramObject.to_dict` and :meth:`~telegram.TelegramObject.to_json`, i.e. when passing objects to Telegram. Passing them to Telegram is however not guaranteed to work for all kinds of objects, e.g. this will fail for objects that can not directly be JSON serialized.
|
||||
|
||||
@@ -18,8 +18,8 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
|
||||
Note:
|
||||
To use arbitrary callback data, you must install ptb via
|
||||
`pip install python-telegram-bot[callback-data]`
|
||||
To use the JobQueue, you must install PTB via
|
||||
`pip install python-telegram-bot[job-queue]`
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
pre-commit
|
||||
|
||||
pytest==7.2.1
|
||||
pytest-asyncio==0.20.3
|
||||
pytest-timeout==2.1.0 # used to timeout tests
|
||||
pytest-xdist==3.1.0 # xdist runs tests in parallel
|
||||
pre-commit # needed for pre-commit hooks in the git commit command
|
||||
|
||||
# For the test suite
|
||||
pytest==7.2.2
|
||||
pytest-asyncio==0.21.0 # needed because pytest doesn't come with native support for coroutines as tests
|
||||
pytest-xdist==3.2.1 # xdist runs tests in parallel
|
||||
flaky # Used for flaky tests (flaky decorator)
|
||||
beautifulsoup4 # used in test_official for parsing tg docs
|
||||
|
||||
wheel # required for building the wheels for releases
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
# versions and only increase the lower bound if necessary
|
||||
|
||||
httpx[socks] # socks
|
||||
httpx[http2] # http2
|
||||
cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3,>=39.0.1 # passport
|
||||
aiolimiter~=1.0.0 # rate-limiter!ext
|
||||
|
||||
@@ -20,7 +21,7 @@ tornado~=6.2 # webhooks!ext
|
||||
# Cachetools and APS don't have a strict stability policy.
|
||||
# Let's be cautious for now.
|
||||
cachetools~=5.3.0 # callback-data!ext
|
||||
APScheduler~=3.10.0 # job-queue!ext
|
||||
APScheduler~=3.10.1 # job-queue!ext
|
||||
|
||||
# pytz is required by APS and just needs the lower bound due to #2120
|
||||
pytz>=2018.6 # job-queue!ext
|
||||
|
||||
+1
-2
@@ -6,5 +6,4 @@
|
||||
# versions and only increase the lower bound if necessary
|
||||
|
||||
# httpx has no stable release yet, so let's be cautious for now
|
||||
# HTTP/2 is more performant and stable than HTTP/1.1, specially for concurrent requests
|
||||
httpx[http2] ~= 0.23.3
|
||||
httpx ~= 0.23.3
|
||||
|
||||
@@ -36,7 +36,10 @@ filterwarnings =
|
||||
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
|
||||
; and instead do a trick directly in tests/conftest.py
|
||||
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
|
||||
markers = dev: If you want to test a specific test, use this
|
||||
markers =
|
||||
dev: If you want to test a specific test, use this
|
||||
no_req
|
||||
req
|
||||
asyncio_mode = auto
|
||||
|
||||
[coverage:run]
|
||||
|
||||
@@ -37,6 +37,8 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
"BotCommandScopeChatAdministrators",
|
||||
"BotCommandScopeChatMember",
|
||||
"BotCommandScopeDefault",
|
||||
"BotDescription",
|
||||
"BotShortDescription",
|
||||
"CallbackGame",
|
||||
"CallbackQuery",
|
||||
"Chat",
|
||||
@@ -114,6 +116,7 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
"InputMediaPhoto",
|
||||
"InputMediaVideo",
|
||||
"InputMessageContent",
|
||||
"InputSticker",
|
||||
"InputTextMessageContent",
|
||||
"InputVenueMessageContent",
|
||||
"Invoice",
|
||||
@@ -200,6 +203,7 @@ from ._botcommandscope import (
|
||||
BotCommandScopeChatMember,
|
||||
BotCommandScopeDefault,
|
||||
)
|
||||
from ._botdescription import BotDescription, BotShortDescription
|
||||
from ._callbackquery import CallbackQuery
|
||||
from ._chat import Chat
|
||||
from ._chatadministratorrights import ChatAdministratorRights
|
||||
@@ -234,6 +238,7 @@ from ._files.inputmedia import (
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
)
|
||||
from ._files.inputsticker import InputSticker
|
||||
from ._files.location import Location
|
||||
from ._files.photosize import PhotoSize
|
||||
from ._files.sticker import MaskPosition, Sticker, StickerSet
|
||||
|
||||
+931
-50
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains two objects that represent a Telegram bots (short) description."""
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
|
||||
class BotDescription(TelegramObject):
|
||||
"""This object represents the bot's description.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`description` is equal.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Args:
|
||||
description (:obj:`str`): The bot's description.
|
||||
|
||||
Attributes:
|
||||
description (:obj:`str`): The bot's description.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("description",)
|
||||
|
||||
def __init__(self, description: str, *, api_kwargs: JSONDict = None):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.description = description
|
||||
|
||||
self._id_attrs = (self.description,)
|
||||
|
||||
self._freeze()
|
||||
|
||||
|
||||
class BotShortDescription(TelegramObject):
|
||||
"""This object represents the bot's short description.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`short_description` is equal.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Args:
|
||||
short_description (:obj:`str`): The bot's short description.
|
||||
|
||||
Attributes:
|
||||
short_description (:obj:`str`): The bot's short description.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("short_description",)
|
||||
|
||||
def __init__(self, short_description: str, *, api_kwargs: JSONDict = None):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.short_description = short_description
|
||||
|
||||
self._id_attrs = (self.short_description,)
|
||||
|
||||
self._freeze()
|
||||
@@ -1532,6 +1532,7 @@ class Chat(TelegramObject):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1572,6 +1573,7 @@ class Chat(TelegramObject):
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def send_document(
|
||||
@@ -1588,6 +1590,7 @@ class Chat(TelegramObject):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1620,6 +1623,7 @@ class Chat(TelegramObject):
|
||||
pool_timeout=pool_timeout,
|
||||
parse_mode=parse_mode,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
disable_content_type_detection=disable_content_type_detection,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
@@ -1875,6 +1879,7 @@ class Chat(TelegramObject):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1916,6 +1921,7 @@ class Chat(TelegramObject):
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def send_sticker(
|
||||
@@ -1927,6 +1933,7 @@ class Chat(TelegramObject):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
emoji: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -1958,6 +1965,7 @@ class Chat(TelegramObject):
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
emoji=emoji,
|
||||
)
|
||||
|
||||
async def send_venue(
|
||||
@@ -2036,6 +2044,7 @@ class Chat(TelegramObject):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2071,6 +2080,7 @@ class Chat(TelegramObject):
|
||||
parse_mode=parse_mode,
|
||||
supports_streaming=supports_streaming,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
caption_entities=caption_entities,
|
||||
@@ -2092,6 +2102,7 @@ class Chat(TelegramObject):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2123,6 +2134,7 @@ class Chat(TelegramObject):
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
filename=filename,
|
||||
|
||||
@@ -22,6 +22,10 @@ from typing import TYPE_CHECKING, Optional, Type, TypeVar
|
||||
from telegram._files._basemedium import _BaseMedium
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
@@ -46,18 +50,25 @@ class _BaseThumbedMedium(_BaseMedium):
|
||||
file_size (:obj:`int`, optional): File size.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by sender.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): File identifier.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): Optional. File size.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Thumbnail as defined by sender.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail as defined by sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("thumb",)
|
||||
__slots__ = ("thumbnail",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -65,6 +76,7 @@ class _BaseThumbedMedium(_BaseMedium):
|
||||
file_unique_id: str,
|
||||
file_size: int = None,
|
||||
thumb: PhotoSize = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -74,7 +86,27 @@ class _BaseThumbedMedium(_BaseMedium):
|
||||
file_size=file_size,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
self.thumb: Optional[PhotoSize] = thumb
|
||||
|
||||
self.thumbnail: Optional[PhotoSize] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb,
|
||||
new_arg=thumbnail,
|
||||
deprecated_arg_name="thumb",
|
||||
new_arg_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
stacklevel=4,
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb(self) -> Optional[PhotoSize]:
|
||||
""":class:`telegram.PhotoSize`: Optional. Thumbnail as defined by sender.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb", new_attr_name="thumbnail", bot_api_version="6.6"
|
||||
)
|
||||
return self.thumbnail
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
@@ -87,7 +119,13 @@ class _BaseThumbedMedium(_BaseMedium):
|
||||
return None
|
||||
|
||||
# In case this wasn't already done by the subclass
|
||||
if not isinstance(data.get("thumb"), PhotoSize):
|
||||
data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot)
|
||||
if not isinstance(data.get("thumbnail"), PhotoSize):
|
||||
data["thumbnail"] = PhotoSize.de_json(data.get("thumbnail"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
# Let's filter it out to speed up the de-json process
|
||||
if data.get("thumb") is not None:
|
||||
api_kwargs["thumb"] = data.pop("thumb")
|
||||
|
||||
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
|
||||
|
||||
@@ -40,9 +40,16 @@ class Animation(_BaseThumbedMedium):
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Animation thumbnail as defined by sender.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
file_name (:obj:`str`, optional): Original animation filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Animation thumbnail as defined by
|
||||
sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
@@ -53,11 +60,13 @@ class Animation(_BaseThumbedMedium):
|
||||
width (:obj:`int`): Video width as defined by sender.
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Animation thumbnail as defined by sender.
|
||||
file_name (:obj:`str`): Optional. Original animation filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Animation thumbnail as defined by
|
||||
sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
@@ -74,6 +83,7 @@ class Animation(_BaseThumbedMedium):
|
||||
file_name: str = None,
|
||||
mime_type: str = None,
|
||||
file_size: int = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -83,6 +93,7 @@ class Animation(_BaseThumbedMedium):
|
||||
file_size=file_size,
|
||||
thumb=thumb,
|
||||
api_kwargs=api_kwargs,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
with self._unfrozen():
|
||||
# Required
|
||||
|
||||
@@ -46,6 +46,13 @@ class Audio(_BaseThumbedMedium):
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to
|
||||
which the music file belongs.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to
|
||||
which the music file belongs.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
or reuse the file.
|
||||
@@ -58,9 +65,11 @@ class Audio(_BaseThumbedMedium):
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Thumbnail of the album cover to
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail of the album cover to
|
||||
which the music file belongs.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
|
||||
"""
|
||||
|
||||
@@ -77,6 +86,7 @@ class Audio(_BaseThumbedMedium):
|
||||
file_size: int = None,
|
||||
thumb: PhotoSize = None,
|
||||
file_name: str = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -85,6 +95,7 @@ class Audio(_BaseThumbedMedium):
|
||||
file_unique_id=file_unique_id,
|
||||
file_size=file_size,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
|
||||
@@ -37,19 +37,27 @@ class Document(_BaseThumbedMedium):
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
|
||||
the same over time and for different bots. Can't be used to download or reuse the file.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by sender.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
|
||||
the same over time and for different bots. Can't be used to download or reuse the file.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Document thumbnail as defined by sender.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Document thumbnail as defined by sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
@@ -63,6 +71,7 @@ class Document(_BaseThumbedMedium):
|
||||
file_name: str = None,
|
||||
mime_type: str = None,
|
||||
file_size: int = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -71,6 +80,7 @@ class Document(_BaseThumbedMedium):
|
||||
file_unique_id=file_unique_id,
|
||||
file_size=file_size,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
|
||||
+123
-12
@@ -31,6 +31,10 @@ from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.files import parse_file_input
|
||||
from telegram._utils.types import FileInput, JSONDict, ODVInput
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_attr_in_property,
|
||||
warn_about_thumb_return_thumbnail,
|
||||
)
|
||||
from telegram.constants import InputMediaType
|
||||
|
||||
MediaType = Union[Animation, Audio, Document, PhotoSize, Video]
|
||||
@@ -138,6 +142,9 @@ class InputMediaAnimation(InputMedia):
|
||||
|
||||
.. versionchanged:: 13.2
|
||||
Accept :obj:`bytes` as input.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
caption (:obj:`str`, optional): Caption of the animation to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
|
||||
after entities parsing.
|
||||
@@ -154,11 +161,14 @@ class InputMediaAnimation(InputMedia):
|
||||
with a spoiler animation.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): |thumbdocstringnopath|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.ANIMATION`.
|
||||
media (:obj:`str` | :class:`telegram.InputFile`): Animation to send.
|
||||
thumb (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
caption (:obj:`str`): Optional. Caption of the animation to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
|
||||
after entities parsing.
|
||||
@@ -176,9 +186,12 @@ class InputMediaAnimation(InputMedia):
|
||||
spoiler animation.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "height", "thumb", "width", "has_spoiler")
|
||||
__slots__ = ("duration", "height", "width", "has_spoiler", "thumbnail")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -192,6 +205,7 @@ class InputMediaAnimation(InputMedia):
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
filename: str = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -205,6 +219,7 @@ class InputMediaAnimation(InputMedia):
|
||||
# things to work in local mode.
|
||||
media = parse_file_input(media, filename=filename, attach=True, local_mode=True)
|
||||
|
||||
thumbnail = warn_about_thumb_return_thumbnail(deprecated_arg=thumb, new_arg=thumbnail)
|
||||
super().__init__(
|
||||
InputMediaType.ANIMATION,
|
||||
media,
|
||||
@@ -214,12 +229,26 @@ class InputMediaAnimation(InputMedia):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
self.thumb: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumb)
|
||||
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumbnail)
|
||||
self.width: Optional[int] = width
|
||||
self.height: Optional[int] = height
|
||||
self.duration: Optional[int] = duration
|
||||
self.has_spoiler: Optional[bool] = has_spoiler
|
||||
|
||||
@property
|
||||
def thumb(self) -> Optional[Union[str, InputFile]]:
|
||||
""":class:`telegram.InputFile`: Optional. |thumbdocstringbase|
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb",
|
||||
new_attr_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail
|
||||
|
||||
|
||||
class InputMediaPhoto(InputMedia):
|
||||
"""Represents a photo to be sent.
|
||||
@@ -343,10 +372,17 @@ class InputMediaVideo(InputMedia):
|
||||
|
||||
.. versionchanged:: 13.2
|
||||
Accept :obj:`bytes` as input.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
has_spoiler (:obj:`bool`, optional): Pass :obj:`True`, if the video needs to be covered
|
||||
with a spoiler animation.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): |thumbdocstringnopath|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.VIDEO`.
|
||||
@@ -366,14 +402,23 @@ class InputMediaVideo(InputMedia):
|
||||
duration (:obj:`int`): Optional. Video duration in seconds.
|
||||
supports_streaming (:obj:`bool`): Optional. :obj:`True`, if the uploaded video is
|
||||
suitable for streaming.
|
||||
thumb (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
has_spoiler (:obj:`bool`): Optional. :obj:`True`, if the video is covered with a
|
||||
spoiler animation.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "height", "thumb", "supports_streaming", "width", "has_spoiler")
|
||||
__slots__ = (
|
||||
"duration",
|
||||
"height",
|
||||
"supports_streaming",
|
||||
"width",
|
||||
"has_spoiler",
|
||||
"thumbnail",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -388,6 +433,7 @@ class InputMediaVideo(InputMedia):
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
filename: str = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -401,6 +447,7 @@ class InputMediaVideo(InputMedia):
|
||||
# things to work in local mode.
|
||||
media = parse_file_input(media, filename=filename, attach=True, local_mode=True)
|
||||
|
||||
thumbnail = warn_about_thumb_return_thumbnail(deprecated_arg=thumb, new_arg=thumbnail)
|
||||
super().__init__(
|
||||
InputMediaType.VIDEO,
|
||||
media,
|
||||
@@ -413,10 +460,24 @@ class InputMediaVideo(InputMedia):
|
||||
self.width: Optional[int] = width
|
||||
self.height: Optional[int] = height
|
||||
self.duration: Optional[int] = duration
|
||||
self.thumb: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumb)
|
||||
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumbnail)
|
||||
self.supports_streaming: Optional[bool] = supports_streaming
|
||||
self.has_spoiler: Optional[bool] = has_spoiler
|
||||
|
||||
@property
|
||||
def thumb(self) -> Optional[Union[str, InputFile]]:
|
||||
""":class:`telegram.InputFile`: Optional. |thumbdocstringbase|
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb",
|
||||
new_attr_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail
|
||||
|
||||
|
||||
class InputMediaAudio(InputMedia):
|
||||
"""Represents an audio file to be treated as music to be sent.
|
||||
@@ -459,6 +520,13 @@ class InputMediaAudio(InputMedia):
|
||||
.. versionchanged:: 13.2
|
||||
Accept :obj:`bytes` as input.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): |thumbdocstringnopath|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.AUDIO`.
|
||||
media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send.
|
||||
@@ -476,11 +544,13 @@ class InputMediaAudio(InputMedia):
|
||||
performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio
|
||||
tags.
|
||||
title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags.
|
||||
thumb (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "performer", "thumb", "title")
|
||||
__slots__ = ("duration", "performer", "title", "thumbnail")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -493,6 +563,7 @@ class InputMediaAudio(InputMedia):
|
||||
title: str = None,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
filename: str = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -506,6 +577,7 @@ class InputMediaAudio(InputMedia):
|
||||
# things to work in local mode.
|
||||
media = parse_file_input(media, filename=filename, attach=True, local_mode=True)
|
||||
|
||||
thumbnail = warn_about_thumb_return_thumbnail(deprecated_arg=thumb, new_arg=thumbnail)
|
||||
super().__init__(
|
||||
InputMediaType.AUDIO,
|
||||
media,
|
||||
@@ -515,11 +587,25 @@ class InputMediaAudio(InputMedia):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
self.thumb: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumb)
|
||||
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumbnail)
|
||||
self.duration: Optional[int] = duration
|
||||
self.title: Optional[str] = title
|
||||
self.performer: Optional[str] = performer
|
||||
|
||||
@property
|
||||
def thumb(self) -> Optional[Union[str, InputFile]]:
|
||||
""":class:`telegram.InputFile`: Optional. |thumbdocstringbase|
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb",
|
||||
new_attr_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail
|
||||
|
||||
|
||||
class InputMediaDocument(InputMedia):
|
||||
"""Represents a general file to be sent.
|
||||
@@ -552,9 +638,16 @@ class InputMediaDocument(InputMedia):
|
||||
|
||||
.. versionchanged:: 13.2
|
||||
Accept :obj:`bytes` as input.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
disable_content_type_detection (:obj:`bool`, optional): Disables automatic server-side
|
||||
content type detection for files uploaded using multipart/form-data. Always
|
||||
:obj:`True`, if the document is sent as part of an album.
|
||||
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): |thumbdocstringnopath|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.DOCUMENT`.
|
||||
@@ -569,14 +662,15 @@ class InputMediaDocument(InputMedia):
|
||||
|
||||
* |tupleclassattrs|
|
||||
* |alwaystuple|
|
||||
thumb (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
disable_content_type_detection (:obj:`bool`): Optional. Disables automatic server-side
|
||||
content type detection for files uploaded using multipart/form-data. Always
|
||||
:obj:`True`, if the document is sent as part of an album.
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ("disable_content_type_detection", "thumb")
|
||||
__slots__ = ("disable_content_type_detection", "thumbnail")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -587,12 +681,15 @@ class InputMediaDocument(InputMedia):
|
||||
disable_content_type_detection: bool = None,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
filename: str = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
# We use local_mode=True because we don't have access to the actual setting and want
|
||||
# things to work in local mode.
|
||||
media = parse_file_input(media, Document, filename=filename, attach=True, local_mode=True)
|
||||
|
||||
thumbnail = warn_about_thumb_return_thumbnail(deprecated_arg=thumb, new_arg=thumbnail)
|
||||
super().__init__(
|
||||
InputMediaType.DOCUMENT,
|
||||
media,
|
||||
@@ -602,5 +699,19 @@ class InputMediaDocument(InputMedia):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
self.thumb: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumb)
|
||||
self.thumbnail: Optional[Union[str, InputFile]] = self._parse_thumb_input(thumbnail)
|
||||
self.disable_content_type_detection: Optional[bool] = disable_content_type_detection
|
||||
|
||||
@property
|
||||
def thumb(self) -> Optional[Union[str, InputFile]]:
|
||||
""":class:`telegram.InputFile`: Optional. |thumbdocstringbase|
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb",
|
||||
new_attr_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram InputSticker."""
|
||||
|
||||
from typing import Optional, Sequence, Tuple, Union
|
||||
|
||||
from telegram._files.inputfile import InputFile
|
||||
from telegram._files.sticker import MaskPosition
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.files import parse_file_input
|
||||
from telegram._utils.types import FileInput, JSONDict
|
||||
|
||||
|
||||
class InputSticker(TelegramObject):
|
||||
"""
|
||||
This object describes a sticker to be added to a sticker set.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Args:
|
||||
sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`): The
|
||||
added sticker. |uploadinputnopath| Animated and video stickers can't be uploaded via
|
||||
HTTP URL.
|
||||
emoji_list (Sequence[:obj:`str`]): Sequence of
|
||||
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
|
||||
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
|
||||
sticker.
|
||||
mask_position (:obj:`telegram.MaskPosition`, optional): Position where the mask should be
|
||||
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
|
||||
keywords (Sequence[:obj:`str`], optional): Sequence of
|
||||
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
|
||||
for the sticker with the total length of up to
|
||||
:tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. For
|
||||
":tg-const:`telegram.constants.StickerType.REGULAR`" and
|
||||
":tg-const:`telegram.constants.StickerType.CUSTOM_EMOJI`" stickers only.
|
||||
|
||||
Attributes:
|
||||
sticker (:obj:`str` | :class:`telegram.InputFile`): The added sticker.
|
||||
emoji_list (Tuple[:obj:`str`]): Tuple of
|
||||
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
|
||||
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
|
||||
sticker.
|
||||
mask_position (:obj:`telegram.MaskPosition`): Optional. Position where the mask should be
|
||||
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
|
||||
keywords (Tuple[:obj:`str`]): Optional. Tuple of
|
||||
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
|
||||
for the sticker with the total length of up to
|
||||
:tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. For
|
||||
":tg-const:`telegram.constants.StickerType.REGULAR`" and
|
||||
":tg-const:`telegram.constants.StickerType.CUSTOM_EMOJI`" stickers only.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("sticker", "emoji_list", "mask_position", "keywords")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sticker: FileInput,
|
||||
emoji_list: Sequence[str],
|
||||
mask_position: MaskPosition = None,
|
||||
keywords: Sequence[str] = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
# We use local_mode=True because we don't have access to the actual setting and want
|
||||
# things to work in local mode.
|
||||
self.sticker: Union[str, InputFile] = parse_file_input(
|
||||
sticker,
|
||||
local_mode=True,
|
||||
attach=True,
|
||||
)
|
||||
self.emoji_list: Tuple[str, ...] = parse_sequence_arg(emoji_list)
|
||||
self.mask_position: Optional[MaskPosition] = mask_position
|
||||
self.keywords: Tuple[str, ...] = parse_sequence_arg(keywords)
|
||||
|
||||
self._freeze()
|
||||
+73
-12
@@ -26,6 +26,10 @@ from telegram._files.photosize import PhotoSize
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_attr_in_property,
|
||||
warn_about_thumb_return_thumbnail,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
@@ -61,6 +65,9 @@ class Sticker(_BaseThumbedMedium):
|
||||
.. versionadded:: 20.0
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the ``.WEBP`` or
|
||||
``.JPG`` format.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
emoji (:obj:`str`, optional): Emoji associated with the sticker
|
||||
set_name (:obj:`str`, optional): Name of the sticker set to which the sticker belongs.
|
||||
mask_position (:class:`telegram.MaskPosition`, optional): For mask stickers, the position
|
||||
@@ -75,6 +82,15 @@ class Sticker(_BaseThumbedMedium):
|
||||
custom emoji.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the ``.WEBP`` or
|
||||
``.JPG`` format.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
needs_repainting (:obj:`bool`, optional): :obj:`True`, if the sticker must be repainted to
|
||||
a text color in messages, the color of the Telegram Premium badge in emoji status,
|
||||
white color on chat photos, or another appropriate color in other places.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
@@ -93,8 +109,6 @@ class Sticker(_BaseThumbedMedium):
|
||||
format, which is determined by the fields :attr:`is_animated` and :attr:`is_video`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the ``.WEBP`` or
|
||||
``.JPG`` format.
|
||||
emoji (:obj:`str`): Optional. Emoji associated with the sticker.
|
||||
set_name (:obj:`str`): Optional. Name of the sticker set to which the sticker belongs.
|
||||
mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position
|
||||
@@ -109,6 +123,15 @@ class Sticker(_BaseThumbedMedium):
|
||||
custom emoji.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the ``.WEBP`` or
|
||||
``.JPG`` format.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
needs_repainting (:obj:`bool`): Optional. :obj:`True`, if the sticker must be repainted to
|
||||
a text color in messages, the color of the Telegram Premium badge in emoji status,
|
||||
white color on chat photos, or another appropriate color in other places.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
@@ -122,6 +145,7 @@ class Sticker(_BaseThumbedMedium):
|
||||
"premium_animation",
|
||||
"type",
|
||||
"custom_emoji_id",
|
||||
"needs_repainting",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -140,6 +164,8 @@ class Sticker(_BaseThumbedMedium):
|
||||
mask_position: "MaskPosition" = None,
|
||||
premium_animation: "File" = None,
|
||||
custom_emoji_id: str = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
needs_repainting: bool = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -148,6 +174,7 @@ class Sticker(_BaseThumbedMedium):
|
||||
file_unique_id=file_unique_id,
|
||||
file_size=file_size,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
@@ -163,6 +190,7 @@ class Sticker(_BaseThumbedMedium):
|
||||
self.mask_position: Optional[MaskPosition] = mask_position
|
||||
self.premium_animation: Optional[File] = premium_animation
|
||||
self.custom_emoji_id: Optional[str] = custom_emoji_id
|
||||
self.needs_repainting: Optional[bool] = needs_repainting
|
||||
|
||||
REGULAR: ClassVar[str] = constants.StickerType.REGULAR
|
||||
""":const:`telegram.constants.StickerType.REGULAR`"""
|
||||
@@ -179,11 +207,17 @@ class Sticker(_BaseThumbedMedium):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot)
|
||||
data["thumbnail"] = PhotoSize.de_json(data.get("thumbnail"), bot)
|
||||
data["mask_position"] = MaskPosition.de_json(data.get("mask_position"), bot)
|
||||
data["premium_animation"] = File.de_json(data.get("premium_animation"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
# Let's filter it out to speed up the de-json process
|
||||
if data.get("thumb") is not None:
|
||||
api_kwargs["thumb"] = data.pop("thumb")
|
||||
|
||||
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
|
||||
|
||||
|
||||
class StickerSet(TelegramObject):
|
||||
@@ -220,6 +254,13 @@ class StickerSet(TelegramObject):
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the ``.WEBP``,
|
||||
``.TGS``, or ``.WEBM`` format.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the ``.WEBP``,
|
||||
``.TGS``, or ``.WEBM`` format.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
name (:obj:`str`): Sticker set name.
|
||||
title (:obj:`str`): Sticker set title.
|
||||
@@ -237,9 +278,10 @@ class StickerSet(TelegramObject):
|
||||
:attr:`telegram.Sticker.CUSTOM_EMOJI`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the ``.WEBP``,
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the ``.WEBP``,
|
||||
``.TGS``, or ``.WEBM`` format.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
@@ -247,7 +289,7 @@ class StickerSet(TelegramObject):
|
||||
"is_video",
|
||||
"name",
|
||||
"stickers",
|
||||
"thumb",
|
||||
"thumbnail",
|
||||
"title",
|
||||
"sticker_type",
|
||||
)
|
||||
@@ -261,6 +303,7 @@ class StickerSet(TelegramObject):
|
||||
is_video: bool,
|
||||
sticker_type: str,
|
||||
thumb: PhotoSize = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -272,26 +315,44 @@ class StickerSet(TelegramObject):
|
||||
self.stickers: Tuple[Sticker, ...] = parse_sequence_arg(stickers)
|
||||
self.sticker_type: str = sticker_type
|
||||
# Optional
|
||||
self.thumb: Optional[PhotoSize] = thumb
|
||||
|
||||
self.thumbnail: Optional[PhotoSize] = warn_about_thumb_return_thumbnail(
|
||||
deprecated_arg=thumb, new_arg=thumbnail
|
||||
)
|
||||
self._id_attrs = (self.name,)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@property
|
||||
def thumb(self) -> Optional[PhotoSize]:
|
||||
""":class:`telegram.PhotoSize`: Optional. Sticker set thumbnail in the ``.WEBP``,
|
||||
``.TGS``, or ``.WEBM`` format.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb",
|
||||
new_attr_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["StickerSet"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot)
|
||||
data["thumbnail"] = PhotoSize.de_json(data.get("thumbnail"), bot)
|
||||
data["stickers"] = Sticker.de_list(data.get("stickers"), bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
# Let's filter it out to speed up the de-json process
|
||||
if "contains_masks" in data:
|
||||
api_kwargs["contains_masks"] = data.pop("contains_masks")
|
||||
# These are deprecated fields that TG still returns for backwards compatibility
|
||||
# Let's filter them out to speed up the de-json process
|
||||
for deprecated_field in ("contains_masks", "thumb"):
|
||||
if deprecated_field in data:
|
||||
api_kwargs[deprecated_field] = data.pop(deprecated_field)
|
||||
|
||||
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
|
||||
|
||||
|
||||
@@ -40,9 +40,15 @@ class Video(_BaseThumbedMedium):
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of a file as defined by sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
@@ -53,11 +59,12 @@ class Video(_BaseThumbedMedium):
|
||||
width (:obj:`int`): Video width as defined by sender.
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of a file as defined by sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "file_name", "height", "mime_type", "width")
|
||||
@@ -73,6 +80,7 @@ class Video(_BaseThumbedMedium):
|
||||
mime_type: str = None,
|
||||
file_size: int = None,
|
||||
file_name: str = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -81,6 +89,7 @@ class Video(_BaseThumbedMedium):
|
||||
file_unique_id=file_unique_id,
|
||||
file_size=file_size,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
|
||||
@@ -39,7 +39,13 @@ class VideoNote(_BaseThumbedMedium):
|
||||
by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail`.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
@@ -50,8 +56,10 @@ class VideoNote(_BaseThumbedMedium):
|
||||
length (:obj:`int`): Video width and height (diameter of the video message) as defined
|
||||
by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
@@ -65,6 +73,7 @@ class VideoNote(_BaseThumbedMedium):
|
||||
duration: int,
|
||||
thumb: PhotoSize = None,
|
||||
file_size: int = None,
|
||||
thumbnail: PhotoSize = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -73,6 +82,7 @@ class VideoNote(_BaseThumbedMedium):
|
||||
file_unique_id=file_unique_id,
|
||||
file_size=file_size,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
with self._unfrozen():
|
||||
|
||||
@@ -23,6 +23,10 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from telegram._inline.inlinequeryresult import InlineQueryResult
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -49,9 +53,27 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
in the message.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
thumb_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_width`.
|
||||
thumb_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_height`.
|
||||
thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.ARTICLE`.
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
@@ -66,22 +88,28 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
hide_url (:obj:`bool`): Optional. Pass :obj:`True`, if you don't want the URL to be shown
|
||||
in the message.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
thumb_width (:obj:`int`): Optional. Thumbnail width.
|
||||
thumb_height (:obj:`int`): Optional. Thumbnail height.
|
||||
thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`): Optional. Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`): Optional. Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"reply_markup",
|
||||
"thumb_width",
|
||||
"thumb_height",
|
||||
"hide_url",
|
||||
"url",
|
||||
"title",
|
||||
"description",
|
||||
"input_message_content",
|
||||
"thumb_url",
|
||||
"thumbnail_width",
|
||||
"thumbnail_height",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -96,6 +124,9 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
thumb_url: str = None,
|
||||
thumb_width: int = None,
|
||||
thumb_height: int = None,
|
||||
thumbnail_url: str = None,
|
||||
thumbnail_width: int = None,
|
||||
thumbnail_height: int = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -110,6 +141,66 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
self.url: Optional[str] = url
|
||||
self.hide_url: Optional[bool] = hide_url
|
||||
self.description: Optional[str] = description
|
||||
self.thumb_url: Optional[str] = thumb_url
|
||||
self.thumb_width: Optional[int] = thumb_width
|
||||
self.thumb_height: Optional[int] = thumb_height
|
||||
self.thumbnail_url: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_width: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_width,
|
||||
new_arg=thumbnail_width,
|
||||
deprecated_arg_name="thumb_width",
|
||||
new_arg_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_height: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_height,
|
||||
new_arg=thumbnail_height,
|
||||
deprecated_arg_name="thumb_height",
|
||||
new_arg_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_width(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_width`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_width",
|
||||
new_attr_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_width
|
||||
|
||||
@property
|
||||
def thumb_height(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_height`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_height",
|
||||
new_attr_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_height
|
||||
|
||||
@@ -23,6 +23,10 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from telegram._inline.inlinequeryresult import InlineQueryResult
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -49,9 +53,27 @@ class InlineQueryResultContact(InlineQueryResult):
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the contact.
|
||||
thumb_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_width`.
|
||||
thumb_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_height`.
|
||||
thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.CONTACT`.
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
@@ -66,22 +88,28 @@ class InlineQueryResultContact(InlineQueryResult):
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the contact.
|
||||
thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
thumb_width (:obj:`int`): Optional. Thumbnail width.
|
||||
thumb_height (:obj:`int`): Optional. Thumbnail height.
|
||||
thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`): Optional. Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`): Optional. Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"reply_markup",
|
||||
"thumb_width",
|
||||
"thumb_height",
|
||||
"thumbnail_width",
|
||||
"thumbnail_height",
|
||||
"vcard",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"phone_number",
|
||||
"input_message_content",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -96,6 +124,9 @@ class InlineQueryResultContact(InlineQueryResult):
|
||||
thumb_width: int = None,
|
||||
thumb_height: int = None,
|
||||
vcard: str = None,
|
||||
thumbnail_url: str = None,
|
||||
thumbnail_width: int = None,
|
||||
thumbnail_height: int = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -110,6 +141,66 @@ class InlineQueryResultContact(InlineQueryResult):
|
||||
self.vcard: Optional[str] = vcard
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
self.thumb_url: Optional[str] = thumb_url
|
||||
self.thumb_width: Optional[int] = thumb_width
|
||||
self.thumb_height: Optional[int] = thumb_height
|
||||
self.thumbnail_url: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_width: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_width,
|
||||
new_arg=thumbnail_width,
|
||||
deprecated_arg_name="thumb_width",
|
||||
new_arg_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_height: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_height,
|
||||
new_arg=thumbnail_height,
|
||||
deprecated_arg_name="thumb_height",
|
||||
new_arg_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_width(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_width`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_width",
|
||||
new_attr_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_width
|
||||
|
||||
@property
|
||||
def thumb_height(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_height`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_height",
|
||||
new_attr_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_height
|
||||
|
||||
@@ -25,6 +25,10 @@ from telegram._messageentity import MessageEntity
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -63,9 +67,28 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the file.
|
||||
thumb_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the file.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_width`.
|
||||
thumb_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_height`.
|
||||
thumbnail_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the file.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.DOCUMENT`.
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
@@ -90,9 +113,15 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the file.
|
||||
thumb_url (:obj:`str`): Optional. URL of the thumbnail (JPEG only) for the file.
|
||||
thumb_width (:obj:`int`): Optional. Thumbnail width.
|
||||
thumb_height (:obj:`int`): Optional. Thumbnail height.
|
||||
thumbnail_url (:obj:`str`): Optional. URL of the thumbnail (JPEG only) for the file.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`): Optional. Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`): Optional. Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
@@ -100,14 +129,14 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
"reply_markup",
|
||||
"caption_entities",
|
||||
"document_url",
|
||||
"thumb_width",
|
||||
"thumb_height",
|
||||
"thumbnail_width",
|
||||
"thumbnail_height",
|
||||
"caption",
|
||||
"title",
|
||||
"description",
|
||||
"parse_mode",
|
||||
"mime_type",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
"input_message_content",
|
||||
)
|
||||
|
||||
@@ -126,6 +155,9 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
thumb_height: int = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
thumbnail_url: str = None,
|
||||
thumbnail_width: int = None,
|
||||
thumbnail_height: int = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -143,6 +175,66 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
self.description: Optional[str] = description
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
self.thumb_url: Optional[str] = thumb_url
|
||||
self.thumb_width: Optional[int] = thumb_width
|
||||
self.thumb_height: Optional[int] = thumb_height
|
||||
self.thumbnail_url: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_width: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_width,
|
||||
new_arg=thumbnail_width,
|
||||
deprecated_arg_name="thumb_width",
|
||||
new_arg_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_height: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_height,
|
||||
new_arg=thumbnail_height,
|
||||
deprecated_arg_name="thumb_height",
|
||||
new_arg_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. URL of the thumbnail (JPEG only) for the file.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_width(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_width`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_width",
|
||||
new_attr_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_width
|
||||
|
||||
@property
|
||||
def thumb_height(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_height`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_height",
|
||||
new_attr_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_height
|
||||
|
||||
@@ -25,6 +25,10 @@ from telegram._messageentity import MessageEntity
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -47,10 +51,20 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
gif_width (:obj:`int`, optional): Width of the GIF.
|
||||
gif_height (:obj:`int`, optional): Height of the GIF.
|
||||
gif_duration (:obj:`int`, optional): Duration of the GIF in seconds.
|
||||
thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for
|
||||
the result.
|
||||
thumb_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of
|
||||
thumbnail_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4)
|
||||
thumbnail for the result.
|
||||
|
||||
Warning:
|
||||
The Bot API does **not** define this as an optional argument. It is formally
|
||||
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
|
||||
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
|
||||
:class:`ValueError` will be raised.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption of the GIF file to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
|
||||
@@ -65,6 +79,20 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the GIF animation.
|
||||
thumb_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_mime_type`.
|
||||
thumb_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4)
|
||||
thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
|
||||
Raises:
|
||||
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
|
||||
supplied or if both are supplied and are not equal.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`.
|
||||
@@ -75,10 +103,14 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
gif_width (:obj:`int`): Optional. Width of the GIF.
|
||||
gif_height (:obj:`int`): Optional. Height of the GIF.
|
||||
gif_duration (:obj:`int`): Optional. Duration of the GIF in seconds.
|
||||
thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for
|
||||
the result.
|
||||
thumb_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of
|
||||
thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail
|
||||
for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption of the GIF file to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
|
||||
@@ -100,7 +132,7 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
__slots__ = (
|
||||
"reply_markup",
|
||||
"gif_height",
|
||||
"thumb_mime_type",
|
||||
"thumbnail_mime_type",
|
||||
"caption_entities",
|
||||
"gif_width",
|
||||
"title",
|
||||
@@ -109,14 +141,17 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
"gif_duration",
|
||||
"input_message_content",
|
||||
"gif_url",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str, # pylint: disable=redefined-builtin
|
||||
gif_url: str,
|
||||
thumb_url: str,
|
||||
# thumbnail_url is not optional in Telegram API, but we want to support thumb_url as well,
|
||||
# so thumbnail_url may not be passed. We will raise ValueError manually if neither
|
||||
# thumbnail_url nor thumb_url are passed
|
||||
thumbnail_url: str = None,
|
||||
gif_width: int = None,
|
||||
gif_height: int = None,
|
||||
title: str = None,
|
||||
@@ -127,14 +162,29 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
thumb_mime_type: str = None,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
thumbnail_mime_type: str = None,
|
||||
# thumb_url is not optional in Telegram API, but it is here, along with thumbnail_url.
|
||||
thumb_url: str = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
if not (thumbnail_url or thumb_url):
|
||||
raise ValueError(
|
||||
"You must pass either 'thumbnail_url' or 'thumb_url'. Note that 'thumb_url' is "
|
||||
"deprecated."
|
||||
)
|
||||
|
||||
# Required
|
||||
super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.gif_url: str = gif_url
|
||||
self.thumb_url: str = thumb_url
|
||||
self.thumbnail_url: str = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
# Optionals
|
||||
self.gif_width: Optional[int] = gif_width
|
||||
@@ -146,4 +196,40 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
self.thumb_mime_type: Optional[str] = thumb_mime_type
|
||||
self.thumbnail_mime_type: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_mime_type,
|
||||
new_arg=thumbnail_mime_type,
|
||||
deprecated_arg_name="thumb_mime_type",
|
||||
new_arg_name="thumbnail_mime_type",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> str:
|
||||
""":obj:`str`: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the
|
||||
result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_mime_type(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Optional. MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_mime_type`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_mime_type",
|
||||
new_attr_name="thumbnail_mime_type",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_mime_type
|
||||
|
||||
@@ -24,6 +24,10 @@ from telegram import constants
|
||||
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from telegram._inline.inlinequeryresult import InlineQueryResult
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import InputMessageContent
|
||||
@@ -63,9 +67,27 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the location.
|
||||
thumb_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_width`.
|
||||
thumb_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_height`.
|
||||
thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.LOCATION`.
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
@@ -94,17 +116,23 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the location.
|
||||
thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
thumb_width (:obj:`int`): Optional. Thumbnail width.
|
||||
thumb_height (:obj:`int`): Optional. Thumbnail height.
|
||||
thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`): Optional. Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`): Optional. Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"longitude",
|
||||
"reply_markup",
|
||||
"thumb_width",
|
||||
"thumb_height",
|
||||
"thumbnail_width",
|
||||
"thumbnail_height",
|
||||
"heading",
|
||||
"title",
|
||||
"live_period",
|
||||
@@ -112,7 +140,7 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
"input_message_content",
|
||||
"latitude",
|
||||
"horizontal_accuracy",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -130,6 +158,9 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
horizontal_accuracy: float = None,
|
||||
heading: int = None,
|
||||
proximity_alert_radius: int = None,
|
||||
thumbnail_url: str = None,
|
||||
thumbnail_width: int = None,
|
||||
thumbnail_height: int = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -144,15 +175,75 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
self.live_period: Optional[int] = live_period
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
self.thumb_url: Optional[str] = thumb_url
|
||||
self.thumb_width: Optional[int] = thumb_width
|
||||
self.thumb_height: Optional[int] = thumb_height
|
||||
self.thumbnail_url: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_width: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_width,
|
||||
new_arg=thumbnail_width,
|
||||
deprecated_arg_name="thumb_width",
|
||||
new_arg_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_height: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_height,
|
||||
new_arg=thumbnail_height,
|
||||
deprecated_arg_name="thumb_height",
|
||||
new_arg_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.horizontal_accuracy: Optional[float] = horizontal_accuracy
|
||||
self.heading: Optional[int] = heading
|
||||
self.proximity_alert_radius: Optional[int] = (
|
||||
int(proximity_alert_radius) if proximity_alert_radius else None
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_width(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_width`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_width",
|
||||
new_attr_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_width
|
||||
|
||||
@property
|
||||
def thumb_height(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_height`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_height",
|
||||
new_attr_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_height
|
||||
|
||||
HORIZONTAL_ACCURACY: ClassVar[int] = constants.LocationLimit.HORIZONTAL_ACCURACY
|
||||
""":const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY`
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ from telegram._messageentity import MessageEntity
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -48,10 +52,20 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
mpeg4_width (:obj:`int`, optional): Video width.
|
||||
mpeg4_height (:obj:`int`, optional): Video height.
|
||||
mpeg4_duration (:obj:`int`, optional): Video duration in seconds.
|
||||
thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for
|
||||
the result.
|
||||
thumb_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of
|
||||
thumbnail_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4)
|
||||
thumbnail for the result.
|
||||
|
||||
Warning:
|
||||
The Bot API does **not** define this as an optional argument. It is formally
|
||||
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
|
||||
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
|
||||
:class:`ValueError` will be raised.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
|
||||
@@ -68,6 +82,10 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the video animation.
|
||||
|
||||
Raises:
|
||||
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
|
||||
supplied or if both are supplied and are not equal.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`.
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
@@ -77,10 +95,14 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
mpeg4_width (:obj:`int`): Optional. Video width.
|
||||
mpeg4_height (:obj:`int`): Optional. Video height.
|
||||
mpeg4_duration (:obj:`int`): Optional. Video duration in seconds.
|
||||
thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for
|
||||
the result.
|
||||
thumb_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of
|
||||
thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail
|
||||
for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
|
||||
@@ -102,7 +124,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
|
||||
__slots__ = (
|
||||
"reply_markup",
|
||||
"thumb_mime_type",
|
||||
"thumbnail_mime_type",
|
||||
"caption_entities",
|
||||
"mpeg4_duration",
|
||||
"mpeg4_width",
|
||||
@@ -112,14 +134,17 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
"input_message_content",
|
||||
"mpeg4_url",
|
||||
"mpeg4_height",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str, # pylint: disable=redefined-builtin
|
||||
mpeg4_url: str,
|
||||
thumb_url: str,
|
||||
# thumbnail_url is not optional in Telegram API, but we want to support thumb_url as well,
|
||||
# so thumbnail_url may not be passed. We will raise ValueError manually if neither
|
||||
# thumbnail_url nor thumb_url are passed
|
||||
thumbnail_url: str = None,
|
||||
mpeg4_width: int = None,
|
||||
mpeg4_height: int = None,
|
||||
title: str = None,
|
||||
@@ -130,14 +155,29 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
thumb_mime_type: str = None,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
thumbnail_mime_type: str = None,
|
||||
# thumb_url is not optional in Telegram API, but it is here, along with thumbnail_url.
|
||||
thumb_url: str = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
if not (thumbnail_url or thumb_url):
|
||||
raise ValueError(
|
||||
"You must pass either 'thumbnail_url' or 'thumb_url'. Note that 'thumb_url' is "
|
||||
"deprecated."
|
||||
)
|
||||
|
||||
# Required
|
||||
super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.mpeg4_url: str = mpeg4_url
|
||||
self.thumb_url: str = thumb_url
|
||||
self.thumbnail_url: str = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
# Optional
|
||||
self.mpeg4_width: Optional[int] = mpeg4_width
|
||||
@@ -149,4 +189,40 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
self.thumb_mime_type: Optional[str] = thumb_mime_type
|
||||
self.thumbnail_mime_type: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_mime_type,
|
||||
new_arg=thumbnail_mime_type,
|
||||
deprecated_arg_name="thumb_mime_type",
|
||||
new_arg_name="thumbnail_mime_type",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> str:
|
||||
""":obj:`str`: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the
|
||||
result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_mime_type(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Optional. MIME type of the thumbnail, must be one of
|
||||
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_mime_type`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_mime_type",
|
||||
new_attr_name="thumbnail_mime_type",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_mime_type
|
||||
|
||||
@@ -25,6 +25,10 @@ from telegram._messageentity import MessageEntity
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -45,7 +49,15 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size
|
||||
must not exceed 5MB.
|
||||
thumb_url (:obj:`str`): URL of the thumbnail for the photo.
|
||||
thumbnail_url (:obj:`str`, optional): URL of the thumbnail for the photo.
|
||||
|
||||
Warning:
|
||||
The Bot API does **not** define this as an optional argument. It is formally
|
||||
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
|
||||
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
|
||||
:class:`ValueError` will be raised.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
photo_width (:obj:`int`, optional): Width of the photo.
|
||||
photo_height (:obj:`int`, optional): Height of the photo.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
@@ -63,6 +75,14 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the photo.
|
||||
thumb_url (:obj:`str`, optional): URL of the thumbnail for the photo.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
|
||||
Raises:
|
||||
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
|
||||
supplied or if both are supplied and are not equal.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`.
|
||||
@@ -71,7 +91,7 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size
|
||||
must not exceed 5MB.
|
||||
thumb_url (:obj:`str`): URL of the thumbnail for the photo.
|
||||
thumbnail_url (:obj:`str`): URL of the thumbnail for the photo.
|
||||
photo_width (:obj:`int`): Optional. Width of the photo.
|
||||
photo_height (:obj:`int`): Optional. Height of the photo.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
@@ -104,14 +124,17 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
"parse_mode",
|
||||
"input_message_content",
|
||||
"photo_height",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str, # pylint: disable=redefined-builtin
|
||||
photo_url: str,
|
||||
thumb_url: str,
|
||||
# thumbnail_url is not optional in Telegram API, but we want to support thumb_url as well,
|
||||
# so thumbnail_url may not be passed. We will raise ValueError manually if neither
|
||||
# thumbnail_url nor thumb_url are passed
|
||||
thumbnail_url: str = None,
|
||||
photo_width: int = None,
|
||||
photo_height: int = None,
|
||||
title: str = None,
|
||||
@@ -121,14 +144,28 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
input_message_content: "InputMessageContent" = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
# thumb_url is not optional in Telegram API, but it is here, along with thumbnail_url.
|
||||
thumb_url: str = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
if not (thumbnail_url or thumb_url):
|
||||
raise ValueError(
|
||||
"You must pass either 'thumbnail_url' or 'thumb_url'. Note that 'thumb_url' is "
|
||||
"deprecated."
|
||||
)
|
||||
|
||||
# Required
|
||||
super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.photo_url: str = photo_url
|
||||
self.thumb_url: str = thumb_url
|
||||
self.thumbnail_url: str = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
# Optionals
|
||||
self.photo_width: Optional[int] = photo_width
|
||||
@@ -140,3 +177,17 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> Optional[str]:
|
||||
""":obj:`str`: URL of the thumbnail for the photo.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@@ -23,6 +23,10 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from telegram._inline.inlinequeryresult import InlineQueryResult
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -60,9 +64,27 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the venue.
|
||||
thumb_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_width`.
|
||||
thumb_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_height`.
|
||||
thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`, optional): Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VENUE`.
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
@@ -84,9 +106,15 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the venue.
|
||||
thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
thumb_width (:obj:`int`): Optional. Thumbnail width.
|
||||
thumb_height (:obj:`int`): Optional. Thumbnail height.
|
||||
thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_width (:obj:`int`): Optional. Thumbnail width.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
thumbnail_height (:obj:`int`): Optional. Thumbnail height.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
"""
|
||||
|
||||
@@ -94,8 +122,8 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
"longitude",
|
||||
"reply_markup",
|
||||
"google_place_type",
|
||||
"thumb_width",
|
||||
"thumb_height",
|
||||
"thumbnail_width",
|
||||
"thumbnail_height",
|
||||
"title",
|
||||
"address",
|
||||
"foursquare_id",
|
||||
@@ -103,7 +131,7 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
"google_place_id",
|
||||
"input_message_content",
|
||||
"latitude",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -122,6 +150,9 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
thumb_height: int = None,
|
||||
google_place_id: str = None,
|
||||
google_place_type: str = None,
|
||||
thumbnail_url: str = None,
|
||||
thumbnail_width: int = None,
|
||||
thumbnail_height: int = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
@@ -140,6 +171,66 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
self.google_place_type: Optional[str] = google_place_type
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
self.thumb_url: Optional[str] = thumb_url
|
||||
self.thumb_width: Optional[int] = thumb_width
|
||||
self.thumb_height: Optional[int] = thumb_height
|
||||
self.thumbnail_url: Optional[str] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_width: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_width,
|
||||
new_arg=thumbnail_width,
|
||||
deprecated_arg_name="thumb_width",
|
||||
new_arg_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.thumbnail_height: Optional[int] = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_height,
|
||||
new_arg=thumbnail_height,
|
||||
deprecated_arg_name="thumb_height",
|
||||
new_arg_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Url of the thumbnail for the result.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@property
|
||||
def thumb_width(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail width.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_width`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_width",
|
||||
new_attr_name="thumbnail_width",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_width
|
||||
|
||||
@property
|
||||
def thumb_height(self) -> Optional[int]:
|
||||
""":obj:`str`: Optional. Thumbnail height.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_height`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_height",
|
||||
new_attr_name="thumbnail_height",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_height
|
||||
|
||||
@@ -25,6 +25,10 @@ from telegram._messageentity import MessageEntity
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings_transition import (
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
warn_about_deprecated_attr_in_property,
|
||||
)
|
||||
from telegram.constants import InlineQueryResultType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -50,8 +54,22 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
video_url (:obj:`str`): A valid URL for the embedded video player or video file.
|
||||
mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4".
|
||||
thumb_url (:obj:`str`): URL of the thumbnail (JPEG only) for the video.
|
||||
title (:obj:`str`): Title for the result.
|
||||
thumbnail_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the video.
|
||||
|
||||
Warning:
|
||||
The Bot API does **not** define this as an optional argument. It is formally
|
||||
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
|
||||
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
|
||||
:class:`ValueError` will be raised.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
|
||||
Warning:
|
||||
The Bot API does **not** define this as an optional argument. It is formally
|
||||
optional to ensure backwards compatibility of :paramref:`thumbnail_url` with the
|
||||
deprecated :paramref:`thumb_url`, which required that :paramref:`thumbnail_url`
|
||||
become optional. :class:`TypeError` will be raised if no ``title`` is passed.
|
||||
caption (:obj:`str`, optional): Caption of the video to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
|
||||
parsing.
|
||||
@@ -71,6 +89,15 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
message to be sent instead of the video. This field is required if
|
||||
``InlineQueryResultVideo`` is used to send an HTML-page as a result
|
||||
(e.g., a YouTube video).
|
||||
thumb_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the video.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbargumentdeprecation| :paramref:`thumbnail_url`.
|
||||
|
||||
Raises:
|
||||
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
|
||||
supplied or if both are supplied and are not equal.
|
||||
:class:`TypeError`: If no :paramref:`title` is passed.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`.
|
||||
@@ -79,7 +106,9 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
video_url (:obj:`str`): A valid URL for the embedded video player or video file.
|
||||
mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4".
|
||||
thumb_url (:obj:`str`): URL of the thumbnail (JPEG only) for the video.
|
||||
thumbnail_url (:obj:`str`): URL of the thumbnail (JPEG only) for the video.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
title (:obj:`str`): Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption of the video to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
|
||||
@@ -119,7 +148,7 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
"input_message_content",
|
||||
"video_height",
|
||||
"video_width",
|
||||
"thumb_url",
|
||||
"thumbnail_url",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -127,8 +156,13 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
id: str, # pylint: disable=redefined-builtin
|
||||
video_url: str,
|
||||
mime_type: str,
|
||||
thumb_url: str,
|
||||
title: str,
|
||||
# thumbnail_url and title are not optional in Telegram API, but we want to support
|
||||
# thumb_url as well, so thumbnail_url may not be passed if thumb_url is passed.
|
||||
# We will raise ValueError manually if neither thumbnail_url nor thumb_url are passed.
|
||||
thumbnail_url: str = None,
|
||||
# title had to be made optional because of thumbnail_url. This is compensated by raising
|
||||
# TypeError manually if title is not passed.
|
||||
title: str = None,
|
||||
caption: str = None,
|
||||
video_width: int = None,
|
||||
video_height: int = None,
|
||||
@@ -138,15 +172,35 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
input_message_content: "InputMessageContent" = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Sequence[MessageEntity] = None,
|
||||
# thumb_url is not optional in Telegram API, but it is here, along with thumbnail_url.
|
||||
thumb_url: str = None,
|
||||
*,
|
||||
api_kwargs: JSONDict = None,
|
||||
):
|
||||
if not (thumbnail_url or thumb_url):
|
||||
raise ValueError(
|
||||
"You must pass either 'thumbnail_url' or 'thumb_url'. Note that 'thumb_url' is "
|
||||
"deprecated."
|
||||
)
|
||||
|
||||
if title is None:
|
||||
raise TypeError(
|
||||
"InlineQueryResultVideo.__init__() missing a required argument: you forgot to pass"
|
||||
" either 'title' or 'thumbnail_url'."
|
||||
)
|
||||
|
||||
# Required
|
||||
super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.video_url: str = video_url
|
||||
self.mime_type: str = mime_type
|
||||
self.thumb_url: str = thumb_url
|
||||
self.thumbnail_url: str = warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg=thumb_url,
|
||||
new_arg=thumbnail_url,
|
||||
deprecated_arg_name="thumb_url",
|
||||
new_arg_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
self.title: str = title
|
||||
|
||||
# Optional
|
||||
@@ -159,3 +213,17 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
self.description: Optional[str] = description
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.input_message_content: Optional[InputMessageContent] = input_message_content
|
||||
|
||||
@property
|
||||
def thumb_url(self) -> str:
|
||||
""":obj:`str`: URL of the thumbnail (JPEG only) for the video.
|
||||
|
||||
.. deprecated:: 20.2
|
||||
|thumbattributedeprecation| :attr:`thumbnail_url`.
|
||||
"""
|
||||
warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name="thumb_url",
|
||||
new_attr_name="thumbnail_url",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
return self.thumbnail_url
|
||||
|
||||
@@ -34,6 +34,10 @@ class KeyboardButtonRequestUser(TelegramObject):
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`request_id` is equal.
|
||||
|
||||
.. seealso::
|
||||
`Telegram Docs on requesting users \
|
||||
<https://core.telegram.org/bots/features#chat-and-user-selection>`_
|
||||
|
||||
.. versionadded:: 20.1
|
||||
|
||||
Args:
|
||||
@@ -87,6 +91,10 @@ class KeyboardButtonRequestChat(TelegramObject):
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`request_id` is equal.
|
||||
|
||||
.. seealso::
|
||||
`Telegram Docs on requesting chats \
|
||||
<https://core.telegram.org/bots/features#chat-and-user-selection>`_
|
||||
|
||||
.. versionadded:: 20.1
|
||||
|
||||
Args:
|
||||
|
||||
@@ -1374,6 +1374,7 @@ class Message(TelegramObject):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
quote: bool = None,
|
||||
@@ -1421,6 +1422,7 @@ class Message(TelegramObject):
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def reply_document(
|
||||
@@ -1437,6 +1439,7 @@ class Message(TelegramObject):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
quote: bool = None,
|
||||
@@ -1482,6 +1485,7 @@ class Message(TelegramObject):
|
||||
caption_entities=caption_entities,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def reply_animation(
|
||||
@@ -1501,6 +1505,7 @@ class Message(TelegramObject):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
quote: bool = None,
|
||||
@@ -1550,6 +1555,7 @@ class Message(TelegramObject):
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def reply_sticker(
|
||||
@@ -1561,6 +1567,7 @@ class Message(TelegramObject):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
emoji: str = None,
|
||||
*,
|
||||
quote: bool = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1599,6 +1606,7 @@ class Message(TelegramObject):
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
emoji=emoji,
|
||||
)
|
||||
|
||||
async def reply_video(
|
||||
@@ -1619,6 +1627,7 @@ class Message(TelegramObject):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
quote: bool = None,
|
||||
@@ -1668,6 +1677,7 @@ class Message(TelegramObject):
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def reply_video_note(
|
||||
@@ -1682,6 +1692,7 @@ class Message(TelegramObject):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
quote: bool = None,
|
||||
@@ -1726,6 +1737,7 @@ class Message(TelegramObject):
|
||||
filename=filename,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def reply_voice(
|
||||
|
||||
@@ -553,6 +553,7 @@ class User(TelegramObject):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -596,6 +597,7 @@ class User(TelegramObject):
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def send_chat_action(
|
||||
@@ -748,6 +750,7 @@ class User(TelegramObject):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -783,6 +786,7 @@ class User(TelegramObject):
|
||||
pool_timeout=pool_timeout,
|
||||
parse_mode=parse_mode,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
disable_content_type_detection=disable_content_type_detection,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
@@ -1005,6 +1009,7 @@ class User(TelegramObject):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1049,6 +1054,7 @@ class User(TelegramObject):
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def send_sticker(
|
||||
@@ -1060,6 +1066,7 @@ class User(TelegramObject):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
emoji: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -1094,6 +1101,7 @@ class User(TelegramObject):
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
emoji=emoji,
|
||||
)
|
||||
|
||||
async def send_video(
|
||||
@@ -1114,6 +1122,7 @@ class User(TelegramObject):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1152,6 +1161,7 @@ class User(TelegramObject):
|
||||
parse_mode=parse_mode,
|
||||
supports_streaming=supports_streaming,
|
||||
thumb=thumb,
|
||||
thumbnail=thumbnail,
|
||||
api_kwargs=api_kwargs,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
caption_entities=caption_entities,
|
||||
@@ -1234,6 +1244,7 @@ class User(TelegramObject):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1273,6 +1284,7 @@ class User(TelegramObject):
|
||||
filename=filename,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
async def send_voice(
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains functionality used for transition warnings issued by this library.
|
||||
|
||||
It was created to prevent circular imports that would be caused by creating the warnings
|
||||
inside warnings.py.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
import functools
|
||||
from typing import Any
|
||||
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
|
||||
# Narrower type hints will cause linting errors and/or circular imports.
|
||||
# We'll use `Any` here and put type hints in the calling code.
|
||||
def warn_about_deprecated_arg_return_new_arg(
|
||||
deprecated_arg: Any,
|
||||
new_arg: Any,
|
||||
deprecated_arg_name: str,
|
||||
new_arg_name: str,
|
||||
bot_api_version: str,
|
||||
stacklevel: int = 3,
|
||||
) -> Any:
|
||||
"""A helper function for the transition in API when argument is renamed.
|
||||
|
||||
Checks the `deprecated_arg` and `new_arg` objects; warns if non-None `deprecated_arg` object
|
||||
was passed. Returns `new_arg` object (either the one originally passed by the user or the one
|
||||
that user passed as `deprecated_arg`).
|
||||
|
||||
Raises `ValueError` if both `deprecated_arg` and `new_arg` objects were passed, and they are
|
||||
different.
|
||||
"""
|
||||
if deprecated_arg and new_arg and deprecated_arg != new_arg:
|
||||
raise ValueError(
|
||||
f"You passed different entities as '{deprecated_arg_name}' and '{new_arg_name}'. "
|
||||
f"The parameter '{deprecated_arg_name}' was renamed to '{new_arg_name}' in Bot API "
|
||||
f"{bot_api_version}. We recommend using '{new_arg_name}' instead of "
|
||||
f"'{deprecated_arg_name}'."
|
||||
)
|
||||
|
||||
if deprecated_arg:
|
||||
warn(
|
||||
f"Bot API {bot_api_version} renamed the argument '{deprecated_arg_name}' to "
|
||||
f"'{new_arg_name}'.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=stacklevel,
|
||||
)
|
||||
return deprecated_arg
|
||||
|
||||
return new_arg
|
||||
|
||||
|
||||
def warn_about_deprecated_attr_in_property(
|
||||
deprecated_attr_name: str,
|
||||
new_attr_name: str,
|
||||
bot_api_version: str,
|
||||
stacklevel: int = 3,
|
||||
) -> None:
|
||||
"""A helper function for the transition in API when attribute is renamed. Call from properties.
|
||||
|
||||
The properties replace deprecated attributes in classes and issue these deprecation warnings.
|
||||
"""
|
||||
warn(
|
||||
f"Bot API {bot_api_version} renamed the attribute '{deprecated_attr_name}' to "
|
||||
f"'{new_attr_name}'.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=stacklevel,
|
||||
)
|
||||
|
||||
|
||||
warn_about_thumb_return_thumbnail = functools.partial(
|
||||
warn_about_deprecated_arg_return_new_arg,
|
||||
deprecated_arg_name="thumb",
|
||||
new_arg_name="thumbnail",
|
||||
bot_api_version="6.6",
|
||||
)
|
||||
"""A helper function to warn about using a deprecated 'thumb' argument and return it or the new
|
||||
'thumbnail' argument, introduced in API 6.6.
|
||||
"""
|
||||
@@ -50,7 +50,7 @@ class Version(NamedTuple):
|
||||
return version
|
||||
|
||||
|
||||
__version_info__ = Version(major=20, minor=1, micro=0, releaselevel="final", serial=0)
|
||||
__version_info__ = Version(major=20, minor=2, micro=0, releaselevel="final", serial=0)
|
||||
__version__ = str(__version_info__)
|
||||
|
||||
# # SETUP.PY MARKER
|
||||
|
||||
+117
-2
@@ -36,6 +36,7 @@ __all__ = [
|
||||
"BOT_API_VERSION_INFO",
|
||||
"BotCommandLimit",
|
||||
"BotCommandScopeType",
|
||||
"BotDescriptionLimit",
|
||||
"CallbackQueryLimit",
|
||||
"ChatAction",
|
||||
"ChatID",
|
||||
@@ -73,7 +74,9 @@ __all__ = [
|
||||
"PollType",
|
||||
"ReplyLimit",
|
||||
"SUPPORTED_WEBHOOK_PORTS",
|
||||
"StickerFormat",
|
||||
"StickerLimit",
|
||||
"StickerSetLimit",
|
||||
"StickerType",
|
||||
"WebhookLimit",
|
||||
"UpdateType",
|
||||
@@ -111,7 +114,7 @@ class _BotAPIVersion(NamedTuple):
|
||||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO = _BotAPIVersion(major=6, minor=5)
|
||||
BOT_API_VERSION_INFO = _BotAPIVersion(major=6, minor=6)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
@@ -184,6 +187,28 @@ class BotCommandScopeType(StringEnum):
|
||||
""":obj:`str`: The type of :class:`telegram.BotCommandScopeChatMember`."""
|
||||
|
||||
|
||||
class BotDescriptionLimit(IntEnum):
|
||||
"""This enum contains limitations for the methods :meth:`telegram.Bot.set_my_description` and
|
||||
:meth:`telegram.Bot.set_my_short_description`. The enum members of this enumeration are
|
||||
instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
MAX_DESCRIPTION_LENGTH = 512
|
||||
""":obj:`int`: Maximum length for the parameter
|
||||
:paramref:`~telegram.Bot.set_my_description.description` of
|
||||
:meth:`telegram.Bot.set_my_description`
|
||||
"""
|
||||
MAX_SHORT_DESCRIPTION_LENGTH = 120
|
||||
""":obj:`int`: Maximum length for the parameter
|
||||
:paramref:`~telegram.Bot.set_my_short_description.short_description` of
|
||||
:meth:`telegram.Bot.set_my_short_description`
|
||||
"""
|
||||
|
||||
|
||||
class CallbackQueryLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.CallbackQuery`/
|
||||
:meth:`telegram.Bot.answer_callback_query`. The enum members of this enumeration are instances
|
||||
@@ -1243,8 +1268,26 @@ class ReplyLimit(IntEnum):
|
||||
"""
|
||||
|
||||
|
||||
class StickerFormat(StringEnum):
|
||||
"""This enum contains the available formats of :class:`telegram.Sticker` in the set. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
STATIC = "static"
|
||||
""":obj:`str`: Static sticker."""
|
||||
ANIMATED = "animated"
|
||||
""":obj:`str`: Animated sticker."""
|
||||
VIDEO = "video"
|
||||
""":obj:`str`: Video sticker."""
|
||||
|
||||
|
||||
class StickerLimit(IntEnum):
|
||||
"""This enum contains limitations for :meth:`telegram.Bot.create_new_sticker_set`.
|
||||
"""This enum contains limitations for various sticker methods, such as
|
||||
:meth:`telegram.Bot.create_new_sticker_set`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
@@ -1264,6 +1307,78 @@ class StickerLimit(IntEnum):
|
||||
:paramref:`~telegram.Bot.create_new_sticker_set.title` parameter of
|
||||
:meth:`telegram.Bot.create_new_sticker_set`.
|
||||
"""
|
||||
MIN_STICKER_EMOJI = 1
|
||||
""":obj:`int`: Minimum number of emojis associated with a sticker, passed as the
|
||||
:paramref:`~telegram.Bot.setStickerEmojiList.emoji_list` parameter of
|
||||
:meth:`telegram.Bot.set_sticker_emoji_list`.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
MAX_STICKER_EMOJI = 20
|
||||
""":obj:`int`: Maximum number of emojis associated with a sticker, passed as the
|
||||
:paramref:`~telegram.Bot.setStickerEmojiList.emoji_list` parameter of
|
||||
:meth:`telegram.Bot.set_sticker_emoji_list`.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
MAX_SEARCH_KEYWORDS = 20
|
||||
""":obj:`int`: Maximum number of search keywords for a sticker, passed as the
|
||||
:paramref:`~telegram.Bot.set_sticker_keywords.keywords` parameter of
|
||||
:meth:`telegram.Bot.set_sticker_keywords`.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
MAX_KEYWORD_LENGTH = 64
|
||||
""":obj:`int`: Maximum number of characters in a search keyword for a sticker, for each item in
|
||||
:paramref:`~telegram.Bot.set_sticker_keywords.keywords` sequence of
|
||||
:meth:`telegram.Bot.set_sticker_keywords`.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
|
||||
class StickerSetLimit(IntEnum):
|
||||
"""This enum contains limitations for various sticker set methods, such as
|
||||
:meth:`telegram.Bot.create_new_sticker_set` and :meth:`telegram.Bot.add_sticker_to_set`.
|
||||
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
MIN_INITIAL_STICKERS = 1
|
||||
""":obj:`int`: Minimum number of stickers needed to create a sticker set, passed as the
|
||||
:paramref:`~telegram.Bot.create_new_sticker_set.stickers` parameter of
|
||||
:meth:`telegram.Bot.create_new_sticker_set`.
|
||||
"""
|
||||
MAX_INITIAL_STICKERS = 50
|
||||
""":obj:`int`: Maximum number of stickers allowed while creating a sticker set, passed as the
|
||||
:paramref:`~telegram.Bot.create_new_sticker_set.stickers` parameter of
|
||||
:meth:`telegram.Bot.create_new_sticker_set`.
|
||||
"""
|
||||
MAX_EMOJI_STICKERS = 200
|
||||
""":obj:`int`: Maximum number of stickers allowed in an emoji sticker set, as given in
|
||||
:meth:`telegram.Bot.add_sticker_to_set`.
|
||||
"""
|
||||
MAX_ANIMATED_STICKERS = 50
|
||||
""":obj:`int`: Maximum number of stickers allowed in an animated or video sticker set, as given
|
||||
in :meth:`telegram.Bot.add_sticker_to_set`.
|
||||
"""
|
||||
MAX_STATIC_STICKERS = 120
|
||||
""":obj:`int`: Maximum number of stickers allowed in a static sticker set, as given in
|
||||
:meth:`telegram.Bot.add_sticker_to_set`.
|
||||
"""
|
||||
MAX_STATIC_THUMBNAIL_SIZE = 128
|
||||
""":obj:`int`: Maximum size of the thumbnail if it is a **.WEBP** or **.PNG** in kilobytes,
|
||||
as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`."""
|
||||
MAX_ANIMATED_THUMBNAIL_SIZE = 32
|
||||
""":obj:`int`: Maximum size of the thumbnail if it is a **.TGS** or **.WEBM** in kilobytes,
|
||||
as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`."""
|
||||
STATIC_THUMB_DIMENSIONS = 100
|
||||
""":obj:`int`: Exact height and width of the thumbnail if it is a **.WEBP** or **.PNG** in
|
||||
pixels, as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`."""
|
||||
|
||||
|
||||
class StickerType(StringEnum):
|
||||
|
||||
@@ -31,10 +31,12 @@ from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AsyncContextManager,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Coroutine,
|
||||
DefaultDict,
|
||||
Dict,
|
||||
Generator,
|
||||
Generic,
|
||||
List,
|
||||
Mapping,
|
||||
@@ -71,7 +73,6 @@ if TYPE_CHECKING:
|
||||
DEFAULT_GROUP: int = 0
|
||||
|
||||
_AppType = TypeVar("_AppType", bound="Application") # pylint: disable=invalid-name
|
||||
_RT = TypeVar("_RT")
|
||||
_STOP_SIGNAL = object()
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -934,7 +935,9 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
loop.close()
|
||||
|
||||
def create_task(
|
||||
self, coroutine: Coroutine[Any, Any, RT], update: object = None
|
||||
self,
|
||||
coroutine: Union[Generator[Optional["asyncio.Future[object]"], None, RT], Awaitable[RT]],
|
||||
update: object = None,
|
||||
) -> "asyncio.Task[RT]":
|
||||
"""Thin wrapper around :func:`asyncio.create_task` that handles exceptions raised by
|
||||
the :paramref:`coroutine` with :meth:`process_error`.
|
||||
@@ -948,7 +951,10 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
.. seealso:: :wiki:`Concurrency`
|
||||
|
||||
Args:
|
||||
coroutine (:term:`coroutine function`): The coroutine to run as task.
|
||||
coroutine (:term:`awaitable`): The awaitable to run as task.
|
||||
|
||||
.. versionchanged:: 20.2
|
||||
Accepts :class:`asyncio.Future` and generator-based coroutine functions.
|
||||
update (:obj:`object`, optional): If set, will be passed to :meth:`process_error`
|
||||
as additional information for the error handlers. Moreover, the corresponding
|
||||
:attr:`chat_data` and :attr:`user_data` entries will be updated in the next run of
|
||||
@@ -960,13 +966,16 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
return self.__create_task(coroutine=coroutine, update=update)
|
||||
|
||||
def __create_task(
|
||||
self, coroutine: Coroutine, update: object = None, is_error_handler: bool = False
|
||||
) -> asyncio.Task:
|
||||
self,
|
||||
coroutine: Union[Generator[Optional["asyncio.Future[object]"], None, RT], Awaitable[RT]],
|
||||
update: object = None,
|
||||
is_error_handler: bool = False,
|
||||
) -> "asyncio.Task[RT]":
|
||||
# Unfortunately, we can't know if `coroutine` runs one of the error handler functions
|
||||
# but by passing `is_error_handler=True` from `process_error`, we can make sure that we
|
||||
# get at most one recursion of the user calls `create_task` manually with an error handler
|
||||
# function
|
||||
task = asyncio.create_task(
|
||||
task: "asyncio.Task[RT]" = asyncio.create_task(
|
||||
self.__create_task_callback(
|
||||
coroutine=coroutine, update=update, is_error_handler=is_error_handler
|
||||
)
|
||||
@@ -995,11 +1004,13 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
|
||||
async def __create_task_callback(
|
||||
self,
|
||||
coroutine: Coroutine[Any, Any, _RT],
|
||||
coroutine: Union[Generator[Optional["asyncio.Future[object]"], None, RT], Awaitable[RT]],
|
||||
update: object = None,
|
||||
is_error_handler: bool = False,
|
||||
) -> _RT:
|
||||
) -> RT:
|
||||
try:
|
||||
if isinstance(coroutine, Generator):
|
||||
return await asyncio.create_task(coroutine)
|
||||
return await coroutine
|
||||
except asyncio.CancelledError as cancel:
|
||||
# TODO: in py3.8+, CancelledError is a subclass of BaseException, so we can drop this
|
||||
@@ -1562,7 +1573,9 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
update: Optional[object],
|
||||
error: Exception,
|
||||
job: "Job[CCT]" = None,
|
||||
coroutine: Coroutine[Any, Any, Any] = None,
|
||||
coroutine: Union[
|
||||
Generator[Optional["asyncio.Future[object]"], None, RT], Awaitable[RT]
|
||||
] = None,
|
||||
) -> bool:
|
||||
"""Processes an error by passing it to all error handlers registered with
|
||||
:meth:`add_error_handler`. If one of the error handlers raises
|
||||
|
||||
@@ -178,7 +178,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
self._get_updates_write_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._get_updates_pool_timeout: ODVInput[float] = DEFAULT_NONE
|
||||
self._get_updates_request: DVInput["BaseRequest"] = DEFAULT_NONE
|
||||
self._get_updates_http_version: DVInput[str] = DefaultValue("2")
|
||||
self._get_updates_http_version: DVInput[str] = DefaultValue("1.1")
|
||||
self._private_key: ODVInput[bytes] = DEFAULT_NONE
|
||||
self._private_key_password: ODVInput[bytes] = DEFAULT_NONE
|
||||
self._defaults: ODVInput["Defaults"] = DEFAULT_NONE
|
||||
@@ -204,7 +204,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
self._post_shutdown: Optional[Callable[[Application], Coroutine[Any, Any, None]]] = None
|
||||
self._post_stop: Optional[Callable[[Application], Coroutine[Any, Any, None]]] = None
|
||||
self._rate_limiter: ODVInput["BaseRateLimiter"] = DEFAULT_NONE
|
||||
self._http_version: DVInput[str] = DefaultValue("2")
|
||||
self._http_version: DVInput[str] = DefaultValue("1.1")
|
||||
|
||||
def _build_request(self, get_updates: bool) -> BaseRequest:
|
||||
prefix = "_get_updates_" if get_updates else "_"
|
||||
@@ -232,7 +232,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
key: value for key, value in timeouts.items() if not isinstance(value, DefaultValue)
|
||||
}
|
||||
|
||||
http_version = DefaultValue.get_value(getattr(self, f"{prefix}http_version")) or "2"
|
||||
http_version = DefaultValue.get_value(getattr(self, f"{prefix}http_version")) or "1.1"
|
||||
|
||||
return HTTPXRequest(
|
||||
connection_pool_size=connection_pool_size,
|
||||
@@ -564,15 +564,32 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
def http_version(self: BuilderType, http_version: str) -> BuilderType:
|
||||
"""Sets the HTTP protocol version which is used for the
|
||||
:paramref:`~telegram.request.HTTPXRequest.http_version` parameter of
|
||||
:attr:`telegram.Bot.request`. By default, HTTP/2 is used.
|
||||
:attr:`telegram.Bot.request`. By default, HTTP/1.1 is used.
|
||||
|
||||
.. seealso:: :meth:`get_updates_http_version`
|
||||
|
||||
Note:
|
||||
Users have observed stability issues with HTTP/2, which happen due to how the `h2
|
||||
library handles <https://github.com/python-hyper/h2/issues/1181>` cancellations of
|
||||
keepalive connections. See `#3556 <https://github.com/python-telegram-bot/
|
||||
python-telegram-bot/issues/3556>`_ for a discussion.
|
||||
|
||||
If you want to use HTTP/2, you must install PTB with the optional requirement
|
||||
``http2``, i.e.
|
||||
.. code-block:: bash
|
||||
|
||||
pip install python-telegram-bot[http2]
|
||||
|
||||
Keep in mind that the HTTP/1.1 implementation may be considered the `"more
|
||||
robust option at this time" <https://www.python-httpx.org/http2#enabling-http2>`_.
|
||||
|
||||
.. versionadded:: 20.1
|
||||
.. versionchanged:: 20.2
|
||||
Reset the default version to 1.1.
|
||||
|
||||
Args:
|
||||
http_version (:obj:`str`): Pass ``"1.1"`` if you'd like to use HTTP/1.1 for making
|
||||
requests to Telegram. Defaults to ``"2"``, in which case HTTP/2 is used.
|
||||
http_version (:obj:`str`): Pass ``"2"`` if you'd like to use HTTP/2 for making
|
||||
requests to Telegram. Defaults to ``"1.1"``, in which case HTTP/1.1 is used.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
@@ -705,15 +722,31 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||
def get_updates_http_version(self: BuilderType, get_updates_http_version: str) -> BuilderType:
|
||||
"""Sets the HTTP protocol version which is used for the
|
||||
:paramref:`~telegram.request.HTTPXRequest.http_version` parameter which is used in the
|
||||
:meth:`telegram.Bot.get_updates` request. By default, HTTP/2 is used.
|
||||
:meth:`telegram.Bot.get_updates` request. By default, HTTP/1.1 is used.
|
||||
|
||||
.. seealso:: :meth:`http_version`
|
||||
|
||||
Note:
|
||||
Users have observed stability issues with HTTP/2, which happen due to how the `h2
|
||||
library handles <https://github.com/python-hyper/h2/issues/1181>` cancellations of
|
||||
keepalive connections. See `#3556 <https://github.com/python-telegram-bot/
|
||||
python-telegram-bot/issues/3556>`_ for a discussion.
|
||||
|
||||
You will also need to install the http2 dependency. Keep in mind that the HTTP/1.1
|
||||
implementation may be considered the `"more robust option at this time"
|
||||
<https://www.python-httpx.org/http2#enabling-http2>`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install httpx[http2]
|
||||
|
||||
.. versionadded:: 20.1
|
||||
.. versionchanged:: 20.2
|
||||
Reset the default version to 1.1.
|
||||
|
||||
Args:
|
||||
get_updates_http_version (:obj:`str`): Pass ``"1.1"`` if you'd like to use HTTP/1.1 for
|
||||
making requests to Telegram. Defaults to ``"2"``, in which case HTTP/2 is used.
|
||||
get_updates_http_version (:obj:`str`): Pass ``"2"`` if you'd like to use HTTP/2 for
|
||||
making requests to Telegram. Defaults to ``"1.1"``, in which case HTTP/1.1 is used.
|
||||
|
||||
Returns:
|
||||
:class:`ApplicationBuilder`: The same builder with the updated argument.
|
||||
|
||||
@@ -20,14 +20,16 @@
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Coroutine,
|
||||
Awaitable,
|
||||
Dict,
|
||||
Generator,
|
||||
Generic,
|
||||
List,
|
||||
Match,
|
||||
NoReturn,
|
||||
Optional,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from telegram._callbackquery import CallbackQuery
|
||||
@@ -37,7 +39,7 @@ from telegram.ext._extbot import ExtBot
|
||||
from telegram.ext._utils.types import BD, BT, CD, UD
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from asyncio import Queue
|
||||
from asyncio import Future, Queue
|
||||
|
||||
from telegram.ext import Application, Job, JobQueue # noqa: F401
|
||||
from telegram.ext._utils.types import CCT
|
||||
@@ -96,8 +98,8 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
|
||||
|
||||
.. versionadded:: 20.0
|
||||
Attributes:
|
||||
coroutine (:term:`coroutine function`): Optional. Only present in error handlers if the
|
||||
error was caused by a coroutine run with :meth:`Application.create_task` or a handler
|
||||
coroutine (:term:`awaitable`): Optional. Only present in error handlers if the
|
||||
error was caused by an awaitable run with :meth:`Application.create_task` or a handler
|
||||
callback with :attr:`block=False <BaseHandler.block>`.
|
||||
matches (List[:meth:`re.Match <re.Match.expand>`]): Optional. If the associated update
|
||||
originated from a :class:`filters.Regex`, this will contain a list of match objects for
|
||||
@@ -143,7 +145,9 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
|
||||
self.matches: Optional[List[Match[str]]] = None
|
||||
self.error: Optional[Exception] = None
|
||||
self.job: Optional["Job[CCT]"] = None
|
||||
self.coroutine: Optional[Coroutine[Any, Any, Any]] = None
|
||||
self.coroutine: Optional[
|
||||
Union[Generator[Optional["Future[object]"], None, Any], Awaitable[Any]]
|
||||
] = None
|
||||
|
||||
@property
|
||||
def application(self) -> "Application[BT, CCT, UD, CD, BD, Any]":
|
||||
@@ -275,7 +279,7 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
|
||||
error: Exception,
|
||||
application: "Application[BT, CCT, UD, CD, BD, Any]",
|
||||
job: "Job[Any]" = None,
|
||||
coroutine: Coroutine[Any, Any, Any] = None,
|
||||
coroutine: Union[Generator[Optional["Future[object]"], None, Any], Awaitable[Any]] = None,
|
||||
) -> "CCT":
|
||||
"""
|
||||
Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the error
|
||||
@@ -295,13 +299,15 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
|
||||
job (:class:`telegram.ext.Job`, optional): The job associated with the error.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
coroutine (:term:`coroutine function`, optional): The coroutine function associated
|
||||
coroutine (:term:`awaitable`, optional): The awaitable associated
|
||||
with this error if the error was caused by a coroutine run with
|
||||
:meth:`Application.create_task` or a handler callback with
|
||||
:attr:`block=False <BaseHandler.block>`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. versionchanged:: 20.2
|
||||
Accepts :class:`asyncio.Future` and generator-based coroutine functions.
|
||||
Returns:
|
||||
:class:`telegram.ext.CallbackContext`
|
||||
"""
|
||||
|
||||
@@ -259,7 +259,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
|
||||
when :attr:`per_message`, :attr:`per_chat`, :attr:`per_user` are all :obj:`False`.
|
||||
|
||||
Attributes:
|
||||
block (:obj:`bool`): Determines whether the callback will run in a blocking way.. Always
|
||||
block (:obj:`bool`): Determines whether the callback will run in a blocking way. Always
|
||||
:obj:`True` since conversation handlers handle any non-blocking callbacks internally.
|
||||
|
||||
"""
|
||||
|
||||
+305
-12
@@ -18,6 +18,7 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Bot with convenience extensions."""
|
||||
import warnings
|
||||
from copy import copy
|
||||
from datetime import datetime
|
||||
from typing import (
|
||||
@@ -44,6 +45,8 @@ from telegram import (
|
||||
Bot,
|
||||
BotCommand,
|
||||
BotCommandScope,
|
||||
BotDescription,
|
||||
BotShortDescription,
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
ChatAdministratorRights,
|
||||
@@ -58,6 +61,7 @@ from telegram import (
|
||||
GameHighScore,
|
||||
InlineKeyboardMarkup,
|
||||
InputMedia,
|
||||
InputSticker,
|
||||
Location,
|
||||
MaskPosition,
|
||||
MenuButton,
|
||||
@@ -82,9 +86,11 @@ from telegram import (
|
||||
from telegram._utils.datetime import to_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.types import DVInput, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.ext._callbackdatacache import CallbackDataCache
|
||||
from telegram.ext._utils.types import RLARGS
|
||||
from telegram.request import BaseRequest
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
@@ -706,11 +712,12 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
name: str,
|
||||
emojis: str,
|
||||
emojis: str = None, # Was made optional for compatibility reasons
|
||||
png_sticker: FileInput = None,
|
||||
mask_position: MaskPosition = None,
|
||||
tgs_sticker: FileInput = None,
|
||||
webm_sticker: FileInput = None,
|
||||
sticker: InputSticker = None, # Actually a required param, but is optional for compat.
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -722,6 +729,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
return await super().add_sticker_to_set(
|
||||
user_id=user_id,
|
||||
name=name,
|
||||
sticker=sticker,
|
||||
emojis=emojis,
|
||||
png_sticker=png_sticker,
|
||||
mask_position=mask_position,
|
||||
@@ -1031,12 +1039,15 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
user_id: Union[str, int],
|
||||
name: str,
|
||||
title: str,
|
||||
emojis: str,
|
||||
emojis: str = None, # Was made optional for compatibility purposes
|
||||
png_sticker: FileInput = None,
|
||||
mask_position: MaskPosition = None,
|
||||
tgs_sticker: FileInput = None,
|
||||
webm_sticker: FileInput = None,
|
||||
sticker_type: str = None,
|
||||
stickers: Sequence[InputSticker] = None, # Actually a required param. Optional for compat.
|
||||
sticker_format: str = None, # Actually a required param. Optional for compat.
|
||||
needs_repainting: bool = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -1049,6 +1060,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
user_id=user_id,
|
||||
name=name,
|
||||
title=title,
|
||||
stickers=stickers,
|
||||
sticker_format=sticker_format,
|
||||
needs_repainting=needs_repainting,
|
||||
emojis=emojis,
|
||||
png_sticker=png_sticker,
|
||||
mask_position=mask_position,
|
||||
@@ -2150,6 +2164,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2176,6 +2191,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
filename=filename,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -2201,6 +2217,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2226,6 +2243,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
caption_entities=caption_entities,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
thumbnail=thumbnail,
|
||||
filename=filename,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -2349,6 +2367,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
caption_entities: Sequence["MessageEntity"] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2372,6 +2391,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
caption_entities=caption_entities,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
thumbnail=thumbnail,
|
||||
filename=filename,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -2724,6 +2744,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
emoji: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -2745,6 +2766,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
emoji=emoji,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
@@ -2817,6 +2839,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
has_spoiler: bool = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2844,6 +2867,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
filename=filename,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -2865,6 +2889,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: int = None,
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
filename: str = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2886,6 +2911,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
thumbnail=thumbnail,
|
||||
filename=filename,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -3218,6 +3244,30 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_sticker_set_thumbnail(
|
||||
self,
|
||||
name: str,
|
||||
user_id: Union[str, int],
|
||||
thumbnail: FileInput = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_sticker_set_thumbnail(
|
||||
name=name,
|
||||
user_id=user_id,
|
||||
thumbnail=thumbnail,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_sticker_set_thumb(
|
||||
self,
|
||||
name: str,
|
||||
@@ -3231,16 +3281,30 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_sticker_set_thumb(
|
||||
name=name,
|
||||
user_id=user_id,
|
||||
thumb=thumb,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
# Manually issue deprecation here to get the stacklevel right
|
||||
# Suppress the warning issued by super().set_sticker_set_thumb just in case
|
||||
# the user explicitly enables deprecation warnings
|
||||
# Unfortunately this is not entirely reliable (see tests), but it's a best effort solution
|
||||
warn(
|
||||
message=(
|
||||
"Bot API 6.6 renamed the method 'setStickerSetThumb' to 'setStickerSetThumbnail', "
|
||||
"hence method 'set_sticker_set_thumb' was renamed to 'set_sticker_set_thumbnail' "
|
||||
"in PTB."
|
||||
),
|
||||
category=PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
with warnings.catch_warnings():
|
||||
return await super().set_sticker_set_thumb(
|
||||
name=name,
|
||||
user_id=user_id,
|
||||
thumb=thumb,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_webhook(
|
||||
self,
|
||||
@@ -3413,7 +3477,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def upload_sticker_file(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
png_sticker: FileInput,
|
||||
png_sticker: FileInput = None, # Deprecated since bot api 6.6. Optional for compatiblity.
|
||||
sticker: FileInput = None, # Actually required, but optional for compatibility.
|
||||
sticker_format: str = None, # Actually required, but optional for compatibility.
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -3424,6 +3490,8 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
) -> File:
|
||||
return await super().upload_sticker_file(
|
||||
user_id=user_id,
|
||||
sticker=sticker,
|
||||
sticker_format=sticker_format,
|
||||
png_sticker=png_sticker,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -3432,6 +3500,220 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_my_description(
|
||||
self,
|
||||
description: str = None,
|
||||
language_code: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_my_description(
|
||||
description=description,
|
||||
language_code=language_code,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_my_short_description(
|
||||
self,
|
||||
short_description: str = None,
|
||||
language_code: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_my_short_description(
|
||||
short_description=short_description,
|
||||
language_code=language_code,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def get_my_description(
|
||||
self,
|
||||
language_code: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> BotDescription:
|
||||
return await super().get_my_description(
|
||||
language_code=language_code,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def get_my_short_description(
|
||||
self,
|
||||
language_code: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> BotShortDescription:
|
||||
return await super().get_my_short_description(
|
||||
language_code=language_code,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_custom_emoji_sticker_set_thumbnail(
|
||||
self,
|
||||
name: str,
|
||||
custom_emoji_id: str = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_custom_emoji_sticker_set_thumbnail(
|
||||
name=name,
|
||||
custom_emoji_id=custom_emoji_id,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_sticker_set_title(
|
||||
self,
|
||||
name: str,
|
||||
title: str,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_sticker_set_title(
|
||||
name=name,
|
||||
title=title,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def delete_sticker_set(
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().delete_sticker_set(
|
||||
name=name,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_sticker_emoji_list(
|
||||
self,
|
||||
sticker: str,
|
||||
emoji_list: Sequence[str],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_sticker_emoji_list(
|
||||
sticker=sticker,
|
||||
emoji_list=emoji_list,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_sticker_keywords(
|
||||
self,
|
||||
sticker: str,
|
||||
keywords: Sequence[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_sticker_keywords(
|
||||
sticker=sticker,
|
||||
keywords=keywords,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def set_sticker_mask_position(
|
||||
self,
|
||||
sticker: str,
|
||||
mask_position: MaskPosition = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
rate_limit_args: RLARGS = None,
|
||||
) -> bool:
|
||||
return await super().set_sticker_mask_position(
|
||||
sticker=sticker,
|
||||
mask_position=mask_position,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
# updated camelCase aliases
|
||||
getMe = get_me
|
||||
sendMessage = send_message
|
||||
@@ -3507,6 +3789,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
setStickerPositionInSet = set_sticker_position_in_set
|
||||
deleteStickerFromSet = delete_sticker_from_set
|
||||
setStickerSetThumb = set_sticker_set_thumb
|
||||
setStickerSetThumbnail = set_sticker_set_thumbnail
|
||||
setPassportDataErrors = set_passport_data_errors
|
||||
sendPoll = send_poll
|
||||
stopPoll = stop_poll
|
||||
@@ -3533,3 +3816,13 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
reopenGeneralForumTopic = reopen_general_forum_topic
|
||||
hideGeneralForumTopic = hide_general_forum_topic
|
||||
unhideGeneralForumTopic = unhide_general_forum_topic
|
||||
setMyDescription = set_my_description
|
||||
getMyDescription = get_my_description
|
||||
setMyShortDescription = set_my_short_description
|
||||
getMyShortDescription = get_my_short_description
|
||||
setCustomEmojiStickerSetThumbnail = set_custom_emoji_sticker_set_thumbnail
|
||||
setStickerSetTitle = set_sticker_set_title
|
||||
deleteStickerSet = delete_sticker_set
|
||||
setStickerEmojiList = set_sticker_emoji_list
|
||||
setStickerKeywords = set_sticker_keywords
|
||||
setStickerMaskPosition = set_sticker_mask_position
|
||||
|
||||
@@ -25,10 +25,13 @@ Warning:
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from types import FrameType
|
||||
from typing import Optional
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def was_called_by(frame: Optional[FrameType], caller: Path) -> bool:
|
||||
"""Checks if the passed frame was called by the specified file.
|
||||
@@ -51,11 +54,22 @@ def was_called_by(frame: Optional[FrameType], caller: Path) -> bool:
|
||||
if frame is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
return _was_called_by(frame, caller)
|
||||
except Exception as exc:
|
||||
_logger.debug(
|
||||
"Failed to check if frame was called by `caller`. Assuming that it was not.",
|
||||
exc_info=exc,
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _was_called_by(frame: FrameType, caller: Path) -> bool:
|
||||
# https://stackoverflow.com/a/57712700/10606962
|
||||
if Path(frame.f_code.co_filename) == caller:
|
||||
if Path(frame.f_code.co_filename).resolve() == caller:
|
||||
return True
|
||||
while frame.f_back:
|
||||
frame = frame.f_back
|
||||
if Path(frame.f_code.co_filename) == caller:
|
||||
if Path(frame.f_code.co_filename).resolve() == caller:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -82,14 +82,16 @@ class HTTPXRequest(BaseRequest):
|
||||
With a finite pool timeout, you must expect :exc:`telegram.error.TimedOut`
|
||||
exceptions to be thrown when more requests are made simultaneously than there are
|
||||
connections in the connection pool!
|
||||
http_version (:obj:`str`, optional): If ``"1.1"``, HTTP/1.1 will be used instead of HTTP/2.
|
||||
Defaults to ``"2"``.
|
||||
http_version (:obj:`str`, optional): If ``"2"``, HTTP/2 will be used instead of HTTP/1.1.
|
||||
Defaults to ``"1.1"``.
|
||||
|
||||
.. versionadded:: 20.1
|
||||
.. versionchanged:: 20.2
|
||||
Reset the default version to 1.1.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("_client", "_client_kwargs")
|
||||
__slots__ = ("_client", "_client_kwargs", "_http_version")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -99,8 +101,9 @@ class HTTPXRequest(BaseRequest):
|
||||
write_timeout: Optional[float] = 5.0,
|
||||
connect_timeout: Optional[float] = 5.0,
|
||||
pool_timeout: Optional[float] = 1.0,
|
||||
http_version: str = "2",
|
||||
http_version: str = "1.1",
|
||||
):
|
||||
self._http_version = http_version
|
||||
timeout = httpx.Timeout(
|
||||
connect=connect_timeout,
|
||||
read=read_timeout,
|
||||
@@ -130,14 +133,28 @@ class HTTPXRequest(BaseRequest):
|
||||
try:
|
||||
self._client = self._build_client()
|
||||
except ImportError as exc:
|
||||
if "httpx[socks]" not in str(exc):
|
||||
if "httpx[http2]" not in str(exc) and "httpx[socks]" not in str(exc):
|
||||
raise exc
|
||||
|
||||
if "httpx[socks]" in str(exc):
|
||||
raise RuntimeError(
|
||||
"To use Socks5 proxies, PTB must be installed via `pip install "
|
||||
"python-telegram-bot[socks]`."
|
||||
) from exc
|
||||
raise RuntimeError(
|
||||
"To use Socks5 proxies, PTB must be installed via `pip install "
|
||||
"python-telegram-bot[socks]`."
|
||||
"To use HTTP/2, PTB must be installed via `pip install "
|
||||
"python-telegram-bot[http2]`."
|
||||
) from exc
|
||||
|
||||
@property
|
||||
def http_version(self) -> str:
|
||||
"""
|
||||
:obj:`str`: Used HTTP version, see :paramref:`http_version`.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
return self._http_version
|
||||
|
||||
def _build_client(self) -> httpx.AsyncClient:
|
||||
return httpx.AsyncClient(**self._client_kwargs) # type: ignore[arg-type]
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from typing import List, Optional, Sequence, Tuple
|
||||
|
||||
from telegram._files.inputfile import InputFile
|
||||
from telegram._files.inputmedia import InputMedia
|
||||
from telegram._files.inputsticker import InputSticker
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.datetime import to_timestamp
|
||||
from telegram._utils.enum import StringEnum
|
||||
@@ -124,15 +125,22 @@ class RequestParameter:
|
||||
else:
|
||||
data.pop("media", None)
|
||||
|
||||
thumb = data.get("thumb", None)
|
||||
if isinstance(thumb, InputFile):
|
||||
if thumb.attach_uri:
|
||||
data["thumb"] = thumb.attach_uri
|
||||
thumbnail = data.get("thumbnail", None)
|
||||
if isinstance(thumbnail, InputFile):
|
||||
if thumbnail.attach_uri:
|
||||
data["thumbnail"] = thumbnail.attach_uri
|
||||
else:
|
||||
data.pop("thumb", None)
|
||||
return data, [value.media, thumb]
|
||||
data.pop("thumbnail", None)
|
||||
return data, [value.media, thumbnail]
|
||||
|
||||
return data, [value.media]
|
||||
if isinstance(value, InputSticker) and isinstance(value.sticker, InputFile):
|
||||
# We call to_dict and change the returned dict instead of overriding
|
||||
# value.sticker in case the same value is reused for another request
|
||||
data = value.to_dict()
|
||||
data["sticker"] = value.sticker.attach_uri
|
||||
return data, [value.sticker]
|
||||
|
||||
if isinstance(value, TelegramObject):
|
||||
# Needs to be last, because InputMedia is a subclass of TelegramObject
|
||||
return value.to_dict(), []
|
||||
|
||||
@@ -47,7 +47,8 @@ class PTBRuntimeWarning(PTBUserWarning, RuntimeWarning):
|
||||
|
||||
|
||||
# https://www.python.org/dev/peps/pep-0565/ recommends using a custom warning class derived from
|
||||
# DeprecationWarning. We also subclass from TGUserWarning so users can easily 'switch off' warnings
|
||||
# DeprecationWarning. We also subclass from PTBUserWarning so users can easily 'switch off'
|
||||
# warnings
|
||||
class PTBDeprecationWarning(PTBUserWarning, DeprecationWarning):
|
||||
"""
|
||||
Custom warning class for deprecations in this library.
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
==============
|
||||
Testing in PTB
|
||||
==============
|
||||
|
||||
PTB uses `pytest`_ for testing. To run the tests, you need to
|
||||
have pytest installed along with a few other dependencies. You can find the list of dependencies
|
||||
in the ``requirements-dev.txt`` file in the root of the repository.
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
To run the entire test suite, you can use the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest
|
||||
|
||||
This will run all the tests, including the ones which make a request to the Telegram servers, which
|
||||
may take a long time (total > 13 mins). To run only the tests that don't require a connection, you
|
||||
can run the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest -m no_req
|
||||
|
||||
Or alternatively, you can run the following command to run only the tests that require a connection:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest -m req
|
||||
|
||||
To further speed up the tests, you can run them in parallel using the ``-n`` flag (requires `pytest-xdist`_). But beware that
|
||||
this will use multiple CPU cores on your machine. The ``--dist`` flag is used to specify how the
|
||||
tests will be distributed across the cores. The ``loadgroup`` option is used to distribute the tests
|
||||
such that tests marked with ``@pytest.mark.xdist_group("name")`` are run on the same core — important if you want avoid race conditions in some tests:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest -n auto --dist=loadgroup
|
||||
|
||||
This will result in a significant speedup, but may cause some tests to fail. If you want to run
|
||||
the failed tests in isolation, you can use the ``--lf`` flag:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --lf
|
||||
|
||||
|
||||
Writing tests
|
||||
=============
|
||||
|
||||
PTB has a separate test file for every file in the ``telegram.*`` namespace. Further, the tests for
|
||||
the ``telegram`` module are split into two classes, based on whether the test methods in them make a
|
||||
request or not. When writing tests, make sure to split them into these two classes, and make sure
|
||||
to name the test class as: ``TestXXXWithoutRequest`` for tests that don't make a request, and ``TestXXXWithRequest`` for tests that do.
|
||||
|
||||
Writing tests is a creative process; allowing you to design your test however you'd like, but there
|
||||
are a few conventions that you should follow:
|
||||
|
||||
- Each new test class needs a ``test_slot_behaviour``, ``test_to_dict``, ``test_de_json`` and
|
||||
``test_equality`` (in most cases).
|
||||
|
||||
- Make use of pytest's fixtures and parametrize wherever possible. Having knowledge of pytest's
|
||||
tooling can help you as well. You can look at the existing tests for examples and inspiration.
|
||||
|
||||
- New fixtures should go into ``conftest.py``. New auxiliary functions and classes, used either directly in the tests or in the fixtures, should go into the ``tests/auxil`` directory.
|
||||
|
||||
If you have made some API changes, you may want to run ``test_official`` to validate that the changes are
|
||||
complete and correct. To run it, export an environment variable first:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export TEST_OFFICIAL=true
|
||||
|
||||
and then run ``pytest tests/test_official.py``.
|
||||
|
||||
We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
|
||||
Use as follows:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest -m dev
|
||||
|
||||
|
||||
Bots used in tests
|
||||
==================
|
||||
|
||||
If you run the tests locally, the test setup will use one of the two public bots available. Which
|
||||
bot of the two gets chosen for the test session is random. Whereas when the tests on the
|
||||
Github Actions CI are run, the test setup allocates a different, but same bot for every combination of Python version and
|
||||
OS.
|
||||
|
||||
Thus, number of bots used for testing locally is 2 (called as fallback bots), and on the CI,
|
||||
its [3.7, 3.8, 3.9, 3.10, 3.11] x [ubuntu-latest, macos-latest, windows-latest] = 15. Bringing the
|
||||
total number of bots used for testing to 17.
|
||||
|
||||
|
||||
That's it! If you have any questions, feel free to ask them in the `PTB dev
|
||||
group`_.
|
||||
|
||||
.. _pytest: https://docs.pytest.org/en/stable/
|
||||
.. _pytest-xdist: https://pypi.org/project/pytest-xdist/
|
||||
.. _PTB dev group: https://t.me/pythontelegrambotgroup
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,26 +31,26 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def animation_file():
|
||||
f = data_file("game.gif").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
async def animation(bot, chat_id):
|
||||
with data_file("game.gif").open("rb") as f:
|
||||
thumb = data_file("thumb.jpg")
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
async def animation(bot, chat_id):
|
||||
with data_file("game.gif").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb:
|
||||
return (
|
||||
await bot.send_animation(chat_id, animation=f, read_timeout=50, thumb=thumb.open("rb"))
|
||||
await bot.send_animation(chat_id, animation=f, read_timeout=50, thumbnail=thumb)
|
||||
).animation
|
||||
|
||||
|
||||
class TestAnimation:
|
||||
class TestAnimationBase:
|
||||
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
|
||||
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
width = 320
|
||||
@@ -63,7 +64,9 @@ class TestAnimation:
|
||||
file_size = 5859
|
||||
caption = "Test *animation*"
|
||||
|
||||
def test_slot_behaviour(self, animation, mro_slots):
|
||||
|
||||
class TestAnimationWithoutRequest(TestAnimationBase):
|
||||
def test_slot_behaviour(self, animation):
|
||||
for attr in animation.__slots__:
|
||||
assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(animation)) == len(set(mro_slots(animation))), "duplicate slot"
|
||||
@@ -78,9 +81,138 @@ class TestAnimation:
|
||||
def test_expected_values(self, animation):
|
||||
assert animation.mime_type == self.mime_type
|
||||
assert animation.file_name.startswith("game.gif") == self.file_name.startswith("game.gif")
|
||||
assert isinstance(animation.thumb, PhotoSize)
|
||||
assert isinstance(animation.thumbnail, PhotoSize)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
animation = Animation(
|
||||
self.animation_file_id,
|
||||
self.animation_file_unique_id,
|
||||
thumb=object(),
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
duration=self.duration,
|
||||
)
|
||||
assert animation.thumb is animation.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_de_json(self, bot, animation):
|
||||
json_dict = {
|
||||
"file_id": self.animation_file_id,
|
||||
"file_unique_id": self.animation_file_unique_id,
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"duration": self.duration,
|
||||
"thumbnail": animation.thumbnail.to_dict(),
|
||||
"file_name": self.file_name,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
}
|
||||
animation = Animation.de_json(json_dict, bot)
|
||||
assert animation.api_kwargs == {}
|
||||
assert animation.file_id == self.animation_file_id
|
||||
assert animation.file_unique_id == self.animation_file_unique_id
|
||||
assert animation.file_name == self.file_name
|
||||
assert animation.mime_type == self.mime_type
|
||||
assert animation.file_size == self.file_size
|
||||
|
||||
def test_to_dict(self, animation):
|
||||
animation_dict = animation.to_dict()
|
||||
|
||||
assert isinstance(animation_dict, dict)
|
||||
assert animation_dict["file_id"] == animation.file_id
|
||||
assert animation_dict["file_unique_id"] == animation.file_unique_id
|
||||
assert animation_dict["width"] == animation.width
|
||||
assert animation_dict["height"] == animation.height
|
||||
assert animation_dict["duration"] == animation.duration
|
||||
assert animation_dict["thumbnail"] == animation.thumbnail.to_dict()
|
||||
assert animation_dict["file_name"] == animation.file_name
|
||||
assert animation_dict["mime_type"] == animation.mime_type
|
||||
assert animation_dict["file_size"] == animation.file_size
|
||||
|
||||
def test_equality(self):
|
||||
a = Animation(
|
||||
self.animation_file_id,
|
||||
self.animation_file_unique_id,
|
||||
self.height,
|
||||
self.width,
|
||||
self.duration,
|
||||
)
|
||||
b = Animation("", self.animation_file_unique_id, self.height, self.width, self.duration)
|
||||
d = Animation("", "", 0, 0, 0)
|
||||
e = Voice(self.animation_file_id, self.animation_file_unique_id, 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_send_animation_custom_filename(self, bot, chat_id, animation_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_animation(chat_id, animation_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = (
|
||||
data.get("animation") == expected and data.get("thumbnail") == expected
|
||||
)
|
||||
else:
|
||||
test_flag = isinstance(data.get("animation"), InputFile) and isinstance(
|
||||
data.get("thumbnail"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_animation(chat_id, file, thumbnail=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
async def test_send_with_animation(self, monkeypatch, bot, chat_id, animation):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["animation"] == animation.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_animation(animation=animation, chat_id=chat_id)
|
||||
|
||||
async def test_send_animation_with_local_files_throws_error_with_different_thumb_and_thumbnail(
|
||||
self, bot, chat_id
|
||||
):
|
||||
file = data_file("telegram.jpg")
|
||||
different_file = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
with pytest.raises(ValueError, match="different entities as 'thumb' and 'thumbnail'"):
|
||||
await bot.send_animation(chat_id, file, thumbnail=file, thumb=different_file)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, animation):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == animation.file_id
|
||||
|
||||
assert check_shortcut_signature(Animation.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(animation.get_file, animation.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(animation.get_file, animation.get_bot())
|
||||
|
||||
monkeypatch.setattr(animation.get_bot(), "get_file", make_assertion)
|
||||
assert await animation.get_file()
|
||||
|
||||
|
||||
class TestAnimationWithRequest(TestAnimationBase):
|
||||
async def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file):
|
||||
message = await bot.send_animation(
|
||||
chat_id,
|
||||
@@ -92,7 +224,7 @@ class TestAnimation:
|
||||
parse_mode="Markdown",
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
thumb=thumb_file,
|
||||
thumbnail=thumb_file,
|
||||
has_spoiler=True,
|
||||
)
|
||||
|
||||
@@ -104,25 +236,14 @@ class TestAnimation:
|
||||
assert message.animation.file_name == animation.file_name
|
||||
assert message.animation.mime_type == animation.mime_type
|
||||
assert message.animation.file_size == animation.file_size
|
||||
assert message.animation.thumb.width == self.width
|
||||
assert message.animation.thumb.height == self.height
|
||||
assert message.animation.thumbnail.width == self.width
|
||||
assert message.animation.thumbnail.height == self.height
|
||||
assert message.has_protected_content
|
||||
try:
|
||||
assert message.has_media_spoiler
|
||||
except AssertionError:
|
||||
pytest.xfail("This is a bug on Telegram's end")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_animation_custom_filename(self, bot, chat_id, animation_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_animation(chat_id, animation_file, filename="custom_filename")
|
||||
monkeypatch.delattr(bot.request, "post")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, animation):
|
||||
path = Path("game.gif")
|
||||
if path.is_file():
|
||||
@@ -130,14 +251,11 @@ class TestAnimation:
|
||||
|
||||
new_file = await bot.get_file(animation.file_id)
|
||||
|
||||
assert new_file.file_id == animation.file_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
new_filepath = await new_file.download_to_drive("game.gif")
|
||||
|
||||
assert new_filepath.is_file()
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_animation_url_file(self, bot, chat_id, animation):
|
||||
message = await bot.send_animation(
|
||||
chat_id=chat_id, animation=self.animation_file_url, caption=self.caption
|
||||
@@ -157,7 +275,6 @@ class TestAnimation:
|
||||
) == animation.file_name.startswith("game.gif")
|
||||
assert message.animation.mime_type == animation.mime_type
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_animation_caption_entities(self, bot, chat_id, animation):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
@@ -172,7 +289,6 @@ class TestAnimation:
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_animation_default_parse_mode_1(self, default_bot, chat_id, animation_file):
|
||||
test_string = "Italic Bold Code"
|
||||
@@ -184,7 +300,6 @@ class TestAnimation:
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_animation_default_parse_mode_2(self, default_bot, chat_id, animation_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
@@ -195,7 +310,6 @@ class TestAnimation:
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_animation_default_parse_mode_3(self, default_bot, chat_id, animation_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
@@ -206,32 +320,6 @@ class TestAnimation:
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("animation") == expected and data.get("thumb") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("animation"), InputFile) and isinstance(
|
||||
data.get("thumb"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_animation(chat_id, file, thumb=file)
|
||||
assert test_flag
|
||||
monkeypatch.delattr(bot, "_post")
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -265,72 +353,26 @@ class TestAnimation:
|
||||
chat_id, animation, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_animation_default_protect_content(self, default_bot, chat_id, animation):
|
||||
animation_protected = await default_bot.send_animation(chat_id, animation)
|
||||
assert animation_protected.has_protected_content
|
||||
ani_unprotected = await default_bot.send_animation(
|
||||
chat_id, animation, protect_content=False
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_animation(chat_id, animation),
|
||||
default_bot.send_animation(chat_id, animation, protect_content=False),
|
||||
)
|
||||
assert not ani_unprotected.has_protected_content
|
||||
anim_protected, anim_unprotected = await tasks
|
||||
assert anim_protected.has_protected_content
|
||||
assert not anim_unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_resend(self, bot, chat_id, animation):
|
||||
message = await bot.send_animation(chat_id, animation.file_id)
|
||||
|
||||
assert message.animation == animation
|
||||
|
||||
async def test_send_with_animation(self, monkeypatch, bot, chat_id, animation):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["animation"] == animation.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_animation(animation=animation, chat_id=chat_id)
|
||||
assert message
|
||||
|
||||
def test_de_json(self, bot, animation):
|
||||
json_dict = {
|
||||
"file_id": self.animation_file_id,
|
||||
"file_unique_id": self.animation_file_unique_id,
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"duration": self.duration,
|
||||
"thumb": animation.thumb.to_dict(),
|
||||
"file_name": self.file_name,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
}
|
||||
animation = Animation.de_json(json_dict, bot)
|
||||
assert animation.api_kwargs == {}
|
||||
assert animation.file_id == self.animation_file_id
|
||||
assert animation.file_unique_id == self.animation_file_unique_id
|
||||
assert animation.file_name == self.file_name
|
||||
assert animation.mime_type == self.mime_type
|
||||
assert animation.file_size == self.file_size
|
||||
|
||||
def test_to_dict(self, animation):
|
||||
animation_dict = animation.to_dict()
|
||||
|
||||
assert isinstance(animation_dict, dict)
|
||||
assert animation_dict["file_id"] == animation.file_id
|
||||
assert animation_dict["file_unique_id"] == animation.file_unique_id
|
||||
assert animation_dict["width"] == animation.width
|
||||
assert animation_dict["height"] == animation.height
|
||||
assert animation_dict["duration"] == animation.duration
|
||||
assert animation_dict["thumb"] == animation.thumb.to_dict()
|
||||
assert animation_dict["file_name"] == animation.file_name
|
||||
assert animation_dict["mime_type"] == animation.mime_type
|
||||
assert animation_dict["file_size"] == animation.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
animation_file = open(os.devnull, "rb")
|
||||
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_animation(chat_id=chat_id, animation=animation_file)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_animation(chat_id=chat_id, animation="")
|
||||
@@ -338,36 +380,3 @@ class TestAnimation:
|
||||
async def test_error_send_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_animation(chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, animation):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == animation.file_id
|
||||
|
||||
assert check_shortcut_signature(Animation.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(animation.get_file, animation.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(animation.get_file, animation.get_bot())
|
||||
|
||||
monkeypatch.setattr(animation.get_bot(), "get_file", make_assertion)
|
||||
assert await animation.get_file()
|
||||
|
||||
def test_equality(self):
|
||||
a = Animation(
|
||||
self.animation_file_id,
|
||||
self.animation_file_unique_id,
|
||||
self.height,
|
||||
self.width,
|
||||
self.duration,
|
||||
)
|
||||
b = Animation("", self.animation_file_unique_id, self.height, self.width, self.duration)
|
||||
d = Animation("", "", 0, 0, 0)
|
||||
e = Voice(self.animation_file_id, self.animation_file_unique_id, 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,25 +31,24 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def audio_file():
|
||||
with open(data_file("telegram.mp3"), "rb") as f:
|
||||
with data_file("telegram.mp3").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
async def audio(bot, chat_id):
|
||||
with data_file("telegram.mp3").open("rb") as f:
|
||||
thumb = data_file("thumb.jpg")
|
||||
return (
|
||||
await bot.send_audio(chat_id, audio=f, read_timeout=50, thumb=thumb.open("rb"))
|
||||
).audio
|
||||
with data_file("telegram.mp3").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb:
|
||||
return (await bot.send_audio(chat_id, audio=f, read_timeout=50, thumbnail=thumb)).audio
|
||||
|
||||
|
||||
class TestAudio:
|
||||
class TestAudioBase:
|
||||
caption = "Test *audio*"
|
||||
performer = "Leandro Toledo"
|
||||
title = "Teste"
|
||||
@@ -65,7 +65,9 @@ class TestAudio:
|
||||
audio_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
|
||||
def test_slot_behaviour(self, audio, mro_slots):
|
||||
|
||||
class TestAudioWithoutRequest(TestAudioBase):
|
||||
def test_slot_behaviour(self, audio):
|
||||
for attr in audio.__slots__:
|
||||
assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(audio)) == len(set(mro_slots(audio))), "duplicate slot"
|
||||
@@ -84,179 +86,14 @@ class TestAudio:
|
||||
assert audio.title is None
|
||||
assert audio.mime_type == self.mime_type
|
||||
assert audio.file_size == self.file_size
|
||||
assert audio.thumb.file_size == self.thumb_file_size
|
||||
assert audio.thumb.width == self.thumb_width
|
||||
assert audio.thumb.height == self.thumb_height
|
||||
assert audio.thumbnail.file_size == self.thumb_file_size
|
||||
assert audio.thumbnail.width == self.thumb_width
|
||||
assert audio.thumbnail.height == self.thumb_height
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
|
||||
message = await bot.send_audio(
|
||||
chat_id,
|
||||
audio=audio_file,
|
||||
caption=self.caption,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
parse_mode="Markdown",
|
||||
thumb=thumb_file,
|
||||
)
|
||||
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
|
||||
assert isinstance(message.audio, Audio)
|
||||
assert isinstance(message.audio.file_id, str)
|
||||
assert isinstance(message.audio.file_unique_id, str)
|
||||
assert message.audio.file_unique_id is not None
|
||||
assert message.audio.file_id is not None
|
||||
assert message.audio.duration == self.duration
|
||||
assert message.audio.performer == self.performer
|
||||
assert message.audio.title == self.title
|
||||
assert message.audio.file_name == self.file_name
|
||||
assert message.audio.mime_type == self.mime_type
|
||||
assert message.audio.file_size == self.file_size
|
||||
assert message.audio.thumb.file_size == self.thumb_file_size
|
||||
assert message.audio.thumb.width == self.thumb_width
|
||||
assert message.audio.thumb.height == self.thumb_height
|
||||
assert message.has_protected_content
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_audio_custom_filename(self, bot, chat_id, audio_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_audio(chat_id, audio_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, audio):
|
||||
path = Path("telegram.mp3")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(audio.file_id)
|
||||
|
||||
assert new_file.file_size == self.file_size
|
||||
assert new_file.file_id == audio.file_id
|
||||
assert new_file.file_unique_id == audio.file_unique_id
|
||||
assert str(new_file.file_path).startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.mp3")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_mp3_url_file(self, bot, chat_id, audio):
|
||||
message = await bot.send_audio(
|
||||
chat_id=chat_id, audio=self.audio_file_url, caption=self.caption
|
||||
)
|
||||
|
||||
assert message.caption == self.caption
|
||||
|
||||
assert isinstance(message.audio, Audio)
|
||||
assert isinstance(message.audio.file_id, str)
|
||||
assert isinstance(message.audio.file_unique_id, str)
|
||||
assert message.audio.file_unique_id is not None
|
||||
assert message.audio.file_id is not None
|
||||
assert message.audio.duration == audio.duration
|
||||
assert message.audio.mime_type == audio.mime_type
|
||||
assert message.audio.file_size == audio.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_resend(self, bot, chat_id, audio):
|
||||
message = await bot.send_audio(chat_id=chat_id, audio=audio.file_id)
|
||||
|
||||
assert message.audio == audio
|
||||
|
||||
async def test_send_with_audio(self, monkeypatch, bot, chat_id, audio):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["audio"] == audio.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_audio(audio=audio, chat_id=chat_id)
|
||||
assert message
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_audio_caption_entities(self, bot, chat_id, audio):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_audio(
|
||||
chat_id, audio, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_audio_default_parse_mode_1(self, default_bot, chat_id, audio_file):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_audio(
|
||||
chat_id, audio_file, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_audio(
|
||||
chat_id, audio_file, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_audio_default_protect_content(self, default_bot, chat_id, audio):
|
||||
protected_audio = await default_bot.send_audio(chat_id, audio)
|
||||
assert protected_audio.has_protected_content
|
||||
unprotected = await default_bot.send_audio(chat_id, audio, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("audio") == expected and data.get("thumb") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("audio"), InputFile) and isinstance(
|
||||
data.get("thumb"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_audio(chat_id, file, thumb=file)
|
||||
assert test_flag
|
||||
monkeypatch.delattr(bot, "_post")
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
audio = Audio(self.audio_file_id, self.audio_file_unique_id, self.duration, thumb=object())
|
||||
assert audio.thumb is audio.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_de_json(self, bot, audio):
|
||||
json_dict = {
|
||||
@@ -268,7 +105,7 @@ class TestAudio:
|
||||
"file_name": self.file_name,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
"thumb": audio.thumb.to_dict(),
|
||||
"thumbnail": audio.thumbnail.to_dict(),
|
||||
}
|
||||
json_audio = Audio.de_json(json_dict, bot)
|
||||
assert json_audio.api_kwargs == {}
|
||||
@@ -281,7 +118,7 @@ class TestAudio:
|
||||
assert json_audio.file_name == self.file_name
|
||||
assert json_audio.mime_type == self.mime_type
|
||||
assert json_audio.file_size == self.file_size
|
||||
assert json_audio.thumb == audio.thumb
|
||||
assert json_audio.thumbnail == audio.thumbnail
|
||||
|
||||
def test_to_dict(self, audio):
|
||||
audio_dict = audio.to_dict()
|
||||
@@ -294,33 +131,6 @@ class TestAudio:
|
||||
assert audio_dict["file_size"] == audio.file_size
|
||||
assert audio_dict["file_name"] == audio.file_name
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
audio_file = open(os.devnull, "rb")
|
||||
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_audio(chat_id=chat_id, audio=audio_file)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_audio(chat_id=chat_id, audio="")
|
||||
|
||||
async def test_error_send_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_audio(chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, audio):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == audio.file_id
|
||||
|
||||
assert check_shortcut_signature(Audio.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(audio.get_file, audio.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(audio.get_file, audio.get_bot())
|
||||
|
||||
monkeypatch.setattr(audio._bot, "get_file", make_assertion)
|
||||
assert await audio.get_file()
|
||||
|
||||
def test_equality(self, audio):
|
||||
a = Audio(audio.file_id, audio.file_unique_id, audio.duration)
|
||||
b = Audio("", audio.file_unique_id, audio.duration)
|
||||
@@ -340,3 +150,196 @@ class TestAudio:
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_send_with_audio(self, monkeypatch, bot, chat_id, audio):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["audio"] == audio.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_audio(audio=audio, chat_id=chat_id)
|
||||
|
||||
async def test_send_audio_custom_filename(self, bot, chat_id, audio_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_audio(chat_id, audio_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("audio") == expected and data.get("thumbnail") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("audio"), InputFile) and isinstance(
|
||||
data.get("thumbnail"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_audio(chat_id, file, thumbnail=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
async def test_send_audio_with_local_files_throws_error_with_different_thumb_and_thumbnail(
|
||||
self, bot, chat_id
|
||||
):
|
||||
file = data_file("telegram.jpg")
|
||||
different_file = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
with pytest.raises(ValueError, match="different entities as 'thumb' and 'thumbnail'"):
|
||||
await bot.send_audio(chat_id, file, thumbnail=file, thumb=different_file)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, audio):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == audio.file_id
|
||||
|
||||
assert check_shortcut_signature(Audio.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(audio.get_file, audio.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(audio.get_file, audio.get_bot())
|
||||
|
||||
monkeypatch.setattr(audio._bot, "get_file", make_assertion)
|
||||
assert await audio.get_file()
|
||||
|
||||
|
||||
class TestAudioWithRequest(TestAudioBase):
|
||||
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
|
||||
message = await bot.send_audio(
|
||||
chat_id,
|
||||
audio=audio_file,
|
||||
caption=self.caption,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
parse_mode="Markdown",
|
||||
thumbnail=thumb_file,
|
||||
)
|
||||
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
|
||||
assert isinstance(message.audio, Audio)
|
||||
assert isinstance(message.audio.file_id, str)
|
||||
assert isinstance(message.audio.file_unique_id, str)
|
||||
assert message.audio.file_unique_id is not None
|
||||
assert message.audio.file_id is not None
|
||||
assert message.audio.duration == self.duration
|
||||
assert message.audio.performer == self.performer
|
||||
assert message.audio.title == self.title
|
||||
assert message.audio.file_name == self.file_name
|
||||
assert message.audio.mime_type == self.mime_type
|
||||
assert message.audio.file_size == self.file_size
|
||||
assert message.audio.thumbnail.file_size == self.thumb_file_size
|
||||
assert message.audio.thumbnail.width == self.thumb_width
|
||||
assert message.audio.thumbnail.height == self.thumb_height
|
||||
assert message.has_protected_content
|
||||
|
||||
async def test_get_and_download(self, bot, chat_id, audio):
|
||||
path = Path("telegram.mp3")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(audio.file_id)
|
||||
|
||||
assert new_file.file_size == self.file_size
|
||||
assert new_file.file_unique_id == audio.file_unique_id
|
||||
assert str(new_file.file_path).startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.mp3")
|
||||
assert path.is_file()
|
||||
|
||||
async def test_send_mp3_url_file(self, bot, chat_id, audio):
|
||||
message = await bot.send_audio(
|
||||
chat_id=chat_id, audio=self.audio_file_url, caption=self.caption
|
||||
)
|
||||
|
||||
assert message.caption == self.caption
|
||||
|
||||
assert isinstance(message.audio, Audio)
|
||||
assert isinstance(message.audio.file_id, str)
|
||||
assert isinstance(message.audio.file_unique_id, str)
|
||||
assert message.audio.file_unique_id is not None
|
||||
assert message.audio.file_id is not None
|
||||
assert message.audio.duration == audio.duration
|
||||
assert message.audio.mime_type == audio.mime_type
|
||||
assert message.audio.file_size == audio.file_size
|
||||
|
||||
async def test_send_audio_caption_entities(self, bot, chat_id, audio):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_audio(
|
||||
chat_id, audio, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_audio_default_parse_mode_1(self, default_bot, chat_id, audio_file):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_audio(
|
||||
chat_id, audio_file, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_audio(
|
||||
chat_id, audio_file, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_audio_default_protect_content(self, default_bot, chat_id, audio):
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_audio(chat_id, audio),
|
||||
default_bot.send_audio(chat_id, audio, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
async def test_resend(self, bot, chat_id, audio):
|
||||
message = await bot.send_audio(chat_id=chat_id, audio=audio.file_id)
|
||||
assert message.audio == audio
|
||||
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
audio_file = open(os.devnull, "rb")
|
||||
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_audio(chat_id=chat_id, audio=audio_file)
|
||||
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_audio(chat_id=chat_id, audio="")
|
||||
|
||||
async def test_error_send_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_audio(chat_id=chat_id)
|
||||
@@ -17,6 +17,7 @@
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,17 +31,18 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file, expect_bad_request
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import expect_bad_request
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def chatphoto_file():
|
||||
f = data_file("telegram.jpg").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
with data_file("telegram.jpg").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@pytest.fixture(scope="module")
|
||||
async def chat_photo(bot, super_group_id):
|
||||
async def func():
|
||||
return (await bot.get_chat(super_group_id, read_timeout=50)).photo
|
||||
@@ -50,61 +52,20 @@ async def chat_photo(bot, super_group_id):
|
||||
)
|
||||
|
||||
|
||||
class TestChatPhoto:
|
||||
class TestChatPhotoBase:
|
||||
chatphoto_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI"
|
||||
chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
|
||||
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
|
||||
chatphoto_big_file_unique_id = "bigadc3145fd2e84d95b64d68eaa22aa33e"
|
||||
chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg"
|
||||
|
||||
def test_slot_behaviour(self, chat_photo, mro_slots):
|
||||
|
||||
class TestChatPhotoWithoutRequest(TestChatPhotoBase):
|
||||
def test_slot_behaviour(self, chat_photo):
|
||||
for attr in chat_photo.__slots__:
|
||||
assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(chat_photo)) == len(set(mro_slots(chat_photo))), "duplicate slot"
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_all_args(
|
||||
self, bot, super_group_id, chatphoto_file, chat_photo, thumb_file
|
||||
):
|
||||
async def func():
|
||||
assert await bot.set_chat_photo(super_group_id, chatphoto_file)
|
||||
|
||||
await expect_bad_request(
|
||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, chat_photo):
|
||||
jpg_file = Path("telegram.jpg")
|
||||
if jpg_file.is_file():
|
||||
jpg_file.unlink()
|
||||
|
||||
new_file = await bot.get_file(chat_photo.small_file_id)
|
||||
|
||||
assert new_file.file_unique_id == chat_photo.small_file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive(jpg_file)
|
||||
|
||||
assert jpg_file.is_file()
|
||||
|
||||
new_file = await bot.get_file(chat_photo.big_file_id)
|
||||
|
||||
assert new_file.file_unique_id == chat_photo.big_file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive(jpg_file)
|
||||
|
||||
assert jpg_file.is_file()
|
||||
|
||||
async def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.parameters["photo"] == chat_photo.to_dict()
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
|
||||
assert message
|
||||
|
||||
def test_de_json(self, bot, chat_photo):
|
||||
json_dict = {
|
||||
"small_file_id": self.chatphoto_small_file_id,
|
||||
@@ -128,46 +89,6 @@ class TestChatPhoto:
|
||||
assert chat_photo_dict["small_file_unique_id"] == chat_photo.small_file_unique_id
|
||||
assert chat_photo_dict["big_file_unique_id"] == chat_photo.big_file_unique_id
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, super_group_id):
|
||||
chatphoto_file = open(os.devnull, "rb")
|
||||
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, super_group_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.set_chat_photo(chat_id=super_group_id, photo="")
|
||||
|
||||
async def test_error_send_without_required_args(self, bot, super_group_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.set_chat_photo(chat_id=super_group_id)
|
||||
|
||||
async def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == chat_photo.small_file_id
|
||||
|
||||
assert check_shortcut_signature(ChatPhoto.get_small_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(
|
||||
chat_photo.get_small_file, chat_photo.get_bot(), "get_file"
|
||||
)
|
||||
assert await check_defaults_handling(chat_photo.get_small_file, chat_photo.get_bot())
|
||||
|
||||
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
|
||||
assert await chat_photo.get_small_file()
|
||||
|
||||
async def test_get_big_file_instance_method(self, monkeypatch, chat_photo):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == chat_photo.big_file_id
|
||||
|
||||
assert check_shortcut_signature(ChatPhoto.get_big_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(chat_photo.get_big_file, chat_photo.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(chat_photo.get_big_file, chat_photo.get_bot())
|
||||
|
||||
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
|
||||
assert await chat_photo.get_big_file()
|
||||
|
||||
def test_equality(self):
|
||||
a = ChatPhoto(
|
||||
self.chatphoto_small_file_id,
|
||||
@@ -199,3 +120,80 @@ class TestChatPhoto:
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.parameters["photo"] == chat_photo.to_dict()
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
|
||||
assert message
|
||||
|
||||
async def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == chat_photo.small_file_id
|
||||
|
||||
assert check_shortcut_signature(ChatPhoto.get_small_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(
|
||||
chat_photo.get_small_file, chat_photo.get_bot(), "get_file"
|
||||
)
|
||||
assert await check_defaults_handling(chat_photo.get_small_file, chat_photo.get_bot())
|
||||
|
||||
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
|
||||
assert await chat_photo.get_small_file()
|
||||
|
||||
async def test_get_big_file_instance_method(self, monkeypatch, chat_photo):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == chat_photo.big_file_id
|
||||
|
||||
assert check_shortcut_signature(ChatPhoto.get_big_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(chat_photo.get_big_file, chat_photo.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(chat_photo.get_big_file, chat_photo.get_bot())
|
||||
|
||||
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
|
||||
assert await chat_photo.get_big_file()
|
||||
|
||||
|
||||
class TestChatPhotoWithRequest:
|
||||
async def test_get_and_download(self, bot, chat_photo):
|
||||
jpg_file = Path("telegram.jpg")
|
||||
if jpg_file.is_file():
|
||||
jpg_file.unlink()
|
||||
|
||||
tasks = {bot.get_file(chat_photo.small_file_id), bot.get_file(chat_photo.big_file_id)}
|
||||
asserts = []
|
||||
|
||||
for task in asyncio.as_completed(tasks):
|
||||
file = await task
|
||||
if file.file_unique_id == chat_photo.small_file_unique_id:
|
||||
asserts.append("small")
|
||||
elif file.file_unique_id == chat_photo.big_file_unique_id:
|
||||
asserts.append("big")
|
||||
assert file.file_path.startswith("https://")
|
||||
|
||||
await file.download_to_drive(jpg_file)
|
||||
assert jpg_file.is_file()
|
||||
|
||||
assert "small" in asserts and "big" in asserts
|
||||
|
||||
async def test_send_all_args(self, bot, super_group_id, chatphoto_file):
|
||||
async def func():
|
||||
assert await bot.set_chat_photo(super_group_id, chatphoto_file)
|
||||
|
||||
await expect_bad_request(
|
||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||
)
|
||||
|
||||
async def test_error_send_empty_file(self, bot, super_group_id):
|
||||
chatphoto_file = open(os.devnull, "rb")
|
||||
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file)
|
||||
|
||||
async def test_error_send_empty_file_id(self, bot, super_group_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.set_chat_photo(chat_id=super_group_id, photo="")
|
||||
|
||||
async def test_error_send_without_required_args(self, bot, super_group_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.set_chat_photo(chat_id=super_group_id)
|
||||
@@ -17,30 +17,35 @@
|
||||
# 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 pytest
|
||||
|
||||
from telegram import Contact, Voice
|
||||
from telegram.error import BadRequest
|
||||
from telegram.request import RequestData
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def contact():
|
||||
return Contact(
|
||||
TestContact.phone_number,
|
||||
TestContact.first_name,
|
||||
TestContact.last_name,
|
||||
TestContact.user_id,
|
||||
TestContactBase.phone_number,
|
||||
TestContactBase.first_name,
|
||||
TestContactBase.last_name,
|
||||
TestContactBase.user_id,
|
||||
)
|
||||
|
||||
|
||||
class TestContact:
|
||||
class TestContactBase:
|
||||
phone_number = "+11234567890"
|
||||
first_name = "Leandro"
|
||||
last_name = "Toledo"
|
||||
user_id = 23
|
||||
|
||||
def test_slot_behaviour(self, contact, mro_slots):
|
||||
|
||||
class TestContactWithoutRequest(TestContactBase):
|
||||
def test_slot_behaviour(self, contact):
|
||||
for attr in contact.__slots__:
|
||||
assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(contact)) == len(set(mro_slots(contact))), "duplicate slot"
|
||||
@@ -68,6 +73,48 @@ class TestContact:
|
||||
assert contact.last_name == self.last_name
|
||||
assert contact.user_id == self.user_id
|
||||
|
||||
def test_to_dict(self, contact):
|
||||
contact_dict = contact.to_dict()
|
||||
|
||||
assert isinstance(contact_dict, dict)
|
||||
assert contact_dict["phone_number"] == contact.phone_number
|
||||
assert contact_dict["first_name"] == contact.first_name
|
||||
assert contact_dict["last_name"] == contact.last_name
|
||||
assert contact_dict["user_id"] == contact.user_id
|
||||
|
||||
def test_equality(self):
|
||||
a = Contact(self.phone_number, self.first_name)
|
||||
b = Contact(self.phone_number, self.first_name)
|
||||
c = Contact(self.phone_number, "")
|
||||
d = Contact("", self.first_name)
|
||||
e = Voice("", "unique_id", 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_send_contact_without_required(self, bot, chat_id):
|
||||
with pytest.raises(ValueError, match="Either contact or phone_number and first_name"):
|
||||
await bot.send_contact(chat_id=chat_id)
|
||||
|
||||
async def test_send_mutually_exclusive(self, bot, chat_id, contact):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.send_contact(
|
||||
chat_id=chat_id,
|
||||
contact=contact,
|
||||
phone_number=contact.phone_number,
|
||||
first_name=contact.first_name,
|
||||
)
|
||||
|
||||
async def test_send_with_contact(self, monkeypatch, bot, chat_id, contact):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
data = request_data.json_parameters
|
||||
@@ -77,10 +124,10 @@ class TestContact:
|
||||
return phone and first and last
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_contact(contact=contact, chat_id=chat_id)
|
||||
assert message
|
||||
assert await bot.send_contact(contact=contact, chat_id=chat_id)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
|
||||
class TestContactWithRequest(TestContactBase):
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -114,54 +161,12 @@ class TestContact:
|
||||
chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_contact_default_protect_content(self, chat_id, default_bot, contact):
|
||||
protected = await default_bot.send_contact(chat_id, contact=contact)
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_contact(
|
||||
chat_id, contact=contact, protect_content=False
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_contact(chat_id, contact=contact),
|
||||
default_bot.send_contact(chat_id, contact=contact, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
async def test_send_contact_without_required(self, bot, chat_id):
|
||||
with pytest.raises(ValueError, match="Either contact or phone_number and first_name"):
|
||||
await bot.send_contact(chat_id=chat_id)
|
||||
|
||||
async def test_send_mutually_exclusive(self, bot, chat_id, contact):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.send_contact(
|
||||
chat_id=chat_id,
|
||||
contact=contact,
|
||||
phone_number=contact.phone_number,
|
||||
first_name=contact.first_name,
|
||||
)
|
||||
|
||||
def test_to_dict(self, contact):
|
||||
contact_dict = contact.to_dict()
|
||||
|
||||
assert isinstance(contact_dict, dict)
|
||||
assert contact_dict["phone_number"] == contact.phone_number
|
||||
assert contact_dict["first_name"] == contact.first_name
|
||||
assert contact_dict["last_name"] == contact.last_name
|
||||
assert contact_dict["user_id"] == contact.user_id
|
||||
|
||||
def test_equality(self):
|
||||
a = Contact(self.phone_number, self.first_name)
|
||||
b = Contact(self.phone_number, self.first_name)
|
||||
c = Contact(self.phone_number, "")
|
||||
d = Contact("", self.first_name)
|
||||
e = Voice("", "unique_id", 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,23 +31,24 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def document_file():
|
||||
f = data_file("telegram.png").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
with data_file("telegram.png").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
async def document(bot, chat_id):
|
||||
with data_file("telegram.png").open("rb") as f:
|
||||
return (await bot.send_document(chat_id, document=f, read_timeout=50)).document
|
||||
|
||||
|
||||
class TestDocument:
|
||||
class TestDocumentBase:
|
||||
caption = "DocumentTest - *Caption*"
|
||||
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
|
||||
file_size = 12948
|
||||
@@ -58,7 +60,9 @@ class TestDocument:
|
||||
document_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
|
||||
def test_slot_behaviour(self, document, mro_slots):
|
||||
|
||||
class TestDocumentWithoutRequest(TestDocumentBase):
|
||||
def test_slot_behaviour(self, document):
|
||||
for attr in document.__slots__:
|
||||
assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(document)) == len(set(mro_slots(document))), "duplicate slot"
|
||||
@@ -74,75 +78,63 @@ class TestDocument:
|
||||
assert document.file_size == self.file_size
|
||||
assert document.mime_type == self.mime_type
|
||||
assert document.file_name == self.file_name
|
||||
assert document.thumb.file_size == self.thumb_file_size
|
||||
assert document.thumb.width == self.thumb_width
|
||||
assert document.thumb.height == self.thumb_height
|
||||
assert document.thumbnail.file_size == self.thumb_file_size
|
||||
assert document.thumbnail.width == self.thumb_width
|
||||
assert document.thumbnail.height == self.thumb_height
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_all_args(self, bot, chat_id, document_file, document, thumb_file):
|
||||
message = await bot.send_document(
|
||||
chat_id,
|
||||
document=document_file,
|
||||
caption=self.caption,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
filename="telegram_custom.png",
|
||||
parse_mode="Markdown",
|
||||
thumb=thumb_file,
|
||||
)
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
document = Document(file_id="file_id", file_unique_id="file_unique_id", thumb=object())
|
||||
assert document.thumb is document.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
assert isinstance(message.document, Document)
|
||||
assert isinstance(message.document.file_id, str)
|
||||
assert message.document.file_id != ""
|
||||
assert isinstance(message.document.file_unique_id, str)
|
||||
assert message.document.file_unique_id != ""
|
||||
assert isinstance(message.document.thumb, PhotoSize)
|
||||
assert message.document.file_name == "telegram_custom.png"
|
||||
assert message.document.mime_type == document.mime_type
|
||||
assert message.document.file_size == document.file_size
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
assert message.document.thumb.width == self.thumb_width
|
||||
assert message.document.thumb.height == self.thumb_height
|
||||
assert message.has_protected_content
|
||||
def test_de_json(self, bot, document):
|
||||
json_dict = {
|
||||
"file_id": self.document_file_id,
|
||||
"file_unique_id": self.document_file_unique_id,
|
||||
"thumbnail": document.thumbnail.to_dict(),
|
||||
"file_name": self.file_name,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
}
|
||||
test_document = Document.de_json(json_dict, bot)
|
||||
assert test_document.api_kwargs == {}
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, document):
|
||||
path = Path("telegram.png")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
assert test_document.file_id == self.document_file_id
|
||||
assert test_document.file_unique_id == self.document_file_unique_id
|
||||
assert test_document.thumbnail == document.thumbnail
|
||||
assert test_document.file_name == self.file_name
|
||||
assert test_document.mime_type == self.mime_type
|
||||
assert test_document.file_size == self.file_size
|
||||
|
||||
new_file = await bot.get_file(document.file_id)
|
||||
def test_to_dict(self, document):
|
||||
document_dict = document.to_dict()
|
||||
|
||||
assert new_file.file_size == document.file_size
|
||||
assert new_file.file_id == document.file_id
|
||||
assert new_file.file_unique_id == document.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
assert isinstance(document_dict, dict)
|
||||
assert document_dict["file_id"] == document.file_id
|
||||
assert document_dict["file_unique_id"] == document.file_unique_id
|
||||
assert document_dict["file_name"] == document.file_name
|
||||
assert document_dict["mime_type"] == document.mime_type
|
||||
assert document_dict["file_size"] == document.file_size
|
||||
|
||||
await new_file.download_to_drive("telegram.png")
|
||||
def test_equality(self, document):
|
||||
a = Document(document.file_id, document.file_unique_id)
|
||||
b = Document("", document.file_unique_id)
|
||||
d = Document("", "")
|
||||
e = Voice(document.file_id, document.file_unique_id, 0)
|
||||
|
||||
assert path.is_file()
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_url_gif_file(self, bot, chat_id):
|
||||
message = await bot.send_document(chat_id, self.document_file_url)
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
document = message.document
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
assert isinstance(document, Document)
|
||||
assert isinstance(document.file_id, str)
|
||||
assert document.file_id != ""
|
||||
assert isinstance(message.document.file_unique_id, str)
|
||||
assert message.document.file_unique_id != ""
|
||||
assert isinstance(document.thumb, PhotoSize)
|
||||
assert document.file_name == "telegram.gif"
|
||||
assert document.mime_type == "image/gif"
|
||||
assert document.file_size == 3878
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_resend(self, bot, chat_id, document):
|
||||
message = await bot.send_document(chat_id=chat_id, document=document.file_id)
|
||||
|
||||
assert message.document == document
|
||||
async def test_error_send_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_document(chat_id=chat_id)
|
||||
|
||||
@pytest.mark.parametrize("disable_content_type_detection", [True, False, None])
|
||||
async def test_send_with_document(
|
||||
@@ -165,7 +157,133 @@ class TestDocument:
|
||||
|
||||
assert message
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_document_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = (
|
||||
data.get("document") == expected and data.get("thumbnail") == expected
|
||||
)
|
||||
else:
|
||||
test_flag = isinstance(data.get("document"), InputFile) and isinstance(
|
||||
data.get("thumbnail"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_document(chat_id, file, thumbnail=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
async def test_send_document_with_local_files_throws_error_with_different_thumb_and_thumbnail(
|
||||
self, bot, chat_id
|
||||
):
|
||||
file = data_file("telegram.jpg")
|
||||
different_file = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
with pytest.raises(ValueError, match="different entities as 'thumb' and 'thumbnail'"):
|
||||
await bot.send_document(chat_id, file, thumbnail=file, thumb=different_file)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, document):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == document.file_id
|
||||
|
||||
assert check_shortcut_signature(Document.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(document.get_file, document.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(document.get_file, document.get_bot())
|
||||
|
||||
monkeypatch.setattr(document.get_bot(), "get_file", make_assertion)
|
||||
assert await document.get_file()
|
||||
|
||||
|
||||
class TestDocumentWithRequest(TestDocumentBase):
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with open(os.devnull, "rb") as f:
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_document(chat_id=chat_id, document=f)
|
||||
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_document(chat_id=chat_id, document="")
|
||||
|
||||
async def test_get_and_download(self, bot, document, chat_id):
|
||||
path = Path("telegram.png")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(document.file_id)
|
||||
|
||||
assert new_file.file_size == document.file_size
|
||||
assert new_file.file_unique_id == document.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.png")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
async def test_send_resend(self, bot, chat_id, document):
|
||||
message = await bot.send_document(chat_id=chat_id, document=document.file_id)
|
||||
assert message.document == document
|
||||
|
||||
async def test_send_all_args(self, bot, chat_id, document_file, document, thumb_file):
|
||||
message = await bot.send_document(
|
||||
chat_id,
|
||||
document=document_file,
|
||||
caption=self.caption,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
filename="telegram_custom.png",
|
||||
parse_mode="Markdown",
|
||||
thumbnail=thumb_file,
|
||||
)
|
||||
|
||||
assert isinstance(message.document, Document)
|
||||
assert isinstance(message.document.file_id, str)
|
||||
assert message.document.file_id != ""
|
||||
assert isinstance(message.document.file_unique_id, str)
|
||||
assert message.document.file_unique_id != ""
|
||||
assert isinstance(message.document.thumbnail, PhotoSize)
|
||||
assert message.document.file_name == "telegram_custom.png"
|
||||
assert message.document.mime_type == document.mime_type
|
||||
assert message.document.file_size == document.file_size
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
assert message.document.thumbnail.width == self.thumb_width
|
||||
assert message.document.thumbnail.height == self.thumb_height
|
||||
assert message.has_protected_content
|
||||
|
||||
async def test_send_url_gif_file(self, bot, chat_id):
|
||||
message = await bot.send_document(chat_id, self.document_file_url)
|
||||
|
||||
document = message.document
|
||||
|
||||
assert isinstance(document, Document)
|
||||
assert isinstance(document.file_id, str)
|
||||
assert document.file_id != ""
|
||||
assert isinstance(message.document.file_unique_id, str)
|
||||
assert message.document.file_unique_id != ""
|
||||
assert isinstance(document.thumbnail, PhotoSize)
|
||||
assert document.file_name == "telegram.gif"
|
||||
assert document.mime_type == "image/gif"
|
||||
assert document.file_size == 3878
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_document_default_protect_content(self, chat_id, default_bot, document):
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_document(chat_id, document),
|
||||
default_bot.send_document(chat_id, document, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
async def test_send_document_caption_entities(self, bot, chat_id, document):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
@@ -180,7 +298,6 @@ class TestDocument:
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_document_default_parse_mode_1(self, default_bot, chat_id, document):
|
||||
test_string = "Italic Bold Code"
|
||||
@@ -190,7 +307,6 @@ class TestDocument:
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_document_default_parse_mode_2(self, default_bot, chat_id, document):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
@@ -201,7 +317,6 @@ class TestDocument:
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_document_default_parse_mode_3(self, default_bot, chat_id, document):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
@@ -212,7 +327,6 @@ class TestDocument:
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -245,106 +359,3 @@ class TestDocument:
|
||||
await default_bot.send_document(
|
||||
chat_id, document, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_document_default_protect_content(self, chat_id, default_bot, document):
|
||||
protected = await default_bot.send_document(chat_id, document)
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_document(chat_id, document, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_document_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("document") == expected and data.get("thumb") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("document"), InputFile) and isinstance(
|
||||
data.get("thumb"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_document(chat_id, file, thumb=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
def test_de_json(self, bot, document):
|
||||
json_dict = {
|
||||
"file_id": self.document_file_id,
|
||||
"file_unique_id": self.document_file_unique_id,
|
||||
"thumb": document.thumb.to_dict(),
|
||||
"file_name": self.file_name,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
}
|
||||
test_document = Document.de_json(json_dict, bot)
|
||||
assert test_document.api_kwargs == {}
|
||||
|
||||
assert test_document.file_id == self.document_file_id
|
||||
assert test_document.file_unique_id == self.document_file_unique_id
|
||||
assert test_document.thumb == document.thumb
|
||||
assert test_document.file_name == self.file_name
|
||||
assert test_document.mime_type == self.mime_type
|
||||
assert test_document.file_size == self.file_size
|
||||
|
||||
def test_to_dict(self, document):
|
||||
document_dict = document.to_dict()
|
||||
|
||||
assert isinstance(document_dict, dict)
|
||||
assert document_dict["file_id"] == document.file_id
|
||||
assert document_dict["file_unique_id"] == document.file_unique_id
|
||||
assert document_dict["file_name"] == document.file_name
|
||||
assert document_dict["mime_type"] == document.mime_type
|
||||
assert document_dict["file_size"] == document.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with open(os.devnull, "rb") as f:
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_document(chat_id=chat_id, document=f)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_document(chat_id=chat_id, document="")
|
||||
|
||||
async def test_error_send_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_document(chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, document):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == document.file_id
|
||||
|
||||
assert check_shortcut_signature(Document.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(document.get_file, document.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(document.get_file, document.get_bot())
|
||||
|
||||
monkeypatch.setattr(document.get_bot(), "get_file", make_assertion)
|
||||
assert await document.get_file()
|
||||
|
||||
def test_equality(self, document):
|
||||
a = Document(document.file_id, document.file_unique_id)
|
||||
b = Document("", document.file_unique_id)
|
||||
d = Document("", "")
|
||||
e = Voice(document.file_id, document.file_unique_id, 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -24,23 +24,24 @@ import pytest
|
||||
|
||||
from telegram import File, FileCredentials, Voice
|
||||
from telegram.error import TelegramError
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def file(bot):
|
||||
file = File(
|
||||
TestFile.file_id,
|
||||
TestFile.file_unique_id,
|
||||
file_path=TestFile.file_path,
|
||||
file_size=TestFile.file_size,
|
||||
TestFileBase.file_id,
|
||||
TestFileBase.file_unique_id,
|
||||
file_path=TestFileBase.file_path,
|
||||
file_size=TestFileBase.file_size,
|
||||
)
|
||||
file.set_bot(bot)
|
||||
file._unfreeze()
|
||||
return file
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def encrypted_file(bot):
|
||||
# check https://github.com/python-telegram-bot/python-telegram-bot/wiki/\
|
||||
# PTB-test-writing-knowledge-base#how-to-generate-encrypted-passport-files
|
||||
@@ -49,13 +50,18 @@ def encrypted_file(bot):
|
||||
"Oq3G4sX+bKZthoyms1YlPqvWou9esb+z0Bi/KqQUG8s=",
|
||||
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
|
||||
)
|
||||
ef = File(TestFile.file_id, TestFile.file_unique_id, TestFile.file_size, TestFile.file_path)
|
||||
ef = File(
|
||||
TestFileBase.file_id,
|
||||
TestFileBase.file_unique_id,
|
||||
TestFileBase.file_size,
|
||||
TestFileBase.file_path,
|
||||
)
|
||||
ef.set_bot(bot)
|
||||
ef.set_credentials(fc)
|
||||
return ef
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def encrypted_local_file(bot):
|
||||
# check encrypted_file() for the source of the fc values
|
||||
fc = FileCredentials(
|
||||
@@ -63,9 +69,9 @@ def encrypted_local_file(bot):
|
||||
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
|
||||
)
|
||||
ef = File(
|
||||
TestFile.file_id,
|
||||
TestFile.file_unique_id,
|
||||
TestFile.file_size,
|
||||
TestFileBase.file_id,
|
||||
TestFileBase.file_unique_id,
|
||||
TestFileBase.file_size,
|
||||
file_path=str(data_file("image_encrypted.jpg")),
|
||||
)
|
||||
ef.set_bot(bot)
|
||||
@@ -73,19 +79,19 @@ def encrypted_local_file(bot):
|
||||
return ef
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def local_file(bot):
|
||||
file = File(
|
||||
TestFile.file_id,
|
||||
TestFile.file_unique_id,
|
||||
TestFileBase.file_id,
|
||||
TestFileBase.file_unique_id,
|
||||
file_path=str(data_file("local_file.txt")),
|
||||
file_size=TestFile.file_size,
|
||||
file_size=TestFileBase.file_size,
|
||||
)
|
||||
file.set_bot(bot)
|
||||
return file
|
||||
|
||||
|
||||
class TestFile:
|
||||
class TestFileBase:
|
||||
file_id = "NOTVALIDDOESNOTMATTER"
|
||||
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
file_path = (
|
||||
@@ -94,7 +100,9 @@ class TestFile:
|
||||
file_size = 28232
|
||||
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
|
||||
|
||||
def test_slot_behaviour(self, file, mro_slots):
|
||||
|
||||
class TestFileWithoutRequest(TestFileBase):
|
||||
def test_slot_behaviour(self, file):
|
||||
for attr in file.__slots__:
|
||||
assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(file)) == len(set(mro_slots(file))), "duplicate slot"
|
||||
@@ -123,10 +131,25 @@ class TestFile:
|
||||
assert file_dict["file_path"] == file.file_path
|
||||
assert file_dict["file_size"] == file.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_get_empty_file_id(self, bot):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.get_file(file_id="")
|
||||
def test_equality(self, bot):
|
||||
a = File(self.file_id, self.file_unique_id, bot)
|
||||
b = File("", self.file_unique_id, bot)
|
||||
c = File(self.file_id, self.file_unique_id, None)
|
||||
d = File("", "", bot)
|
||||
e = Voice(self.file_id, self.file_unique_id, 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_download(self, monkeypatch, file):
|
||||
async def test(*args, **kwargs):
|
||||
@@ -140,9 +163,6 @@ class TestFile:
|
||||
finally:
|
||||
out_file.unlink()
|
||||
|
||||
async def test_download_local_file(self, local_file):
|
||||
assert await local_file.download_to_drive() == Path(local_file.file_path)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
||||
)
|
||||
@@ -161,20 +181,6 @@ class TestFile:
|
||||
os.close(file_handle)
|
||||
custom_path.unlink()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
||||
)
|
||||
async def test_download_custom_path_local_file(self, local_file, custom_path_type):
|
||||
file_handle, custom_path = mkstemp()
|
||||
custom_path = Path(custom_path)
|
||||
try:
|
||||
out_file = await local_file.download_to_drive(custom_path_type(custom_path))
|
||||
assert out_file == custom_path
|
||||
assert out_file.read_bytes() == self.file_content
|
||||
finally:
|
||||
os.close(file_handle)
|
||||
custom_path.unlink()
|
||||
|
||||
async def test_download_no_filename(self, monkeypatch, file):
|
||||
async def test(*args, **kwargs):
|
||||
return self.file_content
|
||||
@@ -200,12 +206,6 @@ class TestFile:
|
||||
custom_fobj.seek(0)
|
||||
assert custom_fobj.read() == self.file_content
|
||||
|
||||
async def test_download_file_obj_local_file(self, local_file):
|
||||
with TemporaryFile() as custom_fobj:
|
||||
await local_file.download_to_memory(out=custom_fobj)
|
||||
custom_fobj.seek(0)
|
||||
assert custom_fobj.read() == self.file_content
|
||||
|
||||
async def test_download_bytearray(self, monkeypatch, file):
|
||||
async def test(*args, **kwargs):
|
||||
return self.file_content
|
||||
@@ -223,18 +223,6 @@ class TestFile:
|
||||
assert buf2[len(buf) :] == buf
|
||||
assert buf2[: len(buf)] == buf
|
||||
|
||||
async def test_download_bytearray_local_file(self, local_file):
|
||||
# Check that a download to a newly allocated bytearray works.
|
||||
buf = await local_file.download_as_bytearray()
|
||||
assert buf == bytearray(self.file_content)
|
||||
|
||||
# Check that a download to a given bytearray works (extends the bytearray).
|
||||
buf2 = buf[:]
|
||||
buf3 = await local_file.download_as_bytearray(buf=buf2)
|
||||
assert buf3 is buf2
|
||||
assert buf2[len(buf) :] == buf
|
||||
assert buf2[: len(buf)] == buf
|
||||
|
||||
async def test_download_encrypted(self, monkeypatch, bot, encrypted_file):
|
||||
async def test(*args, **kwargs):
|
||||
return data_file("image_encrypted.jpg").read_bytes()
|
||||
@@ -257,29 +245,6 @@ class TestFile:
|
||||
custom_fobj.seek(0)
|
||||
assert custom_fobj.read() == data_file("image_decrypted.jpg").read_bytes()
|
||||
|
||||
async def test_download_local_file_encrypted(self, encrypted_local_file):
|
||||
out_file = await encrypted_local_file.download_to_drive()
|
||||
try:
|
||||
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
|
||||
finally:
|
||||
out_file.unlink()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
||||
)
|
||||
async def test_download_custom_path_local_file_encrypted(
|
||||
self, encrypted_local_file, custom_path_type
|
||||
):
|
||||
file_handle, custom_path = mkstemp()
|
||||
custom_path = Path(custom_path)
|
||||
try:
|
||||
out_file = await encrypted_local_file.download_to_drive(custom_path_type(custom_path))
|
||||
assert out_file == custom_path
|
||||
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
|
||||
finally:
|
||||
os.close(file_handle)
|
||||
custom_path.unlink()
|
||||
|
||||
async def test_download_file_obj_local_file_encrypted(self, monkeypatch, encrypted_local_file):
|
||||
async def test(*args, **kwargs):
|
||||
return data_file("image_encrypted.jpg").read_bytes()
|
||||
@@ -307,6 +272,70 @@ class TestFile:
|
||||
assert buf2[len(buf) :] == buf
|
||||
assert buf2[: len(buf)] == buf
|
||||
|
||||
|
||||
class TestFileWithRequest(TestFileBase):
|
||||
async def test_error_get_empty_file_id(self, bot):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.get_file(file_id="")
|
||||
|
||||
async def test_download_local_file(self, local_file):
|
||||
assert await local_file.download_to_drive() == Path(local_file.file_path)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
||||
)
|
||||
async def test_download_custom_path_local_file(self, local_file, custom_path_type):
|
||||
file_handle, custom_path = mkstemp()
|
||||
custom_path = Path(custom_path)
|
||||
try:
|
||||
out_file = await local_file.download_to_drive(custom_path_type(custom_path))
|
||||
assert out_file == custom_path
|
||||
assert out_file.read_bytes() == self.file_content
|
||||
finally:
|
||||
os.close(file_handle)
|
||||
custom_path.unlink()
|
||||
|
||||
async def test_download_file_obj_local_file(self, local_file):
|
||||
with TemporaryFile() as custom_fobj:
|
||||
await local_file.download_to_memory(out=custom_fobj)
|
||||
custom_fobj.seek(0)
|
||||
assert custom_fobj.read() == self.file_content
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
||||
)
|
||||
async def test_download_custom_path_local_file_encrypted(
|
||||
self, encrypted_local_file, custom_path_type
|
||||
):
|
||||
file_handle, custom_path = mkstemp()
|
||||
custom_path = Path(custom_path)
|
||||
try:
|
||||
out_file = await encrypted_local_file.download_to_drive(custom_path_type(custom_path))
|
||||
assert out_file == custom_path
|
||||
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
|
||||
finally:
|
||||
os.close(file_handle)
|
||||
custom_path.unlink()
|
||||
|
||||
async def test_download_local_file_encrypted(self, encrypted_local_file):
|
||||
out_file = await encrypted_local_file.download_to_drive()
|
||||
try:
|
||||
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
|
||||
finally:
|
||||
out_file.unlink()
|
||||
|
||||
async def test_download_bytearray_local_file(self, local_file):
|
||||
# Check that a download to a newly allocated bytearray works.
|
||||
buf = await local_file.download_as_bytearray()
|
||||
assert buf == bytearray(self.file_content)
|
||||
|
||||
# Check that a download to a given bytearray works (extends the bytearray).
|
||||
buf2 = buf[:]
|
||||
buf3 = await local_file.download_as_bytearray(buf=buf2)
|
||||
assert buf3 is buf2
|
||||
assert buf2[len(buf) :] == buf
|
||||
assert buf2[: len(buf)] == buf
|
||||
|
||||
async def test_download_bytearray_local_file_encrypted(self, encrypted_local_file):
|
||||
# Check that a download to a newly allocated bytearray works.
|
||||
buf = await encrypted_local_file.download_as_bytearray()
|
||||
@@ -318,23 +347,3 @@ class TestFile:
|
||||
assert buf3 is buf2
|
||||
assert buf2[len(buf) :] == buf
|
||||
assert buf2[: len(buf)] == buf
|
||||
|
||||
def test_equality(self, bot):
|
||||
a = File(self.file_id, self.file_unique_id, bot)
|
||||
b = File("", self.file_unique_id, bot)
|
||||
c = File(self.file_id, self.file_unique_id, None)
|
||||
d = File("", "", bot)
|
||||
e = Voice(self.file_id, self.file_unique_id, 0)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -23,16 +23,17 @@ from io import BytesIO
|
||||
import pytest
|
||||
|
||||
from telegram import InputFile
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def png_file():
|
||||
return data_file("game.png")
|
||||
|
||||
|
||||
class TestInputFile:
|
||||
def test_slot_behaviour(self, mro_slots):
|
||||
class TestInputFileWithoutRequest:
|
||||
def test_slot_behaviour(self):
|
||||
inst = InputFile(BytesIO(b"blah"), filename="tg.jpg")
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -65,7 +66,7 @@ class TestInputFile:
|
||||
assert input_file.attach_name is None
|
||||
assert input_file.attach_uri is None
|
||||
|
||||
def test_mimetypes(self, caplog):
|
||||
def test_mimetypes(self):
|
||||
# Only test a few to make sure logic works okay
|
||||
assert InputFile(data_file("telegram.jpg").open("rb")).mimetype == "image/jpeg"
|
||||
# For some reason python can guess the type on macOS
|
||||
@@ -139,6 +140,8 @@ class TestInputFile:
|
||||
== "blah.jpg"
|
||||
)
|
||||
|
||||
|
||||
class TestInputFileWithRequest:
|
||||
async def test_send_bytes(self, bot, chat_id):
|
||||
# We test this here and not at the respective test modules because it's not worth
|
||||
# duplicating the test for the different methods
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import copy
|
||||
from collections.abc import Sequence
|
||||
|
||||
@@ -36,9 +37,15 @@ from telegram.constants import ParseMode
|
||||
# noinspection PyUnresolvedReferences
|
||||
from telegram.error import BadRequest
|
||||
from telegram.request import RequestData
|
||||
from tests.conftest import data_file, expect_bad_request
|
||||
from tests._files.test_animation import animation, animation_file # noqa: F401
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import expect_bad_request
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
from .test_animation import animation, animation_file # noqa: F401
|
||||
# noinspection PyUnresolvedReferences
|
||||
from tests.test_forum import emoji_id, real_topic # noqa: F401
|
||||
|
||||
from ..auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from .test_audio import audio, audio_file # noqa: F401
|
||||
@@ -46,9 +53,6 @@ from .test_audio import audio, audio_file # noqa: F401
|
||||
# noinspection PyUnresolvedReferences
|
||||
from .test_document import document, document_file # noqa: F401
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from .test_forum import emoji_id, real_topic # noqa: F401
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from .test_photo import _photo, photo, photo_file, thumb # noqa: F401
|
||||
|
||||
@@ -56,86 +60,75 @@ from .test_photo import _photo, photo, photo_file, thumb # noqa: F401
|
||||
from .test_video import video, video_file # noqa: F401
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def input_media_video(class_thumb_file):
|
||||
return InputMediaVideo(
|
||||
media=TestInputMediaVideo.media,
|
||||
caption=TestInputMediaVideo.caption,
|
||||
width=TestInputMediaVideo.width,
|
||||
height=TestInputMediaVideo.height,
|
||||
duration=TestInputMediaVideo.duration,
|
||||
parse_mode=TestInputMediaVideo.parse_mode,
|
||||
caption_entities=TestInputMediaVideo.caption_entities,
|
||||
thumb=class_thumb_file,
|
||||
supports_streaming=TestInputMediaVideo.supports_streaming,
|
||||
has_spoiler=TestInputMediaVideo.has_spoiler,
|
||||
media=TestInputMediaVideoBase.media,
|
||||
caption=TestInputMediaVideoBase.caption,
|
||||
width=TestInputMediaVideoBase.width,
|
||||
height=TestInputMediaVideoBase.height,
|
||||
duration=TestInputMediaVideoBase.duration,
|
||||
parse_mode=TestInputMediaVideoBase.parse_mode,
|
||||
caption_entities=TestInputMediaVideoBase.caption_entities,
|
||||
thumbnail=class_thumb_file,
|
||||
supports_streaming=TestInputMediaVideoBase.supports_streaming,
|
||||
has_spoiler=TestInputMediaVideoBase.has_spoiler,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def input_media_photo(class_thumb_file):
|
||||
@pytest.fixture(scope="module")
|
||||
def input_media_photo():
|
||||
return InputMediaPhoto(
|
||||
media=TestInputMediaPhoto.media,
|
||||
caption=TestInputMediaPhoto.caption,
|
||||
parse_mode=TestInputMediaPhoto.parse_mode,
|
||||
caption_entities=TestInputMediaPhoto.caption_entities,
|
||||
has_spoiler=TestInputMediaPhoto.has_spoiler,
|
||||
media=TestInputMediaPhotoBase.media,
|
||||
caption=TestInputMediaPhotoBase.caption,
|
||||
parse_mode=TestInputMediaPhotoBase.parse_mode,
|
||||
caption_entities=TestInputMediaPhotoBase.caption_entities,
|
||||
has_spoiler=TestInputMediaPhotoBase.has_spoiler,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def input_media_animation(class_thumb_file):
|
||||
return InputMediaAnimation(
|
||||
media=TestInputMediaAnimation.media,
|
||||
caption=TestInputMediaAnimation.caption,
|
||||
parse_mode=TestInputMediaAnimation.parse_mode,
|
||||
caption_entities=TestInputMediaAnimation.caption_entities,
|
||||
width=TestInputMediaAnimation.width,
|
||||
height=TestInputMediaAnimation.height,
|
||||
thumb=class_thumb_file,
|
||||
duration=TestInputMediaAnimation.duration,
|
||||
has_spoiler=TestInputMediaAnimation.has_spoiler,
|
||||
media=TestInputMediaAnimationBase.media,
|
||||
caption=TestInputMediaAnimationBase.caption,
|
||||
parse_mode=TestInputMediaAnimationBase.parse_mode,
|
||||
caption_entities=TestInputMediaAnimationBase.caption_entities,
|
||||
width=TestInputMediaAnimationBase.width,
|
||||
height=TestInputMediaAnimationBase.height,
|
||||
thumbnail=class_thumb_file,
|
||||
duration=TestInputMediaAnimationBase.duration,
|
||||
has_spoiler=TestInputMediaAnimationBase.has_spoiler,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def input_media_audio(class_thumb_file):
|
||||
return InputMediaAudio(
|
||||
media=TestInputMediaAudio.media,
|
||||
caption=TestInputMediaAudio.caption,
|
||||
duration=TestInputMediaAudio.duration,
|
||||
performer=TestInputMediaAudio.performer,
|
||||
title=TestInputMediaAudio.title,
|
||||
thumb=class_thumb_file,
|
||||
parse_mode=TestInputMediaAudio.parse_mode,
|
||||
caption_entities=TestInputMediaAudio.caption_entities,
|
||||
media=TestInputMediaAudioBase.media,
|
||||
caption=TestInputMediaAudioBase.caption,
|
||||
duration=TestInputMediaAudioBase.duration,
|
||||
performer=TestInputMediaAudioBase.performer,
|
||||
title=TestInputMediaAudioBase.title,
|
||||
thumbnail=class_thumb_file,
|
||||
parse_mode=TestInputMediaAudioBase.parse_mode,
|
||||
caption_entities=TestInputMediaAudioBase.caption_entities,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def input_media_document(class_thumb_file):
|
||||
return InputMediaDocument(
|
||||
media=TestInputMediaDocument.media,
|
||||
caption=TestInputMediaDocument.caption,
|
||||
thumb=class_thumb_file,
|
||||
parse_mode=TestInputMediaDocument.parse_mode,
|
||||
caption_entities=TestInputMediaDocument.caption_entities,
|
||||
disable_content_type_detection=TestInputMediaDocument.disable_content_type_detection,
|
||||
media=TestInputMediaDocumentBase.media,
|
||||
caption=TestInputMediaDocumentBase.caption,
|
||||
thumbnail=class_thumb_file,
|
||||
parse_mode=TestInputMediaDocumentBase.parse_mode,
|
||||
caption_entities=TestInputMediaDocumentBase.caption_entities,
|
||||
disable_content_type_detection=TestInputMediaDocumentBase.disable_content_type_detection,
|
||||
)
|
||||
|
||||
|
||||
class CustomSequence(Sequence):
|
||||
def __init__(self, items):
|
||||
self.items = items
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.items[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.items)
|
||||
|
||||
|
||||
class TestInputMediaVideo:
|
||||
class TestInputMediaVideoBase:
|
||||
type_ = "video"
|
||||
media = "NOTAREALFILEID"
|
||||
caption = "My Caption"
|
||||
@@ -147,7 +140,9 @@ class TestInputMediaVideo:
|
||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||
has_spoiler = True
|
||||
|
||||
def test_slot_behaviour(self, input_media_video, mro_slots):
|
||||
|
||||
class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
|
||||
def test_slot_behaviour(self, input_media_video):
|
||||
inst = input_media_video
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -163,9 +158,15 @@ class TestInputMediaVideo:
|
||||
assert input_media_video.parse_mode == self.parse_mode
|
||||
assert input_media_video.caption_entities == tuple(self.caption_entities)
|
||||
assert input_media_video.supports_streaming == self.supports_streaming
|
||||
assert isinstance(input_media_video.thumb, InputFile)
|
||||
assert isinstance(input_media_video.thumbnail, InputFile)
|
||||
assert input_media_video.thumb is input_media_video.thumbnail
|
||||
assert input_media_video.has_spoiler == self.has_spoiler
|
||||
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
input_media_video = InputMediaVideo(self.media, thumb=object())
|
||||
assert input_media_video.thumb is input_media_video.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_caption_entities_always_tuple(self):
|
||||
input_media_video = InputMediaVideo(self.media)
|
||||
assert input_media_video.caption_entities == ()
|
||||
@@ -204,13 +205,21 @@ class TestInputMediaVideo:
|
||||
|
||||
def test_with_local_files(self):
|
||||
input_media_video = InputMediaVideo(
|
||||
data_file("telegram.mp4"), thumb=data_file("telegram.jpg")
|
||||
data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg")
|
||||
)
|
||||
assert input_media_video.media == data_file("telegram.mp4").as_uri()
|
||||
assert input_media_video.thumb == data_file("telegram.jpg").as_uri()
|
||||
assert input_media_video.thumbnail == data_file("telegram.jpg").as_uri()
|
||||
|
||||
def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self):
|
||||
with pytest.raises(ValueError):
|
||||
InputMediaVideo(
|
||||
data_file("telegram.mp4"),
|
||||
thumbnail=data_file("telegram.jpg"),
|
||||
thumb=data_file("telegram_no_standard_header.jpg"),
|
||||
)
|
||||
|
||||
|
||||
class TestInputMediaPhoto:
|
||||
class TestInputMediaPhotoBase:
|
||||
type_ = "photo"
|
||||
media = "NOTAREALFILEID"
|
||||
caption = "My Caption"
|
||||
@@ -218,7 +227,9 @@ class TestInputMediaPhoto:
|
||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||
has_spoiler = True
|
||||
|
||||
def test_slot_behaviour(self, input_media_photo, mro_slots):
|
||||
|
||||
class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
|
||||
def test_slot_behaviour(self, input_media_photo):
|
||||
inst = input_media_photo
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -266,7 +277,7 @@ class TestInputMediaPhoto:
|
||||
assert input_media_photo.media == data_file("telegram.mp4").as_uri()
|
||||
|
||||
|
||||
class TestInputMediaAnimation:
|
||||
class TestInputMediaAnimationBase:
|
||||
type_ = "animation"
|
||||
media = "NOTAREALFILEID"
|
||||
caption = "My Caption"
|
||||
@@ -277,7 +288,9 @@ class TestInputMediaAnimation:
|
||||
duration = 1
|
||||
has_spoiler = True
|
||||
|
||||
def test_slot_behaviour(self, input_media_animation, mro_slots):
|
||||
|
||||
class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
|
||||
def test_slot_behaviour(self, input_media_animation):
|
||||
inst = input_media_animation
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -289,9 +302,15 @@ class TestInputMediaAnimation:
|
||||
assert input_media_animation.caption == self.caption
|
||||
assert input_media_animation.parse_mode == self.parse_mode
|
||||
assert input_media_animation.caption_entities == tuple(self.caption_entities)
|
||||
assert isinstance(input_media_animation.thumb, InputFile)
|
||||
assert isinstance(input_media_animation.thumbnail, InputFile)
|
||||
assert input_media_animation.thumb is input_media_animation.thumbnail
|
||||
assert input_media_animation.has_spoiler == self.has_spoiler
|
||||
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
input_media_animation = InputMediaAnimation(self.media, thumb=object())
|
||||
assert input_media_animation.thumb is input_media_animation.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_caption_entities_always_tuple(self):
|
||||
input_media_animation = InputMediaAnimation(self.media)
|
||||
assert input_media_animation.caption_entities == ()
|
||||
@@ -326,13 +345,21 @@ class TestInputMediaAnimation:
|
||||
|
||||
def test_with_local_files(self):
|
||||
input_media_animation = InputMediaAnimation(
|
||||
data_file("telegram.mp4"), thumb=data_file("telegram.jpg")
|
||||
data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg")
|
||||
)
|
||||
assert input_media_animation.media == data_file("telegram.mp4").as_uri()
|
||||
assert input_media_animation.thumb == data_file("telegram.jpg").as_uri()
|
||||
assert input_media_animation.thumbnail == data_file("telegram.jpg").as_uri()
|
||||
|
||||
def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self):
|
||||
with pytest.raises(ValueError):
|
||||
InputMediaAnimation(
|
||||
data_file("telegram.mp4"),
|
||||
thumbnail=data_file("telegram.jpg"),
|
||||
thumb=data_file("telegram_no_standard_header.jpg"),
|
||||
)
|
||||
|
||||
|
||||
class TestInputMediaAudio:
|
||||
class TestInputMediaAudioBase:
|
||||
type_ = "audio"
|
||||
media = "NOTAREALFILEID"
|
||||
caption = "My Caption"
|
||||
@@ -342,7 +369,9 @@ class TestInputMediaAudio:
|
||||
parse_mode = "HTML"
|
||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||
|
||||
def test_slot_behaviour(self, input_media_audio, mro_slots):
|
||||
|
||||
class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
|
||||
def test_slot_behaviour(self, input_media_audio):
|
||||
inst = input_media_audio
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -357,7 +386,13 @@ class TestInputMediaAudio:
|
||||
assert input_media_audio.title == self.title
|
||||
assert input_media_audio.parse_mode == self.parse_mode
|
||||
assert input_media_audio.caption_entities == tuple(self.caption_entities)
|
||||
assert isinstance(input_media_audio.thumb, InputFile)
|
||||
assert isinstance(input_media_audio.thumbnail, InputFile)
|
||||
assert input_media_audio.thumb is input_media_audio.thumbnail
|
||||
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
input_media_audio = InputMediaAudio(self.media, thumb=object())
|
||||
assert input_media_audio.thumb is input_media_audio.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_caption_entities_always_tuple(self):
|
||||
input_media_audio = InputMediaAudio(self.media)
|
||||
@@ -395,13 +430,21 @@ class TestInputMediaAudio:
|
||||
|
||||
def test_with_local_files(self):
|
||||
input_media_audio = InputMediaAudio(
|
||||
data_file("telegram.mp4"), thumb=data_file("telegram.jpg")
|
||||
data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg")
|
||||
)
|
||||
assert input_media_audio.media == data_file("telegram.mp4").as_uri()
|
||||
assert input_media_audio.thumb == data_file("telegram.jpg").as_uri()
|
||||
assert input_media_audio.thumbnail == data_file("telegram.jpg").as_uri()
|
||||
|
||||
def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self):
|
||||
with pytest.raises(ValueError):
|
||||
InputMediaAudio(
|
||||
data_file("telegram.mp4"),
|
||||
thumbnail=data_file("telegram.jpg"),
|
||||
thumb=data_file("telegram_no_standard_header.jpg"),
|
||||
)
|
||||
|
||||
|
||||
class TestInputMediaDocument:
|
||||
class TestInputMediaDocumentBase:
|
||||
type_ = "document"
|
||||
media = "NOTAREALFILEID"
|
||||
caption = "My Caption"
|
||||
@@ -409,7 +452,9 @@ class TestInputMediaDocument:
|
||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||
disable_content_type_detection = True
|
||||
|
||||
def test_slot_behaviour(self, input_media_document, mro_slots):
|
||||
|
||||
class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
|
||||
def test_slot_behaviour(self, input_media_document):
|
||||
inst = input_media_document
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -425,7 +470,13 @@ class TestInputMediaDocument:
|
||||
input_media_document.disable_content_type_detection
|
||||
== self.disable_content_type_detection
|
||||
)
|
||||
assert isinstance(input_media_document.thumb, InputFile)
|
||||
assert isinstance(input_media_document.thumbnail, InputFile)
|
||||
assert input_media_document.thumb is input_media_document.thumbnail
|
||||
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
input_media_document = InputMediaDocument(self.media, thumb=object())
|
||||
assert input_media_document.thumb is input_media_document.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_caption_entities_always_tuple(self):
|
||||
input_media_document = InputMediaDocument(self.media)
|
||||
@@ -461,13 +512,21 @@ class TestInputMediaDocument:
|
||||
|
||||
def test_with_local_files(self):
|
||||
input_media_document = InputMediaDocument(
|
||||
data_file("telegram.mp4"), thumb=data_file("telegram.jpg")
|
||||
data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg")
|
||||
)
|
||||
assert input_media_document.media == data_file("telegram.mp4").as_uri()
|
||||
assert input_media_document.thumb == data_file("telegram.jpg").as_uri()
|
||||
assert input_media_document.thumbnail == data_file("telegram.jpg").as_uri()
|
||||
|
||||
def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self):
|
||||
with pytest.raises(ValueError):
|
||||
InputMediaDocument(
|
||||
data_file("telegram.mp4"),
|
||||
thumbnail=data_file("telegram.jpg"),
|
||||
thumb=data_file("telegram_no_standard_header.jpg"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function") # noqa: F811
|
||||
@pytest.fixture(scope="module") # noqa: F811
|
||||
def media_group(photo, thumb): # noqa: F811
|
||||
return [
|
||||
InputMediaPhoto(photo, caption="*photo* 1", parse_mode="Markdown"),
|
||||
@@ -478,12 +537,12 @@ def media_group(photo, thumb): # noqa: F811
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function") # noqa: F811
|
||||
@pytest.fixture(scope="module") # noqa: F811
|
||||
def media_group_no_caption_args(photo, thumb): # noqa: F811
|
||||
return [InputMediaPhoto(photo), InputMediaPhoto(thumb), InputMediaPhoto(photo)]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function") # noqa: F811
|
||||
@pytest.fixture(scope="module") # noqa: F811
|
||||
def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811
|
||||
return [
|
||||
InputMediaPhoto(photo, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)]),
|
||||
@@ -491,7 +550,7 @@ def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function") # noqa: F811
|
||||
@pytest.fixture(scope="module") # noqa: F811
|
||||
def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
|
||||
return [
|
||||
InputMediaPhoto(photo, parse_mode="Markdown"),
|
||||
@@ -499,32 +558,7 @@ def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
|
||||
]
|
||||
|
||||
|
||||
class TestSendMediaGroup:
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_media_group_photo(self, bot, chat_id, media_group):
|
||||
messages = await bot.send_media_group(chat_id, media_group)
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
assert all(mes.caption == f"photo {idx+1}" for idx, mes in enumerate(messages))
|
||||
assert all(
|
||||
mes.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) for mes in messages
|
||||
)
|
||||
|
||||
async def test_send_media_group_with_message_thread_id(
|
||||
self, bot, real_topic, forum_group_id, media_group # noqa: F811
|
||||
):
|
||||
messages = await bot.send_media_group(
|
||||
forum_group_id,
|
||||
media_group,
|
||||
message_thread_id=real_topic.message_thread_id,
|
||||
)
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(i.message_thread_id == real_topic.message_thread_id for i in messages)
|
||||
|
||||
class TestSendMediaGroupWithoutRequest:
|
||||
async def test_send_media_group_throws_error_with_group_caption_and_individual_captions(
|
||||
self,
|
||||
bot,
|
||||
@@ -544,6 +578,147 @@ class TestSendMediaGroup:
|
||||
):
|
||||
await bot.send_media_group(chat_id, group, caption="foo")
|
||||
|
||||
async def test_send_media_group_custom_filename(
|
||||
self,
|
||||
bot,
|
||||
chat_id,
|
||||
photo_file, # noqa: F811
|
||||
animation_file, # noqa: F811
|
||||
audio_file, # noqa: F811
|
||||
video_file, # noqa: F811
|
||||
monkeypatch,
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
result = all(
|
||||
field_tuple[0] == "custom_filename"
|
||||
for field_tuple in request_data.multipart_data.values()
|
||||
)
|
||||
if result is True:
|
||||
raise Exception("Test was successful")
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
media = [
|
||||
InputMediaAnimation(animation_file, filename="custom_filename"),
|
||||
InputMediaAudio(audio_file, filename="custom_filename"),
|
||||
InputMediaPhoto(photo_file, filename="custom_filename"),
|
||||
InputMediaVideo(video_file, filename="custom_filename"),
|
||||
]
|
||||
|
||||
with pytest.raises(Exception, match="Test was successful"):
|
||||
await bot.send_media_group(chat_id, media)
|
||||
|
||||
async def test_send_media_group_with_thumbs(
|
||||
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
|
||||
):
|
||||
async def make_assertion(method, url, request_data: RequestData, *args, **kwargs):
|
||||
nonlocal input_video
|
||||
files = request_data.multipart_data
|
||||
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
|
||||
thumb_check = (
|
||||
files[input_video.thumbnail.attach_name] == input_video.thumbnail.field_tuple
|
||||
)
|
||||
result = video_check and thumb_check
|
||||
raise Exception(f"Test was {'successful' if result else 'failing'}")
|
||||
|
||||
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
|
||||
input_video = InputMediaVideo(video_file, thumbnail=photo_file)
|
||||
with pytest.raises(Exception, match="Test was successful"):
|
||||
await bot.send_media_group(chat_id, [input_video, input_video])
|
||||
|
||||
async def test_edit_message_media_with_thumb(
|
||||
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
|
||||
):
|
||||
async def make_assertion(
|
||||
method: str, url: str, request_data: RequestData = None, *args, **kwargs
|
||||
):
|
||||
files = request_data.multipart_data
|
||||
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
|
||||
thumb_check = (
|
||||
files[input_video.thumbnail.attach_name] == input_video.thumbnail.field_tuple
|
||||
)
|
||||
result = video_check and thumb_check
|
||||
raise Exception(f"Test was {'successful' if result else 'failing'}")
|
||||
|
||||
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
|
||||
input_video = InputMediaVideo(video_file, thumbnail=photo_file)
|
||||
with pytest.raises(Exception, match="Test was successful"):
|
||||
await bot.edit_message_media(chat_id=chat_id, message_id=123, media=input_video)
|
||||
|
||||
|
||||
class CustomSequence(Sequence):
|
||||
def __init__(self, items):
|
||||
self.items = items
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.items[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.items)
|
||||
|
||||
|
||||
class TestSendMediaGroupWithRequest:
|
||||
async def test_send_media_group_photo(self, bot, chat_id, media_group):
|
||||
messages = await bot.send_media_group(chat_id, media_group)
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
assert all(mes.caption == f"photo {idx+1}" for idx, mes in enumerate(messages))
|
||||
assert all(
|
||||
mes.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) for mes in messages
|
||||
)
|
||||
|
||||
async def test_send_media_group_new_files(
|
||||
self, bot, chat_id, video_file, photo_file # noqa: F811
|
||||
):
|
||||
async def func():
|
||||
return await bot.send_media_group(
|
||||
chat_id,
|
||||
[
|
||||
InputMediaVideo(video_file),
|
||||
InputMediaPhoto(photo_file),
|
||||
InputMediaPhoto(data_file("telegram.jpg").read_bytes()),
|
||||
],
|
||||
)
|
||||
|
||||
messages = await expect_bad_request(
|
||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||
)
|
||||
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
|
||||
@pytest.mark.parametrize("sequence_type", [list, tuple, CustomSequence])
|
||||
@pytest.mark.parametrize("bot_class", ["raw_bot", "ext_bot"])
|
||||
async def test_send_media_group_different_sequences(
|
||||
self, bot, chat_id, media_group, sequence_type, bot_class, raw_bot
|
||||
):
|
||||
"""Test that send_media_group accepts different sequence types. This test ensures that
|
||||
Bot._insert_defaults works for arbitrary sequence types."""
|
||||
bot = bot if bot_class == "ext_bot" else raw_bot
|
||||
|
||||
messages = await bot.send_media_group(chat_id, sequence_type(media_group))
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
|
||||
async def test_send_media_group_with_message_thread_id(
|
||||
self, bot, real_topic, forum_group_id, media_group # noqa: F811
|
||||
):
|
||||
messages = await bot.send_media_group(
|
||||
forum_group_id,
|
||||
media_group,
|
||||
message_thread_id=real_topic.message_thread_id,
|
||||
)
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(i.message_thread_id == real_topic.message_thread_id for i in messages)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"caption, parse_mode, caption_entities",
|
||||
[
|
||||
@@ -553,7 +728,6 @@ class TestSendMediaGroup:
|
||||
("photo 1", None, [MessageEntity(MessageEntity.BOLD, 0, 5)]),
|
||||
],
|
||||
)
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_media_group_with_group_caption(
|
||||
self,
|
||||
bot,
|
||||
@@ -598,16 +772,15 @@ class TestSendMediaGroup:
|
||||
assert all(mes.caption is None for mes in other_messages)
|
||||
assert not any(mes.caption_entities for mes in other_messages)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_media_group_all_args(self, bot, raw_bot, chat_id, media_group):
|
||||
ext_bot = bot
|
||||
for bot in (ext_bot, raw_bot):
|
||||
# We need to test 1) below both the bot and raw_bot and setting this up with
|
||||
# pytest.parametrize appears to be difficult ...
|
||||
|
||||
m1 = await bot.send_message(chat_id, text="test")
|
||||
# We need to test 1) below both the bot and raw_bot and setting this up with
|
||||
# pytest.parametrize appears to be difficult ...
|
||||
aws = {b.send_message(chat_id, text="test") for b in (ext_bot, raw_bot)}
|
||||
for msg_task in asyncio.as_completed(aws):
|
||||
m1 = await msg_task
|
||||
copied_media_group = copy.copy(media_group)
|
||||
messages = await bot.send_media_group(
|
||||
messages = await m1.get_bot().send_media_group(
|
||||
chat_id,
|
||||
media_group,
|
||||
disable_notification=True,
|
||||
@@ -633,7 +806,6 @@ class TestSendMediaGroup:
|
||||
)
|
||||
assert all(mes.has_protected_content for mes in messages)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_media_group_with_spoiler(
|
||||
self, bot, chat_id, photo_file, video_file # noqa: F811
|
||||
):
|
||||
@@ -649,97 +821,36 @@ class TestSendMediaGroup:
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
assert all(mes.has_media_spoiler for mes in messages)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_media_group_custom_filename(
|
||||
self,
|
||||
bot,
|
||||
chat_id,
|
||||
photo_file, # noqa: F811
|
||||
animation_file, # noqa: F811
|
||||
audio_file, # noqa: F811
|
||||
video_file, # noqa: F811
|
||||
monkeypatch,
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
result = all(
|
||||
field_tuple[0] == "custom_filename"
|
||||
for field_tuple in request_data.multipart_data.values()
|
||||
async def test_edit_message_media(self, bot, raw_bot, chat_id, media_group):
|
||||
ext_bot = bot
|
||||
# We need to test 1) below both the bot and raw_bot and setting this up with
|
||||
# pytest.parametrize appears to be difficult ...
|
||||
aws = {b.send_media_group(chat_id, media_group) for b in (ext_bot, raw_bot)}
|
||||
for msg_task in asyncio.as_completed(aws):
|
||||
messages = await msg_task
|
||||
cid = messages[-1].chat.id
|
||||
mid = messages[-1].message_id
|
||||
copied_media = copy.copy(media_group[0])
|
||||
new_message = (
|
||||
await messages[-1]
|
||||
.get_bot()
|
||||
.edit_message_media(chat_id=cid, message_id=mid, media=media_group[0])
|
||||
)
|
||||
if result is True:
|
||||
raise Exception("Test was successful")
|
||||
assert isinstance(new_message, Message)
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
# 1)
|
||||
# make sure that the media was not modified
|
||||
assert media_group[0].parse_mode == copied_media.parse_mode
|
||||
|
||||
media = [
|
||||
InputMediaAnimation(animation_file, filename="custom_filename"),
|
||||
InputMediaAudio(audio_file, filename="custom_filename"),
|
||||
InputMediaPhoto(photo_file, filename="custom_filename"),
|
||||
InputMediaVideo(video_file, filename="custom_filename"),
|
||||
]
|
||||
|
||||
with pytest.raises(Exception, match="Test was successful"):
|
||||
await bot.send_media_group(chat_id, media)
|
||||
|
||||
async def test_send_media_group_with_thumbs(
|
||||
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
|
||||
):
|
||||
async def make_assertion(method, url, request_data: RequestData, *args, **kwargs):
|
||||
files = request_data.multipart_data
|
||||
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
|
||||
thumb_check = files[input_video.thumb.attach_name] == input_video.thumb.field_tuple
|
||||
result = video_check and thumb_check
|
||||
raise Exception(f"Test was {'successful' if result else 'failing'}")
|
||||
|
||||
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
|
||||
input_video = InputMediaVideo(video_file, thumb=photo_file)
|
||||
with pytest.raises(Exception, match="Test was successful"):
|
||||
await bot.send_media_group(chat_id, [input_video, input_video])
|
||||
|
||||
@pytest.mark.flaky(3, 1) # noqa: F811
|
||||
async def test_send_media_group_new_files(
|
||||
self,
|
||||
bot,
|
||||
chat_id,
|
||||
video_file, # noqa: F811
|
||||
photo_file, # noqa: F811
|
||||
animation_file, # noqa: F811
|
||||
):
|
||||
async def func():
|
||||
return await bot.send_media_group(
|
||||
chat_id,
|
||||
[
|
||||
InputMediaVideo(video_file),
|
||||
InputMediaPhoto(photo_file),
|
||||
InputMediaPhoto(data_file("telegram.jpg").read_bytes()),
|
||||
],
|
||||
)
|
||||
|
||||
messages = await expect_bad_request(
|
||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||
async def test_edit_message_media_new_file(self, bot, chat_id, media_group, thumb_file):
|
||||
messages = await bot.send_media_group(chat_id, media_group)
|
||||
cid = messages[-1].chat.id
|
||||
mid = messages[-1].message_id
|
||||
new_message = await bot.edit_message_media(
|
||||
chat_id=cid, message_id=mid, media=InputMediaPhoto(thumb_file)
|
||||
)
|
||||
assert isinstance(new_message, Message)
|
||||
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("sequence_type", [list, tuple, CustomSequence])
|
||||
@pytest.mark.parametrize("bot_class", ["raw_bot", "ext_bot"])
|
||||
async def test_send_media_group_different_sequences(
|
||||
self, bot, chat_id, media_group, sequence_type, bot_class, raw_bot
|
||||
):
|
||||
"""Test that send_media_group accepts different sequence types. This test ensures that
|
||||
Bot._insert_defaults works for arbitrary sequence types."""
|
||||
bot = bot if bot_class == "ext_bot" else raw_bot
|
||||
|
||||
messages = await bot.send_media_group(chat_id, sequence_type(media_group))
|
||||
assert isinstance(messages, tuple)
|
||||
assert len(messages) == 3
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -773,19 +884,18 @@ class TestSendMediaGroup:
|
||||
chat_id, media_group, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_media_group_default_protect_content(
|
||||
self, chat_id, media_group, default_bot
|
||||
):
|
||||
protected = await default_bot.send_media_group(chat_id, media_group)
|
||||
assert all(msg.has_protected_content for msg in protected)
|
||||
unprotected = await default_bot.send_media_group(
|
||||
chat_id, media_group, protect_content=False
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_media_group(chat_id, media_group),
|
||||
default_bot.send_media_group(chat_id, media_group, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert all(msg.has_protected_content for msg in protected)
|
||||
assert not all(msg.has_protected_content for msg in unprotected)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True)
|
||||
async def test_send_media_group_default_parse_mode(
|
||||
self, chat_id, media_group_no_caption_args, default_bot
|
||||
@@ -797,19 +907,21 @@ class TestSendMediaGroup:
|
||||
# make sure no parse_mode was set as a side effect
|
||||
assert not any(item.parse_mode for item in media_group_no_caption_args)
|
||||
|
||||
overridden_markdown_v2 = await default_bot.send_media_group(
|
||||
chat_id,
|
||||
media_group_no_caption_args.copy(),
|
||||
caption="*photo* 1",
|
||||
parse_mode=ParseMode.MARKDOWN_V2,
|
||||
)
|
||||
|
||||
overridden_none = await default_bot.send_media_group(
|
||||
chat_id,
|
||||
media_group_no_caption_args.copy(),
|
||||
caption="<b>photo</b> 1",
|
||||
parse_mode=None,
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_media_group(
|
||||
chat_id,
|
||||
media_group_no_caption_args.copy(),
|
||||
caption="*photo* 1",
|
||||
parse_mode=ParseMode.MARKDOWN_V2,
|
||||
),
|
||||
default_bot.send_media_group(
|
||||
chat_id,
|
||||
media_group_no_caption_args.copy(),
|
||||
caption="<b>photo</b> 1",
|
||||
parse_mode=None,
|
||||
),
|
||||
)
|
||||
overridden_markdown_v2, overridden_none = await tasks
|
||||
|
||||
# Make sure first message got the caption, which will lead to Telegram
|
||||
# displaying its caption as group caption
|
||||
@@ -830,53 +942,6 @@ class TestSendMediaGroup:
|
||||
assert all(mes.caption is None for mes in other_messages)
|
||||
assert not any(mes.caption_entities for mes in other_messages)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_edit_message_media(self, bot, raw_bot, chat_id, media_group):
|
||||
ext_bot = bot
|
||||
for bot in (ext_bot, raw_bot):
|
||||
# We need to test 1) below both the bot and raw_bot and setting this up with
|
||||
# pytest.parametrize appears to be difficult ...
|
||||
messages = await bot.send_media_group(chat_id, media_group)
|
||||
cid = messages[-1].chat.id
|
||||
mid = messages[-1].message_id
|
||||
copied_media = copy.copy(media_group[0])
|
||||
new_message = await bot.edit_message_media(
|
||||
chat_id=cid, message_id=mid, media=media_group[0]
|
||||
)
|
||||
assert isinstance(new_message, Message)
|
||||
|
||||
# 1)
|
||||
# make sure that the media was not modified
|
||||
assert media_group[0].parse_mode == copied_media.parse_mode
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_edit_message_media_new_file(self, bot, chat_id, media_group, thumb_file):
|
||||
messages = await bot.send_media_group(chat_id, media_group)
|
||||
cid = messages[-1].chat.id
|
||||
mid = messages[-1].message_id
|
||||
new_message = await bot.edit_message_media(
|
||||
chat_id=cid, message_id=mid, media=InputMediaPhoto(thumb_file)
|
||||
)
|
||||
assert isinstance(new_message, Message)
|
||||
|
||||
async def test_edit_message_media_with_thumb(
|
||||
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
|
||||
):
|
||||
async def make_assertion(
|
||||
method: str, url: str, request_data: RequestData = None, *args, **kwargs
|
||||
):
|
||||
files = request_data.multipart_data
|
||||
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
|
||||
thumb_check = files[input_video.thumb.attach_name] == input_video.thumb.field_tuple
|
||||
result = video_check and thumb_check
|
||||
raise Exception(f"Test was {'successful' if result else 'failing'}")
|
||||
|
||||
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
|
||||
input_video = InputMediaVideo(video_file, thumb=photo_file)
|
||||
with pytest.raises(Exception, match="Test was successful"):
|
||||
await bot.edit_message_media(chat_id=chat_id, message_id=123, media=input_video)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True, ids=["HTML-Bot"]
|
||||
)
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import InputSticker, MaskPosition
|
||||
from telegram._files.inputfile import InputFile
|
||||
from tests._files.test_sticker import video_sticker_file # noqa: F401
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def input_sticker():
|
||||
return InputSticker(
|
||||
sticker=TestInputStickerBase.sticker,
|
||||
emoji_list=TestInputStickerBase.emoji_list,
|
||||
mask_position=TestInputStickerBase.mask_position,
|
||||
keywords=TestInputStickerBase.keywords,
|
||||
)
|
||||
|
||||
|
||||
class TestInputStickerBase:
|
||||
sticker = "fake_file_id"
|
||||
emoji_list = ("👍", "👎")
|
||||
mask_position = MaskPosition("forehead", 0.5, 0.5, 0.5)
|
||||
keywords = ("thumbsup", "thumbsdown")
|
||||
|
||||
|
||||
class TestInputStickerNoRequest(TestInputStickerBase):
|
||||
def test_slot_behaviour(self, input_sticker):
|
||||
inst = input_sticker
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_expected_values(self, input_sticker):
|
||||
assert input_sticker.sticker == self.sticker and isinstance(input_sticker.sticker, str)
|
||||
assert input_sticker.emoji_list == self.emoji_list
|
||||
assert input_sticker.mask_position == self.mask_position
|
||||
assert input_sticker.keywords == self.keywords
|
||||
|
||||
def test_attributes_tuple(self, input_sticker):
|
||||
assert isinstance(input_sticker.keywords, tuple)
|
||||
assert isinstance(input_sticker.emoji_list, tuple)
|
||||
a = InputSticker("sticker", ["emoji"])
|
||||
assert isinstance(a.emoji_list, tuple) and a.keywords == ()
|
||||
|
||||
def test_to_dict(self, input_sticker):
|
||||
input_sticker_dict = input_sticker.to_dict()
|
||||
|
||||
assert isinstance(input_sticker_dict, dict)
|
||||
assert input_sticker_dict["sticker"] == input_sticker.sticker
|
||||
assert input_sticker_dict["emoji_list"] == list(input_sticker.emoji_list)
|
||||
assert input_sticker_dict["mask_position"] == input_sticker.mask_position.to_dict()
|
||||
assert input_sticker_dict["keywords"] == list(input_sticker.keywords)
|
||||
|
||||
def test_with_sticker_input_types(self, video_sticker_file): # noqa: F811
|
||||
sticker = InputSticker(sticker=video_sticker_file, emoji_list=["👍"])
|
||||
assert isinstance(sticker.sticker, InputFile)
|
||||
sticker = InputSticker(data_file("telegram_video_sticker.webm"), ["👍"])
|
||||
assert sticker.sticker == data_file("telegram_video_sticker.webm").as_uri()
|
||||
@@ -16,26 +16,29 @@
|
||||
#
|
||||
# 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 pytest
|
||||
|
||||
from telegram import Location
|
||||
from telegram.error import BadRequest
|
||||
from telegram.request import RequestData
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def location():
|
||||
return Location(
|
||||
latitude=TestLocation.latitude,
|
||||
longitude=TestLocation.longitude,
|
||||
horizontal_accuracy=TestLocation.horizontal_accuracy,
|
||||
live_period=TestLocation.live_period,
|
||||
heading=TestLocation.live_period,
|
||||
proximity_alert_radius=TestLocation.proximity_alert_radius,
|
||||
latitude=TestLocationBase.latitude,
|
||||
longitude=TestLocationBase.longitude,
|
||||
horizontal_accuracy=TestLocationBase.horizontal_accuracy,
|
||||
live_period=TestLocationBase.live_period,
|
||||
heading=TestLocationBase.live_period,
|
||||
proximity_alert_radius=TestLocationBase.proximity_alert_radius,
|
||||
)
|
||||
|
||||
|
||||
class TestLocation:
|
||||
class TestLocationBase:
|
||||
latitude = -23.691288
|
||||
longitude = -46.788279
|
||||
horizontal_accuracy = 999
|
||||
@@ -43,19 +46,21 @@ class TestLocation:
|
||||
heading = 90
|
||||
proximity_alert_radius = 50
|
||||
|
||||
def test_slot_behaviour(self, location, mro_slots):
|
||||
|
||||
class TestLocationWithoutRequest(TestLocationBase):
|
||||
def test_slot_behaviour(self, location):
|
||||
for attr in location.__slots__:
|
||||
assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(location)) == len(set(mro_slots(location))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
"latitude": TestLocation.latitude,
|
||||
"longitude": TestLocation.longitude,
|
||||
"horizontal_accuracy": TestLocation.horizontal_accuracy,
|
||||
"live_period": TestLocation.live_period,
|
||||
"heading": TestLocation.heading,
|
||||
"proximity_alert_radius": TestLocation.proximity_alert_radius,
|
||||
"latitude": self.latitude,
|
||||
"longitude": self.longitude,
|
||||
"horizontal_accuracy": self.horizontal_accuracy,
|
||||
"live_period": self.live_period,
|
||||
"heading": self.heading,
|
||||
"proximity_alert_radius": self.proximity_alert_radius,
|
||||
}
|
||||
location = Location.de_json(json_dict, bot)
|
||||
assert location.api_kwargs == {}
|
||||
@@ -67,7 +72,139 @@ class TestLocation:
|
||||
assert location.heading == self.heading
|
||||
assert location.proximity_alert_radius == self.proximity_alert_radius
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
def test_to_dict(self, location):
|
||||
location_dict = location.to_dict()
|
||||
|
||||
assert location_dict["latitude"] == location.latitude
|
||||
assert location_dict["longitude"] == location.longitude
|
||||
assert location_dict["horizontal_accuracy"] == location.horizontal_accuracy
|
||||
assert location_dict["live_period"] == location.live_period
|
||||
assert location["heading"] == location.heading
|
||||
assert location["proximity_alert_radius"] == location.proximity_alert_radius
|
||||
|
||||
def test_equality(self):
|
||||
a = Location(self.longitude, self.latitude)
|
||||
b = Location(self.longitude, self.latitude)
|
||||
d = Location(0, self.latitude)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
async def test_send_location_without_required(self, bot, chat_id):
|
||||
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
|
||||
await bot.send_location(chat_id=chat_id)
|
||||
|
||||
async def test_edit_location_without_required(self, bot):
|
||||
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
|
||||
await bot.edit_message_live_location(chat_id=2, message_id=3)
|
||||
|
||||
async def test_send_location_with_all_args(self, bot, location):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.send_location(chat_id=1, latitude=2.5, longitude=4.6, location=location)
|
||||
|
||||
async def test_edit_location_with_all_args(self, bot, location):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.edit_message_live_location(
|
||||
chat_id=1, message_id=7, latitude=2.5, longitude=4.6, location=location
|
||||
)
|
||||
|
||||
# TODO: Needs improvement with in inline sent live location.
|
||||
async def test_edit_live_inline_message(self, monkeypatch, bot, location):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
data = request_data.json_parameters
|
||||
lat = data["latitude"] == str(location.latitude)
|
||||
lon = data["longitude"] == str(location.longitude)
|
||||
id_ = data["inline_message_id"] == "1234"
|
||||
ha = data["horizontal_accuracy"] == "50"
|
||||
heading = data["heading"] == "90"
|
||||
prox_alert = data["proximity_alert_radius"] == "1000"
|
||||
return lat and lon and id_ and ha and heading and prox_alert
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.edit_message_live_location(
|
||||
inline_message_id=1234,
|
||||
location=location,
|
||||
horizontal_accuracy=50,
|
||||
heading=90,
|
||||
proximity_alert_radius=1000,
|
||||
)
|
||||
|
||||
# TODO: Needs improvement with in inline sent live location.
|
||||
async def test_stop_live_inline_message(self, monkeypatch, bot):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
id_ = request_data.json_parameters["inline_message_id"] == "1234"
|
||||
return id_
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.stop_message_live_location(inline_message_id=1234)
|
||||
|
||||
async def test_send_with_location(self, monkeypatch, bot, chat_id, location):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
lat = request_data.json_parameters["latitude"] == str(location.latitude)
|
||||
lon = request_data.json_parameters["longitude"] == str(location.longitude)
|
||||
return lat and lon
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_location(location=location, chat_id=chat_id)
|
||||
|
||||
async def test_edit_live_location_with_location(self, monkeypatch, bot, location):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
lat = request_data.json_parameters["latitude"] == str(location.latitude)
|
||||
lon = request_data.json_parameters["longitude"] == str(location.longitude)
|
||||
return lat and lon
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.edit_message_live_location(None, None, location=location)
|
||||
|
||||
|
||||
class TestLocationWithRequest:
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
({"allow_sending_without_reply": True}, None),
|
||||
({"allow_sending_without_reply": False}, None),
|
||||
({"allow_sending_without_reply": False}, True),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_location_default_allow_sending_without_reply(
|
||||
self, default_bot, chat_id, location, custom
|
||||
):
|
||||
reply_to_message = await default_bot.send_message(chat_id, "test")
|
||||
await reply_to_message.delete()
|
||||
if custom is not None:
|
||||
message = await default_bot.send_location(
|
||||
chat_id,
|
||||
location=location,
|
||||
allow_sending_without_reply=custom,
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
elif default_bot.defaults.allow_sending_without_reply:
|
||||
message = await default_bot.send_location(
|
||||
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
await default_bot.send_location(
|
||||
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_location_default_protect_content(self, chat_id, default_bot, location):
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_location(chat_id, location=location),
|
||||
default_bot.send_location(chat_id, location=location, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.xfail
|
||||
async def test_send_live_location(self, bot, chat_id):
|
||||
message = await bot.send_location(
|
||||
@@ -110,135 +247,3 @@ class TestLocation:
|
||||
await bot.edit_message_live_location(
|
||||
message.chat_id, message.message_id, latitude=52.223880, longitude=5.164306
|
||||
)
|
||||
|
||||
# TODO: Needs improvement with in inline sent live location.
|
||||
async def test_edit_live_inline_message(self, monkeypatch, bot, location):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
data = request_data.json_parameters
|
||||
lat = data["latitude"] == str(location.latitude)
|
||||
lon = data["longitude"] == str(location.longitude)
|
||||
id_ = data["inline_message_id"] == "1234"
|
||||
ha = data["horizontal_accuracy"] == "50"
|
||||
heading = data["heading"] == "90"
|
||||
prox_alert = data["proximity_alert_radius"] == "1000"
|
||||
return lat and lon and id_ and ha and heading and prox_alert
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.edit_message_live_location(
|
||||
inline_message_id=1234,
|
||||
location=location,
|
||||
horizontal_accuracy=50,
|
||||
heading=90,
|
||||
proximity_alert_radius=1000,
|
||||
)
|
||||
|
||||
# TODO: Needs improvement with in inline sent live location.
|
||||
async def test_stop_live_inline_message(self, monkeypatch, bot):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
id_ = request_data.json_parameters["inline_message_id"] == "1234"
|
||||
return id_
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.stop_message_live_location(inline_message_id=1234)
|
||||
|
||||
async def test_send_with_location(self, monkeypatch, bot, chat_id, location):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
lat = request_data.json_parameters["latitude"] == str(location.latitude)
|
||||
lon = request_data.json_parameters["longitude"] == str(location.longitude)
|
||||
return lat and lon
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_location(location=location, chat_id=chat_id)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
({"allow_sending_without_reply": True}, None),
|
||||
({"allow_sending_without_reply": False}, None),
|
||||
({"allow_sending_without_reply": False}, True),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_location_default_allow_sending_without_reply(
|
||||
self, default_bot, chat_id, location, custom
|
||||
):
|
||||
reply_to_message = await default_bot.send_message(chat_id, "test")
|
||||
await reply_to_message.delete()
|
||||
if custom is not None:
|
||||
message = await default_bot.send_location(
|
||||
chat_id,
|
||||
location=location,
|
||||
allow_sending_without_reply=custom,
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
elif default_bot.defaults.allow_sending_without_reply:
|
||||
message = await default_bot.send_location(
|
||||
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
await default_bot.send_location(
|
||||
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_location_default_protect_content(self, chat_id, default_bot, location):
|
||||
protected = await default_bot.send_location(chat_id, location=location)
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_location(
|
||||
chat_id, location=location, protect_content=False
|
||||
)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
async def test_edit_live_location_with_location(self, monkeypatch, bot, location):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
lat = request_data.json_parameters["latitude"] == str(location.latitude)
|
||||
lon = request_data.json_parameters["longitude"] == str(location.longitude)
|
||||
return lat and lon
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.edit_message_live_location(None, None, location=location)
|
||||
|
||||
async def test_send_location_without_required(self, bot, chat_id):
|
||||
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
|
||||
await bot.send_location(chat_id=chat_id)
|
||||
|
||||
async def test_edit_location_without_required(self, bot):
|
||||
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
|
||||
await bot.edit_message_live_location(chat_id=2, message_id=3)
|
||||
|
||||
async def test_send_location_with_all_args(self, bot, location):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.send_location(chat_id=1, latitude=2.5, longitude=4.6, location=location)
|
||||
|
||||
async def test_edit_location_with_all_args(self, bot, location):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.edit_message_live_location(
|
||||
chat_id=1, message_id=7, latitude=2.5, longitude=4.6, location=location
|
||||
)
|
||||
|
||||
def test_to_dict(self, location):
|
||||
location_dict = location.to_dict()
|
||||
|
||||
assert location_dict["latitude"] == location.latitude
|
||||
assert location_dict["longitude"] == location.longitude
|
||||
assert location_dict["horizontal_accuracy"] == location.horizontal_accuracy
|
||||
assert location_dict["live_period"] == location.live_period
|
||||
assert location["heading"] == location.heading
|
||||
assert location["proximity_alert_radius"] == location.proximity_alert_radius
|
||||
|
||||
def test_equality(self):
|
||||
a = Location(self.longitude, self.latitude)
|
||||
b = Location(self.longitude, self.latitude)
|
||||
d = Location(0, self.latitude)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
@@ -15,6 +15,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
@@ -30,39 +31,39 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file, expect_bad_request
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import expect_bad_request
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def photo_file():
|
||||
f = data_file("telegram.jpg").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
with data_file("telegram.jpg").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
async def _photo(bot, chat_id):
|
||||
async def func():
|
||||
with data_file("telegram.jpg").open("rb") as f:
|
||||
photo = (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo
|
||||
return photo
|
||||
return (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo
|
||||
|
||||
return await expect_bad_request(
|
||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def thumb(_photo):
|
||||
return _photo[0]
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def photo(_photo):
|
||||
return _photo[-1]
|
||||
|
||||
|
||||
class TestPhoto:
|
||||
class TestPhotoBase:
|
||||
width = 800
|
||||
height = 800
|
||||
caption = "<b>PhotoTest</b> - *Caption*"
|
||||
@@ -71,7 +72,9 @@ class TestPhoto:
|
||||
# so we accept three different sizes here. Shouldn't be too much
|
||||
file_size = [29176, 27662]
|
||||
|
||||
def test_slot_behaviour(self, photo, mro_slots):
|
||||
|
||||
class TestPhotoWithoutRequest(TestPhotoBase):
|
||||
def test_slot_behaviour(self, photo):
|
||||
for attr in photo.__slots__:
|
||||
assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(photo)) == len(set(mro_slots(photo))), "duplicate slot"
|
||||
@@ -98,328 +101,6 @@ class TestPhoto:
|
||||
assert thumb.height == 90
|
||||
assert thumb.file_size == 1477
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_photo_all_args(self, bot, chat_id, photo_file, thumb, photo):
|
||||
message = await bot.send_photo(
|
||||
chat_id,
|
||||
photo_file,
|
||||
caption=self.caption,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
parse_mode="Markdown",
|
||||
has_spoiler=True,
|
||||
)
|
||||
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
assert message.caption == TestPhoto.caption.replace("*", "")
|
||||
assert message.has_protected_content
|
||||
assert message.has_media_spoiler
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_photo_custom_filename(self, bot, chat_id, photo_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_photo(chat_id, photo_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file, thumb, photo):
|
||||
message = await bot.send_photo(
|
||||
chat_id, photo_file, caption=self.caption, parse_mode="Markdown"
|
||||
)
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
assert message.caption == TestPhoto.caption.replace("*", "")
|
||||
assert len(message.caption_entities) == 1
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file, thumb, photo):
|
||||
message = await bot.send_photo(
|
||||
chat_id, photo_file, caption=self.caption, parse_mode="HTML"
|
||||
)
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
assert message.caption == TestPhoto.caption.replace("<b>", "").replace("</b>", "")
|
||||
assert len(message.caption_entities) == 1
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_photo_caption_entities(self, bot, chat_id, photo_file, thumb, photo):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_photo(
|
||||
chat_id, photo_file, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_photo_default_parse_mode_1(
|
||||
self, default_bot, chat_id, photo_file, thumb, photo
|
||||
):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_photo(chat_id, photo_file, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_photo_default_parse_mode_2(
|
||||
self, default_bot, chat_id, photo_file, thumb, photo
|
||||
):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_photo(
|
||||
chat_id, photo_file, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_photo_default_parse_mode_3(
|
||||
self, default_bot, chat_id, photo_file, thumb, photo
|
||||
):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_photo(
|
||||
chat_id, photo_file, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_photo_default_protect_content(self, chat_id, default_bot, photo):
|
||||
protected = await default_bot.send_photo(chat_id, photo)
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_photo(chat_id, photo, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_photo_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("photo") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("photo"), InputFile)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_photo(chat_id, file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
({"allow_sending_without_reply": True}, None),
|
||||
({"allow_sending_without_reply": False}, None),
|
||||
({"allow_sending_without_reply": False}, True),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_photo_default_allow_sending_without_reply(
|
||||
self, default_bot, chat_id, photo_file, thumb, photo, custom
|
||||
):
|
||||
reply_to_message = await default_bot.send_message(chat_id, "test")
|
||||
await reply_to_message.delete()
|
||||
if custom is not None:
|
||||
message = await default_bot.send_photo(
|
||||
chat_id,
|
||||
photo_file,
|
||||
allow_sending_without_reply=custom,
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
elif default_bot.defaults.allow_sending_without_reply:
|
||||
message = await default_bot.send_photo(
|
||||
chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
await default_bot.send_photo(
|
||||
chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, photo):
|
||||
path = Path("telegram.jpg")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.getFile(photo.file_id)
|
||||
|
||||
assert new_file.file_size == photo.file_size
|
||||
assert new_file.file_unique_id == photo.file_unique_id
|
||||
assert new_file.file_path.startswith("https://") is True
|
||||
|
||||
await new_file.download_to_drive("telegram.jpg")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_url_jpg_file(self, bot, chat_id, thumb, photo):
|
||||
message = await bot.send_photo(chat_id, photo=self.photo_file_url)
|
||||
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_url_png_file(self, bot, chat_id):
|
||||
message = await bot.send_photo(
|
||||
photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id
|
||||
)
|
||||
|
||||
photo = message.photo[-1]
|
||||
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_url_gif_file(self, bot, chat_id):
|
||||
message = await bot.send_photo(
|
||||
photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id
|
||||
)
|
||||
|
||||
photo = message.photo[-1]
|
||||
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_file_unicode_filename(self, bot, chat_id):
|
||||
"""
|
||||
Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/1202
|
||||
"""
|
||||
with data_file("测试.png").open("rb") as f:
|
||||
message = await bot.send_photo(photo=f, chat_id=chat_id)
|
||||
|
||||
photo = message.photo[-1]
|
||||
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_bytesio_jpg_file(self, bot, chat_id):
|
||||
filepath = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
# raw image bytes
|
||||
raw_bytes = BytesIO(filepath.read_bytes())
|
||||
input_file = InputFile(raw_bytes)
|
||||
assert input_file.mimetype == "application/octet-stream"
|
||||
|
||||
# raw image bytes with name info
|
||||
raw_bytes = BytesIO(filepath.read_bytes())
|
||||
raw_bytes.name = str(filepath)
|
||||
input_file = InputFile(raw_bytes)
|
||||
assert input_file.mimetype == "image/jpeg"
|
||||
|
||||
# send raw photo
|
||||
raw_bytes = BytesIO(filepath.read_bytes())
|
||||
message = await bot.send_photo(chat_id, photo=raw_bytes)
|
||||
photo = message.photo[-1]
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert photo.width == 1280
|
||||
assert photo.height == 720
|
||||
assert photo.file_size == 33372
|
||||
|
||||
async def test_send_with_photosize(self, monkeypatch, bot, chat_id, photo):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["photo"] == photo.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_photo(photo=photo, chat_id=chat_id)
|
||||
assert message
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_resend(self, bot, chat_id, photo, thumb):
|
||||
message = await bot.send_photo(chat_id=chat_id, photo=photo.file_id)
|
||||
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
def test_de_json(self, bot, photo):
|
||||
json_dict = {
|
||||
"file_id": photo.file_id,
|
||||
@@ -447,31 +128,6 @@ class TestPhoto:
|
||||
assert photo_dict["height"] == photo.height
|
||||
assert photo_dict["file_size"] == photo.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_photo(chat_id=chat_id, photo=open(os.devnull, "rb"))
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_photo(chat_id=chat_id, photo="")
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_photo(chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, photo):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == photo.file_id
|
||||
|
||||
assert check_shortcut_signature(PhotoSize.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(photo.get_file, photo.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(photo.get_file, photo.get_bot())
|
||||
|
||||
monkeypatch.setattr(photo.get_bot(), "get_file", make_assertion)
|
||||
assert await photo.get_file()
|
||||
|
||||
def test_equality(self, photo):
|
||||
a = PhotoSize(photo.file_id, photo.file_unique_id, self.width, self.height)
|
||||
b = PhotoSize("", photo.file_unique_id, self.width, self.height)
|
||||
@@ -499,3 +155,315 @@ class TestPhoto:
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_photo(chat_id=chat_id)
|
||||
|
||||
async def test_send_photo_custom_filename(self, bot, chat_id, photo_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_photo(chat_id, photo_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_photo_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("photo") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("photo"), InputFile)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_photo(chat_id, file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
async def test_send_with_photosize(self, monkeypatch, bot, chat_id, photo):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["photo"] == photo.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_photo(photo=photo, chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, photo):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == photo.file_id
|
||||
|
||||
assert check_shortcut_signature(PhotoSize.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(photo.get_file, photo.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(photo.get_file, photo.get_bot())
|
||||
|
||||
monkeypatch.setattr(photo.get_bot(), "get_file", make_assertion)
|
||||
assert await photo.get_file()
|
||||
|
||||
|
||||
class TestPhotoWithRequest(TestPhotoBase):
|
||||
async def test_send_photo_all_args(self, bot, chat_id, photo_file):
|
||||
message = await bot.send_photo(
|
||||
chat_id,
|
||||
photo_file,
|
||||
caption=self.caption,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
parse_mode="Markdown",
|
||||
has_spoiler=True,
|
||||
)
|
||||
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
assert message.has_protected_content
|
||||
assert message.has_media_spoiler
|
||||
|
||||
async def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file):
|
||||
message = await bot.send_photo(
|
||||
chat_id, photo_file, caption=self.caption, parse_mode="Markdown"
|
||||
)
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
assert len(message.caption_entities) == 1
|
||||
|
||||
async def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file):
|
||||
message = await bot.send_photo(
|
||||
chat_id, photo_file, caption=self.caption, parse_mode="HTML"
|
||||
)
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
assert message.caption == self.caption.replace("<b>", "").replace("</b>", "")
|
||||
assert len(message.caption_entities) == 1
|
||||
|
||||
async def test_send_photo_caption_entities(self, bot, chat_id, photo_file):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_photo(
|
||||
chat_id, photo_file, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_photo_default_parse_mode_1(self, default_bot, chat_id, photo_file):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_photo(chat_id, photo_file, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_photo_default_parse_mode_2(self, default_bot, chat_id, photo_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_photo(
|
||||
chat_id, photo_file, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_photo_default_parse_mode_3(self, default_bot, chat_id, photo_file):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_photo(
|
||||
chat_id, photo_file, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_photo_default_protect_content(self, chat_id, default_bot, photo):
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_photo(chat_id, photo),
|
||||
default_bot.send_photo(chat_id, photo, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
({"allow_sending_without_reply": True}, None),
|
||||
({"allow_sending_without_reply": False}, None),
|
||||
({"allow_sending_without_reply": False}, True),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_photo_default_allow_sending_without_reply(
|
||||
self, default_bot, chat_id, photo_file, custom
|
||||
):
|
||||
reply_to_message = await default_bot.send_message(chat_id, "test")
|
||||
await reply_to_message.delete()
|
||||
if custom is not None:
|
||||
message = await default_bot.send_photo(
|
||||
chat_id,
|
||||
photo_file,
|
||||
allow_sending_without_reply=custom,
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
elif default_bot.defaults.allow_sending_without_reply:
|
||||
message = await default_bot.send_photo(
|
||||
chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
await default_bot.send_photo(
|
||||
chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
async def test_get_and_download(self, bot, photo):
|
||||
path = Path("telegram.jpg")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.getFile(photo.file_id)
|
||||
|
||||
assert new_file.file_size == photo.file_size
|
||||
assert new_file.file_unique_id == photo.file_unique_id
|
||||
assert new_file.file_path.startswith("https://") is True
|
||||
|
||||
await new_file.download_to_drive("telegram.jpg")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
async def test_send_url_jpg_file(self, bot, chat_id):
|
||||
message = await bot.send_photo(chat_id, photo=self.photo_file_url)
|
||||
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
async def test_send_url_png_file(self, bot, chat_id):
|
||||
message = await bot.send_photo(
|
||||
photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id
|
||||
)
|
||||
|
||||
photo = message.photo[-1]
|
||||
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
|
||||
async def test_send_file_unicode_filename(self, bot, chat_id):
|
||||
"""
|
||||
Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/1202
|
||||
"""
|
||||
with data_file("测试.png").open("rb") as f:
|
||||
message = await bot.send_photo(photo=f, chat_id=chat_id)
|
||||
|
||||
photo = message.photo[-1]
|
||||
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
|
||||
async def test_send_bytesio_jpg_file(self, bot, chat_id):
|
||||
filepath = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
# raw image bytes
|
||||
raw_bytes = BytesIO(filepath.read_bytes())
|
||||
input_file = InputFile(raw_bytes)
|
||||
assert input_file.mimetype == "application/octet-stream"
|
||||
|
||||
# raw image bytes with name info
|
||||
raw_bytes = BytesIO(filepath.read_bytes())
|
||||
raw_bytes.name = str(filepath)
|
||||
input_file = InputFile(raw_bytes)
|
||||
assert input_file.mimetype == "image/jpeg"
|
||||
|
||||
# send raw photo
|
||||
raw_bytes = BytesIO(filepath.read_bytes())
|
||||
message = await bot.send_photo(chat_id, photo=raw_bytes)
|
||||
photo = message.photo[-1]
|
||||
assert isinstance(photo.file_id, str)
|
||||
assert isinstance(photo.file_unique_id, str)
|
||||
assert photo.file_id != ""
|
||||
assert photo.file_unique_id != ""
|
||||
assert isinstance(photo, PhotoSize)
|
||||
assert photo.width == 1280
|
||||
assert photo.height == 720
|
||||
assert photo.file_size == 33372
|
||||
|
||||
async def test_resend(self, bot, chat_id, photo):
|
||||
message = await bot.send_photo(chat_id=chat_id, photo=photo.file_id)
|
||||
|
||||
assert isinstance(message.photo[-2], PhotoSize)
|
||||
assert isinstance(message.photo[-2].file_id, str)
|
||||
assert isinstance(message.photo[-2].file_unique_id, str)
|
||||
assert message.photo[-2].file_id != ""
|
||||
assert message.photo[-2].file_unique_id != ""
|
||||
|
||||
assert isinstance(message.photo[-1], PhotoSize)
|
||||
assert isinstance(message.photo[-1].file_id, str)
|
||||
assert isinstance(message.photo[-1].file_unique_id, str)
|
||||
assert message.photo[-1].file_id != ""
|
||||
assert message.photo[-1].file_unique_id != ""
|
||||
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_photo(chat_id=chat_id, photo=open(os.devnull, "rb"))
|
||||
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_photo(chat_id=chat_id, photo="")
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,27 +16,30 @@
|
||||
#
|
||||
# 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 pytest
|
||||
|
||||
from telegram import Location, Venue
|
||||
from telegram.error import BadRequest
|
||||
from telegram.request import RequestData
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def venue():
|
||||
return Venue(
|
||||
TestVenue.location,
|
||||
TestVenue.title,
|
||||
TestVenue.address,
|
||||
foursquare_id=TestVenue.foursquare_id,
|
||||
foursquare_type=TestVenue.foursquare_type,
|
||||
google_place_id=TestVenue.google_place_id,
|
||||
google_place_type=TestVenue.google_place_type,
|
||||
TestVenueBase.location,
|
||||
TestVenueBase.title,
|
||||
TestVenueBase.address,
|
||||
foursquare_id=TestVenueBase.foursquare_id,
|
||||
foursquare_type=TestVenueBase.foursquare_type,
|
||||
google_place_id=TestVenueBase.google_place_id,
|
||||
google_place_type=TestVenueBase.google_place_type,
|
||||
)
|
||||
|
||||
|
||||
class TestVenue:
|
||||
class TestVenueBase:
|
||||
location = Location(longitude=-46.788279, latitude=-23.691288)
|
||||
title = "title"
|
||||
address = "address"
|
||||
@@ -45,20 +48,22 @@ class TestVenue:
|
||||
google_place_id = "google place id"
|
||||
google_place_type = "google place type"
|
||||
|
||||
def test_slot_behaviour(self, venue, mro_slots):
|
||||
|
||||
class TestVenueWithoutRequest(TestVenueBase):
|
||||
def test_slot_behaviour(self, venue):
|
||||
for attr in venue.__slots__:
|
||||
assert getattr(venue, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(venue)) == len(set(mro_slots(venue))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
"location": TestVenue.location.to_dict(),
|
||||
"title": TestVenue.title,
|
||||
"address": TestVenue.address,
|
||||
"foursquare_id": TestVenue.foursquare_id,
|
||||
"foursquare_type": TestVenue.foursquare_type,
|
||||
"google_place_id": TestVenue.google_place_id,
|
||||
"google_place_type": TestVenue.google_place_type,
|
||||
"location": self.location.to_dict(),
|
||||
"title": self.title,
|
||||
"address": self.address,
|
||||
"foursquare_id": self.foursquare_id,
|
||||
"foursquare_type": self.foursquare_type,
|
||||
"google_place_id": self.google_place_id,
|
||||
"google_place_type": self.google_place_type,
|
||||
}
|
||||
venue = Venue.de_json(json_dict, bot)
|
||||
assert venue.api_kwargs == {}
|
||||
@@ -71,6 +76,53 @@ class TestVenue:
|
||||
assert venue.google_place_id == self.google_place_id
|
||||
assert venue.google_place_type == self.google_place_type
|
||||
|
||||
def test_to_dict(self, venue):
|
||||
venue_dict = venue.to_dict()
|
||||
|
||||
assert isinstance(venue_dict, dict)
|
||||
assert venue_dict["location"] == venue.location.to_dict()
|
||||
assert venue_dict["title"] == venue.title
|
||||
assert venue_dict["address"] == venue.address
|
||||
assert venue_dict["foursquare_id"] == venue.foursquare_id
|
||||
assert venue_dict["foursquare_type"] == venue.foursquare_type
|
||||
assert venue_dict["google_place_id"] == venue.google_place_id
|
||||
assert venue_dict["google_place_type"] == venue.google_place_type
|
||||
|
||||
def test_equality(self):
|
||||
a = Venue(Location(0, 0), self.title, self.address)
|
||||
b = Venue(Location(0, 0), self.title, self.address)
|
||||
c = Venue(Location(0, 0), self.title, "")
|
||||
d = Venue(Location(0, 1), self.title, self.address)
|
||||
d2 = Venue(Location(0, 0), "", self.address)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != d2
|
||||
assert hash(a) != hash(d2)
|
||||
|
||||
async def test_send_venue_without_required(self, bot, chat_id):
|
||||
with pytest.raises(ValueError, match="Either venue or latitude, longitude, address and"):
|
||||
await bot.send_venue(chat_id=chat_id)
|
||||
|
||||
async def test_send_venue_mutually_exclusive(self, bot, chat_id, venue):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.send_venue(
|
||||
chat_id=chat_id,
|
||||
latitude=1,
|
||||
longitude=1,
|
||||
address="address",
|
||||
title="title",
|
||||
venue=venue,
|
||||
)
|
||||
|
||||
async def test_send_with_venue(self, monkeypatch, bot, chat_id, venue):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
data = request_data.json_parameters
|
||||
@@ -89,7 +141,8 @@ class TestVenue:
|
||||
message = await bot.send_venue(chat_id, venue=venue)
|
||||
assert message
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
|
||||
class TestVenueWithRequest(TestVenueBase):
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -123,57 +176,12 @@ class TestVenue:
|
||||
chat_id, venue=venue, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_venue_default_protect_content(self, default_bot, chat_id, venue):
|
||||
protected = await default_bot.send_venue(chat_id, venue=venue)
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_venue(chat_id, venue=venue),
|
||||
default_bot.send_venue(chat_id, venue=venue, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_venue(chat_id, venue=venue, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
async def test_send_venue_without_required(self, bot, chat_id):
|
||||
with pytest.raises(ValueError, match="Either venue or latitude, longitude, address and"):
|
||||
await bot.send_venue(chat_id=chat_id)
|
||||
|
||||
async def test_send_venue_mutually_exclusive(self, bot, chat_id, venue):
|
||||
with pytest.raises(ValueError, match="Not both"):
|
||||
await bot.send_venue(
|
||||
chat_id=chat_id,
|
||||
latitude=1,
|
||||
longitude=1,
|
||||
address="address",
|
||||
title="title",
|
||||
venue=venue,
|
||||
)
|
||||
|
||||
def test_to_dict(self, venue):
|
||||
venue_dict = venue.to_dict()
|
||||
|
||||
assert isinstance(venue_dict, dict)
|
||||
assert venue_dict["location"] == venue.location.to_dict()
|
||||
assert venue_dict["title"] == venue.title
|
||||
assert venue_dict["address"] == venue.address
|
||||
assert venue_dict["foursquare_id"] == venue.foursquare_id
|
||||
assert venue_dict["foursquare_type"] == venue.foursquare_type
|
||||
assert venue_dict["google_place_id"] == venue.google_place_id
|
||||
assert venue_dict["google_place_type"] == venue.google_place_type
|
||||
|
||||
def test_equality(self):
|
||||
a = Venue(Location(0, 0), self.title, self.address)
|
||||
b = Venue(Location(0, 0), self.title, self.address)
|
||||
c = Venue(Location(0, 0), self.title, "")
|
||||
d = Venue(Location(0, 1), self.title, self.address)
|
||||
d2 = Venue(Location(0, 0), "", self.address)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != d2
|
||||
assert hash(a) != hash(d2)
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,23 +31,24 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def video_file():
|
||||
f = data_file("telegram.mp4").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
with data_file("telegram.mp4").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
async def video(bot, chat_id):
|
||||
with data_file("telegram.mp4").open("rb") as f:
|
||||
return (await bot.send_video(chat_id, video=f, read_timeout=50)).video
|
||||
|
||||
|
||||
class TestVideo:
|
||||
class TestVideoBase:
|
||||
width = 360
|
||||
height = 640
|
||||
duration = 5
|
||||
@@ -54,18 +56,17 @@ class TestVideo:
|
||||
mime_type = "video/mp4"
|
||||
supports_streaming = True
|
||||
file_name = "telegram.mp4"
|
||||
|
||||
thumb_width = 180
|
||||
thumb_height = 320
|
||||
thumb_file_size = 1767
|
||||
|
||||
caption = "<b>VideoTest</b> - *Caption*"
|
||||
video_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.mp4"
|
||||
|
||||
video_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||
video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
|
||||
def test_slot_behaviour(self, video, mro_slots):
|
||||
|
||||
class TestVideoWithoutRequest(TestVideoBase):
|
||||
def test_slot_behaviour(self, video):
|
||||
for attr in video.__slots__:
|
||||
assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(video)) == len(set(mro_slots(video))), "duplicate slot"
|
||||
@@ -78,11 +79,11 @@ class TestVideo:
|
||||
assert video.file_id != ""
|
||||
assert video.file_unique_id != ""
|
||||
|
||||
assert isinstance(video.thumb, PhotoSize)
|
||||
assert isinstance(video.thumb.file_id, str)
|
||||
assert isinstance(video.thumb.file_unique_id, str)
|
||||
assert video.thumb.file_id != ""
|
||||
assert video.thumb.file_unique_id != ""
|
||||
assert isinstance(video.thumbnail, PhotoSize)
|
||||
assert isinstance(video.thumbnail.file_id, str)
|
||||
assert isinstance(video.thumbnail.file_unique_id, str)
|
||||
assert video.thumbnail.file_id != ""
|
||||
assert video.thumbnail.file_unique_id != ""
|
||||
|
||||
def test_expected_values(self, video):
|
||||
assert video.width == self.width
|
||||
@@ -91,220 +92,17 @@ class TestVideo:
|
||||
assert video.file_size == self.file_size
|
||||
assert video.mime_type == self.mime_type
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
|
||||
message = await bot.send_video(
|
||||
chat_id,
|
||||
video_file,
|
||||
duration=self.duration,
|
||||
caption=self.caption,
|
||||
supports_streaming=self.supports_streaming,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
width=video.width,
|
||||
height=video.height,
|
||||
parse_mode="Markdown",
|
||||
thumb=thumb_file,
|
||||
has_spoiler=True,
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
video = Video(
|
||||
self.video_file_id,
|
||||
self.video_file_unique_id,
|
||||
self.width,
|
||||
self.height,
|
||||
self.duration,
|
||||
thumb=object(),
|
||||
)
|
||||
|
||||
assert isinstance(message.video, Video)
|
||||
assert isinstance(message.video.file_id, str)
|
||||
assert isinstance(message.video.file_unique_id, str)
|
||||
assert message.video.file_id != ""
|
||||
assert message.video.file_unique_id != ""
|
||||
assert message.video.width == video.width
|
||||
assert message.video.height == video.height
|
||||
assert message.video.duration == video.duration
|
||||
assert message.video.file_size == video.file_size
|
||||
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
|
||||
assert message.video.thumb.file_size == self.thumb_file_size
|
||||
assert message.video.thumb.width == self.thumb_width
|
||||
assert message.video.thumb.height == self.thumb_height
|
||||
|
||||
assert message.video.file_name == self.file_name
|
||||
assert message.has_protected_content
|
||||
assert message.has_media_spoiler
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_video_custom_filename(self, bot, chat_id, video_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_video(chat_id, video_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, video):
|
||||
path = Path("telegram.mp4")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(video.file_id)
|
||||
|
||||
assert new_file.file_size == self.file_size
|
||||
assert new_file.file_id == video.file_id
|
||||
assert new_file.file_unique_id == video.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.mp4")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_mp4_file_url(self, bot, chat_id, video):
|
||||
message = await bot.send_video(chat_id, self.video_file_url, caption=self.caption)
|
||||
|
||||
assert isinstance(message.video, Video)
|
||||
assert isinstance(message.video.file_id, str)
|
||||
assert isinstance(message.video.file_unique_id, str)
|
||||
assert message.video.file_id != ""
|
||||
assert message.video.file_unique_id != ""
|
||||
assert message.video.width == video.width
|
||||
assert message.video.height == video.height
|
||||
assert message.video.duration == video.duration
|
||||
assert message.video.file_size == video.file_size
|
||||
|
||||
assert isinstance(message.video.thumb, PhotoSize)
|
||||
assert isinstance(message.video.thumb.file_id, str)
|
||||
assert isinstance(message.video.thumb.file_unique_id, str)
|
||||
assert message.video.thumb.file_id != ""
|
||||
assert message.video.thumb.file_unique_id != ""
|
||||
assert message.video.thumb.width == 51 # This seems odd that it's not self.thumb_width
|
||||
assert message.video.thumb.height == 90 # Ditto
|
||||
assert message.video.thumb.file_size == 645 # same
|
||||
|
||||
assert message.caption == self.caption
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_video_caption_entities(self, bot, chat_id, video):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_video(
|
||||
chat_id, video, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_resend(self, bot, chat_id, video):
|
||||
message = await bot.send_video(chat_id, video.file_id)
|
||||
|
||||
assert message.video == video
|
||||
|
||||
async def test_send_with_video(self, monkeypatch, bot, chat_id, video):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["video"] == video.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_video(chat_id, video=video)
|
||||
assert message
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_video_default_parse_mode_1(self, default_bot, chat_id, video):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_video(chat_id, video, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_video_default_parse_mode_2(self, default_bot, chat_id, video):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_video(
|
||||
chat_id, video, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_video_default_parse_mode_3(self, default_bot, chat_id, video):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_video(
|
||||
chat_id, video, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_video_default_protect_content(self, chat_id, default_bot, video):
|
||||
protected = await default_bot.send_video(chat_id, video)
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_video(chat_id, video, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_video_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("video") == expected and data.get("thumb") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("video"), InputFile) and isinstance(
|
||||
data.get("thumb"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_video(chat_id, file, thumb=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
({"allow_sending_without_reply": True}, None),
|
||||
({"allow_sending_without_reply": False}, None),
|
||||
({"allow_sending_without_reply": False}, True),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_video_default_allow_sending_without_reply(
|
||||
self, default_bot, chat_id, video, custom
|
||||
):
|
||||
reply_to_message = await default_bot.send_message(chat_id, "test")
|
||||
await reply_to_message.delete()
|
||||
if custom is not None:
|
||||
message = await default_bot.send_video(
|
||||
chat_id,
|
||||
video,
|
||||
allow_sending_without_reply=custom,
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
elif default_bot.defaults.allow_sending_without_reply:
|
||||
message = await default_bot.send_video(
|
||||
chat_id, video, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
await default_bot.send_video(
|
||||
chat_id, video, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert video.thumb is video.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
@@ -342,31 +140,6 @@ class TestVideo:
|
||||
assert video_dict["file_size"] == video.file_size
|
||||
assert video_dict["file_name"] == video.file_name
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_video(chat_id, open(os.devnull, "rb"))
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_video(chat_id, "")
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_video(chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, video):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == video.file_id
|
||||
|
||||
assert check_shortcut_signature(Video.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(video.get_file, video.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(video.get_file, video.get_bot())
|
||||
|
||||
monkeypatch.setattr(video.get_bot(), "get_file", make_assertion)
|
||||
assert await video.get_file()
|
||||
|
||||
def test_equality(self, video):
|
||||
a = Video(video.file_id, video.file_unique_id, self.width, self.height, self.duration)
|
||||
b = Video("", video.file_unique_id, self.width, self.height, self.duration)
|
||||
@@ -386,3 +159,241 @@ class TestVideo:
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_video(chat_id=chat_id)
|
||||
|
||||
async def test_send_with_video(self, monkeypatch, bot, chat_id, video):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["video"] == video.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_video(chat_id, video=video)
|
||||
|
||||
async def test_send_video_custom_filename(self, bot, chat_id, video_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_video(chat_id, video_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_video_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
bot._local_mode = local_mode
|
||||
# For just test that the correct paths are passed as we have no local bot API set up
|
||||
test_flag = False
|
||||
file = data_file("telegram.jpg")
|
||||
expected = file.as_uri()
|
||||
|
||||
async def make_assertion(_, data, *args, **kwargs):
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = data.get("video") == expected and data.get("thumbnail") == expected
|
||||
else:
|
||||
test_flag = isinstance(data.get("video"), InputFile) and isinstance(
|
||||
data.get("thumbnail"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_video(chat_id, file, thumbnail=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
async def test_send_video_with_local_files_throws_exception_with_different_thumb_and_thumbnail(
|
||||
self, bot, chat_id
|
||||
):
|
||||
file = data_file("telegram.jpg")
|
||||
different_file = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
with pytest.raises(ValueError, match="different entities as 'thumb' and 'thumbnail'"):
|
||||
await bot.send_video(chat_id, file, thumbnail=file, thumb=different_file)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, video):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == video.file_id
|
||||
|
||||
assert check_shortcut_signature(Video.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(video.get_file, video.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(video.get_file, video.get_bot())
|
||||
|
||||
monkeypatch.setattr(video.get_bot(), "get_file", make_assertion)
|
||||
assert await video.get_file()
|
||||
|
||||
|
||||
class TestVideoWithRequest(TestVideoBase):
|
||||
async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
|
||||
message = await bot.send_video(
|
||||
chat_id,
|
||||
video_file,
|
||||
duration=self.duration,
|
||||
caption=self.caption,
|
||||
supports_streaming=self.supports_streaming,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
width=video.width,
|
||||
height=video.height,
|
||||
parse_mode="Markdown",
|
||||
thumbnail=thumb_file,
|
||||
has_spoiler=True,
|
||||
)
|
||||
|
||||
assert isinstance(message.video, Video)
|
||||
assert isinstance(message.video.file_id, str)
|
||||
assert isinstance(message.video.file_unique_id, str)
|
||||
assert message.video.file_id != ""
|
||||
assert message.video.file_unique_id != ""
|
||||
assert message.video.width == video.width
|
||||
assert message.video.height == video.height
|
||||
assert message.video.duration == video.duration
|
||||
assert message.video.file_size == video.file_size
|
||||
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
|
||||
assert message.video.thumbnail.file_size == self.thumb_file_size
|
||||
assert message.video.thumbnail.width == self.thumb_width
|
||||
assert message.video.thumbnail.height == self.thumb_height
|
||||
|
||||
assert message.video.file_name == self.file_name
|
||||
assert message.has_protected_content
|
||||
assert message.has_media_spoiler
|
||||
|
||||
async def test_get_and_download(self, bot, video, chat_id):
|
||||
path = Path("telegram.mp4")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(video.file_id)
|
||||
|
||||
assert new_file.file_size == self.file_size
|
||||
assert new_file.file_unique_id == video.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.mp4")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
async def test_send_mp4_file_url(self, bot, chat_id, video):
|
||||
message = await bot.send_video(chat_id, self.video_file_url, caption=self.caption)
|
||||
|
||||
assert isinstance(message.video, Video)
|
||||
assert isinstance(message.video.file_id, str)
|
||||
assert isinstance(message.video.file_unique_id, str)
|
||||
assert message.video.file_id != ""
|
||||
assert message.video.file_unique_id != ""
|
||||
assert message.video.width == video.width
|
||||
assert message.video.height == video.height
|
||||
assert message.video.duration == video.duration
|
||||
assert message.video.file_size == video.file_size
|
||||
|
||||
assert isinstance(message.video.thumbnail, PhotoSize)
|
||||
assert isinstance(message.video.thumbnail.file_id, str)
|
||||
assert isinstance(message.video.thumbnail.file_unique_id, str)
|
||||
assert message.video.thumbnail.file_id != ""
|
||||
assert message.video.thumbnail.file_unique_id != ""
|
||||
assert message.video.thumbnail.width == 51 # This seems odd that it's not self.thumb_width
|
||||
assert message.video.thumbnail.height == 90 # Ditto
|
||||
assert message.video.thumbnail.file_size == 645 # same
|
||||
|
||||
assert message.caption == self.caption
|
||||
|
||||
async def test_send_video_caption_entities(self, bot, chat_id, video):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_video(
|
||||
chat_id, video, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
async def test_resend(self, bot, chat_id, video):
|
||||
message = await bot.send_video(chat_id, video.file_id)
|
||||
assert message.video == video
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_video_default_parse_mode_1(self, default_bot, chat_id, video):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_video(chat_id, video, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_video_default_parse_mode_2(self, default_bot, chat_id, video):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_video(
|
||||
chat_id, video, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_video_default_parse_mode_3(self, default_bot, chat_id, video):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_video(
|
||||
chat_id, video, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_video_default_protect_content(self, chat_id, default_bot, video):
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_video(chat_id, video),
|
||||
default_bot.send_video(chat_id, video, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
({"allow_sending_without_reply": True}, None),
|
||||
({"allow_sending_without_reply": False}, None),
|
||||
({"allow_sending_without_reply": False}, True),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_video_default_allow_sending_without_reply(
|
||||
self, default_bot, chat_id, video, custom
|
||||
):
|
||||
reply_to_message = await default_bot.send_message(chat_id, "test")
|
||||
await reply_to_message.delete()
|
||||
if custom is not None:
|
||||
message = await default_bot.send_video(
|
||||
chat_id,
|
||||
video,
|
||||
allow_sending_without_reply=custom,
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
elif default_bot.defaults.allow_sending_without_reply:
|
||||
message = await default_bot.send_video(
|
||||
chat_id, video, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
assert message.reply_to_message is None
|
||||
else:
|
||||
with pytest.raises(BadRequest, match="message not found"):
|
||||
await default_bot.send_video(
|
||||
chat_id, video, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_video(chat_id, open(os.devnull, "rb"))
|
||||
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_video(chat_id, "")
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -29,36 +30,37 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def video_note_file():
|
||||
f = data_file("telegram2.mp4").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
with data_file("telegram2.mp4").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
async def video_note(bot, chat_id):
|
||||
with data_file("telegram2.mp4").open("rb") as f:
|
||||
return (await bot.send_video_note(chat_id, video_note=f, read_timeout=50)).video_note
|
||||
|
||||
|
||||
class TestVideoNote:
|
||||
class TestVideoNoteBase:
|
||||
length = 240
|
||||
duration = 3
|
||||
file_size = 132084
|
||||
|
||||
thumb_width = 240
|
||||
thumb_height = 240
|
||||
thumb_file_size = 11547
|
||||
|
||||
caption = "VideoNoteTest - Caption"
|
||||
videonote_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||
videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
|
||||
def test_slot_behaviour(self, video_note, mro_slots):
|
||||
|
||||
class TestVideoNoteWithoutRequest(TestVideoNoteBase):
|
||||
def test_slot_behaviour(self, video_note):
|
||||
for attr in video_note.__slots__:
|
||||
assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(video_note)) == len(set(mro_slots(video_note))), "duplicate slot"
|
||||
@@ -71,84 +73,23 @@ class TestVideoNote:
|
||||
assert video_note.file_id != ""
|
||||
assert video_note.file_unique_id != ""
|
||||
|
||||
assert isinstance(video_note.thumb, PhotoSize)
|
||||
assert isinstance(video_note.thumb.file_id, str)
|
||||
assert isinstance(video_note.thumb.file_unique_id, str)
|
||||
assert video_note.thumb.file_id != ""
|
||||
assert video_note.thumb.file_unique_id != ""
|
||||
assert isinstance(video_note.thumbnail, PhotoSize)
|
||||
assert isinstance(video_note.thumbnail.file_id, str)
|
||||
assert isinstance(video_note.thumbnail.file_unique_id, str)
|
||||
assert video_note.thumbnail.file_id != ""
|
||||
assert video_note.thumbnail.file_unique_id != ""
|
||||
|
||||
def test_expected_values(self, video_note):
|
||||
assert video_note.length == self.length
|
||||
assert video_note.duration == self.duration
|
||||
assert video_note.file_size == self.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file):
|
||||
message = await bot.send_video_note(
|
||||
chat_id,
|
||||
video_note_file,
|
||||
duration=self.duration,
|
||||
length=self.length,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
thumb=thumb_file,
|
||||
def test_thumb_property_deprecation_warning(self, recwarn):
|
||||
video_note = VideoNote(
|
||||
file_id="id", file_unique_id="unique_id", length=1, duration=1, thumb=object()
|
||||
)
|
||||
|
||||
assert isinstance(message.video_note, VideoNote)
|
||||
assert isinstance(message.video_note.file_id, str)
|
||||
assert isinstance(message.video_note.file_unique_id, str)
|
||||
assert message.video_note.file_id != ""
|
||||
assert message.video_note.file_unique_id != ""
|
||||
assert message.video_note.length == video_note.length
|
||||
assert message.video_note.duration == video_note.duration
|
||||
assert message.video_note.file_size == video_note.file_size
|
||||
|
||||
assert message.video_note.thumb.file_size == self.thumb_file_size
|
||||
assert message.video_note.thumb.width == self.thumb_width
|
||||
assert message.video_note.thumb.height == self.thumb_height
|
||||
assert message.has_protected_content
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_video_note_custom_filename(
|
||||
self, bot, chat_id, video_note_file, monkeypatch
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_video_note(chat_id, video_note_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, video_note):
|
||||
path = Path("telegram2.mp4")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(video_note.file_id)
|
||||
|
||||
assert new_file.file_size == self.file_size
|
||||
assert new_file.file_id == video_note.file_id
|
||||
assert new_file.file_unique_id == video_note.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram2.mp4")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_resend(self, bot, chat_id, video_note):
|
||||
message = await bot.send_video_note(chat_id, video_note.file_id)
|
||||
|
||||
assert message.video_note == video_note
|
||||
|
||||
async def test_send_with_video_note(self, monkeypatch, bot, chat_id, video_note):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["video_note"] == video_note.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_video_note(chat_id, video_note=video_note)
|
||||
assert message
|
||||
assert video_note.thumb is video_note.thumbnail
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(recwarn, __file__)
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
@@ -177,6 +118,47 @@ class TestVideoNote:
|
||||
assert video_note_dict["duration"] == video_note.duration
|
||||
assert video_note_dict["file_size"] == video_note.file_size
|
||||
|
||||
def test_equality(self, video_note):
|
||||
a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration)
|
||||
b = VideoNote("", video_note.file_unique_id, self.length, self.duration)
|
||||
c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0)
|
||||
d = VideoNote("", "", self.length, self.duration)
|
||||
e = Voice(video_note.file_id, video_note.file_unique_id, self.duration)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_video_note(chat_id=chat_id)
|
||||
|
||||
async def test_send_with_video_note(self, monkeypatch, bot, chat_id, video_note):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["video_note"] == video_note.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.send_video_note(chat_id, video_note=video_note)
|
||||
|
||||
async def test_send_video_note_custom_filename(
|
||||
self, bot, chat_id, video_note_file, monkeypatch
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
|
||||
assert await bot.send_video_note(chat_id, video_note_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_video_note_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
try:
|
||||
@@ -190,20 +172,85 @@ class TestVideoNote:
|
||||
nonlocal test_flag
|
||||
if local_mode:
|
||||
test_flag = (
|
||||
data.get("video_note") == expected and data.get("thumb") == expected
|
||||
data.get("video_note") == expected and data.get("thumbnail") == expected
|
||||
)
|
||||
else:
|
||||
test_flag = isinstance(data.get("video_note"), InputFile) and isinstance(
|
||||
data.get("thumb"), InputFile
|
||||
data.get("thumbnail"), InputFile
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
await bot.send_video_note(chat_id, file, thumb=file)
|
||||
await bot.send_video_note(chat_id, file, thumbnail=file)
|
||||
assert test_flag
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_videonote_local_files_throws_exception_with_different_thumb_and_thumbnail(
|
||||
self, bot, chat_id
|
||||
):
|
||||
file = data_file("telegram.jpg")
|
||||
different_file = data_file("telegram_no_standard_header.jpg")
|
||||
|
||||
with pytest.raises(ValueError, match="different entities as 'thumb' and 'thumbnail'"):
|
||||
await bot.send_video_note(chat_id, file, thumbnail=file, thumb=different_file)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, video_note):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == video_note.file_id
|
||||
|
||||
assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(video_note.get_file, video_note.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(video_note.get_file, video_note.get_bot())
|
||||
|
||||
monkeypatch.setattr(video_note.get_bot(), "get_file", make_assertion)
|
||||
assert await video_note.get_file()
|
||||
|
||||
|
||||
class TestVideoNoteWithRequest(TestVideoNoteBase):
|
||||
async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file):
|
||||
message = await bot.send_video_note(
|
||||
chat_id,
|
||||
video_note_file,
|
||||
duration=self.duration,
|
||||
length=self.length,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
thumbnail=thumb_file,
|
||||
)
|
||||
|
||||
assert isinstance(message.video_note, VideoNote)
|
||||
assert isinstance(message.video_note.file_id, str)
|
||||
assert isinstance(message.video_note.file_unique_id, str)
|
||||
assert message.video_note.file_id != ""
|
||||
assert message.video_note.file_unique_id != ""
|
||||
assert message.video_note.length == video_note.length
|
||||
assert message.video_note.duration == video_note.duration
|
||||
assert message.video_note.file_size == video_note.file_size
|
||||
|
||||
assert message.video_note.thumbnail.file_size == self.thumb_file_size
|
||||
assert message.video_note.thumbnail.width == self.thumb_width
|
||||
assert message.video_note.thumbnail.height == self.thumb_height
|
||||
assert message.has_protected_content
|
||||
|
||||
async def test_get_and_download(self, bot, video_note, chat_id):
|
||||
path = Path("telegram2.mp4")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(video_note.file_id)
|
||||
|
||||
assert new_file.file_size == self.file_size
|
||||
assert new_file.file_unique_id == video_note.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram2.mp4")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
async def test_resend(self, bot, chat_id, video_note):
|
||||
message = await bot.send_video_note(chat_id, video_note.file_id)
|
||||
assert message.video_note == video_note
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -237,55 +284,20 @@ class TestVideoNote:
|
||||
chat_id, video_note, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_video_note_default_protect_content(self, chat_id, default_bot, video_note):
|
||||
protected = await default_bot.send_video_note(chat_id, video_note)
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_video_note(chat_id, video_note),
|
||||
default_bot.send_video_note(chat_id, video_note, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_video_note(chat_id, video_note, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_video_note(chat_id, open(os.devnull, "rb"))
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.send_video_note(chat_id, "")
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.send_video_note(chat_id=chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, video_note):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == video_note.file_id
|
||||
|
||||
assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(video_note.get_file, video_note.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(video_note.get_file, video_note.get_bot())
|
||||
|
||||
monkeypatch.setattr(video_note.get_bot(), "get_file", make_assertion)
|
||||
assert await video_note.get_file()
|
||||
|
||||
def test_equality(self, video_note):
|
||||
a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration)
|
||||
b = VideoNote("", video_note.file_unique_id, self.length, self.duration)
|
||||
c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0)
|
||||
d = VideoNote("", "", self.length, self.duration)
|
||||
e = Voice(video_note.file_id, video_note.file_unique_id, self.duration)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# 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 os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,34 +31,34 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.conftest import data_file
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def voice_file():
|
||||
f = data_file("telegram.ogg").open("rb")
|
||||
yield f
|
||||
f.close()
|
||||
with data_file("telegram.ogg").open("rb") as f:
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
async def voice(bot, chat_id):
|
||||
with data_file("telegram.ogg").open("rb") as f:
|
||||
return (await bot.send_voice(chat_id, voice=f, read_timeout=50)).voice
|
||||
|
||||
|
||||
class TestVoice:
|
||||
class TestVoiceBase:
|
||||
duration = 3
|
||||
mime_type = "audio/ogg"
|
||||
file_size = 9199
|
||||
|
||||
caption = "Test *voice*"
|
||||
voice_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.ogg"
|
||||
|
||||
voice_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||
voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||
|
||||
def test_slot_behaviour(self, voice, mro_slots):
|
||||
|
||||
class TestVoiceWithoutRequest(TestVoiceBase):
|
||||
def test_slot_behaviour(self, voice):
|
||||
for attr in voice.__slots__:
|
||||
assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(voice)) == len(set(mro_slots(voice))), "duplicate slot"
|
||||
@@ -75,30 +76,57 @@ class TestVoice:
|
||||
assert voice.mime_type == self.mime_type
|
||||
assert voice.file_size == self.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_all_args(self, bot, chat_id, voice_file, voice):
|
||||
message = await bot.send_voice(
|
||||
chat_id,
|
||||
voice_file,
|
||||
duration=self.duration,
|
||||
caption=self.caption,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
parse_mode="Markdown",
|
||||
)
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
"file_id": self.voice_file_id,
|
||||
"file_unique_id": self.voice_file_unique_id,
|
||||
"duration": self.duration,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
}
|
||||
json_voice = Voice.de_json(json_dict, bot)
|
||||
assert json_voice.api_kwargs == {}
|
||||
|
||||
assert isinstance(message.voice, Voice)
|
||||
assert isinstance(message.voice.file_id, str)
|
||||
assert isinstance(message.voice.file_unique_id, str)
|
||||
assert message.voice.file_id != ""
|
||||
assert message.voice.file_unique_id != ""
|
||||
assert message.voice.duration == voice.duration
|
||||
assert message.voice.mime_type == voice.mime_type
|
||||
assert message.voice.file_size == voice.file_size
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
assert message.has_protected_content
|
||||
assert json_voice.file_id == self.voice_file_id
|
||||
assert json_voice.file_unique_id == self.voice_file_unique_id
|
||||
assert json_voice.duration == self.duration
|
||||
assert json_voice.mime_type == self.mime_type
|
||||
assert json_voice.file_size == self.file_size
|
||||
|
||||
def test_to_dict(self, voice):
|
||||
voice_dict = voice.to_dict()
|
||||
|
||||
assert isinstance(voice_dict, dict)
|
||||
assert voice_dict["file_id"] == voice.file_id
|
||||
assert voice_dict["file_unique_id"] == voice.file_unique_id
|
||||
assert voice_dict["duration"] == voice.duration
|
||||
assert voice_dict["mime_type"] == voice.mime_type
|
||||
assert voice_dict["file_size"] == voice.file_size
|
||||
|
||||
def test_equality(self, voice):
|
||||
a = Voice(voice.file_id, voice.file_unique_id, self.duration)
|
||||
b = Voice("", voice.file_unique_id, self.duration)
|
||||
c = Voice(voice.file_id, voice.file_unique_id, 0)
|
||||
d = Voice("", "", self.duration)
|
||||
e = Audio(voice.file_id, voice.file_unique_id, self.duration)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.sendVoice(chat_id)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_voice_custom_filename(self, bot, chat_id, voice_file, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
|
||||
@@ -107,104 +135,12 @@ class TestVoice:
|
||||
|
||||
assert await bot.send_voice(chat_id, voice_file, filename="custom_filename")
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_and_download(self, bot, voice):
|
||||
path = Path("telegram.ogg")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(voice.file_id)
|
||||
|
||||
assert new_file.file_size == voice.file_size
|
||||
assert new_file.file_id == voice.file_id
|
||||
assert new_file.file_unique_id == voice.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.ogg")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_ogg_url_file(self, bot, chat_id, voice):
|
||||
message = await bot.sendVoice(chat_id, self.voice_file_url, duration=self.duration)
|
||||
|
||||
assert isinstance(message.voice, Voice)
|
||||
assert isinstance(message.voice.file_id, str)
|
||||
assert isinstance(message.voice.file_unique_id, str)
|
||||
assert message.voice.file_id != ""
|
||||
assert message.voice.file_unique_id != ""
|
||||
assert message.voice.duration == voice.duration
|
||||
assert message.voice.mime_type == voice.mime_type
|
||||
assert message.voice.file_size == voice.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_resend(self, bot, chat_id, voice):
|
||||
message = await bot.sendVoice(chat_id, voice.file_id)
|
||||
|
||||
assert message.voice == voice
|
||||
|
||||
async def test_send_with_voice(self, monkeypatch, bot, chat_id, voice):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["voice"] == voice.file_id
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
message = await bot.send_voice(chat_id, voice=voice)
|
||||
assert message
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_voice_caption_entities(self, bot, chat_id, voice_file):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_voice(
|
||||
chat_id, voice_file, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_voice_default_parse_mode_1(self, default_bot, chat_id, voice):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_voice(chat_id, voice, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_voice_default_parse_mode_2(self, default_bot, chat_id, voice):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_voice(
|
||||
chat_id, voice, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_voice_default_parse_mode_3(self, default_bot, chat_id, voice):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_voice(
|
||||
chat_id, voice, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_voice_default_protect_content(self, chat_id, default_bot, voice):
|
||||
protected = await default_bot.send_voice(chat_id, voice)
|
||||
assert protected.has_protected_content
|
||||
unprotected = await default_bot.send_voice(chat_id, voice, protect_content=False)
|
||||
assert not unprotected.has_protected_content
|
||||
assert await bot.send_voice(chat_id, voice=voice)
|
||||
|
||||
@pytest.mark.parametrize("local_mode", [True, False])
|
||||
async def test_send_voice_local_files(self, monkeypatch, bot, chat_id, local_mode):
|
||||
@@ -228,7 +164,126 @@ class TestVoice:
|
||||
finally:
|
||||
bot._local_mode = False
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_get_file_instance_method(self, monkeypatch, voice):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == voice.file_id
|
||||
|
||||
assert check_shortcut_signature(Voice.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(voice.get_file, voice.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(voice.get_file, voice.get_bot())
|
||||
|
||||
monkeypatch.setattr(voice.get_bot(), "get_file", make_assertion)
|
||||
assert await voice.get_file()
|
||||
|
||||
|
||||
class TestVoiceWithRequest(TestVoiceBase):
|
||||
async def test_send_all_args(self, bot, chat_id, voice_file, voice):
|
||||
message = await bot.send_voice(
|
||||
chat_id,
|
||||
voice_file,
|
||||
duration=self.duration,
|
||||
caption=self.caption,
|
||||
disable_notification=False,
|
||||
protect_content=True,
|
||||
parse_mode="Markdown",
|
||||
)
|
||||
|
||||
assert isinstance(message.voice, Voice)
|
||||
assert isinstance(message.voice.file_id, str)
|
||||
assert isinstance(message.voice.file_unique_id, str)
|
||||
assert message.voice.file_id != ""
|
||||
assert message.voice.file_unique_id != ""
|
||||
assert message.voice.duration == voice.duration
|
||||
assert message.voice.mime_type == voice.mime_type
|
||||
assert message.voice.file_size == voice.file_size
|
||||
assert message.caption == self.caption.replace("*", "")
|
||||
assert message.has_protected_content
|
||||
|
||||
async def test_get_and_download(self, bot, voice, chat_id):
|
||||
path = Path("telegram.ogg")
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
new_file = await bot.get_file(voice.file_id)
|
||||
|
||||
assert new_file.file_size == voice.file_size
|
||||
assert new_file.file_unique_id == voice.file_unique_id
|
||||
assert new_file.file_path.startswith("https://")
|
||||
|
||||
await new_file.download_to_drive("telegram.ogg")
|
||||
|
||||
assert path.is_file()
|
||||
|
||||
async def test_send_ogg_url_file(self, bot, chat_id, voice):
|
||||
message = await bot.sendVoice(chat_id, self.voice_file_url, duration=self.duration)
|
||||
|
||||
assert isinstance(message.voice, Voice)
|
||||
assert isinstance(message.voice.file_id, str)
|
||||
assert isinstance(message.voice.file_unique_id, str)
|
||||
assert message.voice.file_id != ""
|
||||
assert message.voice.file_unique_id != ""
|
||||
assert message.voice.duration == voice.duration
|
||||
assert message.voice.mime_type == voice.mime_type
|
||||
assert message.voice.file_size == voice.file_size
|
||||
|
||||
async def test_resend(self, bot, chat_id, voice):
|
||||
message = await bot.sendVoice(chat_id, voice.file_id)
|
||||
|
||||
assert message.voice == voice
|
||||
|
||||
async def test_send_voice_caption_entities(self, bot, chat_id, voice_file):
|
||||
test_string = "Italic Bold Code"
|
||||
entities = [
|
||||
MessageEntity(MessageEntity.ITALIC, 0, 6),
|
||||
MessageEntity(MessageEntity.ITALIC, 7, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 12, 4),
|
||||
]
|
||||
message = await bot.send_voice(
|
||||
chat_id, voice_file, caption=test_string, caption_entities=entities
|
||||
)
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == tuple(entities)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_voice_default_parse_mode_1(self, default_bot, chat_id, voice):
|
||||
test_string = "Italic Bold Code"
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_voice(chat_id, voice, caption=test_markdown_string)
|
||||
assert message.caption_markdown == test_markdown_string
|
||||
assert message.caption == test_string
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_voice_default_parse_mode_2(self, default_bot, chat_id, voice):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_voice(
|
||||
chat_id, voice, caption=test_markdown_string, parse_mode=None
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
||||
async def test_send_voice_default_parse_mode_3(self, default_bot, chat_id, voice):
|
||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||
|
||||
message = await default_bot.send_voice(
|
||||
chat_id, voice, caption=test_markdown_string, parse_mode="HTML"
|
||||
)
|
||||
assert message.caption == test_markdown_string
|
||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||
|
||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||
async def test_send_voice_default_protect_content(self, chat_id, default_bot, voice):
|
||||
tasks = asyncio.gather(
|
||||
default_bot.send_voice(chat_id, voice),
|
||||
default_bot.send_voice(chat_id, voice, protect_content=False),
|
||||
)
|
||||
protected, unprotected = await tasks
|
||||
assert protected.has_protected_content
|
||||
assert not unprotected.has_protected_content
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"default_bot,custom",
|
||||
[
|
||||
@@ -262,74 +317,10 @@ class TestVoice:
|
||||
chat_id, voice, reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
"file_id": self.voice_file_id,
|
||||
"file_unique_id": self.voice_file_unique_id,
|
||||
"duration": self.duration,
|
||||
"mime_type": self.mime_type,
|
||||
"file_size": self.file_size,
|
||||
}
|
||||
json_voice = Voice.de_json(json_dict, bot)
|
||||
assert json_voice.api_kwargs == {}
|
||||
|
||||
assert json_voice.file_id == self.voice_file_id
|
||||
assert json_voice.file_unique_id == self.voice_file_unique_id
|
||||
assert json_voice.duration == self.duration
|
||||
assert json_voice.mime_type == self.mime_type
|
||||
assert json_voice.file_size == self.file_size
|
||||
|
||||
def test_to_dict(self, voice):
|
||||
voice_dict = voice.to_dict()
|
||||
|
||||
assert isinstance(voice_dict, dict)
|
||||
assert voice_dict["file_id"] == voice.file_id
|
||||
assert voice_dict["file_unique_id"] == voice.file_unique_id
|
||||
assert voice_dict["duration"] == voice.duration
|
||||
assert voice_dict["mime_type"] == voice.mime_type
|
||||
assert voice_dict["file_size"] == voice.file_size
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.sendVoice(chat_id, open(os.devnull, "rb"))
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
with pytest.raises(TelegramError):
|
||||
await bot.sendVoice(chat_id, "")
|
||||
|
||||
async def test_error_without_required_args(self, bot, chat_id):
|
||||
with pytest.raises(TypeError):
|
||||
await bot.sendVoice(chat_id)
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, voice):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["file_id"] == voice.file_id
|
||||
|
||||
assert check_shortcut_signature(Voice.get_file, Bot.get_file, ["file_id"], [])
|
||||
assert await check_shortcut_call(voice.get_file, voice.get_bot(), "get_file")
|
||||
assert await check_defaults_handling(voice.get_file, voice.get_bot())
|
||||
|
||||
monkeypatch.setattr(voice.get_bot(), "get_file", make_assertion)
|
||||
assert await voice.get_file()
|
||||
|
||||
def test_equality(self, voice):
|
||||
a = Voice(voice.file_id, voice.file_unique_id, self.duration)
|
||||
b = Voice("", voice.file_unique_id, self.duration)
|
||||
c = Voice(voice.file_id, voice.file_unique_id, 0)
|
||||
d = Voice("", "", self.duration)
|
||||
e = Audio(voice.file_id, voice.file_unique_id, self.duration)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
@@ -20,23 +20,24 @@
|
||||
import pytest
|
||||
|
||||
from telegram import Animation, Game, MessageEntity, PhotoSize
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@pytest.fixture(scope="module")
|
||||
def game():
|
||||
game = Game(
|
||||
TestGame.title,
|
||||
TestGame.description,
|
||||
TestGame.photo,
|
||||
text=TestGame.text,
|
||||
text_entities=TestGame.text_entities,
|
||||
animation=TestGame.animation,
|
||||
TestGameBase.title,
|
||||
TestGameBase.description,
|
||||
TestGameBase.photo,
|
||||
text=TestGameBase.text,
|
||||
text_entities=TestGameBase.text_entities,
|
||||
animation=TestGameBase.animation,
|
||||
)
|
||||
game._unfreeze()
|
||||
return game
|
||||
|
||||
|
||||
class TestGame:
|
||||
class TestGameBase:
|
||||
title = "Python-telegram-bot Test Game"
|
||||
description = "description"
|
||||
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
|
||||
@@ -47,7 +48,9 @@ class TestGame:
|
||||
text_entities = [MessageEntity(13, 17, MessageEntity.URL)]
|
||||
animation = Animation("blah", "unique_id", 320, 180, 1)
|
||||
|
||||
def test_slot_behaviour(self, game, mro_slots):
|
||||
|
||||
class TestGameWithoutRequest(TestGameBase):
|
||||
def test_slot_behaviour(self, game):
|
||||
for attr in game.__slots__:
|
||||
assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(game)) == len(set(mro_slots(game))), "duplicate slot"
|
||||
@@ -95,20 +98,6 @@ class TestGame:
|
||||
assert game_dict["text_entities"] == [game.text_entities[0].to_dict()]
|
||||
assert game_dict["animation"] == game.animation.to_dict()
|
||||
|
||||
def test_parse_entity(self, game):
|
||||
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
|
||||
game.text_entities = [entity]
|
||||
|
||||
assert game.parse_text_entity(entity) == "http://google.com"
|
||||
|
||||
def test_parse_entities(self, game):
|
||||
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
|
||||
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
|
||||
game.text_entities = [entity_2, entity]
|
||||
|
||||
assert game.parse_text_entities(MessageEntity.URL) == {entity: "http://google.com"}
|
||||
assert game.parse_text_entities() == {entity: "http://google.com", entity_2: "h"}
|
||||
|
||||
def test_equality(self):
|
||||
a = Game("title", "description", [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)])
|
||||
b = Game(
|
||||
@@ -133,3 +122,17 @@ class TestGame:
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
def test_parse_entity(self, game):
|
||||
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
|
||||
game.text_entities = [entity]
|
||||
|
||||
assert game.parse_text_entity(entity) == "http://google.com"
|
||||
|
||||
def test_parse_entities(self, game):
|
||||
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
|
||||
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
|
||||
game.text_entities = [entity_2, entity]
|
||||
|
||||
assert game.parse_text_entities(MessageEntity.URL) == {entity: "http://google.com"}
|
||||
assert game.parse_text_entities() == {entity: "http://google.com", entity_2: "h"}
|
||||
@@ -20,27 +20,34 @@
|
||||
import pytest
|
||||
|
||||
from telegram import GameHighScore, User
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def game_highscore():
|
||||
return GameHighScore(
|
||||
TestGameHighScore.position, TestGameHighScore.user, TestGameHighScore.score
|
||||
TestGameHighScoreBase.position, TestGameHighScoreBase.user, TestGameHighScoreBase.score
|
||||
)
|
||||
|
||||
|
||||
class TestGameHighScore:
|
||||
class TestGameHighScoreBase:
|
||||
position = 12
|
||||
user = User(2, "test user", False)
|
||||
score = 42
|
||||
|
||||
def test_slot_behaviour(self, game_highscore, mro_slots):
|
||||
|
||||
class TestGameHighScoreWithoutRequest(TestGameHighScoreBase):
|
||||
def test_slot_behaviour(self, game_highscore):
|
||||
for attr in game_highscore.__slots__:
|
||||
assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(game_highscore)) == len(set(mro_slots(game_highscore))), "same slot"
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {"position": self.position, "user": self.user.to_dict(), "score": self.score}
|
||||
json_dict = {
|
||||
"position": self.position,
|
||||
"user": self.user.to_dict(),
|
||||
"score": self.score,
|
||||
}
|
||||
highscore = GameHighScore.de_json(json_dict, bot)
|
||||
assert highscore.api_kwargs == {}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
@@ -20,24 +20,25 @@
|
||||
import pytest
|
||||
|
||||
from telegram import CallbackGame, InlineKeyboardButton, LoginUrl, WebAppInfo
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_keyboard_button():
|
||||
return InlineKeyboardButton(
|
||||
TestInlineKeyboardButton.text,
|
||||
url=TestInlineKeyboardButton.url,
|
||||
callback_data=TestInlineKeyboardButton.callback_data,
|
||||
switch_inline_query=TestInlineKeyboardButton.switch_inline_query,
|
||||
switch_inline_query_current_chat=TestInlineKeyboardButton.switch_inline_query_current_chat,
|
||||
callback_game=TestInlineKeyboardButton.callback_game,
|
||||
pay=TestInlineKeyboardButton.pay,
|
||||
login_url=TestInlineKeyboardButton.login_url,
|
||||
web_app=TestInlineKeyboardButton.web_app,
|
||||
TestInlineKeyboardButtonBase.text,
|
||||
url=TestInlineKeyboardButtonBase.url,
|
||||
callback_data=TestInlineKeyboardButtonBase.callback_data,
|
||||
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query,
|
||||
switch_inline_query_current_chat=TestInlineKeyboardButtonBase.switch_inline_query_current_chat, # noqa: E501
|
||||
callback_game=TestInlineKeyboardButtonBase.callback_game,
|
||||
pay=TestInlineKeyboardButtonBase.pay,
|
||||
login_url=TestInlineKeyboardButtonBase.login_url,
|
||||
web_app=TestInlineKeyboardButtonBase.web_app,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineKeyboardButton:
|
||||
class TestInlineKeyboardButtonBase:
|
||||
text = "text"
|
||||
url = "url"
|
||||
callback_data = "callback data"
|
||||
@@ -48,7 +49,9 @@ class TestInlineKeyboardButton:
|
||||
login_url = LoginUrl("http://google.com")
|
||||
web_app = WebAppInfo(url="https://example.com")
|
||||
|
||||
def test_slot_behaviour(self, inline_keyboard_button, mro_slots):
|
||||
|
||||
class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase):
|
||||
def test_slot_behaviour(self, inline_keyboard_button):
|
||||
inst = inline_keyboard_button
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -26,14 +26,15 @@ from telegram import (
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_keyboard_markup():
|
||||
return InlineKeyboardMarkup(TestInlineKeyboardMarkup.inline_keyboard)
|
||||
return InlineKeyboardMarkup(TestInlineKeyboardMarkupBase.inline_keyboard)
|
||||
|
||||
|
||||
class TestInlineKeyboardMarkup:
|
||||
class TestInlineKeyboardMarkupBase:
|
||||
inline_keyboard = [
|
||||
[
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
@@ -41,104 +42,14 @@ class TestInlineKeyboardMarkup:
|
||||
]
|
||||
]
|
||||
|
||||
def test_slot_behaviour(self, inline_keyboard_markup, mro_slots):
|
||||
|
||||
class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
|
||||
def test_slot_behaviour(self, inline_keyboard_markup):
|
||||
inst = inline_keyboard_markup
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
@pytest.mark.flaky(3, 1)
|
||||
async def test_send_message_with_inline_keyboard_markup(
|
||||
self, bot, chat_id, inline_keyboard_markup
|
||||
):
|
||||
message = await bot.send_message(
|
||||
chat_id, "Testing InlineKeyboardMarkup", reply_markup=inline_keyboard_markup
|
||||
)
|
||||
|
||||
assert message.text == "Testing InlineKeyboardMarkup"
|
||||
|
||||
def test_from_button(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text="button1", callback_data="data1")
|
||||
).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 1
|
||||
assert len(inline_keyboard_markup[0]) == 1
|
||||
|
||||
def test_from_row(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_row(
|
||||
[
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
]
|
||||
).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 1
|
||||
assert len(inline_keyboard_markup[0]) == 2
|
||||
|
||||
def test_from_column(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_column(
|
||||
[
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
]
|
||||
).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 2
|
||||
assert len(inline_keyboard_markup[0]) == 1
|
||||
assert len(inline_keyboard_markup[1]) == 1
|
||||
|
||||
def test_expected_values(self, inline_keyboard_markup):
|
||||
assert inline_keyboard_markup.inline_keyboard == tuple(
|
||||
tuple(row) for row in self.inline_keyboard
|
||||
)
|
||||
|
||||
def test_wrong_keyboard_inputs(self):
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup(
|
||||
[[InlineKeyboardButton("b1", "1")], InlineKeyboardButton("b2", "2")]
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup("strings_are_not_allowed")
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"])
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup(InlineKeyboardButton("b1", "1"))
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup([[[InlineKeyboardButton("only_2d_array_is_allowed", "1")]]])
|
||||
|
||||
async def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch):
|
||||
async def make_assertion(
|
||||
url,
|
||||
data,
|
||||
reply_to_message_id=None,
|
||||
disable_notification=None,
|
||||
reply_markup=None,
|
||||
timeout=None,
|
||||
**kwargs,
|
||||
):
|
||||
if reply_markup is not None:
|
||||
markups = (
|
||||
InlineKeyboardMarkup,
|
||||
ReplyKeyboardMarkup,
|
||||
ForceReply,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
if isinstance(reply_markup, markups):
|
||||
data["reply_markup"] = reply_markup.to_dict()
|
||||
else:
|
||||
data["reply_markup"] = reply_markup
|
||||
|
||||
assert bool("'switch_inline_query': ''" in str(data["reply_markup"]))
|
||||
assert bool("'switch_inline_query_current_chat': ''" in str(data["reply_markup"]))
|
||||
|
||||
inline_keyboard_markup.inline_keyboard[0][0]._unfreeze()
|
||||
inline_keyboard_markup.inline_keyboard[0][0].callback_data = None
|
||||
inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = ""
|
||||
inline_keyboard_markup.inline_keyboard[0][1]._unfreeze()
|
||||
inline_keyboard_markup.inline_keyboard[0][1].callback_data = None
|
||||
inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = ""
|
||||
|
||||
monkeypatch.setattr(bot, "_send_message", make_assertion)
|
||||
await bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
|
||||
|
||||
def test_to_dict(self, inline_keyboard_markup):
|
||||
inline_keyboard_markup_dict = inline_keyboard_markup.to_dict()
|
||||
|
||||
@@ -233,3 +144,96 @@ class TestInlineKeyboardMarkup:
|
||||
|
||||
assert a != g
|
||||
assert hash(a) != hash(g)
|
||||
|
||||
def test_from_button(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text="button1", callback_data="data1")
|
||||
).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 1
|
||||
assert len(inline_keyboard_markup[0]) == 1
|
||||
|
||||
def test_from_row(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_row(
|
||||
[
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
]
|
||||
).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 1
|
||||
assert len(inline_keyboard_markup[0]) == 2
|
||||
|
||||
def test_from_column(self):
|
||||
inline_keyboard_markup = InlineKeyboardMarkup.from_column(
|
||||
[
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
||||
]
|
||||
).inline_keyboard
|
||||
assert len(inline_keyboard_markup) == 2
|
||||
assert len(inline_keyboard_markup[0]) == 1
|
||||
assert len(inline_keyboard_markup[1]) == 1
|
||||
|
||||
def test_expected_values(self, inline_keyboard_markup):
|
||||
assert inline_keyboard_markup.inline_keyboard == tuple(
|
||||
tuple(row) for row in self.inline_keyboard
|
||||
)
|
||||
|
||||
def test_wrong_keyboard_inputs(self):
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup(
|
||||
[[InlineKeyboardButton("b1", "1")], InlineKeyboardButton("b2", "2")]
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup("strings_are_not_allowed")
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"])
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup(InlineKeyboardButton("b1", "1"))
|
||||
with pytest.raises(ValueError):
|
||||
InlineKeyboardMarkup([[[InlineKeyboardButton("only_2d_array_is_allowed", "1")]]])
|
||||
|
||||
async def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch):
|
||||
async def make_assertion(
|
||||
url,
|
||||
data,
|
||||
reply_to_message_id=None,
|
||||
disable_notification=None,
|
||||
reply_markup=None,
|
||||
timeout=None,
|
||||
**kwargs,
|
||||
):
|
||||
if reply_markup is not None:
|
||||
markups = (
|
||||
InlineKeyboardMarkup,
|
||||
ReplyKeyboardMarkup,
|
||||
ForceReply,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
if isinstance(reply_markup, markups):
|
||||
data["reply_markup"] = reply_markup.to_dict()
|
||||
else:
|
||||
data["reply_markup"] = reply_markup
|
||||
|
||||
assert bool("'switch_inline_query': ''" in str(data["reply_markup"]))
|
||||
assert bool("'switch_inline_query_current_chat': ''" in str(data["reply_markup"]))
|
||||
|
||||
inline_keyboard_markup.inline_keyboard[0][0]._unfreeze()
|
||||
inline_keyboard_markup.inline_keyboard[0][0].callback_data = None
|
||||
inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = ""
|
||||
inline_keyboard_markup.inline_keyboard[0][1]._unfreeze()
|
||||
inline_keyboard_markup.inline_keyboard[0][1].callback_data = None
|
||||
inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = ""
|
||||
|
||||
monkeypatch.setattr(bot, "_send_message", make_assertion)
|
||||
await bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
|
||||
|
||||
|
||||
class TestInlineKeyborardMarkupWithRequest(TestInlineKeyboardMarkupBase):
|
||||
async def test_send_message_with_inline_keyboard_markup(
|
||||
self, bot, chat_id, inline_keyboard_markup
|
||||
):
|
||||
message = await bot.send_message(
|
||||
chat_id, "Testing InlineKeyboardMarkup", reply_markup=inline_keyboard_markup
|
||||
)
|
||||
|
||||
assert message.text == "Testing InlineKeyboardMarkup"
|
||||
@@ -25,29 +25,32 @@ from tests.auxil.bot_method_checks import (
|
||||
check_shortcut_call,
|
||||
check_shortcut_signature,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query(bot):
|
||||
ilq = InlineQuery(
|
||||
TestInlineQuery.id_,
|
||||
TestInlineQuery.from_user,
|
||||
TestInlineQuery.query,
|
||||
TestInlineQuery.offset,
|
||||
location=TestInlineQuery.location,
|
||||
TestInlineQueryBase.id_,
|
||||
TestInlineQueryBase.from_user,
|
||||
TestInlineQueryBase.query,
|
||||
TestInlineQueryBase.offset,
|
||||
location=TestInlineQueryBase.location,
|
||||
)
|
||||
ilq.set_bot(bot)
|
||||
return ilq
|
||||
|
||||
|
||||
class TestInlineQuery:
|
||||
class TestInlineQueryBase:
|
||||
id_ = 1234
|
||||
from_user = User(1, "First name", False)
|
||||
query = "query text"
|
||||
offset = "offset"
|
||||
location = Location(8.8, 53.1)
|
||||
|
||||
def test_slot_behaviour(self, inline_query, mro_slots):
|
||||
|
||||
class TestInlineQueryWithoutRequest(TestInlineQueryBase):
|
||||
def test_slot_behaviour(self, inline_query):
|
||||
for attr in inline_query.__slots__:
|
||||
assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inline_query)) == len(set(mro_slots(inline_query))), "duplicate slot"
|
||||
@@ -79,34 +82,6 @@ class TestInlineQuery:
|
||||
assert inline_query_dict["query"] == inline_query.query
|
||||
assert inline_query_dict["offset"] == inline_query.offset
|
||||
|
||||
async def test_answer(self, monkeypatch, inline_query):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["inline_query_id"] == inline_query.id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
InlineQuery.answer, Bot.answer_inline_query, ["inline_query_id"], ["auto_pagination"]
|
||||
)
|
||||
assert await check_shortcut_call(
|
||||
inline_query.answer, inline_query.get_bot(), "answer_inline_query"
|
||||
)
|
||||
assert await check_defaults_handling(inline_query.answer, inline_query.get_bot())
|
||||
|
||||
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
|
||||
assert await inline_query.answer(results=[])
|
||||
|
||||
async def test_answer_error(self, inline_query):
|
||||
with pytest.raises(ValueError, match="mutually exclusive"):
|
||||
await inline_query.answer(results=[], auto_pagination=True, current_offset="foobar")
|
||||
|
||||
async def test_answer_auto_pagination(self, monkeypatch, inline_query):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
inline_query_id_matches = kwargs["inline_query_id"] == inline_query.id
|
||||
offset_matches = kwargs.get("current_offset") == inline_query.offset
|
||||
return offset_matches and inline_query_id_matches
|
||||
|
||||
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
|
||||
assert await inline_query.answer(results=[], auto_pagination=True)
|
||||
|
||||
def test_equality(self):
|
||||
a = InlineQuery(self.id_, User(1, "", False), "", "")
|
||||
b = InlineQuery(self.id_, User(1, "", False), "", "")
|
||||
@@ -126,3 +101,31 @@ class TestInlineQuery:
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
async def test_answer_error(self, inline_query):
|
||||
with pytest.raises(ValueError, match="mutually exclusive"):
|
||||
await inline_query.answer(results=[], auto_pagination=True, current_offset="foobar")
|
||||
|
||||
async def test_answer(self, monkeypatch, inline_query):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return kwargs["inline_query_id"] == inline_query.id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
InlineQuery.answer, Bot.answer_inline_query, ["inline_query_id"], ["auto_pagination"]
|
||||
)
|
||||
assert await check_shortcut_call(
|
||||
inline_query.answer, inline_query.get_bot(), "answer_inline_query"
|
||||
)
|
||||
assert await check_defaults_handling(inline_query.answer, inline_query.get_bot())
|
||||
|
||||
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
|
||||
assert await inline_query.answer(results=[])
|
||||
|
||||
async def test_answer_auto_pagination(self, monkeypatch, inline_query):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
inline_query_id_matches = kwargs["inline_query_id"] == inline_query.id
|
||||
offset_matches = kwargs.get("current_offset") == inline_query.offset
|
||||
return offset_matches and inline_query_id_matches
|
||||
|
||||
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
|
||||
assert await inline_query.answer(results=[], auto_pagination=True)
|
||||
@@ -34,6 +34,7 @@ from telegram import (
|
||||
User,
|
||||
)
|
||||
from telegram.ext import CallbackContext, InlineQueryHandler, JobQueue
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text")
|
||||
|
||||
@@ -87,7 +88,7 @@ def inline_query(bot):
|
||||
class TestInlineQueryHandler:
|
||||
test_flag = False
|
||||
|
||||
def test_slot_behaviour(self, mro_slots):
|
||||
def test_slot_behaviour(self):
|
||||
handler = InlineQueryHandler(self.callback)
|
||||
for attr in handler.__slots__:
|
||||
assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResultArticle,
|
||||
InlineQueryResultAudio,
|
||||
InputTextMessageContent,
|
||||
)
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_article():
|
||||
return InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultArticleBase:
|
||||
id_ = "id"
|
||||
type_ = "article"
|
||||
title = "title"
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
url = "url"
|
||||
hide_url = True
|
||||
description = "description"
|
||||
thumbnail_url = "thumb url"
|
||||
thumbnail_height = 10
|
||||
thumbnail_width = 15
|
||||
|
||||
|
||||
class TestInlineQueryResultArticleWithoutRequest(TestInlineQueryResultArticleBase):
|
||||
def test_slot_behaviour(self, inline_query_result_article, recwarn):
|
||||
inst = inline_query_result_article
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_expected_values(self, inline_query_result_article):
|
||||
assert inline_query_result_article.type == self.type_
|
||||
assert inline_query_result_article.id == self.id_
|
||||
assert inline_query_result_article.title == self.title
|
||||
assert (
|
||||
inline_query_result_article.input_message_content.to_dict()
|
||||
== self.input_message_content.to_dict()
|
||||
)
|
||||
assert inline_query_result_article.reply_markup.to_dict() == self.reply_markup.to_dict()
|
||||
assert inline_query_result_article.url == self.url
|
||||
assert inline_query_result_article.hide_url == self.hide_url
|
||||
assert inline_query_result_article.description == self.description
|
||||
assert inline_query_result_article.thumbnail_url == self.thumbnail_url
|
||||
assert inline_query_result_article.thumbnail_height == self.thumbnail_height
|
||||
assert inline_query_result_article.thumbnail_width == self.thumbnail_width
|
||||
|
||||
def test_thumb_url_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_article = InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumb_url=TestInlineQueryResultArticleBase.thumbnail_url, # deprecated arg
|
||||
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
|
||||
)
|
||||
assert inline_query_result_article.thumb_url == inline_query_result_article.thumbnail_url
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_url", new_name="thumbnail_url"
|
||||
)
|
||||
|
||||
def test_thumb_height_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_article = InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url,
|
||||
thumb_height=TestInlineQueryResultArticleBase.thumbnail_height, # deprecated arg
|
||||
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
|
||||
)
|
||||
assert (
|
||||
inline_query_result_article.thumb_height
|
||||
== inline_query_result_article.thumbnail_height
|
||||
)
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_height", new_name="thumbnail_height"
|
||||
)
|
||||
|
||||
def test_thumb_width_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_article = InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
|
||||
thumb_width=TestInlineQueryResultArticleBase.thumbnail_width, # deprecated arg
|
||||
)
|
||||
assert (
|
||||
inline_query_result_article.thumb_width == inline_query_result_article.thumbnail_width
|
||||
)
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_width", new_name="thumbnail_width"
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_url(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_url' and 'thumbnail_url'",
|
||||
):
|
||||
InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url,
|
||||
thumb_url="some other url",
|
||||
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_height(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_height' and 'thumbnail_height'",
|
||||
):
|
||||
InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
|
||||
thumb_height=TestInlineQueryResultArticleBase.thumbnail_height + 1,
|
||||
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_width(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_width' and 'thumbnail_width'",
|
||||
):
|
||||
InlineQueryResultArticle(
|
||||
TestInlineQueryResultArticleBase.id_,
|
||||
TestInlineQueryResultArticleBase.title,
|
||||
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||
url=TestInlineQueryResultArticleBase.url,
|
||||
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||
description=TestInlineQueryResultArticleBase.description,
|
||||
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
|
||||
thumb_width=TestInlineQueryResultArticleBase.thumbnail_width + 1,
|
||||
)
|
||||
|
||||
def test_to_dict(self, inline_query_result_article):
|
||||
inline_query_result_article_dict = inline_query_result_article.to_dict()
|
||||
|
||||
assert isinstance(inline_query_result_article_dict, dict)
|
||||
assert inline_query_result_article_dict["type"] == inline_query_result_article.type
|
||||
assert inline_query_result_article_dict["id"] == inline_query_result_article.id
|
||||
assert inline_query_result_article_dict["title"] == inline_query_result_article.title
|
||||
assert (
|
||||
inline_query_result_article_dict["input_message_content"]
|
||||
== inline_query_result_article.input_message_content.to_dict()
|
||||
)
|
||||
assert (
|
||||
inline_query_result_article_dict["reply_markup"]
|
||||
== inline_query_result_article.reply_markup.to_dict()
|
||||
)
|
||||
assert inline_query_result_article_dict["url"] == inline_query_result_article.url
|
||||
assert inline_query_result_article_dict["hide_url"] == inline_query_result_article.hide_url
|
||||
assert (
|
||||
inline_query_result_article_dict["description"]
|
||||
== inline_query_result_article.description
|
||||
)
|
||||
assert (
|
||||
inline_query_result_article_dict["thumbnail_url"]
|
||||
== inline_query_result_article.thumbnail_url
|
||||
)
|
||||
assert (
|
||||
inline_query_result_article_dict["thumbnail_height"]
|
||||
== inline_query_result_article.thumbnail_height
|
||||
)
|
||||
assert (
|
||||
inline_query_result_article_dict["thumbnail_width"]
|
||||
== inline_query_result_article.thumbnail_width
|
||||
)
|
||||
|
||||
def test_equality(self):
|
||||
a = InlineQueryResultArticle(self.id_, self.title, self.input_message_content)
|
||||
b = InlineQueryResultArticle(self.id_, self.title, self.input_message_content)
|
||||
c = InlineQueryResultArticle(self.id_, "", self.input_message_content)
|
||||
d = InlineQueryResultArticle("", self.title, self.input_message_content)
|
||||
e = InlineQueryResultAudio(self.id_, "", "")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
+16
-13
@@ -27,25 +27,26 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_audio():
|
||||
return InlineQueryResultAudio(
|
||||
TestInlineQueryResultAudio.id_,
|
||||
TestInlineQueryResultAudio.audio_url,
|
||||
TestInlineQueryResultAudio.title,
|
||||
performer=TestInlineQueryResultAudio.performer,
|
||||
audio_duration=TestInlineQueryResultAudio.audio_duration,
|
||||
caption=TestInlineQueryResultAudio.caption,
|
||||
parse_mode=TestInlineQueryResultAudio.parse_mode,
|
||||
caption_entities=TestInlineQueryResultAudio.caption_entities,
|
||||
input_message_content=TestInlineQueryResultAudio.input_message_content,
|
||||
reply_markup=TestInlineQueryResultAudio.reply_markup,
|
||||
TestInlineQueryResultAudioBase.id_,
|
||||
TestInlineQueryResultAudioBase.audio_url,
|
||||
TestInlineQueryResultAudioBase.title,
|
||||
performer=TestInlineQueryResultAudioBase.performer,
|
||||
audio_duration=TestInlineQueryResultAudioBase.audio_duration,
|
||||
caption=TestInlineQueryResultAudioBase.caption,
|
||||
parse_mode=TestInlineQueryResultAudioBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultAudioBase.caption_entities,
|
||||
input_message_content=TestInlineQueryResultAudioBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultAudioBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultAudio:
|
||||
class TestInlineQueryResultAudioBase:
|
||||
id_ = "id"
|
||||
type_ = "audio"
|
||||
audio_url = "audio url"
|
||||
@@ -58,7 +59,9 @@ class TestInlineQueryResultAudio:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_audio, mro_slots):
|
||||
|
||||
class TestInlineQueryResultAudioWithoutRequest(TestInlineQueryResultAudioBase):
|
||||
def test_slot_behaviour(self, inline_query_result_audio):
|
||||
inst = inline_query_result_audio
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+13
-10
@@ -27,22 +27,23 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_audio():
|
||||
return InlineQueryResultCachedAudio(
|
||||
TestInlineQueryResultCachedAudio.id_,
|
||||
TestInlineQueryResultCachedAudio.audio_file_id,
|
||||
caption=TestInlineQueryResultCachedAudio.caption,
|
||||
parse_mode=TestInlineQueryResultCachedAudio.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedAudio.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedAudio.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedAudio.reply_markup,
|
||||
TestInlineQueryResultCachedAudioBase.id_,
|
||||
TestInlineQueryResultCachedAudioBase.audio_file_id,
|
||||
caption=TestInlineQueryResultCachedAudioBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedAudioBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedAudioBase.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedAudioBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedAudioBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedAudio:
|
||||
class TestInlineQueryResultCachedAudioBase:
|
||||
id_ = "id"
|
||||
type_ = "audio"
|
||||
audio_file_id = "audio file id"
|
||||
@@ -52,7 +53,9 @@ class TestInlineQueryResultCachedAudio:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_audio, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedAudioWithoutRequest(TestInlineQueryResultCachedAudioBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_audio):
|
||||
inst = inline_query_result_cached_audio
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+15
-12
@@ -27,24 +27,25 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_document():
|
||||
return InlineQueryResultCachedDocument(
|
||||
TestInlineQueryResultCachedDocument.id_,
|
||||
TestInlineQueryResultCachedDocument.title,
|
||||
TestInlineQueryResultCachedDocument.document_file_id,
|
||||
caption=TestInlineQueryResultCachedDocument.caption,
|
||||
parse_mode=TestInlineQueryResultCachedDocument.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedDocument.caption_entities,
|
||||
description=TestInlineQueryResultCachedDocument.description,
|
||||
input_message_content=TestInlineQueryResultCachedDocument.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedDocument.reply_markup,
|
||||
TestInlineQueryResultCachedDocumentBase.id_,
|
||||
TestInlineQueryResultCachedDocumentBase.title,
|
||||
TestInlineQueryResultCachedDocumentBase.document_file_id,
|
||||
caption=TestInlineQueryResultCachedDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultCachedDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultCachedDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedDocumentBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedDocument:
|
||||
class TestInlineQueryResultCachedDocumentBase:
|
||||
id_ = "id"
|
||||
type_ = "document"
|
||||
document_file_id = "document file id"
|
||||
@@ -56,7 +57,9 @@ class TestInlineQueryResultCachedDocument:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_document, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedDocumentWithoutRequest(TestInlineQueryResultCachedDocumentBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_document):
|
||||
inst = inline_query_result_cached_document
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+14
-11
@@ -26,23 +26,24 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_gif():
|
||||
return InlineQueryResultCachedGif(
|
||||
TestInlineQueryResultCachedGif.id_,
|
||||
TestInlineQueryResultCachedGif.gif_file_id,
|
||||
title=TestInlineQueryResultCachedGif.title,
|
||||
caption=TestInlineQueryResultCachedGif.caption,
|
||||
parse_mode=TestInlineQueryResultCachedGif.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedGif.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedGif.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedGif.reply_markup,
|
||||
TestInlineQueryResultCachedGifBase.id_,
|
||||
TestInlineQueryResultCachedGifBase.gif_file_id,
|
||||
title=TestInlineQueryResultCachedGifBase.title,
|
||||
caption=TestInlineQueryResultCachedGifBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedGifBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedGifBase.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedGifBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedGifBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedGif:
|
||||
class TestInlineQueryResultCachedGifBase:
|
||||
id_ = "id"
|
||||
type_ = "gif"
|
||||
gif_file_id = "gif file id"
|
||||
@@ -53,7 +54,9 @@ class TestInlineQueryResultCachedGif:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_gif, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedGifWithoutRequest(TestInlineQueryResultCachedGifBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_gif):
|
||||
inst = inline_query_result_cached_gif
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+14
-11
@@ -26,23 +26,24 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_mpeg4_gif():
|
||||
return InlineQueryResultCachedMpeg4Gif(
|
||||
TestInlineQueryResultCachedMpeg4Gif.id_,
|
||||
TestInlineQueryResultCachedMpeg4Gif.mpeg4_file_id,
|
||||
title=TestInlineQueryResultCachedMpeg4Gif.title,
|
||||
caption=TestInlineQueryResultCachedMpeg4Gif.caption,
|
||||
parse_mode=TestInlineQueryResultCachedMpeg4Gif.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedMpeg4Gif.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedMpeg4Gif.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedMpeg4Gif.reply_markup,
|
||||
TestInlineQueryResultCachedMpeg4GifBase.id_,
|
||||
TestInlineQueryResultCachedMpeg4GifBase.mpeg4_file_id,
|
||||
title=TestInlineQueryResultCachedMpeg4GifBase.title,
|
||||
caption=TestInlineQueryResultCachedMpeg4GifBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedMpeg4GifBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedMpeg4GifBase.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedMpeg4GifBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedMpeg4GifBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedMpeg4Gif:
|
||||
class TestInlineQueryResultCachedMpeg4GifBase:
|
||||
id_ = "id"
|
||||
type_ = "mpeg4_gif"
|
||||
mpeg4_file_id = "mpeg4 file id"
|
||||
@@ -53,7 +54,9 @@ class TestInlineQueryResultCachedMpeg4Gif:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedMpeg4GifWithoutRequest(TestInlineQueryResultCachedMpeg4GifBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif):
|
||||
inst = inline_query_result_cached_mpeg4_gif
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+15
-12
@@ -26,24 +26,25 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_photo():
|
||||
return InlineQueryResultCachedPhoto(
|
||||
TestInlineQueryResultCachedPhoto.id_,
|
||||
TestInlineQueryResultCachedPhoto.photo_file_id,
|
||||
title=TestInlineQueryResultCachedPhoto.title,
|
||||
description=TestInlineQueryResultCachedPhoto.description,
|
||||
caption=TestInlineQueryResultCachedPhoto.caption,
|
||||
parse_mode=TestInlineQueryResultCachedPhoto.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedPhoto.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedPhoto.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedPhoto.reply_markup,
|
||||
TestInlineQueryResultCachedPhotoBase.id_,
|
||||
TestInlineQueryResultCachedPhotoBase.photo_file_id,
|
||||
title=TestInlineQueryResultCachedPhotoBase.title,
|
||||
description=TestInlineQueryResultCachedPhotoBase.description,
|
||||
caption=TestInlineQueryResultCachedPhotoBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedPhotoBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedPhotoBase.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedPhotoBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedPhotoBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedPhoto:
|
||||
class TestInlineQueryResultCachedPhotoBase:
|
||||
id_ = "id"
|
||||
type_ = "photo"
|
||||
photo_file_id = "photo file id"
|
||||
@@ -55,7 +56,9 @@ class TestInlineQueryResultCachedPhoto:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_photo, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedPhotoWithoutRequest(TestInlineQueryResultCachedPhotoBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_photo):
|
||||
inst = inline_query_result_cached_photo
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+10
-7
@@ -25,26 +25,29 @@ from telegram import (
|
||||
InlineQueryResultCachedVoice,
|
||||
InputTextMessageContent,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_sticker():
|
||||
return InlineQueryResultCachedSticker(
|
||||
TestInlineQueryResultCachedSticker.id_,
|
||||
TestInlineQueryResultCachedSticker.sticker_file_id,
|
||||
input_message_content=TestInlineQueryResultCachedSticker.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedSticker.reply_markup,
|
||||
TestInlineQueryResultCachedStickerBase.id_,
|
||||
TestInlineQueryResultCachedStickerBase.sticker_file_id,
|
||||
input_message_content=TestInlineQueryResultCachedStickerBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedStickerBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedSticker:
|
||||
class TestInlineQueryResultCachedStickerBase:
|
||||
id_ = "id"
|
||||
type_ = "sticker"
|
||||
sticker_file_id = "sticker file id"
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_sticker, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedStickerWithoutRequest(TestInlineQueryResultCachedStickerBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_sticker):
|
||||
inst = inline_query_result_cached_sticker
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+15
-12
@@ -26,24 +26,25 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_video():
|
||||
return InlineQueryResultCachedVideo(
|
||||
TestInlineQueryResultCachedVideo.id_,
|
||||
TestInlineQueryResultCachedVideo.video_file_id,
|
||||
TestInlineQueryResultCachedVideo.title,
|
||||
caption=TestInlineQueryResultCachedVideo.caption,
|
||||
parse_mode=TestInlineQueryResultCachedVideo.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedVideo.caption_entities,
|
||||
description=TestInlineQueryResultCachedVideo.description,
|
||||
input_message_content=TestInlineQueryResultCachedVideo.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedVideo.reply_markup,
|
||||
TestInlineQueryResultCachedVideoBase.id_,
|
||||
TestInlineQueryResultCachedVideoBase.video_file_id,
|
||||
TestInlineQueryResultCachedVideoBase.title,
|
||||
caption=TestInlineQueryResultCachedVideoBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedVideoBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedVideoBase.caption_entities,
|
||||
description=TestInlineQueryResultCachedVideoBase.description,
|
||||
input_message_content=TestInlineQueryResultCachedVideoBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedVideoBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedVideo:
|
||||
class TestInlineQueryResultCachedVideoBase:
|
||||
id_ = "id"
|
||||
type_ = "video"
|
||||
video_file_id = "video file id"
|
||||
@@ -55,7 +56,9 @@ class TestInlineQueryResultCachedVideo:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_video, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedVideoWithoutRequest(TestInlineQueryResultCachedVideoBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_video):
|
||||
inst = inline_query_result_cached_video
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
+14
-11
@@ -26,23 +26,24 @@ from telegram import (
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_cached_voice():
|
||||
return InlineQueryResultCachedVoice(
|
||||
TestInlineQueryResultCachedVoice.id_,
|
||||
TestInlineQueryResultCachedVoice.voice_file_id,
|
||||
TestInlineQueryResultCachedVoice.title,
|
||||
caption=TestInlineQueryResultCachedVoice.caption,
|
||||
parse_mode=TestInlineQueryResultCachedVoice.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedVoice.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedVoice.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedVoice.reply_markup,
|
||||
TestInlineQueryResultCachedVoiceBase.id_,
|
||||
TestInlineQueryResultCachedVoiceBase.voice_file_id,
|
||||
TestInlineQueryResultCachedVoiceBase.title,
|
||||
caption=TestInlineQueryResultCachedVoiceBase.caption,
|
||||
parse_mode=TestInlineQueryResultCachedVoiceBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultCachedVoiceBase.caption_entities,
|
||||
input_message_content=TestInlineQueryResultCachedVoiceBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultCachedVoiceBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultCachedVoice:
|
||||
class TestInlineQueryResultCachedVoiceBase:
|
||||
id_ = "id"
|
||||
type_ = "voice"
|
||||
voice_file_id = "voice file id"
|
||||
@@ -53,7 +54,9 @@ class TestInlineQueryResultCachedVoice:
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_cached_voice, mro_slots):
|
||||
|
||||
class TestInlineQueryResultCachedVoiceWithoutRequest(TestInlineQueryResultCachedVoiceBase):
|
||||
def test_slot_behaviour(self, inline_query_result_cached_voice):
|
||||
inst = inline_query_result_cached_voice
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
@@ -0,0 +1,248 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResultContact,
|
||||
InlineQueryResultVoice,
|
||||
InputTextMessageContent,
|
||||
)
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_contact():
|
||||
return InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
|
||||
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
|
||||
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultContactBase:
|
||||
id_ = "id"
|
||||
type_ = "contact"
|
||||
phone_number = "phone_number"
|
||||
first_name = "first_name"
|
||||
last_name = "last_name"
|
||||
thumbnail_url = "thumb url"
|
||||
thumbnail_width = 10
|
||||
thumbnail_height = 15
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
|
||||
class TestInlineQueryResultContactWithoutRequest(TestInlineQueryResultContactBase):
|
||||
def test_slot_behaviour(self, inline_query_result_contact):
|
||||
inst = inline_query_result_contact
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_expected_values(self, inline_query_result_contact):
|
||||
assert inline_query_result_contact.id == self.id_
|
||||
assert inline_query_result_contact.type == self.type_
|
||||
assert inline_query_result_contact.phone_number == self.phone_number
|
||||
assert inline_query_result_contact.first_name == self.first_name
|
||||
assert inline_query_result_contact.last_name == self.last_name
|
||||
assert inline_query_result_contact.thumbnail_url == self.thumbnail_url
|
||||
assert inline_query_result_contact.thumbnail_width == self.thumbnail_width
|
||||
assert inline_query_result_contact.thumbnail_height == self.thumbnail_height
|
||||
assert (
|
||||
inline_query_result_contact.input_message_content.to_dict()
|
||||
== self.input_message_content.to_dict()
|
||||
)
|
||||
assert inline_query_result_contact.reply_markup.to_dict() == self.reply_markup.to_dict()
|
||||
|
||||
def test_thumb_url_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_contact = InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
thumb_url=TestInlineQueryResultContactBase.thumbnail_url, # deprecated arg
|
||||
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
|
||||
)
|
||||
assert inline_query_result_contact.thumb_url == inline_query_result_contact.thumbnail_url
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_url", new_name="thumbnail_url"
|
||||
)
|
||||
|
||||
def test_thumb_height_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_contact = InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
|
||||
thumb_height=TestInlineQueryResultContactBase.thumbnail_height, # deprecated arg
|
||||
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact.thumb_height
|
||||
== inline_query_result_contact.thumbnail_height
|
||||
)
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_height", new_name="thumbnail_height"
|
||||
)
|
||||
|
||||
def test_thumb_width_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_contact = InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
|
||||
thumb_width=TestInlineQueryResultContactBase.thumbnail_width, # deprecated arg
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact.thumb_width == inline_query_result_contact.thumbnail_width
|
||||
)
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_width", new_name="thumbnail_width"
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_url(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_url' and 'thumbnail_url'",
|
||||
):
|
||||
InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
|
||||
thumb_url="some other url",
|
||||
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_height(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_height' and 'thumbnail_height'",
|
||||
):
|
||||
InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
|
||||
thumb_height=TestInlineQueryResultContactBase.thumbnail_height + 1,
|
||||
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_width(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_width' and 'thumbnail_width'",
|
||||
):
|
||||
InlineQueryResultContact(
|
||||
TestInlineQueryResultContactBase.id_,
|
||||
TestInlineQueryResultContactBase.phone_number,
|
||||
TestInlineQueryResultContactBase.first_name,
|
||||
last_name=TestInlineQueryResultContactBase.last_name,
|
||||
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
|
||||
thumb_width=TestInlineQueryResultContactBase.thumbnail_width + 1,
|
||||
)
|
||||
|
||||
def test_to_dict(self, inline_query_result_contact):
|
||||
inline_query_result_contact_dict = inline_query_result_contact.to_dict()
|
||||
|
||||
assert isinstance(inline_query_result_contact_dict, dict)
|
||||
assert inline_query_result_contact_dict["id"] == inline_query_result_contact.id
|
||||
assert inline_query_result_contact_dict["type"] == inline_query_result_contact.type
|
||||
assert (
|
||||
inline_query_result_contact_dict["phone_number"]
|
||||
== inline_query_result_contact.phone_number
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["first_name"]
|
||||
== inline_query_result_contact.first_name
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["last_name"] == inline_query_result_contact.last_name
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["thumbnail_url"]
|
||||
== inline_query_result_contact.thumbnail_url
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["thumbnail_width"]
|
||||
== inline_query_result_contact.thumbnail_width
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["thumbnail_height"]
|
||||
== inline_query_result_contact.thumbnail_height
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["input_message_content"]
|
||||
== inline_query_result_contact.input_message_content.to_dict()
|
||||
)
|
||||
assert (
|
||||
inline_query_result_contact_dict["reply_markup"]
|
||||
== inline_query_result_contact.reply_markup.to_dict()
|
||||
)
|
||||
|
||||
def test_equality(self):
|
||||
a = InlineQueryResultContact(self.id_, self.phone_number, self.first_name)
|
||||
b = InlineQueryResultContact(self.id_, self.phone_number, self.first_name)
|
||||
c = InlineQueryResultContact(self.id_, "", self.first_name)
|
||||
d = InlineQueryResultContact("", self.phone_number, self.first_name)
|
||||
e = InlineQueryResultVoice(self.id_, "", "")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResultDocument,
|
||||
InlineQueryResultVoice,
|
||||
InputTextMessageContent,
|
||||
MessageEntity,
|
||||
)
|
||||
from tests.auxil.deprecations import check_thumb_deprecation_warnings_for_args_and_attrs
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_document():
|
||||
return InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
|
||||
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
|
||||
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultDocumentBase:
|
||||
id_ = "id"
|
||||
type_ = "document"
|
||||
document_url = "document url"
|
||||
title = "title"
|
||||
caption = "caption"
|
||||
parse_mode = "Markdown"
|
||||
caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
|
||||
mime_type = "mime type"
|
||||
description = "description"
|
||||
thumbnail_url = "thumb url"
|
||||
thumbnail_width = 10
|
||||
thumbnail_height = 15
|
||||
input_message_content = InputTextMessageContent("input_message_content")
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
|
||||
class TestInlineQueryResultDocumentWithoutRequest(TestInlineQueryResultDocumentBase):
|
||||
def test_slot_behaviour(self, inline_query_result_document):
|
||||
inst = inline_query_result_document
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_expected_values(self, inline_query_result_document):
|
||||
assert inline_query_result_document.id == self.id_
|
||||
assert inline_query_result_document.type == self.type_
|
||||
assert inline_query_result_document.document_url == self.document_url
|
||||
assert inline_query_result_document.title == self.title
|
||||
assert inline_query_result_document.caption == self.caption
|
||||
assert inline_query_result_document.parse_mode == self.parse_mode
|
||||
assert inline_query_result_document.caption_entities == tuple(self.caption_entities)
|
||||
assert inline_query_result_document.mime_type == self.mime_type
|
||||
assert inline_query_result_document.description == self.description
|
||||
assert inline_query_result_document.thumbnail_url == self.thumbnail_url
|
||||
assert inline_query_result_document.thumbnail_width == self.thumbnail_width
|
||||
assert inline_query_result_document.thumbnail_height == self.thumbnail_height
|
||||
assert (
|
||||
inline_query_result_document.input_message_content.to_dict()
|
||||
== self.input_message_content.to_dict()
|
||||
)
|
||||
assert inline_query_result_document.reply_markup.to_dict() == self.reply_markup.to_dict()
|
||||
|
||||
def test_thumb_url_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_document = InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
thumb_url=TestInlineQueryResultDocumentBase.thumbnail_url, # deprecated arg
|
||||
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
|
||||
)
|
||||
assert inline_query_result_document.thumb_url == inline_query_result_document.thumbnail_url
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_url", new_name="thumbnail_url"
|
||||
)
|
||||
|
||||
def test_thumb_height_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_document = InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
|
||||
thumb_height=TestInlineQueryResultDocumentBase.thumbnail_height, # deprecated arg
|
||||
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document.thumb_height
|
||||
== inline_query_result_document.thumbnail_height
|
||||
)
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_height", new_name="thumbnail_height"
|
||||
)
|
||||
|
||||
def test_thumb_width_property_deprecation_warning(self, recwarn):
|
||||
inline_query_result_document = InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
|
||||
thumb_width=TestInlineQueryResultDocumentBase.thumbnail_width, # deprecated arg
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document.thumb_width
|
||||
== inline_query_result_document.thumbnail_width
|
||||
)
|
||||
check_thumb_deprecation_warnings_for_args_and_attrs(
|
||||
recwarn, __file__, deprecated_name="thumb_width", new_name="thumbnail_width"
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_url(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_url' and 'thumbnail_url'",
|
||||
):
|
||||
InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
|
||||
thumb_url="some other url",
|
||||
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_height(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_height' and 'thumbnail_height'",
|
||||
):
|
||||
InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
|
||||
thumb_height=TestInlineQueryResultDocumentBase.thumbnail_height + 1,
|
||||
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
|
||||
)
|
||||
|
||||
def test_throws_value_error_with_different_deprecated_and_new_arg_thumb_width(self):
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="different entities as 'thumb_width' and 'thumbnail_width'",
|
||||
):
|
||||
InlineQueryResultDocument(
|
||||
TestInlineQueryResultDocumentBase.id_,
|
||||
TestInlineQueryResultDocumentBase.document_url,
|
||||
TestInlineQueryResultDocumentBase.title,
|
||||
TestInlineQueryResultDocumentBase.mime_type,
|
||||
caption=TestInlineQueryResultDocumentBase.caption,
|
||||
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||
description=TestInlineQueryResultDocumentBase.description,
|
||||
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
|
||||
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
|
||||
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
|
||||
thumb_width=TestInlineQueryResultDocumentBase.thumbnail_width + 1,
|
||||
)
|
||||
|
||||
def test_caption_entities_always_tuple(self):
|
||||
result = InlineQueryResultDocument(self.id_, self.document_url, self.title, self.mime_type)
|
||||
assert result.caption_entities == ()
|
||||
|
||||
def test_to_dict(self, inline_query_result_document):
|
||||
inline_query_result_document_dict = inline_query_result_document.to_dict()
|
||||
|
||||
assert isinstance(inline_query_result_document_dict, dict)
|
||||
assert inline_query_result_document_dict["id"] == inline_query_result_document.id
|
||||
assert inline_query_result_document_dict["type"] == inline_query_result_document.type
|
||||
assert (
|
||||
inline_query_result_document_dict["document_url"]
|
||||
== inline_query_result_document.document_url
|
||||
)
|
||||
assert inline_query_result_document_dict["title"] == inline_query_result_document.title
|
||||
assert inline_query_result_document_dict["caption"] == inline_query_result_document.caption
|
||||
assert (
|
||||
inline_query_result_document_dict["parse_mode"]
|
||||
== inline_query_result_document.parse_mode
|
||||
)
|
||||
assert inline_query_result_document_dict["caption_entities"] == [
|
||||
ce.to_dict() for ce in inline_query_result_document.caption_entities
|
||||
]
|
||||
assert (
|
||||
inline_query_result_document_dict["mime_type"]
|
||||
== inline_query_result_document.mime_type
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document_dict["description"]
|
||||
== inline_query_result_document.description
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document_dict["thumbnail_url"]
|
||||
== inline_query_result_document.thumbnail_url
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document_dict["thumbnail_width"]
|
||||
== inline_query_result_document.thumbnail_width
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document_dict["thumbnail_height"]
|
||||
== inline_query_result_document.thumbnail_height
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document_dict["input_message_content"]
|
||||
== inline_query_result_document.input_message_content.to_dict()
|
||||
)
|
||||
assert (
|
||||
inline_query_result_document_dict["reply_markup"]
|
||||
== inline_query_result_document.reply_markup.to_dict()
|
||||
)
|
||||
|
||||
def test_equality(self):
|
||||
a = InlineQueryResultDocument(self.id_, self.document_url, self.title, self.mime_type)
|
||||
b = InlineQueryResultDocument(self.id_, self.document_url, self.title, self.mime_type)
|
||||
c = InlineQueryResultDocument(self.id_, "", self.title, self.mime_type)
|
||||
d = InlineQueryResultDocument("", self.document_url, self.title, self.mime_type)
|
||||
e = InlineQueryResultVoice(self.id_, "", "")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -24,24 +24,27 @@ from telegram import (
|
||||
InlineQueryResultGame,
|
||||
InlineQueryResultVoice,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
@pytest.fixture(scope="module")
|
||||
def inline_query_result_game():
|
||||
return InlineQueryResultGame(
|
||||
TestInlineQueryResultGame.id_,
|
||||
TestInlineQueryResultGame.game_short_name,
|
||||
reply_markup=TestInlineQueryResultGame.reply_markup,
|
||||
TestInlineQueryResultGameBase.id_,
|
||||
TestInlineQueryResultGameBase.game_short_name,
|
||||
reply_markup=TestInlineQueryResultGameBase.reply_markup,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQueryResultGame:
|
||||
class TestInlineQueryResultGameBase:
|
||||
id_ = "id"
|
||||
type_ = "game"
|
||||
game_short_name = "game short name"
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||
|
||||
def test_slot_behaviour(self, inline_query_result_game, mro_slots):
|
||||
|
||||
class TestInlineQueryResultGameWithoutRequest(TestInlineQueryResultGameBase):
|
||||
def test_slot_behaviour(self, inline_query_result_game):
|
||||
inst = inline_query_result_game
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user