Compare commits

...

60 Commits

Author SHA1 Message Date
Hinrich Mahler 59105b240f Bump Version to v20.4 2023-07-09 12:18:32 +02:00
Bibo-Joshi db6030ea83 Documentation Improvements (#3698, #3708, #3767)
Co-authored-by: Iulian Onofrei <5748627+revolter@users.noreply.github.com>
Co-authored-by: Poolitzer <github@poolitzer.eu>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Aditya <clot27@apx_managed.vanilla>
2023-07-09 11:22:08 +02:00
pre-commit-ci[bot] 7d52ead228 pre-commit autoupdate (#3791)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2023-07-06 18:57:51 +02:00
Harshil 589047ddbf Add Python 3.12 Beta to the Test Matrix (#3751) 2023-07-05 21:58:57 +02:00
Bibo-Joshi 5534ddfaa0 Use Temporary Files for Testing File Downloads (#3777) 2023-07-03 10:06:16 +02:00
dependabot[bot] 1d27a0fadb Bump srvaroa/labeler from 1.5.0 to 1.6.0 (#3786)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-02 18:07:30 +02:00
dependabot[bot] 79cda7582e Bump dependabot/fetch-metadata from 1.5.1 to 1.6.0 (#3787)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-02 17:56:58 +02:00
dependabot[bot] dccf62eb1c Bump dessant/lock-threads from 4.0.0 to 4.0.1 (#3785)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-02 17:56:21 +02:00
Bibo-Joshi fb86bb3417 Drop Support for Python 3.7 (#3728, #3742, #3749, #3740, #3754, #3753, #3764, #3762, #3759)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Luca Bellanti <luca.bellanti@gmail.com>
Co-authored-by: thefunkycat <104379699+thefunkycat@users.noreply.github.com>
Co-authored-by: Aditya Yadav <69784758+clot27@users.noreply.github.com>
Co-authored-by: Dmitry Kolomatskiy <58207913+lemontree210@users.noreply.github.com>
2023-06-29 18:17:47 +02:00
Harshil 58b89cf0e9 Add More ruff Rules (#3763) 2023-06-29 11:38:09 +02:00
Bibo-Joshi 4a6e0fd7a6 Add Quotes for Installation Instructions With Optional Dependencies (#3780) 2023-06-29 07:46:00 +02:00
dependabot[bot] 623d2f7f0b Bump pytest from 7.3.2 to 7.4.0 (#3774)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-06-26 22:14:19 +02:00
Bibo-Joshi 62a8cfc395 Rework pytest Integration into GitHub Actions (#3776) 2023-06-26 19:48:38 +02:00
Harshil b0cff31fc1 Rename _handler.py to _basehandler.py (#3761) 2023-06-25 15:08:26 +02:00
Harshil 3c87e450fb Fix Wrong Warning Text in KeyboardButton.__eq__ (#3768) 2023-06-22 10:21:53 +02:00
dependabot[bot] 8e91a6adba Bump pytest from 7.3.1 to 7.3.2 (#3758) 2023-06-17 09:16:31 +02:00
Dmitry Kolomatskiy 63fd846233 Set httpx Logging Level to Warning in Examples (#3746) 2023-06-07 22:32:04 +02:00
pre-commit-ci[bot] 814c72052f pre-commit autoupdate (#3747)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2023-06-07 21:51:40 +02:00
Luca Bellanti 1a7edd7a5d Explicitly set allowed_updates in Examples (#3741) 2023-06-04 17:11:58 +02:00
dependabot[bot] f0e71216fe Update cachetools requirement from ~=5.3.0 to ~=5.3.1 (#3738) 2023-06-03 08:44:14 +02:00
Bibo-Joshi ca37219a68 Fix Two Bugs in GitHub Actions Workflows (#3739) 2023-06-03 08:28:08 +02:00
dependabot[bot] c9636726f7 Bump sphinxcontrib-mermaid from 0.8.1 to 0.9.2 (#3737) 2023-06-03 07:48:17 +02:00
Bibo-Joshi 9c8d6efe7a Make Integration of APScheduler into JobQueue More Explicit (#3695) 2023-06-02 22:17:46 +02:00
Aditya Yadav bf54599618 Introduce BaseUpdateProcessor for Customized Concurrent Handling of Updates (#3654)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2023-06-02 18:17:08 +02:00
Bibo-Joshi 4c8d7332db Auto-Update Changed Version in Other Files After Dependabot PRs (#3716) 2023-05-31 22:19:56 +02:00
Harshil c185137c9e Bump furo and sphinx 7 (#3719) 2023-05-25 22:47:05 +02:00
Bibo-Joshi 3c5a16be1c Exclude Type Hints from Stability Policy (#3712) 2023-05-24 21:42:30 +02:00
dependabot[bot] cd25964419 Update httpx requirement from ~=0.24.0 to ~=0.24.1 (#3715)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2023-05-20 14:23:40 +02:00
Bibo-Joshi 5309cf6555 Automatically Label pre-commit-ci PRs (#3713) 2023-05-20 10:27:51 +02:00
dependabot[bot] bb8b508a22 Bump pytest-xdist from 3.3.0 to 3.3.1 (#3714)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-20 10:15:57 +02:00
Iulian Onofrei 4f15a7feee Fix Inconsistent Type Hints for timeout Parameter of Bot.get_updates (#3709) 2023-05-19 22:09:16 +02:00
dependabot[bot] 57c780c62f Update aiolimiter requirement from ~=1.0.0 to ~=1.1.0 (#3707)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2023-05-18 07:59:44 +02:00
MiguelX413 99fd4432db Use Explicit Optionals (#3692)
Co-authored-by: Dmitry Kolomatskiy <58207913+lemontree210@users.noreply.github.com>
2023-05-18 07:57:59 +02:00
dependabot[bot] e432c296a9 Bump pytest-xdist from 3.2.1 to 3.3.0 (#3705) 2023-05-15 18:20:50 +02:00
Bibo-Joshi 1fdccd7bf9 Drop a Legacy pre-commit.ci Configuration (#3697) 2023-05-12 22:36:52 +02:00
Hinrich Mahler bfbf6d3f94 Bump Version to v20.3 2023-05-07 15:31:23 +02:00
Bibo-Joshi 0c4180c74b Documentation Improvements (#3628, #3636, #3694)
Co-authored-by: Yossi Rafelson <yossi.rafelson@gmail.com>
Co-authored-by: Aditya <clot27@apx_managed.vanilla>
Co-authored-by: ibragimovgeorge <103066850+ibragimovgeorge@users.noreply.github.com>
2023-05-07 14:51:22 +02:00
Bibo-Joshi 1c6ae435bf Add a Stability Policy (#3622)
Co-authored-by: poolitzer <github@poolitzer.eu>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Dmitry Kolomatskiy <58207913+lemontree210@users.noreply.github.com>
2023-05-07 14:50:41 +02:00
Bibo-Joshi 66b6d3c497 Recover 100% Type Completeness (#3676) 2023-05-07 14:10:20 +02:00
Bibo-Joshi 8c252c9822 API 6.7 (#3673)
Co-authored-by: Dmitry Kolomatskiy <58207913+lemontree210@users.noreply.github.com>
Co-authored-by: Harshil Mehta <37377066+harshil21@users.noreply.github.com>
Co-authored-by: poolitzer <github@poolitzer.eu>
Co-authored-by: Aditya <clot27@apx_managed.vanilla>
2023-05-07 13:44:34 +02:00
Bibo-Joshi 450dc2115c Shield Update Fetcher Task in Application.start (#3657) 2023-05-06 21:10:12 +02:00
pre-commit-ci[bot] 87a6890900 pre-commit autoupdate (#3688) 2023-05-06 20:52:58 +02:00
Bibo-Joshi 83ab12c387 Stabilize test_delete_sticker_set (#3685)
Co-authored-by: poolitzer <github@poolitzer.eu>
2023-04-30 11:03:00 +02:00
Bibo-Joshi 3f444dad8d Improve Warning Categories & Stacklevels (#3674) 2023-04-27 22:36:04 +02:00
Bibo-Joshi f23315d08b Add Logging for Invalid JSON Data in BasePersistence.parse_json_payload (#3668) 2023-04-18 16:17:20 +02:00
Luca Bellanti 7b116be344 Localize Received datetime Objects According to Defaults.tzinfo (#3632) 2023-04-18 16:16:23 +02:00
dependabot[bot] 934e4c9bd4 Bump pytest from 7.2.2 to 7.3.1 (#3661)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-04-16 11:24:50 +02:00
dependabot[bot] 1966fb25c5 Update httpx requirement from ~=0.23.3 to ~=0.24.0 (#3660)
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-04-16 11:20:36 +02:00
dependabot[bot] a333d8514a Bump sphinx-copybutton from 0.5.1 to 0.5.2 (#3662)
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-04-16 11:15:06 +02:00
Bibo-Joshi b146c7131e Remove Deprecated codecov Package from CI (#3664) 2023-04-16 10:54:49 +02:00
Bibo-Joshi 53093ebceb Give Loggers Better Names (#3623) 2023-04-10 17:01:35 +02:00
Luca Bellanti 401b2decce Make Message.link Point to Thread View Where Possible (#3640) 2023-04-07 17:13:45 +02:00
pre-commit-ci[bot] 83a164e5ef pre-commit autoupdate (#3646)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Dmitry Kolomatskiy <58207913+lemontree210@users.noreply.github.com>
Co-authored-by: Harshil Mehta <37377066+harshil21@users.noreply.github.com>
2023-04-05 20:52:52 +02:00
Bibo-Joshi d91bc45cdc Add Application.mark_data_for_update_persistence (#3607) 2023-04-02 22:29:16 +02:00
dependabot[bot] 8967912f46 Bump furo from 2023.3.23 to 2023.3.27 (#3643) 2023-04-02 21:42:57 +02:00
dependabot[bot] 7ab2cafbee Bump actions/stale from 7 to 8 (#3644) 2023-04-02 21:41:37 +02:00
Harshil 7e0ed2235e Stabilize CI by Rerunning Failed Tests (#3631)
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-04-01 22:35:20 +02:00
Harshil 53abb7b4bd Add String Representation for RequestParameter (#3634) 2023-03-27 22:03:02 +02:00
Harshil 11f86b8813 Drop Usage of sys.maxunicode (#3630) 2023-03-26 21:58:23 +02:00
Harshil 9997a9f47e Empower ruff (#3594) 2023-03-25 19:18:04 +01:00
317 changed files with 8340 additions and 6107 deletions
+51 -23
View File
@@ -67,6 +67,7 @@ Here's how to make a one-off code change.
$ git checkout -b your-branch-name
3. **Make a commit to your feature branch**. Each commit should be self-contained and have a descriptive commit message that helps other developers understand why the changes were made.
We also have a check-list for PRs `below`_.
- You can refer to relevant issues in the commit message by writing, e.g., "#105".
@@ -80,7 +81,7 @@ Here's how to make a one-off code change.
- The following exceptions to the above (Google's) style guides applies:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- 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.
@@ -121,11 +122,11 @@ Here's how to make a one-off code change.
- When your reviewer has reviewed the code, you'll get a notification. You'll need to respond in two ways:
- Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)".
- Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)".
- In order to keep the commit history intact, please avoid squashing or amending history and then force-pushing to the PR. Reviewers often want to look at individual commits.
- In order to keep the commit history intact, please avoid squashing or amending history and then force-pushing to the PR. Reviewers often want to look at individual commits.
- In addition, please reply to each comment. Each reply should be either "Done" or a response explaining why the corresponding suggestion wasn't implemented. All comments must be resolved before LGTM can be given.
- In addition, please reply to each comment. Each reply should be either "Done" or a response explaining why the corresponding suggestion wasn't implemented. All comments must be resolved before LGTM can be given.
- Resolve any merge conflicts that arise. To resolve conflicts between 'your-branch-name' (in your fork) and 'master' (in the ``python-telegram-bot`` repository), run:
@@ -150,6 +151,51 @@ Here's how to make a one-off code change.
7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``!
Check-list for PRs
------------------
This checklist is a non-exhaustive reminder of things that should be done before a PR is merged, both for you as contributor and for the maintainers.
Feel free to copy (parts of) the checklist to the PR description to remind you or the maintainers of open points or if you have questions on anything.
- Added ``.. versionadded:: NEXT.VERSION``, ``.. versionchanged:: NEXT.VERSION`` or ``.. deprecated:: NEXT.VERSION`` to the docstrings for user facing changes (for methods/class descriptions, arguments and attributes)
- Created new or adapted existing unit tests
- Documented code changes according to the `CSI standard <https://standards.mousepawmedia.com/en/stable/csi.html>`__
- Added myself alphabetically to ``AUTHORS.rst`` (optional)
- Added new classes & modules to the docs and all suitable ``__all__`` s
- Checked the `Stability Policy <https://docs.python-telegram-bot.org/stability_policy.html>`_ in case of deprecations or changes to documented behavior
**If the PR contains API changes (otherwise, you can ignore this passage)**
- Checked the Bot API specific sections of the `Stability Policy <https://docs.python-telegram-bot.org/stability_policy.html>`_
- New classes:
- Added ``self._id_attrs`` and corresponding documentation
- ``__init__`` accepts ``api_kwargs`` as kw-only
- Added new shortcuts:
- In :class:`~telegram.Chat` & :class:`~telegram.User` for all methods that accept ``chat/user_id``
- In :class:`~telegram.Message` for all methods that accept ``chat_id`` and ``message_id``
- For new :class:`~telegram.Message` shortcuts: Added ``quote`` argument if methods accepts ``reply_to_message_id``
- In :class:`~telegram.CallbackQuery` for all methods that accept either ``chat_id`` and ``message_id`` or ``inline_message_id``
- If relevant:
- Added new constants at :mod:`telegram.constants` and shortcuts to them as class variables
- Link new and existing constants in docstrings instead of hard-coded numbers and strings
- Add new message types to :attr:`telegram.Message.effective_attachment`
- Added new handlers for new update types
- Add the handlers to the warning loop in the :class:`~telegram.ext.ConversationHandler`
- Added new filters for new message (sub)types
- Added or updated documentation for the changed class(es) and/or method(s)
- Added the new method(s) to ``_extbot.py``
- Added or updated ``bot_methods.rst``
- Updated the Bot API version number in all places: ``README.rst`` and ``README_RAW.rst`` (including the badge), as well as ``telegram.constants.BOT_API_VERSION_INFO``
- Added logic for arbitrary callback data in :class:`telegram.ext.ExtBot` for new methods that either accept a ``reply_markup`` in some form or have a return type that is/contains :class:`~telegram.Message`
Documenting
===========
@@ -228,25 +274,6 @@ callable we prefer that the call also uses keyword arg syntax. For example:
This gives us the flexibility to re-order arguments and more importantly
to add new required arguments. It's also more explicit and easier to read.
Properly defining optional arguments
------------------------------------
It's always good to not initialize optional arguments at class creation,
instead use ``**kwargs`` to get them. It's well known Telegram API can
change without notice, in that case if a new argument is added it won't
break the API classes. For example:
.. code-block:: python
# GOOD
def __init__(self, id, name, last_name=None, **kwargs):
self.last_name = last_name
# BAD
def __init__(self, id, name, last_name=None):
self.last_name = last_name
.. _`Code of Conduct`: https://www.python.org/psf/conduct/
.. _`issue tracker`: https://github.com/python-telegram-bot/python-telegram-bot/issues
@@ -266,3 +293,4 @@ break the API classes. For example:
.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html
.. _`section`: #documenting
.. _`testing page`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/tests/README.rst
.. _`below`: #check-list-for-prs
+9
View File
@@ -0,0 +1,9 @@
# Config file for workflows/labelling.yml
version: 1
labels:
- label: "dependencies"
authors: ["dependabot[bot]", "pre-commit-ci[bot]"]
- label: "code quality ✨"
authors: ["pre-commit-ci[bot]"]
+3 -35
View File
@@ -1,38 +1,6 @@
<!--
Hey! You're PRing? Cool! Please have a look at the below checklist. It's here to help both you and the maintainers to remember some aspects. Make sure to check out our contribution guide (https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst).
Hey! You're PRing? Cool!
Please be sure to check out our contribution guide (https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst).
Especially, please have a look at the check list for PRs (https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst#checklist-for-prs). Feel free to copy (parts of) the checklist to the PR description to remind you or the maintainers of open points or if you have questions on anything.
-->
### Checklist for PRs
- [ ] Added `.. versionadded:: version`, `.. versionchanged:: version` or `.. deprecated:: version` to the docstrings for user facing changes (for methods/class descriptions, arguments and attributes)
- [ ] Created new or adapted existing unit tests
- [ ] Documented code changes according to the [CSI standard](https://standards.mousepawmedia.com/en/stable/csi.html)
- [ ] Added myself alphabetically to `AUTHORS.rst` (optional)
- [ ] Added new classes & modules to the docs and all suitable `__all__` s
### If the PR contains API changes (otherwise, you can delete this passage)
* New classes:
- [ ] Added `self._id_attrs` and corresponding documentation
- [ ] `__init__` accepts `api_kwargs` as kw-only
* Added new shortcuts:
- [ ] In `Chat` & `User` for all methods that accept `chat/user_id`
- [ ] In `Message` for all methods that accept `chat_id` and `message_id`
- [ ] For new `Message` shortcuts: Added `quote` argument if methods accepts `reply_to_message_id`
- [ ] In `CallbackQuery` for all methods that accept either `chat_id` and `message_id` or `inline_message_id`
* If relevant:
- [ ] Added new constants at `telegram.constants` and shortcuts to them as class variables
- [ ] Link new and existing constants in docstrings instead of hard coded number and strings
- [ ] Add new message types to `Message.effective_attachment`
- [ ] Added new handlers for new update types
- [ ] Add the handlers to the warning loop in the `ConversationHandler`
- [ ] Added new filters for new message (sub)types
- [ ] Added or updated documentation for the changed class(es) and/or method(s)
- [ ] Added the new method(s) to `_extbot.py`
- [ ] Added or updated `bot_methods.rst`
- [ ] Updated the Bot API version number in all places: `README.rst` and `README_RAW.rst` (including the badge), as well as `telegram.constants.BOT_API_VERSION_INFO`
- [ ] Added logic for arbitrary callback data in `tg.ext.Bot` for new methods that either accept a `reply_markup` in some form or have a return type that is/contains `telegram.Message`
+38
View File
@@ -0,0 +1,38 @@
name: Process Dependabot PRs
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
process-dependabot-prs:
permissions:
pull-requests: read
contents: write
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
steps:
- name: Fetch Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v1.6.0
- uses: actions/checkout@v3.5.2
with:
ref: ${{ github.event.pull_request.head.ref }}
- name: Update Version Number in Other Files
uses: jacobtomlinson/gha-find-replace@v3
with:
find: ${{ steps.dependabot-metadata.outputs.previous-version }}
replace: ${{ steps.dependabot-metadata.outputs.new-version }}
regex: false
exclude: CHANGES.rst
- name: Commit & Push Changes to PR
uses: EndBug/add-and-commit@v9.1.3
with:
message: 'Update version number in other files'
committer_name: GitHub Actions
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
+17
View File
@@ -0,0 +1,17 @@
name: PR Labeler
on:
pull_request:
types: [opened]
jobs:
pre-commit-ci:
permissions:
contents: read # for srvaroa/labeler to read config file
pull-requests: write # for srvaroa/labeler to add labels in PR
runs-on: ubuntu-latest
steps:
- uses: srvaroa/labeler@v1.6.0
# Config file at .github/labeler.yml
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4.0.0
- uses: dessant/lock-threads@v4.0.1
with:
github-token: ${{ github.token }}
issue-inactive-days: '7'
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v7
- uses: actions/stale@v8
with:
# PRs never get stale
days-before-stale: 3
File diff suppressed because one or more lines are too long
+10 -10
View File
@@ -22,7 +22,7 @@ jobs:
cache-dependency-path: '**/requirements*.txt'
- name: Install Pyright
run: |
python -W ignore -m pip install pyright~=1.1.291
python -W ignore -m pip install pyright~=1.1.316
- 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
@@ -49,21 +49,21 @@ jobs:
pr = float(
json.load(open("pr.json", "rb"))["typeCompleteness"]["completenessScore"]
)
base_text = f"After this PR, type completeness will be {round(pr, 3)}."
if pr < (base - 0.1):
text = f"This PR decreases type completeness by {round(base - pr, 3)}. ❌"
base_text = f"This PR changes type completeness from {round(base, 3)} to {round(pr, 3)}."
if pr < (base - 0.001):
text = f"{base_text} ❌"
set_summary(text)
print(Path("pr.readable").read_text(encoding="utf-8"))
error(f"{text}\n{base_text}")
error(text)
exit(1)
elif pr > (base + 0.1):
text = f"This PR increases type completeness by {round(pr - base, 3)}. ✨"
elif pr > (base + 0.001):
text = f"{base_text} ✨"
set_summary(text)
if pr < 1:
print(Path("pr.readable").read_text(encoding="utf-8"))
print(f"{text}\n{base_text}")
print(text)
else:
text = f"This PR does not change type completeness by more than 0.1. ✅"
text = f"{base_text} This is less than 0.1 percentage points. ✅"
set_summary(text)
print(Path("pr.readable").read_text(encoding="utf-8"))
print(f"{text}\n{base_text}")
print(text)
+18 -21
View File
@@ -3,13 +3,10 @@
ci:
autofix_prs: false
autoupdate_schedule: monthly
# We currently only need this behavior on the v13.x branch were we have the vendored urllib
# TODO: Remove once we discontinue v13
submodules: true
repos:
- repo: https://github.com/psf/black
rev: 23.1.0
rev: 23.3.0
hooks:
- id: black
args:
@@ -20,7 +17,7 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/PyCQA/pylint
rev: v2.16.4
rev: v3.0.0a6
hooks:
- id: pylint
files: ^(telegram|examples)/.*\.py$
@@ -31,14 +28,14 @@ repos:
- --jobs=0
additional_dependencies:
- httpx~=0.23.3
- httpx~=0.24.1
- tornado~=6.2
- APScheduler~=3.10.1
- cachetools~=5.3.0
- aiolimiter~=1.0.0
- cachetools~=5.3.1
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.0.1
rev: v1.4.1
hooks:
- id: mypy
name: mypy-ptb
@@ -47,11 +44,11 @@ repos:
- types-pytz
- types-cryptography
- types-cachetools
- httpx~=0.23.3
- httpx~=0.24.1
- tornado~=6.2
- APScheduler~=3.10.1
- cachetools~=5.3.0
- aiolimiter~=1.0.0
- cachetools~=5.3.1
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- id: mypy
name: mypy-examples
@@ -62,15 +59,15 @@ repos:
additional_dependencies:
- tornado~=6.2
- APScheduler~=3.10.1
- cachetools~=5.3.0
- cachetools~=5.3.1
- . # this basically does `pip install -e .`
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
rev: v3.8.0
hooks:
- id: pyupgrade
files: ^(telegram|examples|tests|docs)/.*\.py$
args:
- --py37-plus
- --py38-plus
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
@@ -79,15 +76,15 @@ repos:
args:
- --diff
- --check
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.254'
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.0.277'
hooks:
- id: ruff
name: ruff
files: ^(telegram|examples)/.*\.py$
files: ^(telegram|examples|tests)/.*\.py$
additional_dependencies:
- httpx~=0.23.3
- httpx~=0.24.1
- tornado~=6.2
- APScheduler~=3.10.1
- cachetools~=5.3.0
- aiolimiter~=1.0.0
- cachetools~=5.3.1
- aiolimiter~=1.1.0
+4
View File
@@ -24,6 +24,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Abshar <https://github.com/abxhr>`_
- `Alateas <https://github.com/alateas>`_
- `Ales Dokshanin <https://github.com/alesdokshanin>`_
- `Alizia <https://github.com/thefunkycat>`_
- `Ambro17 <https://github.com/Ambro17>`_
- `Andrej Zhilenkov <https://github.com/Andrej730>`_
- `Anton Tagunov <https://github.com/anton-tagunov>`_
@@ -56,6 +57,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Harshil <https://github.com/harshil21>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Iulian Onofrei <https://github.com/revolter>`_
- `Jasmin Bom <https://github.com/jsmnbom>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `jeffffc <https://github.com/jeffffc>`_
@@ -72,10 +74,12 @@ The following wonderful people contributed directly or indirectly to this projec
- `Li-aung Yip <https://github.com/LiaungYip>`_
- `Loo Zheng Yuan <https://github.com/loozhengyuan>`_
- `LRezende <https://github.com/lrezende>`_
- `Luca Bellanti <https://github.com/Trifase>`_
- `macrojames <https://github.com/macrojames>`_
- `Matheus Lemos <https://github.com/mlemosf>`_
- `Michael Dix <https://github.com/Eisberge>`_
- `Michael Elovskikh <https://github.com/wronglink>`_
- `Miguel C. R. <https://github.com/MiguelX413>`_
- `miles <https://github.com/miles170>`_
- `Mischa Krüger <https://github.com/Makman2>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
+745 -1380
View File
File diff suppressed because it is too large Load Diff
+18 -18
View File
@@ -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.6-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-6.7-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions
@@ -46,8 +46,8 @@
:target: https://app.codacy.com/gh/python-telegram-bot/python-telegram-bot/dashboard
:alt: Code quality: Codacy
.. image:: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues
:target: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge
.. image:: https://app.deepsource.com/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues
:target: https://app.deepsource.com/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge
:alt: Code quality: DeepSource
.. image:: https://results.pre-commit.ci/badge/github/python-telegram-bot/python-telegram-bot/master.svg
@@ -77,7 +77,7 @@ Introduction
This library provides a pure Python, asynchronous interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions **3.7+**.
It's compatible with Python versions **3.8+**.
In addition to the pure API implementation, this library features a number of high-level classes to
make the development of bots easy and straightforward. These classes are contained in the
@@ -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.6** are supported.
All types and methods of the Telegram Bot API **6.7** are supported.
Installing
==========
@@ -135,7 +135,7 @@ As these features are *optional*, the corresponding 3rd party dependencies are n
Instead, they are listed as optional dependencies.
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
The only required dependency is `httpx ~= 0.23.3 <https://www.python-httpx.org>`_ for
The only required dependency is `httpx ~= 0.24.1 <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.
@@ -148,26 +148,26 @@ Optional Dependencies
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.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``.
* ``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.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.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.1 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
* ``pip install "python-telegram-bot[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]``.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.
Additionally, two shortcuts are provided:
* ``pip install python-telegram-bot[all]`` installs all optional dependencies.
* ``pip install python-telegram-bot[ext]`` installs all optional dependencies that are related to ``telegram.ext``, i.e. ``[rate-limiter, webhooks, callback-data, job-queue]``.
* ``pip install "python-telegram-bot[all]"`` installs all optional dependencies.
* ``pip install "python-telegram-bot[ext]"`` installs all optional dependencies that are related to ``telegram.ext``, i.e. ``[rate-limiter, webhooks, callback-data, job-queue]``.
Quick Start
===========
Our Wiki contains an `Introduction to the API <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API>`_ explaining how the pure Bot API can be accessed via ``python-telegram-bot``.
Moreover, the `Tutorial: Your first Bot <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot>`_ gives an introduction on how chatbots can be easily programmed with the help of the ``telegram.ext`` module.
Moreover, the `Tutorial: Your first Bot <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions---Your-first-Bot>`_ gives an introduction on how chatbots can be easily programmed with the help of the ``telegram.ext`` module.
Resources
=========
@@ -209,7 +209,7 @@ Contributing
Contributions of all sizes are welcome.
Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started.
You can also help by `reporting bugs or feature requests <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
You can also help by `reporting bugs or feature requests <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_.
Donating
========
+12 -12
View File
@@ -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.6-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-6.7-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions
@@ -46,8 +46,8 @@
:target: https://app.codacy.com/gh/python-telegram-bot/python-telegram-bot/dashboard
:alt: Code quality: Codacy
.. image:: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues
:target: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge
.. image:: https://app.deepsource.com/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues
:target: https://app.deepsource.com/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge
:alt: Code quality: DeepSource
.. image:: https://results.pre-commit.ci/badge/github/python-telegram-bot/python-telegram-bot/master.svg
@@ -77,7 +77,7 @@ Introduction
This library provides a pure Python, asynchronous interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions **3.7+**.
It's compatible with Python versions **3.8+**.
``python-telegram-bot-raw`` is part of the `python-telegram-bot <https://python-telegram-bot.org>`_ ecosystem and provides the pure API functionality extracted from PTB. It therefore does not have independent release schedules, changelogs or documentation.
@@ -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.6** are supported.
All types and methods of the Telegram Bot API **6.7** are supported.
Installing
==========
@@ -136,7 +136,7 @@ As these features are *optional*, the corresponding 3rd party dependencies are n
Instead, they are listed as optional dependencies.
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
The only required dependency is `httpx ~= 0.23.3 <https://www.python-httpx.org>`_ for
The only required dependency is `httpx ~= 0.24.1 <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.
@@ -149,13 +149,13 @@ Optional Dependencies
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.
* ``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-raw[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]``.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot-raw[passport,socks]"``.
Additionally, the shortcut ``pip install python-telegram-bot-raw[all]`` installs all optional dependencies.
Additionally, the shortcut ``pip install "python-telegram-bot-raw[all]"`` installs all optional dependencies.
Quick Start
===========
@@ -195,7 +195,7 @@ Contributing
Contributions of all sizes are welcome.
Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started.
You can also help by `reporting bugs or feature requests <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
You can also help by `reporting bugs or feature requests <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_.
Donating
========
+6 -3
View File
@@ -174,8 +174,7 @@ class AdmonitionInserter:
break
for line in lines_with_attrs:
line_match = attr_docstr_pattern.match(line)
if not line_match:
if not (line_match := attr_docstr_pattern.match(line)):
continue
target_attr = line_match.group("attr_name")
@@ -529,7 +528,11 @@ class AdmonitionInserter:
# For custom generics like telegram.ext._application.Application[~BT, ~CCT, ~UD...].
# This must come before the check for isinstance(type) because GenericAlias can also be
# recognized as type if it belongs to <class 'types.GenericAlias'>.
elif str(type(arg)) in ("<class 'typing._GenericAlias'>", "<class 'types.GenericAlias'>"):
elif str(type(arg)) in (
"<class 'typing._GenericAlias'>",
"<class 'types.GenericAlias'>",
"<class 'typing._LiteralGenericAlias'>",
):
if "telegram" in str(arg):
# get_origin() of telegram.ext._application.Application[~BT, ~CCT, ~UD...]
# will produce <class 'telegram.ext._application.Application'>
+4 -2
View File
@@ -189,8 +189,10 @@ def autodoc_process_bases(app, name, obj, option, bases: list):
bases[idx] = f":class:`{base}`"
# Now convert `telegram._message.Message` to `telegram.Message` etc
match = re.search(pattern=r"(telegram(\.ext|))\.[_\w\.]+", string=base)
if not match or "_utils" in base:
if (
not (match := re.search(pattern=r"(telegram(\.ext|))\.[_\w\.]+", string=base))
or "_utils" in base
):
continue
parts = match.group(0).split(".")
+4 -4
View File
@@ -1,7 +1,7 @@
sphinx==6.1.3
sphinx==7.0.1
sphinx-pypi-upload
furo==2023.3.23
furo==2023.5.20
git+https://github.com/harshil21/furo-sphinx-search@01efc7be422d7dc02390aab9be68d6f5ce1a5618#egg=furo-sphinx-search
sphinx-paramlinks==0.5.4
sphinxcontrib-mermaid==0.8.1
sphinx-copybutton==0.5.1
sphinxcontrib-mermaid==0.9.2
sphinx-copybutton==0.5.2
+7 -3
View File
@@ -21,9 +21,9 @@ author = "Leandro Toledo"
# built documents.
#
# The short X.Y version.
version = "20.2" # telegram.__version__[:3]
version = "20.4" # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = "20.2" # telegram.__version__
release = "20.3" # telegram.__version__
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "6.1.3"
@@ -44,7 +44,11 @@ extensions = [
]
# For shorter links to Wiki in docstrings
extlinks = {"wiki": ("https://github.com/python-telegram-bot/python-telegram-bot/wiki/%s", "%s")}
extlinks = {
"wiki": ("https://github.com/python-telegram-bot/python-telegram-bot/wiki/%s", "%s"),
"pr": ("https://github.com/python-telegram-bot/python-telegram-bot/pull/%s", "#%s"),
"issue": ("https://github.com/python-telegram-bot/python-telegram-bot/issues/%s", "#%s"),
}
# Use intersphinx to reference the python builtin library docs
intersphinx_mapping = {
+5 -6
View File
@@ -8,7 +8,7 @@ aspect of the Telegram Bot API while others focus on one of the
mechanics of this library. Except for the
:any:`examples.rawapibot` example, they all use the high-level
framework this library provides with the
:any:`telegram.ext <telegram.ext>` submodule.
:mod:`telegram.ext` submodule.
All examples are licensed under the `CC0
License <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt>`__
@@ -38,9 +38,8 @@ class to send timed messages. The user sets a timer by using ``/set``
command with a specific time, for example ``/set 30``. The bot then sets
up a job to send a message to that user after 30 seconds. The user can
also cancel the timer by sending ``/unset``. To learn more about the
``JobQueue``, read `this wiki
article <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue>`__.
Note: To use ``JobQueue``, you must install PTB via ``pip install python-telegram-bot[job-queue]``
``JobQueue``, read `this wiki article <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions---JobQueue>`__.
Note: To use ``JobQueue``, you must install PTB via ``pip install "python-telegram-bot[job-queue]"``
:any:`examples.conversationbot`
-------------------------------
@@ -116,7 +115,7 @@ Dont forget to enable and configure payments with
`@BotFather <https://telegram.me/BotFather>`_. Check out this
`guide <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport>`__
on Telegram passports in PTB.
Note: To use Telegram Passport, you must install PTB via ``pip install python-telegram-bot[passport]``
Note: To use Telegram Passport, you must install PTB via ``pip install "python-telegram-bot[passport]"``
:any:`examples.paymentbot`
--------------------------
@@ -164,7 +163,7 @@ combination with ``telegram.ext.Application``.
This example showcases how PTBs “arbitrary callback data” feature can be
used.
Note: To use arbitrary callback data, you must install PTB via ``pip install python-telegram-bot[callback-data]``
Note: To use arbitrary callback data, you must install PTB via ``pip install "python-telegram-bot[callback-data]"``
Pure API
--------
+4
View File
@@ -196,6 +196,10 @@
- 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
* - :meth:`~telegram.Bot.set_my_name`
- Used for setting the name of the bot
* - :meth:`~telegram.Bot.get_my_name`
- Used for obtaining the name of the bot
.. raw:: html
+4 -3
View File
@@ -27,11 +27,12 @@
:hidden:
:caption: Project
stability_policy
changelog
coc
contributing
testing
Website <https://python-telegram-bot.org>
GitHub Repository <https://github.com/python-telegram-bot/python-telegram-bot/>
Telegram Channel <https://t.me/pythontelegrambotchannel/>
Telegram User Group <https://t.me/pythontelegrambotgroup/>
coc
contributing
testing
+146
View File
@@ -0,0 +1,146 @@
Stability Policy
================
.. important::
This stability policy is in place since version 20.3.
While earlier versions of ``python-telegram-bot`` also had stable interfaces, they had no explicit stability policy and hence did not follow the rules outlined below in all detail.
Please also refer to the :ref:`changelog <ptb-changelog>`.
.. caution::
Large parts of the :mod:`telegram` package are the Python representations of the Telegram Bot API, whose stability policy PTB can not influence.
This policy hence includes some special cases for those parts.
What does this policy cover?
----------------------------
This policy includes any API or behavior that is covered in this documentation.
This covers both the :mod:`telegram` package and the :mod:`telegram.ext` package.
What doesn't this policy cover?
-------------------------------
Introduction of new features or changes of flavors of comparable behavior (e.g. the default for the HTTP protocol version being used) are not covered by this policy.
The internal structure of classes in PTB, i.e. things like the result of ``dir(obj))`` or the contents of ``obj.__dict__``, is not covered by this policy.
Objects are in general not guaranteed to be pickleable (unless stated otherwise) and pickled objects from one version of PTB may not be loadable in future versions.
We may provide a way to convert pickled objects from one version to another, but this is not guaranteed.
Functionality that is part of PTBs API but is explicitly documented as not being intended to be used directly by users (e.g. :meth:`telegram.request.BaseRequest.do_request`) may change.
This also applies to functions or attributes marked as final in the sense of `PEP 591 <https://peps.python.org/pep-0591/>`__.
PTB has dependencies to third-party packages.
The versions that PTB uses of these third-party packages may change if that does not affect PTBs public API.
PTB does not give guarantees about which Python versions are supported.
In general, we will try to support all Python versions that have not yet reached their end of life, but we reserve ourselves the option to drop support for Python versions earlier if that benefits the advancement of the library.
PTB provides static type hints for all public attributes, parameters, return values and generic classes.
These type hints are not covered by this policy and may change at any time under the condition that these changes have no impact on the runtime behavior of PTB.
.. _bot-api-functionality-1:
Bot API Functionality
~~~~~~~~~~~~~~~~~~~~~
Comparison of equality of instances of the classes in the :mod:`telegram` package is subject to change and the PTB team will update the behavior to best reflect updates in the Bot API.
Changes in this regard will be documented in the affected classes.
Note that equality comparison with objects that where serialized by an older version of PTB may hence give unexpected results.
When the order of arguments of the Bot API methods changes or they become optional/mandatory due to changes in the Bot API, PTB will always try to reflect these changes.
While we try to make such changes backward compatible, this is not always possible or only with significant effort.
In such cases we will find a trade-off between backward compatibility and fully complying with the Bot API, which may result in breaking changes.
We highly recommend using keyword arguments, which can help make such changes non-breaking on your end.
..
We have documented a few common cases and possible backwards compatible solutions in the wiki as a reference for the dev team: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Backward-Compatibility
When the Bot API changes attributes of classes, the method :meth:`telegram.TelegramObject.to_dict` will change as necessary to reflect these changes.
In particular, attributes deprecated by Telegram will be removed from the returned dictionary.
Deprecated attributes that are still passed by Telegram will be available in the :attr:`~telegram.TelegramObject.api_kwargs` dictionary as long as PTB can support that with feasible effort.
Since attributes of the classes in the :mod:`telegram` package are not writable, we may change them to properties where appropriate.
Development Versions
~~~~~~~~~~~~~~~~~~~~
Pre-releases marked as alpha, beta or release candidate are not covered by this policy.
Before a feature is in a stable release, i.e. the feature was merged into the ``master`` branch but not released yet (or only in a pre-release), it is not covered by this policy either and may change.
Security
~~~~~~~~
We make exceptions from our stability policy for security.
We will violate this policy as necessary in order to resolve a security issue or harden PTB against a possible attack.
Versioning
----------
PTB uses a versioning scheme that roughly follows `https://semver.org/ <https://semver.org/>`_, although it may not be quite as strict.
Given a version of PTB X.Y.Z,
- X indicates the major version number.
This is incremented when backward incompatible changes are introduced.
- Y indicates the minor version number.
This is incremented when new functionality or backward compatible changes are introduced by PTB.
*This is also incremented when PTB adds support for a new Bot API version, which may include backward incompatible changes in some cases as outlined* :ref:`below <bot-api-versioning>`.
- Z is the patch version.
This is incremented if backward compatible bug fixes or smaller changes are introduced.
If this number is 0, it can be omitted, i.e. we just write X.Y instead of X.Y.0.
Deprecation
~~~~~~~~~~~
From time to time we will want to change the behavior of an API or remove it entirely, or we do so to comply with changes in the Telegram Bot API.
In those cases, we follow a deprecation schedule as detailed below.
Functionality is marked as deprecated by a corresponding note in the release notes and the documentation.
Where possible, a :class:`~telegram.warnings.PTBDeprecationWarning` is issued when deprecated functionality is used, but this is not mandatory.
From time to time, we may decide to deprecate an API that is particularly widely used.
In these cases, we may decide to provide an extended deprecation period, at our discretion.
With version 20.0.0, PTB introduced major structural breaking changes without the above deprecation period.
Should a similarly big change ever be deemed necessary again by the development team and should a deprecation period prove too much additional effort, this violation of the stability policy will be announced well ahead of the release in our channel, `as was done for v20 <https://t.me/pythontelegrambotchannel/94>`_.
Non-Bot API Functionality
#########################
Starting with version 20.3, deprecated functionality will stay available for the current and the next major version.
For example:
- In PTB v20.1.1 the feature exists
- In PTB v20.1.2 or v20.2.0 the feature is marked as deprecated
- In PTB v21.*.* the feature is marked as deprecated
- In PTB v22.0 the feature is removed or changed
.. _bot-api-versioning:
Bot API Functionality
#####################
As PTB has no control over deprecations introduced by Telegram and the schedule of these deprecations rarely coincides with PTBs deprecation schedule, we have a special policy for Bot API functionality.
Starting with 20.3, deprecated Bot API functionality will stay available for the current and the next major version of PTB *or* until the next version of the Bot API.
More precisely, two cases are possible, for which we show examples below.
Case 1
^^^^^^
- In PTB v20.1 the feature exists
- Bot API version 6.6 is released and deprecates the feature
- PTB v20.2 adds support for Bot API 6.6 and the feature is
marked as deprecated
- In PTB v21.0 the feature is removed or changed
Case 2
^^^^^^
- In PTB v20.1 the feature exists
- Bot API version 6.6 is released and deprecates the feature
- PTB v20.2 adds support for Bot API version 6.6 and the feature is marked as deprecated
- In PTB v20.2.* and v20.3.* the feature is marked as deprecated
- Bot API version 6.7 is released
- PTB v20.4 adds support for Bot API version 6.7 and the feature is removed or changed
+2
View File
@@ -16,6 +16,7 @@ Available Types
telegram.botcommandscopechatmember
telegram.botcommandscopedefault
telegram.botdescription
telegram.botname
telegram.botshortdescription
telegram.callbackquery
telegram.chat
@@ -78,6 +79,7 @@ Available Types
telegram.replykeyboardmarkup
telegram.replykeyboardremove
telegram.sentwebappmessage
telegram.switchinlinequerychosenchat
telegram.telegramobject
telegram.update
telegram.user
+6
View File
@@ -0,0 +1,6 @@
BotName
=======
.. autoclass:: telegram.BotName
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
BaseUpdateProcessor
===================
.. autoclass:: telegram.ext.BaseUpdateProcessor
:members:
:show-inheritance:
+4
View File
@@ -1,18 +1,22 @@
telegram.ext package
====================
.. automodule:: telegram.ext
.. toctree::
:titlesonly:
telegram.ext.application
telegram.ext.applicationbuilder
telegram.ext.applicationhandlerstop
telegram.ext.baseupdateprocessor
telegram.ext.callbackcontext
telegram.ext.contexttypes
telegram.ext.defaults
telegram.ext.extbot
telegram.ext.job
telegram.ext.jobqueue
telegram.ext.simpleupdateprocessor
telegram.ext.updater
telegram.ext.handlers-tree.rst
telegram.ext.persistence-tree.rst
@@ -0,0 +1,6 @@
SimpleUpdateProcessor
=====================
.. autoclass:: telegram.ext.SimpleUpdateProcessor
:members:
:show-inheritance:
+1
View File
@@ -24,6 +24,7 @@ Inline Mode
telegram.inlinequeryresultlocation
telegram.inlinequeryresultmpeg4gif
telegram.inlinequeryresultphoto
telegram.inlinequeryresultsbutton
telegram.inlinequeryresultvenue
telegram.inlinequeryresultvideo
telegram.inlinequeryresultvoice
@@ -0,0 +1,6 @@
InlineQueryResultsButton
========================
.. autoclass:: telegram.InlineQueryResultsButton
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
SwitchInlineQueryChosenChat
===========================
.. autoclass:: telegram.SwitchInlineQueryChosenChat
:members:
:show-inheritance:
+2
View File
@@ -55,3 +55,5 @@
.. |sequenceargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list.
.. |captionentitiesattr| replace:: Tuple of special entities that appear in the caption, which can be specified instead of ``parse_mode``.
.. |datetime_localization| replace:: The default timezone of the bot is used for localization, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is used.
+5 -2
View File
@@ -9,7 +9,7 @@ https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callba
Note:
To use arbitrary callback data, you must install PTB via
`pip install python-telegram-bot[callback-data]`
`pip install "python-telegram-bot[callback-data]"`
"""
import logging
from typing import List, Tuple, cast
@@ -41,6 +41,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -122,7 +125,7 @@ def main() -> None:
application.add_handler(CallbackQueryHandler(list_button))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+9 -7
View File
@@ -44,6 +44,9 @@ logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -103,13 +106,12 @@ async def track_chats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
elif was_member and not is_member:
logger.info("%s removed the bot from the group %s", cause_name, chat.title)
context.bot_data.setdefault("group_ids", set()).discard(chat.id)
else:
if not was_member and is_member:
logger.info("%s added the bot to the channel %s", cause_name, chat.title)
context.bot_data.setdefault("channel_ids", set()).add(chat.id)
elif was_member and not is_member:
logger.info("%s removed the bot from the channel %s", cause_name, chat.title)
context.bot_data.setdefault("channel_ids", set()).discard(chat.id)
elif not was_member and is_member:
logger.info("%s added the bot to the channel %s", cause_name, chat.title)
context.bot_data.setdefault("channel_ids", set()).add(chat.id)
elif was_member and not is_member:
logger.info("%s removed the bot from the channel %s", cause_name, chat.title)
context.bot_data.setdefault("channel_ids", set()).discard(chat.id)
async def show_chats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
+10 -2
View File
@@ -43,6 +43,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -57,7 +60,12 @@ class ChatData:
class CustomContext(CallbackContext[ExtBot, dict, ChatData, dict]):
"""Custom class for context."""
def __init__(self, application: Application, chat_id: int = None, user_id: int = None):
def __init__(
self,
application: Application,
chat_id: Optional[int] = None,
user_id: Optional[int] = None,
):
super().__init__(application=application, chat_id=chat_id, user_id=user_id)
self._message_id: Optional[int] = None
@@ -142,7 +150,7 @@ def main() -> None:
application.add_handler(CallbackQueryHandler(count_click))
application.add_handler(CommandHandler("print_users", print_users))
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -43,6 +43,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
GENDER, PHOTO, LOCATION, BIO = range(4)
@@ -169,7 +172,7 @@ def main() -> None:
application.add_handler(conv_handler)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -44,6 +44,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
@@ -157,7 +160,7 @@ def main() -> None:
application.add_handler(conv_handler)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -54,6 +54,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -129,7 +132,7 @@ async def main() -> None:
application.add_handler(TypeHandler(type=WebhookUpdate, callback=webhook_update))
# Pass webhook settings to telegram
await application.bot.set_webhook(url=f"{url}/telegram")
await application.bot.set_webhook(url=f"{url}/telegram", allowed_updates=Update.ALL_TYPES)
# Set up webserver
async def telegram(request: Request) -> Response:
+4 -1
View File
@@ -42,6 +42,9 @@ logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# Define constants that will allow us to reuse the deep-linking parameters.
@@ -144,7 +147,7 @@ def main() -> None:
application.add_handler(CommandHandler("start", start))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -37,6 +37,9 @@ from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandl
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -74,7 +77,7 @@ def main() -> None:
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+5 -2
View File
@@ -29,6 +29,9 @@ from telegram.ext import Application, CommandHandler, ContextTypes
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# This can be your own ID, or one for a developer group/channel.
@@ -39,7 +42,7 @@ DEVELOPER_CHAT_ID = 123456789
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Log the error and send a telegram message to notify the developer."""
# Log the error before we do anything else, so we can see it even if something breaks.
logger.error(msg="Exception while handling an update:", exc_info=context.error)
logger.error("Exception while handling an update:", exc_info=context.error)
# traceback.format_exception returns the usual python message about an exception, but as a
# list of strings rather than a single string, so we have to join them together.
@@ -90,7 +93,7 @@ def main() -> None:
application.add_error_handler(error_handler)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+6 -3
View File
@@ -39,6 +39,9 @@ from telegram.ext import Application, CommandHandler, ContextTypes, InlineQueryH
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -58,7 +61,7 @@ async def inline_query(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
"""Handle the inline query. This is run when you type: @botusername <query>"""
query = update.inline_query.query
if query == "":
if not query: # empty query should not be handled
return
results = [
@@ -95,11 +98,11 @@ def main() -> None:
application.add_handler(CommandHandler("start", start))
application.add_handler(CommandHandler("help", help_command))
# on non command i.e message - echo the message on Telegram
# on inline queries - show corresponding inline results
application.add_handler(InlineQueryHandler(inline_query))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -28,6 +28,9 @@ from telegram.ext import Application, CallbackQueryHandler, CommandHandler, Cont
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -72,7 +75,7 @@ def main() -> None:
application.add_handler(CommandHandler("help", help_command))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -42,6 +42,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# Stages
@@ -204,7 +207,7 @@ def main() -> None:
application.add_handler(conv_handler)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -45,6 +45,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# State definitions for top level conversation
@@ -392,7 +395,7 @@ def main() -> None:
application.add_handler(conv_handler)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+5 -2
View File
@@ -12,7 +12,7 @@ See https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Pas
Note:
To use Telegram Passport, you must install PTB via
`pip install python-telegram-bot[passport]`
`pip install "python-telegram-bot[passport]"`
"""
import logging
from pathlib import Path
@@ -39,6 +39,9 @@ logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -129,7 +132,7 @@ def main() -> None:
application.add_handler(MessageHandler(filters.PASSPORT_DATA, msg))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -34,6 +34,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
PAYMENT_PROVIDER_TOKEN = "PAYMENT_PROVIDER_TOKEN"
@@ -165,7 +168,7 @@ def main() -> None:
)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -45,6 +45,9 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
@@ -180,7 +183,7 @@ def main() -> None:
application.add_handler(show_data_handler)
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+9 -3
View File
@@ -45,9 +45,15 @@ from telegram.ext import (
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
TOTAL_VOTER_COUNT = 3
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Inform user about what this bot can do"""
await update.message.reply_text(
@@ -101,7 +107,7 @@ async def receive_poll_answer(update: Update, context: ContextTypes.DEFAULT_TYPE
)
answered_poll["answers"] += 1
# Close poll after three participants voted
if answered_poll["answers"] == 3:
if answered_poll["answers"] == TOTAL_VOTER_COUNT:
await context.bot.stop_poll(answered_poll["chat_id"], answered_poll["message_id"])
@@ -123,7 +129,7 @@ async def receive_quiz_answer(update: Update, context: ContextTypes.DEFAULT_TYPE
# the bot can receive closed poll updates we don't care about
if update.poll.is_closed:
return
if update.poll.total_voter_count == 3:
if update.poll.total_voter_count == TOTAL_VOTER_COUNT:
try:
quiz_data = context.bot_data[update.poll.id]
# this means this poll answer update is from an old poll, we can't stop it then
@@ -176,7 +182,7 @@ def main() -> None:
application.add_handler(PollHandler(receive_quiz_answer))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+7 -5
View File
@@ -7,6 +7,7 @@ on the telegram.ext bot framework.
This program is dedicated to the public domain under the CC0 license.
"""
import asyncio
import contextlib
import logging
from typing import NoReturn
@@ -23,12 +24,15 @@ if __version_info__ < (20, 0, 0, "alpha", 1):
f"{TG_VER} version of this example, "
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
)
from telegram import Bot
from telegram import Bot, Update
from telegram.error import Forbidden, NetworkError
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -57,7 +61,7 @@ async def main() -> NoReturn:
async def echo(bot: Bot, update_id: int) -> int:
"""Echo the message the user sent."""
# Request updates after the last update_id
updates = await bot.get_updates(offset=update_id, timeout=10)
updates = await bot.get_updates(offset=update_id, timeout=10, allowed_updates=Update.ALL_TYPES)
for update in updates:
next_update_id = update.update_id + 1
@@ -72,7 +76,5 @@ async def echo(bot: Bot, update_id: int) -> int:
if __name__ == "__main__":
try:
with contextlib.suppress(KeyboardInterrupt): # Ignore exception when Ctrl-C is pressed
asyncio.run(main())
except KeyboardInterrupt: # Ignore exception when Ctrl-C is pressed
pass
+2 -2
View File
@@ -19,7 +19,7 @@ bot.
Note:
To use the JobQueue, you must install PTB via
`pip install python-telegram-bot[job-queue]`
`pip install "python-telegram-bot[job-queue]"`
"""
import logging
@@ -114,7 +114,7 @@ def main() -> None:
application.add_handler(CommandHandler("unset", unset))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+4 -1
View File
@@ -31,6 +31,9 @@ from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandl
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
@@ -70,7 +73,7 @@ def main() -> None:
application.add_handler(MessageHandler(filters.StatusUpdate.WEB_APP_DATA, web_app_data))
# Run the bot until the user presses Ctrl-C
application.run_polling()
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
+12 -2
View File
@@ -1,6 +1,6 @@
[tool.black]
line-length = 99
target-version = ['py37', 'py38', 'py39', 'py310', 'py311']
target-version = ['py38', 'py39', 'py310', 'py311']
[tool.isort] # black config
profile = "black"
@@ -8,4 +8,14 @@ line_length = 99
[tool.ruff]
line-length = 99
target-version = "py37"
target-version = "py38"
show-fixes = true
ignore = ["PLR2004", "PLR0911", "PLR0912", "PLR0913", "PLR0915", "PERF203"]
select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET", "RSE",
"G", "ISC", "PT", "ASYNC", "TCH", "CPY", "SLOT", "PERF",]
[tool.ruff.per-file-ignores]
"tests/*.py" = ["B018"]
"**/__init__.py" = ["CPY001"]
"examples/**.py" = ["CPY001"]
"tests/**.py" = ["RUF012"]
+2 -2
View File
@@ -1,9 +1,9 @@
pre-commit # needed for pre-commit hooks in the git commit command
# For the test suite
pytest==7.2.2
pytest==7.4.0
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
pytest-xdist==3.3.1 # xdist runs tests in parallel
flaky # Used for flaky tests (flaky decorator)
beautifulsoup4 # used in test_official for parsing tg docs
+2 -2
View File
@@ -13,14 +13,14 @@
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
aiolimiter~=1.1.0 # rate-limiter!ext
# tornado is rather stable, but let's not allow the next mayor release without prior testing
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
cachetools~=5.3.1 # callback-data!ext
APScheduler~=3.10.1 # job-queue!ext
# pytz is required by APS and just needs the lower bound due to #2120
+1 -1
View File
@@ -6,4 +6,4 @@
# versions and only increase the lower bound if necessary
# httpx has no stable release yet, so let's be cautious for now
httpx ~= 0.23.3
httpx ~= 0.24.1
-1
View File
@@ -64,7 +64,6 @@ disallow_untyped_defs = True
disallow_incomplete_defs = True
disallow_untyped_decorators = True
show_error_codes = True
implicit_optional = True
# For some files, it's easier to just disable strict-optional all together instead of
# cluttering the code with `# type: ignore`s or stuff like
+1 -2
View File
@@ -105,13 +105,12 @@ def get_setup_kwargs(raw=False):
"Topic :: Internet",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
python_requires=">=3.7",
python_requires=">=3.8",
)
return kwargs
+6
View File
@@ -38,6 +38,7 @@ __all__ = ( # Keep this alphabetically ordered
"BotCommandScopeChatMember",
"BotCommandScopeDefault",
"BotDescription",
"BotName",
"BotShortDescription",
"CallbackGame",
"CallbackQuery",
@@ -102,6 +103,7 @@ __all__ = ( # Keep this alphabetically ordered
"InlineQueryResultLocation",
"InlineQueryResultMpeg4Gif",
"InlineQueryResultPhoto",
"InlineQueryResultsButton",
"InlineQueryResultVenue",
"InlineQueryResultVideo",
"InlineQueryResultVoice",
@@ -169,6 +171,7 @@ __all__ = ( # Keep this alphabetically ordered
"Sticker",
"StickerSet",
"SuccessfulPayment",
"SwitchInlineQueryChosenChat",
"TelegramObject",
"Update",
"User",
@@ -204,6 +207,7 @@ from ._botcommandscope import (
BotCommandScopeDefault,
)
from ._botdescription import BotDescription, BotShortDescription
from ._botname import BotName
from ._callbackquery import CallbackQuery
from ._chat import Chat
from ._chatadministratorrights import ChatAdministratorRights
@@ -280,6 +284,7 @@ from ._inline.inlinequeryresultgif import InlineQueryResultGif
from ._inline.inlinequeryresultlocation import InlineQueryResultLocation
from ._inline.inlinequeryresultmpeg4gif import InlineQueryResultMpeg4Gif
from ._inline.inlinequeryresultphoto import InlineQueryResultPhoto
from ._inline.inlinequeryresultsbutton import InlineQueryResultsButton
from ._inline.inlinequeryresultvenue import InlineQueryResultVenue
from ._inline.inlinequeryresultvideo import InlineQueryResultVideo
from ._inline.inlinequeryresultvoice import InlineQueryResultVoice
@@ -336,6 +341,7 @@ from ._replykeyboardmarkup import ReplyKeyboardMarkup
from ._replykeyboardremove import ReplyKeyboardRemove
from ._sentwebappmessage import SentWebAppMessage
from ._shared import ChatShared, UserShared
from ._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
from ._telegramobject import TelegramObject
from ._update import Update
from ._user import User
+712 -609
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Bot Command."""
from typing import ClassVar
from typing import Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -52,7 +52,7 @@ class BotCommand(TelegramObject):
__slots__ = ("description", "command")
def __init__(self, command: str, description: str, *, api_kwargs: JSONDict = None):
def __init__(self, command: str, description: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.command: str = command
self.description: str = description
@@ -61,22 +61,22 @@ class BotCommand(TelegramObject):
self._freeze()
MIN_COMMAND: ClassVar[int] = constants.BotCommandLimit.MIN_COMMAND
MIN_COMMAND: Final[int] = constants.BotCommandLimit.MIN_COMMAND
""":const:`telegram.constants.BotCommandLimit.MIN_COMMAND`
.. versionadded:: 20.0
"""
MAX_COMMAND: ClassVar[int] = constants.BotCommandLimit.MAX_COMMAND
MAX_COMMAND: Final[int] = constants.BotCommandLimit.MAX_COMMAND
""":const:`telegram.constants.BotCommandLimit.MAX_COMMAND`
.. versionadded:: 20.0
"""
MIN_DESCRIPTION: ClassVar[int] = constants.BotCommandLimit.MIN_DESCRIPTION
MIN_DESCRIPTION: Final[int] = constants.BotCommandLimit.MIN_DESCRIPTION
""":const:`telegram.constants.BotCommandLimit.MIN_DESCRIPTION`
.. versionadded:: 20.0
"""
MAX_DESCRIPTION: ClassVar[int] = constants.BotCommandLimit.MAX_DESCRIPTION
MAX_DESCRIPTION: Final[int] = constants.BotCommandLimit.MAX_DESCRIPTION
""":const:`telegram.constants.BotCommandLimit.MAX_DESCRIPTION`
.. versionadded:: 20.0
+18 -16
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=redefined-builtin
"""This module contains objects representing Telegram bot command scopes."""
from typing import TYPE_CHECKING, ClassVar, Dict, Optional, Type, Union
from typing import TYPE_CHECKING, Dict, Final, Optional, Type, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -60,22 +60,22 @@ class BotCommandScope(TelegramObject):
__slots__ = ("type",)
DEFAULT: ClassVar[str] = constants.BotCommandScopeType.DEFAULT
DEFAULT: Final[str] = constants.BotCommandScopeType.DEFAULT
""":const:`telegram.constants.BotCommandScopeType.DEFAULT`"""
ALL_PRIVATE_CHATS: ClassVar[str] = constants.BotCommandScopeType.ALL_PRIVATE_CHATS
ALL_PRIVATE_CHATS: Final[str] = constants.BotCommandScopeType.ALL_PRIVATE_CHATS
""":const:`telegram.constants.BotCommandScopeType.ALL_PRIVATE_CHATS`"""
ALL_GROUP_CHATS: ClassVar[str] = constants.BotCommandScopeType.ALL_GROUP_CHATS
ALL_GROUP_CHATS: Final[str] = constants.BotCommandScopeType.ALL_GROUP_CHATS
""":const:`telegram.constants.BotCommandScopeType.ALL_GROUP_CHATS`"""
ALL_CHAT_ADMINISTRATORS: ClassVar[str] = constants.BotCommandScopeType.ALL_CHAT_ADMINISTRATORS
ALL_CHAT_ADMINISTRATORS: Final[str] = constants.BotCommandScopeType.ALL_CHAT_ADMINISTRATORS
""":const:`telegram.constants.BotCommandScopeType.ALL_CHAT_ADMINISTRATORS`"""
CHAT: ClassVar[str] = constants.BotCommandScopeType.CHAT
CHAT: Final[str] = constants.BotCommandScopeType.CHAT
""":const:`telegram.constants.BotCommandScopeType.CHAT`"""
CHAT_ADMINISTRATORS: ClassVar[str] = constants.BotCommandScopeType.CHAT_ADMINISTRATORS
CHAT_ADMINISTRATORS: Final[str] = constants.BotCommandScopeType.CHAT_ADMINISTRATORS
""":const:`telegram.constants.BotCommandScopeType.CHAT_ADMINISTRATORS`"""
CHAT_MEMBER: ClassVar[str] = constants.BotCommandScopeType.CHAT_MEMBER
CHAT_MEMBER: Final[str] = constants.BotCommandScopeType.CHAT_MEMBER
""":const:`telegram.constants.BotCommandScopeType.CHAT_MEMBER`"""
def __init__(self, type: str, *, api_kwargs: JSONDict = None):
def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.type: str = type
self._id_attrs = (self.type,)
@@ -128,7 +128,7 @@ class BotCommandScopeDefault(BotCommandScope):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None):
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs)
self._freeze()
@@ -144,7 +144,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None):
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs)
self._freeze()
@@ -159,7 +159,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None):
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs)
self._freeze()
@@ -174,7 +174,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None):
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs)
self._freeze()
@@ -197,7 +197,7 @@ class BotCommandScopeChat(BotCommandScope):
__slots__ = ("chat_id",)
def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None):
def __init__(self, chat_id: Union[str, int], *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs)
with self._unfrozen():
self.chat_id: Union[str, int] = (
@@ -224,7 +224,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope):
__slots__ = ("chat_id",)
def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None):
def __init__(self, chat_id: Union[str, int], *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs)
with self._unfrozen():
self.chat_id: Union[str, int] = (
@@ -254,7 +254,9 @@ class BotCommandScopeChatMember(BotCommandScope):
__slots__ = ("chat_id", "user_id")
def __init__(self, chat_id: Union[str, int], user_id: int, *, api_kwargs: JSONDict = None):
def __init__(
self, chat_id: Union[str, int], user_id: int, *, api_kwargs: Optional[JSONDict] = None
):
super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs)
with self._unfrozen():
self.chat_id: Union[str, int] = (
+6 -4
View File
@@ -17,6 +17,8 @@
# 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 typing import Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
@@ -39,9 +41,9 @@ class BotDescription(TelegramObject):
__slots__ = ("description",)
def __init__(self, description: str, *, api_kwargs: JSONDict = None):
def __init__(self, description: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.description = description
self.description: str = description
self._id_attrs = (self.description,)
@@ -66,9 +68,9 @@ class BotShortDescription(TelegramObject):
__slots__ = ("short_description",)
def __init__(self, short_description: str, *, api_kwargs: JSONDict = None):
def __init__(self, short_description: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.short_description = short_description
self.short_description: str = short_description
self._id_attrs = (self.short_description,)
+54
View File
@@ -0,0 +1,54 @@
#!/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 represent a Telegram bots name."""
from typing import Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
class BotName(TelegramObject):
"""This object represents the bot's name.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`name` is equal.
.. versionadded:: 20.3
Args:
name (:obj:`str`): The bot's name.
Attributes:
name (:obj:`str`): The bot's name.
"""
__slots__ = ("name",)
def __init__(self, name: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.name: str = name
self._id_attrs = (self.name,)
self._freeze()
MAX_LENGTH: Final[int] = constants.BotNameLimit.MAX_NAME_LENGTH
""":const:`telegram.constants.BotNameLimit.MAX_NAME_LENGTH`"""
+45 -45
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=redefined-builtin
"""This module contains an object that represents a Telegram CallbackQuery"""
from typing import TYPE_CHECKING, ClassVar, Optional, Sequence, Tuple, Union
from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union
from telegram import constants
from telegram._files.location import Location
@@ -118,12 +118,12 @@ class CallbackQuery(TelegramObject):
id: str,
from_user: User,
chat_instance: str,
message: Message = None,
data: str = None,
inline_message_id: str = None,
game_short_name: str = None,
message: Optional[Message] = None,
data: Optional[str] = None,
inline_message_id: Optional[str] = None,
game_short_name: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -155,16 +155,16 @@ class CallbackQuery(TelegramObject):
async def answer(
self,
text: str = None,
show_alert: bool = None,
url: str = None,
cache_time: int = None,
text: Optional[str] = None,
show_alert: Optional[bool] = None,
url: Optional[str] = None,
cache_time: Optional[int] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
@@ -195,14 +195,14 @@ class CallbackQuery(TelegramObject):
text: str,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
reply_markup: "InlineKeyboardMarkup" = None,
entities: Sequence["MessageEntity"] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
entities: Optional[Sequence["MessageEntity"]] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -253,16 +253,16 @@ class CallbackQuery(TelegramObject):
async def edit_message_caption(
self,
caption: str = None,
reply_markup: "InlineKeyboardMarkup" = None,
caption: Optional[str] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Sequence["MessageEntity"] = None,
caption_entities: Optional[Sequence["MessageEntity"]] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -317,7 +317,7 @@ class CallbackQuery(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -362,13 +362,13 @@ class CallbackQuery(TelegramObject):
async def edit_message_media(
self,
media: "InputMedia",
reply_markup: "InlineKeyboardMarkup" = None,
reply_markup: Optional["InlineKeyboardMarkup"] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -413,19 +413,19 @@ class CallbackQuery(TelegramObject):
async def edit_message_live_location(
self,
latitude: float = None,
longitude: float = None,
reply_markup: "InlineKeyboardMarkup" = None,
horizontal_accuracy: float = None,
heading: int = None,
proximity_alert_radius: int = None,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
horizontal_accuracy: Optional[float] = None,
heading: Optional[int] = None,
proximity_alert_radius: Optional[int] = None,
*,
location: Location = None,
location: Optional[Location] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -481,13 +481,13 @@ class CallbackQuery(TelegramObject):
async def stop_message_live_location(
self,
reply_markup: "InlineKeyboardMarkup" = None,
reply_markup: Optional["InlineKeyboardMarkup"] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -533,14 +533,14 @@ class CallbackQuery(TelegramObject):
self,
user_id: Union[int, str],
score: int,
force: bool = None,
disable_edit_message: bool = None,
force: Optional[bool] = None,
disable_edit_message: Optional[bool] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
@@ -595,7 +595,7 @@ class CallbackQuery(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["GameHighScore", ...]:
"""Shortcut for either::
@@ -643,7 +643,7 @@ class CallbackQuery(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
@@ -671,7 +671,7 @@ class CallbackQuery(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
@@ -699,7 +699,7 @@ class CallbackQuery(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
@@ -722,21 +722,21 @@ class CallbackQuery(TelegramObject):
async def copy_message(
self,
chat_id: Union[int, str],
caption: str = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Sequence["MessageEntity"] = None,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
reply_to_message_id: int = None,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
reply_markup: ReplyMarkup = None,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: int = None,
message_thread_id: Optional[int] = 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,
api_kwargs: Optional[JSONDict] = None,
) -> "MessageId":
"""Shortcut for::
@@ -771,7 +771,7 @@ class CallbackQuery(TelegramObject):
message_thread_id=message_thread_id,
)
MAX_ANSWER_TEXT_LENGTH: ClassVar[
MAX_ANSWER_TEXT_LENGTH: Final[
int
] = constants.CallbackQueryLimit.ANSWER_CALLBACK_QUERY_TEXT_LENGTH
"""
+297 -290
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -127,12 +127,12 @@ class ChatAdministratorRights(TelegramObject):
can_promote_members: bool,
can_change_info: bool,
can_invite_users: bool,
can_post_messages: bool = None,
can_edit_messages: bool = None,
can_pin_messages: bool = None,
can_manage_topics: bool = None,
can_post_messages: Optional[bool] = None,
can_edit_messages: Optional[bool] = None,
can_pin_messages: Optional[bool] = None,
can_manage_topics: Optional[bool] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> None:
super().__init__(api_kwargs=api_kwargs)
# Required
+16 -7
View File
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Optional
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.datetime import from_timestamp
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
@@ -54,6 +54,9 @@ class ChatInviteLink(TelegramObject):
is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked.
expire_date (:class:`datetime.datetime`, optional): Date when the link will expire or
has been expired.
.. versionchanged:: 20.3
|datetime_localization|
member_limit (:obj:`int`, optional): Maximum number of users that can be members of the
chat simultaneously after joining the chat via this invite link;
:tg-const:`telegram.constants.ChatInviteLinkLimit.MIN_MEMBER_LIMIT`-
@@ -78,6 +81,9 @@ class ChatInviteLink(TelegramObject):
is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked.
expire_date (:class:`datetime.datetime`): Optional. Date when the link will expire or
has been expired.
.. versionchanged:: 20.3
|datetime_localization|
member_limit (:obj:`int`): Optional. Maximum number of users that can be members
of the chat simultaneously after joining the chat via this invite link;
:tg-const:`telegram.constants.ChatInviteLinkLimit.MIN_MEMBER_LIMIT`-
@@ -112,12 +118,12 @@ class ChatInviteLink(TelegramObject):
creates_join_request: bool,
is_primary: bool,
is_revoked: bool,
expire_date: datetime.datetime = None,
member_limit: int = None,
name: str = None,
pending_join_request_count: int = None,
expire_date: Optional[datetime.datetime] = None,
member_limit: Optional[int] = None,
name: Optional[str] = None,
pending_join_request_count: Optional[int] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -152,7 +158,10 @@ class ChatInviteLink(TelegramObject):
if not data:
return None
# Get the local timezone from the bot if it has defaults
loc_tzinfo = extract_tzinfo_from_defaults(bot)
data["creator"] = User.de_json(data.get("creator"), bot)
data["expire_date"] = from_timestamp(data.get("expire_date", None))
data["expire_date"] = from_timestamp(data.get("expire_date", None), tzinfo=loc_tzinfo)
return super().de_json(data=data, bot=bot)
+23 -8
View File
@@ -24,7 +24,7 @@ from telegram._chat import Chat
from telegram._chatinvitelink import ChatInviteLink
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.datetime import from_timestamp
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.types import JSONDict, ODVInput
@@ -41,7 +41,7 @@ class ChatJoinRequest(TelegramObject):
Note:
* Since Bot API 5.5, bots are allowed to contact users who sent a join request to a chat
where the bot is an administrator with the
:attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right even
:attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right - even
if the user never interacted with the bot before.
* Telegram does not guarantee that :attr:`from_user.id <from_user>` coincides with the
``chat_id`` of the user. Please use :attr:`user_chat_id` to contact the user in
@@ -56,6 +56,9 @@ class ChatJoinRequest(TelegramObject):
chat (:class:`telegram.Chat`): Chat to which the request was sent.
from_user (:class:`telegram.User`): User that sent the join request.
date (:class:`datetime.datetime`): Date the request was sent.
.. versionchanged:: 20.3
|datetime_localization|
user_chat_id (:obj:`int`): Identifier of a private chat with the user who sent the join
request. This number may have more than 32 significant bits and some programming
languages may have difficulty/silent defects in interpreting it. But it has at most 52
@@ -73,6 +76,9 @@ class ChatJoinRequest(TelegramObject):
chat (:class:`telegram.Chat`): Chat to which the request was sent.
from_user (:class:`telegram.User`): User that sent the join request.
date (:class:`datetime.datetime`): Date the request was sent.
.. versionchanged:: 20.3
|datetime_localization|
user_chat_id (:obj:`int`): Identifier of a private chat with the user who sent the join
request. This number may have more than 32 significant bits and some programming
languages may have difficulty/silent defects in interpreting it. But it has at most 52
@@ -86,6 +92,12 @@ class ChatJoinRequest(TelegramObject):
invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link that was used
by the user to send the join request.
Note:
When a user joins a *public* group via an invite link, this attribute may not
be present. However, this behavior is undocument and may be subject to change.
See `this GitHub thread <https://github.com/tdlib/telegram-bot-api/issues/428>`_
for some discussion.
"""
__slots__ = ("chat", "from_user", "date", "bio", "invite_link", "user_chat_id")
@@ -96,10 +108,10 @@ class ChatJoinRequest(TelegramObject):
from_user: User,
date: datetime.datetime,
user_chat_id: int,
bio: str = None,
invite_link: ChatInviteLink = None,
bio: Optional[str] = None,
invite_link: Optional[ChatInviteLink] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -124,9 +136,12 @@ class ChatJoinRequest(TelegramObject):
if not data:
return None
# Get the local timezone from the bot if it has defaults
loc_tzinfo = extract_tzinfo_from_defaults(bot)
data["chat"] = Chat.de_json(data.get("chat"), bot)
data["from_user"] = User.de_json(data.pop("from", None), bot)
data["date"] = from_timestamp(data.get("date", None))
data["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo)
data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot)
return super().de_json(data=data, bot=bot)
@@ -138,7 +153,7 @@ class ChatJoinRequest(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
@@ -170,7 +185,7 @@ class ChatJoinRequest(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
+4 -4
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a location to which a chat is connected."""
from typing import TYPE_CHECKING, ClassVar, Optional
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._files.location import Location
@@ -57,7 +57,7 @@ class ChatLocation(TelegramObject):
location: Location,
address: str,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.location: Location = location
@@ -79,12 +79,12 @@ class ChatLocation(TelegramObject):
return super().de_json(data=data, bot=bot)
MIN_ADDRESS: ClassVar[int] = constants.LocationLimit.MIN_CHAT_LOCATION_ADDRESS
MIN_ADDRESS: Final[int] = constants.LocationLimit.MIN_CHAT_LOCATION_ADDRESS
""":const:`telegram.constants.LocationLimit.MIN_CHAT_LOCATION_ADDRESS`
.. versionadded:: 20.0
"""
MAX_ADDRESS: ClassVar[int] = constants.LocationLimit.MAX_CHAT_LOCATION_ADDRESS
MAX_ADDRESS: Final[int] = constants.LocationLimit.MAX_CHAT_LOCATION_ADDRESS
""":const:`telegram.constants.LocationLimit.MAX_CHAT_LOCATION_ADDRESS`
.. versionadded:: 20.0
+37 -22
View File
@@ -18,12 +18,12 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatMember."""
import datetime
from typing import TYPE_CHECKING, ClassVar, Dict, Optional, Type
from typing import TYPE_CHECKING, Dict, Final, Optional, Type
from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.datetime import from_timestamp
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
@@ -74,17 +74,17 @@ class ChatMember(TelegramObject):
__slots__ = ("user", "status")
ADMINISTRATOR: ClassVar[str] = constants.ChatMemberStatus.ADMINISTRATOR
ADMINISTRATOR: Final[str] = constants.ChatMemberStatus.ADMINISTRATOR
""":const:`telegram.constants.ChatMemberStatus.ADMINISTRATOR`"""
OWNER: ClassVar[str] = constants.ChatMemberStatus.OWNER
OWNER: Final[str] = constants.ChatMemberStatus.OWNER
""":const:`telegram.constants.ChatMemberStatus.OWNER`"""
BANNED: ClassVar[str] = constants.ChatMemberStatus.BANNED
BANNED: Final[str] = constants.ChatMemberStatus.BANNED
""":const:`telegram.constants.ChatMemberStatus.BANNED`"""
LEFT: ClassVar[str] = constants.ChatMemberStatus.LEFT
LEFT: Final[str] = constants.ChatMemberStatus.LEFT
""":const:`telegram.constants.ChatMemberStatus.LEFT`"""
MEMBER: ClassVar[str] = constants.ChatMemberStatus.MEMBER
MEMBER: Final[str] = constants.ChatMemberStatus.MEMBER
""":const:`telegram.constants.ChatMemberStatus.MEMBER`"""
RESTRICTED: ClassVar[str] = constants.ChatMemberStatus.RESTRICTED
RESTRICTED: Final[str] = constants.ChatMemberStatus.RESTRICTED
""":const:`telegram.constants.ChatMemberStatus.RESTRICTED`"""
def __init__(
@@ -92,7 +92,7 @@ class ChatMember(TelegramObject):
user: User,
status: str,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required by all subclasses
@@ -125,7 +125,10 @@ class ChatMember(TelegramObject):
data["user"] = User.de_json(data.get("user"), bot)
if "until_date" in data:
data["until_date"] = from_timestamp(data["until_date"])
# Get the local timezone from the bot if it has defaults
loc_tzinfo = extract_tzinfo_from_defaults(bot)
data["until_date"] = from_timestamp(data["until_date"], tzinfo=loc_tzinfo)
return super().de_json(data=data, bot=bot)
@@ -159,9 +162,9 @@ class ChatMemberOwner(ChatMember):
self,
user: User,
is_anonymous: bool,
custom_title: str = None,
custom_title: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.OWNER, user=user, api_kwargs=api_kwargs)
with self._unfrozen():
@@ -292,13 +295,13 @@ class ChatMemberAdministrator(ChatMember):
can_promote_members: bool,
can_change_info: bool,
can_invite_users: bool,
can_post_messages: bool = None,
can_edit_messages: bool = None,
can_pin_messages: bool = None,
can_manage_topics: bool = None,
custom_title: str = None,
can_post_messages: Optional[bool] = None,
can_edit_messages: Optional[bool] = None,
can_pin_messages: Optional[bool] = None,
can_manage_topics: Optional[bool] = None,
custom_title: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.ADMINISTRATOR, user=user, api_kwargs=api_kwargs)
with self._unfrozen():
@@ -341,7 +344,7 @@ class ChatMemberMember(ChatMember):
self,
user: User,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs)
self._freeze()
@@ -386,6 +389,9 @@ class ChatMemberRestricted(ChatMember):
.. versionadded:: 20.0
until_date (:class:`datetime.datetime`): Date when restrictions
will be lifted for this user.
.. versionchanged:: 20.3
|datetime_localization|
can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios.
.. versionadded:: 20.1
@@ -438,6 +444,9 @@ class ChatMemberRestricted(ChatMember):
.. versionadded:: 20.0
until_date (:class:`datetime.datetime`): Date when restrictions
will be lifted for this user.
.. versionchanged:: 20.3
|datetime_localization|
can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios.
.. versionadded:: 20.1
@@ -502,7 +511,7 @@ class ChatMemberRestricted(ChatMember):
can_send_video_notes: bool,
can_send_voice_notes: bool,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.RESTRICTED, user=user, api_kwargs=api_kwargs)
with self._unfrozen():
@@ -547,7 +556,7 @@ class ChatMemberLeft(ChatMember):
self,
user: User,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs)
self._freeze()
@@ -565,6 +574,9 @@ class ChatMemberBanned(ChatMember):
until_date (:class:`datetime.datetime`): Date when restrictions
will be lifted for this user.
.. versionchanged:: 20.3
|datetime_localization|
Attributes:
status (:obj:`str`): The member's status in the chat,
always :tg-const:`telegram.ChatMember.BANNED`.
@@ -572,6 +584,9 @@ class ChatMemberBanned(ChatMember):
until_date (:class:`datetime.datetime`): Date when restrictions
will be lifted for this user.
.. versionchanged:: 20.3
|datetime_localization|
"""
__slots__ = ("until_date",)
@@ -581,7 +596,7 @@ class ChatMemberBanned(ChatMember):
user: User,
until_date: datetime.datetime,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs)
with self._unfrozen():
+24 -4
View File
@@ -25,7 +25,7 @@ from telegram._chatinvitelink import ChatInviteLink
from telegram._chatmember import ChatMember
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.datetime import from_timestamp
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
@@ -52,20 +52,34 @@ class ChatMemberUpdated(TelegramObject):
from_user (:class:`telegram.User`): Performer of the action, which resulted in the change.
date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to
:class:`datetime.datetime`.
.. versionchanged:: 20.3
|datetime_localization|
old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member.
new_chat_member (:class:`telegram.ChatMember`): New information about the chat member.
invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link, which was used
by the user to join the chat. For joining by invite link events only.
via_chat_folder_invite_link (:obj:`bool`, optional): :obj:`True`, if the user joined the
chat via a chat folder invite link
.. versionadded:: 20.3
Attributes:
chat (:class:`telegram.Chat`): Chat the user belongs to.
from_user (:class:`telegram.User`): Performer of the action, which resulted in the change.
date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to
:class:`datetime.datetime`.
.. versionchanged:: 20.3
|datetime_localization|
old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member.
new_chat_member (:class:`telegram.ChatMember`): New information about the chat member.
invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link, which was used
by the user to join the chat. For joining by invite link events only.
via_chat_folder_invite_link (:obj:`bool`): Optional. :obj:`True`, if the user joined the
chat via a chat folder invite link
.. versionadded:: 20.3
"""
@@ -76,6 +90,7 @@ class ChatMemberUpdated(TelegramObject):
"old_chat_member",
"new_chat_member",
"invite_link",
"via_chat_folder_invite_link",
)
def __init__(
@@ -85,9 +100,10 @@ class ChatMemberUpdated(TelegramObject):
date: datetime.datetime,
old_chat_member: ChatMember,
new_chat_member: ChatMember,
invite_link: ChatInviteLink = None,
invite_link: Optional[ChatInviteLink] = None,
via_chat_folder_invite_link: Optional[bool] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -96,6 +112,7 @@ class ChatMemberUpdated(TelegramObject):
self.date: datetime.datetime = date
self.old_chat_member: ChatMember = old_chat_member
self.new_chat_member: ChatMember = new_chat_member
self.via_chat_folder_invite_link: Optional[bool] = via_chat_folder_invite_link
# Optionals
self.invite_link: Optional[ChatInviteLink] = invite_link
@@ -118,9 +135,12 @@ class ChatMemberUpdated(TelegramObject):
if not data:
return None
# Get the local timezone from the bot if it has defaults
loc_tzinfo = extract_tzinfo_from_defaults(bot)
data["chat"] = Chat.de_json(data.get("chat"), bot)
data["from_user"] = User.de_json(data.pop("from", None), bot)
data["date"] = from_timestamp(data.get("date"))
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
data["old_chat_member"] = ChatMember.de_json(data.get("old_chat_member"), bot)
data["new_chat_member"] = ChatMember.de_json(data.get("new_chat_member"), bot)
data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot)
+16 -16
View File
@@ -166,23 +166,23 @@ class ChatPermissions(TelegramObject):
def __init__(
self,
can_send_messages: bool = None,
can_send_media_messages: bool = None,
can_send_polls: bool = None,
can_send_other_messages: bool = None,
can_add_web_page_previews: bool = None,
can_change_info: bool = None,
can_invite_users: bool = None,
can_pin_messages: bool = None,
can_manage_topics: bool = None,
can_send_audios: bool = None,
can_send_documents: bool = None,
can_send_photos: bool = None,
can_send_videos: bool = None,
can_send_video_notes: bool = None,
can_send_voice_notes: bool = None,
can_send_messages: Optional[bool] = None,
can_send_media_messages: Optional[bool] = None,
can_send_polls: Optional[bool] = None,
can_send_other_messages: Optional[bool] = None,
can_add_web_page_previews: Optional[bool] = None,
can_change_info: Optional[bool] = None,
can_invite_users: Optional[bool] = None,
can_pin_messages: Optional[bool] = None,
can_manage_topics: Optional[bool] = None,
can_send_audios: Optional[bool] = None,
can_send_documents: Optional[bool] = None,
can_send_photos: Optional[bool] = None,
can_send_videos: Optional[bool] = None,
can_send_video_notes: Optional[bool] = None,
can_send_voice_notes: Optional[bool] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
+3 -3
View File
@@ -72,10 +72,10 @@ class ChosenInlineResult(TelegramObject):
result_id: str,
from_user: User,
query: str,
location: Location = None,
inline_message_id: str = None,
location: Optional[Location] = None,
inline_message_id: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
+16 -16
View File
@@ -17,7 +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/].
"""This module contains an object that represents a Telegram Dice."""
from typing import ClassVar, List
from typing import Final, List, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -89,7 +89,7 @@ class Dice(TelegramObject):
__slots__ = ("emoji", "value")
def __init__(self, value: int, emoji: str, *, api_kwargs: JSONDict = None):
def __init__(self, value: int, emoji: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.value: int = value
self.emoji: str = emoji
@@ -98,62 +98,62 @@ class Dice(TelegramObject):
self._freeze()
DICE: ClassVar[str] = constants.DiceEmoji.DICE # skipcq: PTC-W0052
DICE: Final[str] = constants.DiceEmoji.DICE # skipcq: PTC-W0052
""":const:`telegram.constants.DiceEmoji.DICE`"""
DARTS: ClassVar[str] = constants.DiceEmoji.DARTS
DARTS: Final[str] = constants.DiceEmoji.DARTS
""":const:`telegram.constants.DiceEmoji.DARTS`"""
BASKETBALL: ClassVar[str] = constants.DiceEmoji.BASKETBALL
BASKETBALL: Final[str] = constants.DiceEmoji.BASKETBALL
""":const:`telegram.constants.DiceEmoji.BASKETBALL`"""
FOOTBALL: ClassVar[str] = constants.DiceEmoji.FOOTBALL
FOOTBALL: Final[str] = constants.DiceEmoji.FOOTBALL
""":const:`telegram.constants.DiceEmoji.FOOTBALL`"""
SLOT_MACHINE: ClassVar[str] = constants.DiceEmoji.SLOT_MACHINE
SLOT_MACHINE: Final[str] = constants.DiceEmoji.SLOT_MACHINE
""":const:`telegram.constants.DiceEmoji.SLOT_MACHINE`"""
BOWLING: ClassVar[str] = constants.DiceEmoji.BOWLING
BOWLING: Final[str] = constants.DiceEmoji.BOWLING
"""
:const:`telegram.constants.DiceEmoji.BOWLING`
.. versionadded:: 13.4
"""
ALL_EMOJI: ClassVar[List[str]] = list(constants.DiceEmoji)
ALL_EMOJI: Final[List[str]] = list(constants.DiceEmoji)
"""List[:obj:`str`]: A list of all available dice emoji."""
MIN_VALUE: ClassVar[int] = constants.DiceLimit.MIN_VALUE
MIN_VALUE: Final[int] = constants.DiceLimit.MIN_VALUE
""":const:`telegram.constants.DiceLimit.MIN_VALUE`
.. versionadded:: 20.0
"""
MAX_VALUE_BOWLING: ClassVar[int] = constants.DiceLimit.MAX_VALUE_BOWLING
MAX_VALUE_BOWLING: Final[int] = constants.DiceLimit.MAX_VALUE_BOWLING
""":const:`telegram.constants.DiceLimit.MAX_VALUE_BOWLING`
.. versionadded:: 20.0
"""
MAX_VALUE_DARTS: ClassVar[int] = constants.DiceLimit.MAX_VALUE_DARTS
MAX_VALUE_DARTS: Final[int] = constants.DiceLimit.MAX_VALUE_DARTS
""":const:`telegram.constants.DiceLimit.MAX_VALUE_DARTS`
.. versionadded:: 20.0
"""
MAX_VALUE_DICE: ClassVar[int] = constants.DiceLimit.MAX_VALUE_DICE
MAX_VALUE_DICE: Final[int] = constants.DiceLimit.MAX_VALUE_DICE
""":const:`telegram.constants.DiceLimit.MAX_VALUE_DICE`
.. versionadded:: 20.0
"""
MAX_VALUE_BASKETBALL: ClassVar[int] = constants.DiceLimit.MAX_VALUE_BASKETBALL
MAX_VALUE_BASKETBALL: Final[int] = constants.DiceLimit.MAX_VALUE_BASKETBALL
""":const:`telegram.constants.DiceLimit.MAX_VALUE_BASKETBALL`
.. versionadded:: 20.0
"""
MAX_VALUE_FOOTBALL: ClassVar[int] = constants.DiceLimit.MAX_VALUE_FOOTBALL
MAX_VALUE_FOOTBALL: Final[int] = constants.DiceLimit.MAX_VALUE_FOOTBALL
""":const:`telegram.constants.DiceLimit.MAX_VALUE_FOOTBALL`
.. versionadded:: 20.0
"""
MAX_VALUE_SLOT_MACHINE: ClassVar[int] = constants.DiceLimit.MAX_VALUE_SLOT_MACHINE
MAX_VALUE_SLOT_MACHINE: Final[int] = constants.DiceLimit.MAX_VALUE_SLOT_MACHINE
""":const:`telegram.constants.DiceLimit.MAX_VALUE_SLOT_MACHINE`
.. versionadded:: 20.0
+3 -3
View File
@@ -56,9 +56,9 @@ class _BaseMedium(TelegramObject):
self,
file_id: str,
file_unique_id: str,
file_size: int = None,
file_size: Optional[int] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
@@ -77,7 +77,7 @@ class _BaseMedium(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> "File":
"""Convenience wrapper over :meth:`telegram.Bot.get_file`
+5 -5
View File
@@ -74,11 +74,11 @@ class _BaseThumbedMedium(_BaseMedium):
self,
file_id: str,
file_unique_id: str,
file_size: int = None,
thumb: PhotoSize = None,
thumbnail: PhotoSize = None,
file_size: Optional[int] = None,
thumb: Optional[PhotoSize] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
@@ -93,7 +93,7 @@ class _BaseThumbedMedium(_BaseMedium):
deprecated_arg_name="thumb",
new_arg_name="thumbnail",
bot_api_version="6.6",
stacklevel=4,
stacklevel=3,
)
@property
+6 -6
View File
@@ -79,13 +79,13 @@ class Animation(_BaseThumbedMedium):
width: int,
height: int,
duration: int,
thumb: PhotoSize = None,
file_name: str = None,
mime_type: str = None,
file_size: int = None,
thumbnail: PhotoSize = None,
thumb: Optional[PhotoSize] = None,
file_name: Optional[str] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+8 -8
View File
@@ -80,15 +80,15 @@ class Audio(_BaseThumbedMedium):
file_id: str,
file_unique_id: str,
duration: int,
performer: str = None,
title: str = None,
mime_type: str = None,
file_size: int = None,
thumb: PhotoSize = None,
file_name: str = None,
thumbnail: PhotoSize = None,
performer: Optional[str] = None,
title: Optional[str] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
thumb: Optional[PhotoSize] = None,
file_name: Optional[str] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+6 -6
View File
@@ -17,7 +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/].
"""This module contains an object that represents a Telegram ChatPhoto."""
from typing import TYPE_CHECKING, ClassVar
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -87,7 +87,7 @@ class ChatPhoto(TelegramObject):
big_file_id: str,
big_file_unique_id: str,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.small_file_id: str = small_file_id
@@ -109,7 +109,7 @@ class ChatPhoto(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> "File":
"""Convenience wrapper over :meth:`telegram.Bot.get_file` for getting the small
(:tg-const:`telegram.ChatPhoto.SIZE_SMALL` x :tg-const:`telegram.ChatPhoto.SIZE_SMALL`)
@@ -140,7 +140,7 @@ class ChatPhoto(TelegramObject):
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
) -> "File":
"""Convenience wrapper over :meth:`telegram.Bot.get_file` for getting the
big (:tg-const:`telegram.ChatPhoto.SIZE_BIG` x :tg-const:`telegram.ChatPhoto.SIZE_BIG`)
@@ -164,12 +164,12 @@ class ChatPhoto(TelegramObject):
api_kwargs=api_kwargs,
)
SIZE_SMALL: ClassVar[int] = constants.ChatPhotoSize.SMALL
SIZE_SMALL: Final[int] = constants.ChatPhotoSize.SMALL
""":const:`telegram.constants.ChatPhotoSize.SMALL`
.. versionadded:: 20.0
"""
SIZE_BIG: ClassVar[int] = constants.ChatPhotoSize.BIG
SIZE_BIG: Final[int] = constants.ChatPhotoSize.BIG
""":const:`telegram.constants.ChatPhotoSize.BIG`
.. versionadded:: 20.0
+4 -4
View File
@@ -51,11 +51,11 @@ class Contact(TelegramObject):
self,
phone_number: str,
first_name: str,
last_name: str = None,
user_id: int = None,
vcard: str = None,
last_name: Optional[str] = None,
user_id: Optional[int] = None,
vcard: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
+6 -6
View File
@@ -67,13 +67,13 @@ class Document(_BaseThumbedMedium):
self,
file_id: str,
file_unique_id: str,
thumb: PhotoSize = None,
file_name: str = None,
mime_type: str = None,
file_size: int = None,
thumbnail: PhotoSize = None,
thumb: Optional[PhotoSize] = None,
file_name: Optional[str] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+5 -5
View File
@@ -85,10 +85,10 @@ class File(TelegramObject):
self,
file_id: str,
file_unique_id: str,
file_size: int = None,
file_path: str = None,
file_size: Optional[int] = None,
file_path: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
@@ -119,7 +119,7 @@ class File(TelegramObject):
async def download_to_drive(
self,
custom_path: FilePathInput = None,
custom_path: Optional[FilePathInput] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -270,7 +270,7 @@ class File(TelegramObject):
async def download_as_bytearray(
self,
buf: bytearray = None,
buf: Optional[bytearray] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
+4 -3
View File
@@ -18,7 +18,6 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InputFile."""
import logging
import mimetypes
from typing import IO, Optional, Union
from uuid import uuid4
@@ -27,7 +26,6 @@ from telegram._utils.files import load_file
from telegram._utils.types import FieldTuple
_DEFAULT_MIME_TYPE = "application/octet-stream"
logger = logging.getLogger(__name__)
class InputFile:
@@ -68,7 +66,10 @@ class InputFile:
__slots__ = ("filename", "attach_name", "input_file_content", "mimetype")
def __init__(
self, obj: Union[IO[bytes], bytes, str], filename: str = None, attach: bool = False
self,
obj: Union[IO[bytes], bytes, str],
filename: Optional[str] = None,
attach: bool = False,
):
if isinstance(obj, bytes):
self.input_file_content: bytes = obj
+45 -45
View File
@@ -91,11 +91,11 @@ class InputMedia(TelegramObject):
self,
media_type: str,
media: Union[str, InputFile, MediaType],
caption: str = None,
caption_entities: Sequence[MessageEntity] = None,
caption: Optional[str] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.type: str = media_type
@@ -196,18 +196,18 @@ class InputMediaAnimation(InputMedia):
def __init__(
self,
media: Union[FileInput, Animation],
thumb: FileInput = None,
caption: str = None,
thumb: Optional[FileInput] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
width: int = None,
height: int = None,
duration: int = None,
caption_entities: Sequence[MessageEntity] = None,
filename: str = None,
has_spoiler: bool = None,
thumbnail: FileInput = None,
width: Optional[int] = None,
height: Optional[int] = None,
duration: Optional[int] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
has_spoiler: Optional[bool] = None,
thumbnail: Optional[FileInput] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Animation):
width = media.width if width is None else width
@@ -304,13 +304,13 @@ class InputMediaPhoto(InputMedia):
def __init__(
self,
media: Union[FileInput, PhotoSize],
caption: str = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Sequence[MessageEntity] = None,
filename: str = None,
has_spoiler: bool = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
has_spoiler: Optional[bool] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
@@ -423,19 +423,19 @@ class InputMediaVideo(InputMedia):
def __init__(
self,
media: Union[FileInput, Video],
caption: str = None,
width: int = None,
height: int = None,
duration: int = None,
supports_streaming: bool = None,
caption: Optional[str] = None,
width: Optional[int] = None,
height: Optional[int] = None,
duration: Optional[int] = None,
supports_streaming: Optional[bool] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
thumb: FileInput = None,
caption_entities: Sequence[MessageEntity] = None,
filename: str = None,
has_spoiler: bool = None,
thumbnail: FileInput = None,
thumb: Optional[FileInput] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
has_spoiler: Optional[bool] = None,
thumbnail: Optional[FileInput] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Video):
width = width if width is not None else media.width
@@ -555,17 +555,17 @@ class InputMediaAudio(InputMedia):
def __init__(
self,
media: Union[FileInput, Audio],
thumb: FileInput = None,
caption: str = None,
thumb: Optional[FileInput] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
duration: int = None,
performer: str = None,
title: str = None,
caption_entities: Sequence[MessageEntity] = None,
filename: str = None,
thumbnail: FileInput = None,
duration: Optional[int] = None,
performer: Optional[str] = None,
title: Optional[str] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
thumbnail: Optional[FileInput] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
if isinstance(media, Audio):
duration = media.duration if duration is None else duration
@@ -675,15 +675,15 @@ class InputMediaDocument(InputMedia):
def __init__(
self,
media: Union[FileInput, Document],
thumb: FileInput = None,
caption: str = None,
thumb: Optional[FileInput] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_content_type_detection: bool = None,
caption_entities: Sequence[MessageEntity] = None,
filename: str = None,
thumbnail: FileInput = None,
disable_content_type_detection: Optional[bool] = None,
caption_entities: Optional[Sequence[MessageEntity]] = None,
filename: Optional[str] = None,
thumbnail: Optional[FileInput] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
# We use local_mode=True because we don't have access to the actual setting and want
# things to work in local mode.
+7 -5
View File
@@ -18,15 +18,17 @@
# 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 typing import TYPE_CHECKING, 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
if TYPE_CHECKING:
from telegram._files.inputfile import InputFile
class InputSticker(TelegramObject):
"""
@@ -74,10 +76,10 @@ class InputSticker(TelegramObject):
self,
sticker: FileInput,
emoji_list: Sequence[str],
mask_position: MaskPosition = None,
keywords: Sequence[str] = None,
mask_position: Optional[MaskPosition] = None,
keywords: Optional[Sequence[str]] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
+9 -9
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Location."""
from typing import ClassVar, Optional
from typing import Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -72,12 +72,12 @@ class Location(TelegramObject):
self,
longitude: float,
latitude: float,
horizontal_accuracy: float = None,
live_period: int = None,
heading: int = None,
proximity_alert_radius: int = None,
horizontal_accuracy: Optional[float] = None,
live_period: Optional[int] = None,
heading: Optional[int] = None,
proximity_alert_radius: Optional[int] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -96,17 +96,17 @@ class Location(TelegramObject):
self._freeze()
HORIZONTAL_ACCURACY: ClassVar[int] = constants.LocationLimit.HORIZONTAL_ACCURACY
HORIZONTAL_ACCURACY: Final[int] = constants.LocationLimit.HORIZONTAL_ACCURACY
""":const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY`
.. versionadded:: 20.0
"""
MIN_HEADING: ClassVar[int] = constants.LocationLimit.MIN_HEADING
MIN_HEADING: Final[int] = constants.LocationLimit.MIN_HEADING
""":const:`telegram.constants.LocationLimit.MIN_HEADING`
.. versionadded:: 20.0
"""
MAX_HEADING: ClassVar[int] = constants.LocationLimit.MAX_HEADING
MAX_HEADING: Final[int] = constants.LocationLimit.MAX_HEADING
""":const:`telegram.constants.LocationLimit.MAX_HEADING`
.. versionadded:: 20.0
+4 -2
View File
@@ -18,6 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram PhotoSize."""
from typing import Optional
from telegram._files._basemedium import _BaseMedium
from telegram._utils.types import JSONDict
@@ -59,9 +61,9 @@ class PhotoSize(_BaseMedium):
file_unique_id: str,
width: int,
height: int,
file_size: int = None,
file_size: Optional[int] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+22 -22
View File
@@ -17,7 +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/].
"""This module contains objects that represent stickers."""
from typing import TYPE_CHECKING, ClassVar, Optional, Sequence, Tuple
from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple
from telegram import constants
from telegram._files._basethumbedmedium import _BaseThumbedMedium
@@ -157,17 +157,17 @@ class Sticker(_BaseThumbedMedium):
is_animated: bool,
is_video: bool,
type: str, # pylint: disable=redefined-builtin
thumb: PhotoSize = None,
emoji: str = None,
file_size: int = None,
set_name: str = None,
mask_position: "MaskPosition" = None,
premium_animation: "File" = None,
custom_emoji_id: str = None,
thumbnail: PhotoSize = None,
needs_repainting: bool = None,
thumb: Optional[PhotoSize] = None,
emoji: Optional[str] = None,
file_size: Optional[int] = None,
set_name: Optional[str] = None,
mask_position: Optional["MaskPosition"] = None,
premium_animation: Optional["File"] = None,
custom_emoji_id: Optional[str] = None,
thumbnail: Optional[PhotoSize] = None,
needs_repainting: Optional[bool] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
@@ -192,11 +192,11 @@ class Sticker(_BaseThumbedMedium):
self.custom_emoji_id: Optional[str] = custom_emoji_id
self.needs_repainting: Optional[bool] = needs_repainting
REGULAR: ClassVar[str] = constants.StickerType.REGULAR
REGULAR: Final[str] = constants.StickerType.REGULAR
""":const:`telegram.constants.StickerType.REGULAR`"""
MASK: ClassVar[str] = constants.StickerType.MASK
MASK: Final[str] = constants.StickerType.MASK
""":const:`telegram.constants.StickerType.MASK`"""
CUSTOM_EMOJI: ClassVar[str] = constants.StickerType.CUSTOM_EMOJI
CUSTOM_EMOJI: Final[str] = constants.StickerType.CUSTOM_EMOJI
""":const:`telegram.constants.StickerType.CUSTOM_EMOJI`"""
@classmethod
@@ -302,10 +302,10 @@ class StickerSet(TelegramObject):
stickers: Sequence[Sticker],
is_video: bool,
sticker_type: str,
thumb: PhotoSize = None,
thumbnail: PhotoSize = None,
thumb: Optional[PhotoSize] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.name: str = name
@@ -390,13 +390,13 @@ class MaskPosition(TelegramObject):
__slots__ = ("point", "scale", "x_shift", "y_shift")
FOREHEAD: ClassVar[str] = constants.MaskPosition.FOREHEAD
FOREHEAD: Final[str] = constants.MaskPosition.FOREHEAD
""":const:`telegram.constants.MaskPosition.FOREHEAD`"""
EYES: ClassVar[str] = constants.MaskPosition.EYES
EYES: Final[str] = constants.MaskPosition.EYES
""":const:`telegram.constants.MaskPosition.EYES`"""
MOUTH: ClassVar[str] = constants.MaskPosition.MOUTH
MOUTH: Final[str] = constants.MaskPosition.MOUTH
""":const:`telegram.constants.MaskPosition.MOUTH`"""
CHIN: ClassVar[str] = constants.MaskPosition.CHIN
CHIN: Final[str] = constants.MaskPosition.CHIN
""":const:`telegram.constants.MaskPosition.CHIN`"""
def __init__(
@@ -406,7 +406,7 @@ class MaskPosition(TelegramObject):
y_shift: float,
scale: float,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.point: str = point
+5 -5
View File
@@ -79,12 +79,12 @@ class Venue(TelegramObject):
location: Location,
title: str,
address: str,
foursquare_id: str = None,
foursquare_type: str = None,
google_place_id: str = None,
google_place_type: str = None,
foursquare_id: Optional[str] = None,
foursquare_type: Optional[str] = None,
google_place_id: Optional[str] = None,
google_place_type: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
+6 -6
View File
@@ -76,13 +76,13 @@ class Video(_BaseThumbedMedium):
width: int,
height: int,
duration: int,
thumb: PhotoSize = None,
mime_type: str = None,
file_size: int = None,
file_name: str = None,
thumbnail: PhotoSize = None,
thumb: Optional[PhotoSize] = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
file_name: Optional[str] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+6 -4
View File
@@ -18,6 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram VideoNote."""
from typing import Optional
from telegram._files._basethumbedmedium import _BaseThumbedMedium
from telegram._files.photosize import PhotoSize
from telegram._utils.types import JSONDict
@@ -71,11 +73,11 @@ class VideoNote(_BaseThumbedMedium):
file_unique_id: str,
length: int,
duration: int,
thumb: PhotoSize = None,
file_size: int = None,
thumbnail: PhotoSize = None,
thumb: Optional[PhotoSize] = None,
file_size: Optional[int] = None,
thumbnail: Optional[PhotoSize] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+3 -3
View File
@@ -58,10 +58,10 @@ class Voice(_BaseMedium):
file_id: str,
file_unique_id: str,
duration: int,
mime_type: str = None,
file_size: int = None,
mime_type: Optional[str] = None,
file_size: Optional[int] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(
file_id=file_id,
+6 -6
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ForceReply."""
from typing import ClassVar, Optional
from typing import Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -79,10 +79,10 @@ class ForceReply(TelegramObject):
def __init__(
self,
selective: bool = None,
input_field_placeholder: str = None,
selective: Optional[bool] = None,
input_field_placeholder: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.force_reply: bool = True
@@ -93,12 +93,12 @@ class ForceReply(TelegramObject):
self._freeze()
MIN_INPUT_FIELD_PLACEHOLDER: ClassVar[int] = constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER
MIN_INPUT_FIELD_PLACEHOLDER: Final[int] = constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER
""":const:`telegram.constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER`
.. versionadded:: 20.0
"""
MAX_INPUT_FIELD_PLACEHOLDER: ClassVar[int] = constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER
MAX_INPUT_FIELD_PLACEHOLDER: Final[int] = constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER
""":const:`telegram.constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER`
.. versionadded:: 20.0
+11 -11
View File
@@ -55,9 +55,9 @@ class ForumTopic(TelegramObject):
message_thread_id: int,
name: str,
icon_color: int,
icon_custom_emoji_id: str = None,
icon_custom_emoji_id: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.message_thread_id: int = message_thread_id
@@ -99,9 +99,9 @@ class ForumTopicCreated(TelegramObject):
self,
name: str,
icon_color: int,
icon_custom_emoji_id: str = None,
icon_custom_emoji_id: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.name: str = name
@@ -123,7 +123,7 @@ class ForumTopicClosed(TelegramObject):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()
@@ -139,7 +139,7 @@ class ForumTopicReopened(TelegramObject):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()
@@ -169,10 +169,10 @@ class ForumTopicEdited(TelegramObject):
def __init__(
self,
name: str = None,
icon_custom_emoji_id: str = None,
name: Optional[str] = None,
icon_custom_emoji_id: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.name: Optional[str] = name
@@ -193,7 +193,7 @@ class GeneralForumTopicHidden(TelegramObject):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None):
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self._freeze()
@@ -209,7 +209,7 @@ class GeneralForumTopicUnhidden(TelegramObject):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None):
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self._freeze()
+3 -1
View File
@@ -18,6 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram CallbackGame."""
from typing import Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
@@ -27,7 +29,7 @@ class CallbackGame(TelegramObject):
__slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()
+5 -9
View File
@@ -17,7 +17,6 @@
# 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 Game."""
import sys
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple
from telegram._files.animation import Animation
@@ -102,11 +101,11 @@ class Game(TelegramObject):
title: str,
description: str,
photo: Sequence[PhotoSize],
text: str = None,
text_entities: Sequence[MessageEntity] = None,
animation: Animation = None,
text: Optional[str] = None,
text_entities: Optional[Sequence[MessageEntity]] = None,
animation: Optional[Animation] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -158,15 +157,12 @@ class Game(TelegramObject):
if not self.text:
raise RuntimeError("This Game has no 'text'.")
# Is it a narrow build, if so we don't need to convert
if sys.maxunicode == 0xFFFF:
return self.text[entity.offset : entity.offset + entity.length]
entity_text = self.text.encode("utf-16-le")
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
return entity_text.decode("utf-16-le")
def parse_text_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]:
def parse_text_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]:
"""
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
It contains entities from this message filtered by their
+3 -1
View File
@@ -48,7 +48,9 @@ class GameHighScore(TelegramObject):
__slots__ = ("position", "user", "score")
def __init__(self, position: int, user: User, score: int, *, api_kwargs: JSONDict = None):
def __init__(
self, position: int, user: User, score: int, *, api_kwargs: Optional[JSONDict] = None
):
super().__init__(api_kwargs=api_kwargs)
self.position: int = position
self.user: User = user
+56 -12
View File
@@ -18,11 +18,12 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InlineKeyboardButton."""
from typing import TYPE_CHECKING, ClassVar, Optional, Union
from typing import TYPE_CHECKING, Final, Optional, Union
from telegram import constants
from telegram._games.callbackgame import CallbackGame
from telegram._loginurl import LoginUrl
from telegram._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
from telegram._webappinfo import WebAppInfo
@@ -111,6 +112,10 @@ class InlineKeyboardButton(TelegramObject):
in inline mode when they are currently in a private chat with it. Especially useful
when combined with ``switch_pm*`` actions - in this case the user will be automatically
returned to the chat they switched from, skipping the chat selection screen.
Tip:
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`,
but gives no control over which chats can be selected.
switch_inline_query_current_chat (:obj:`str`, optional): If set, pressing the button will
insert the bot's username and the specified inline query in the current chat's input
field. Can be empty, in which case only the bot's username will be inserted. This
@@ -122,6 +127,20 @@ class InlineKeyboardButton(TelegramObject):
pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. This type of button
**must** always be the **first** button in the first row and can only be used in
invoice messages.
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`, optional):
If set, pressing the button will prompt the user to select one of their chats of the
specified type, open that chat and insert the bot's username and the specified inline
query in the input field.
.. versionadded:: 20.3
Tip:
This is similar to :paramref:`switch_inline_query`, but gives more control on
which chats can be selected.
Caution:
The PTB team has discovered that this field works correctly only if your Telegram
client is released after April 20th 2023.
Attributes:
text (:obj:`str`): Label text on the button.
@@ -154,6 +173,10 @@ class InlineKeyboardButton(TelegramObject):
in inline mode when they are currently in a private chat with it. Especially useful
when combined with ``switch_pm*`` actions - in this case the user will be automatically
returned to the chat they switched from, skipping the chat selection screen.
Tip:
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`,
but gives no control over which chats can be selected.
switch_inline_query_current_chat (:obj:`str`): Optional. If set, pressing the button will
insert the bot's username and the specified inline query in the current chat's input
field. Can be empty, in which case only the bot's username will be inserted. This
@@ -165,7 +188,20 @@ class InlineKeyboardButton(TelegramObject):
pay (:obj:`bool`): Optional. Specify :obj:`True`, to send a Pay button. This type of button
**must** always be the **first** button in the first row and can only be used in
invoice messages.
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`): Optional.
If set, pressing the button will prompt the user to select one of their chats of the
specified type, open that chat and insert the bot's username and the specified inline
query in the input field.
.. versionadded:: 20.3
Tip:
This is similar to :attr:`switch_inline_query`, but gives more control on
which chats can be selected.
Caution:
The PTB team has discovered that this field works correctly only if your Telegram
client is released after April 20th 2023.
"""
__slots__ = (
@@ -178,21 +214,23 @@ class InlineKeyboardButton(TelegramObject):
"text",
"login_url",
"web_app",
"switch_inline_query_chosen_chat",
)
def __init__(
self,
text: str,
url: str = None,
callback_data: Union[str, object] = None,
switch_inline_query: str = None,
switch_inline_query_current_chat: str = None,
callback_game: CallbackGame = None,
pay: bool = None,
login_url: LoginUrl = None,
web_app: WebAppInfo = None,
url: Optional[str] = None,
callback_data: Optional[Union[str, object]] = None,
switch_inline_query: Optional[str] = None,
switch_inline_query_current_chat: Optional[str] = None,
callback_game: Optional[CallbackGame] = None,
pay: Optional[bool] = None,
login_url: Optional[LoginUrl] = None,
web_app: Optional[WebAppInfo] = None,
switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -207,6 +245,9 @@ class InlineKeyboardButton(TelegramObject):
self.callback_game: Optional[CallbackGame] = callback_game
self.pay: Optional[bool] = pay
self.web_app: Optional[WebAppInfo] = web_app
self.switch_inline_query_chosen_chat: Optional[
SwitchInlineQueryChosenChat
] = switch_inline_query_chosen_chat
self._id_attrs = ()
self._set_id_attrs()
@@ -236,6 +277,9 @@ class InlineKeyboardButton(TelegramObject):
data["login_url"] = LoginUrl.de_json(data.get("login_url"), bot)
data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot)
data["callback_game"] = CallbackGame.de_json(data.get("callback_game"), bot)
data["switch_inline_query_chosen_chat"] = SwitchInlineQueryChosenChat.de_json(
data.get("switch_inline_query_chosen_chat"), bot
)
return super().de_json(data=data, bot=bot)
@@ -253,12 +297,12 @@ class InlineKeyboardButton(TelegramObject):
self.callback_data = callback_data
self._set_id_attrs()
MIN_CALLBACK_DATA: ClassVar[int] = constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA
MIN_CALLBACK_DATA: Final[int] = constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA
""":const:`telegram.constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA`
.. versionadded:: 20.0
"""
MAX_CALLBACK_DATA: ClassVar[int] = constants.InlineKeyboardButtonLimit.MAX_CALLBACK_DATA
MAX_CALLBACK_DATA: Final[int] = constants.InlineKeyboardButtonLimit.MAX_CALLBACK_DATA
""":const:`telegram.constants.InlineKeyboardButtonLimit.MAX_CALLBACK_DATA`
.. versionadded:: 20.0
+1 -1
View File
@@ -72,7 +72,7 @@ class InlineKeyboardMarkup(TelegramObject):
self,
inline_keyboard: Sequence[Sequence[InlineKeyboardButton]],
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
if not check_keyboard_type(inline_keyboard):
+19 -16
View File
@@ -19,10 +19,11 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InlineQuery."""
from typing import TYPE_CHECKING, Callable, ClassVar, Optional, Sequence, Union
from typing import TYPE_CHECKING, Callable, Final, Optional, Sequence, Union
from telegram import constants
from telegram._files.location import Location
from telegram._inline.inlinequeryresultsbutton import InlineQueryResultsButton
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.defaultvalue import DEFAULT_NONE
@@ -103,10 +104,10 @@ class InlineQuery(TelegramObject):
from_user: User,
query: str,
offset: str,
location: Location = None,
chat_type: str = None,
location: Optional[Location] = None,
chat_type: Optional[str] = None,
*,
api_kwargs: JSONDict = None,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
@@ -141,19 +142,20 @@ class InlineQuery(TelegramObject):
results: Union[
Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]]
],
cache_time: int = None,
is_personal: bool = None,
next_offset: str = None,
switch_pm_text: str = None,
switch_pm_parameter: str = None,
cache_time: Optional[int] = None,
is_personal: Optional[bool] = None,
next_offset: Optional[str] = None,
switch_pm_text: Optional[str] = None,
switch_pm_parameter: Optional[str] = None,
button: Optional[InlineQueryResultsButton] = None,
*,
current_offset: str = None,
current_offset: Optional[str] = None,
auto_pagination: bool = False,
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,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
@@ -192,6 +194,7 @@ class InlineQuery(TelegramObject):
next_offset=next_offset,
switch_pm_text=switch_pm_text,
switch_pm_parameter=switch_pm_parameter,
button=button,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
@@ -199,27 +202,27 @@ class InlineQuery(TelegramObject):
api_kwargs=api_kwargs,
)
MAX_RESULTS: ClassVar[int] = constants.InlineQueryLimit.RESULTS
MAX_RESULTS: Final[int] = constants.InlineQueryLimit.RESULTS
""":const:`telegram.constants.InlineQueryLimit.RESULTS`
.. versionadded:: 13.2
"""
MIN_SWITCH_PM_TEXT_LENGTH: ClassVar[int] = constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH
MIN_SWITCH_PM_TEXT_LENGTH: Final[int] = constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH
""":const:`telegram.constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH`
.. versionadded:: 20.0
"""
MAX_SWITCH_PM_TEXT_LENGTH: ClassVar[int] = constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH
MAX_SWITCH_PM_TEXT_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH
""":const:`telegram.constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH`
.. versionadded:: 20.0
"""
MAX_OFFSET_LENGTH: ClassVar[int] = constants.InlineQueryLimit.MAX_OFFSET_LENGTH
MAX_OFFSET_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_OFFSET_LENGTH
""":const:`telegram.constants.InlineQueryLimit.MAX_OFFSET_LENGTH`
.. versionadded:: 20.0
"""
MAX_QUERY_LENGTH: ClassVar[int] = constants.InlineQueryLimit.MAX_QUERY_LENGTH
MAX_QUERY_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_QUERY_LENGTH
""":const:`telegram.constants.InlineQueryLimit.MAX_QUERY_LENGTH`
.. versionadded:: 20.0

Some files were not shown because too many files have changed in this diff Show More