mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-20 08:05:27 +00:00
Compare commits
43 Commits
feature/fsm
..
v22.1
| Author | SHA1 | Date | |
|---|---|---|---|
| fc3863ac9a | |||
| c57e9fa5d6 | |||
| 7078059e80 | |||
| c34e19edfd | |||
| 2fc04e1e10 | |||
| 08006013c3 | |||
| 4c61403322 | |||
| b0faae9d47 | |||
| 4868565b71 | |||
| 486ceaa6cf | |||
| 54ce1d8d82 | |||
| 17ae6a7028 | |||
| c6e12b1958 | |||
| ed9496b91a | |||
| 7823822a41 | |||
| a9e53af3d1 | |||
| e69069d2c8 | |||
| 511222c191 | |||
| 036910ec0c | |||
| 3cd8a409ee | |||
| 83676dec16 | |||
| 9ebd48903b | |||
| 1cf000c806 | |||
| 150328799a | |||
| 6d7134608f | |||
| 8266870ed7 | |||
| 519dee7e0c | |||
| 753f3727aa | |||
| 892a66d0e8 | |||
| d5e5874f96 | |||
| a5dacab8a9 | |||
| 76d8eaf0f2 | |||
| 1e4f31f1bb | |||
| 3464f24129 | |||
| 9323caf2b8 | |||
| 77c25931a9 | |||
| b75948ede4 | |||
| b0d22acedb | |||
| 35a48f82f4 | |||
| 5d73132838 | |||
| 7c23087d08 | |||
| 2d5f4a68bb | |||
| f9f1533c40 |
+31
-29
@@ -157,45 +157,47 @@ 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``, ``.. deprecated:: NEXT.VERSION`` or ``.. versionremoved:: 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
|
||||
.. code-block:: markdown
|
||||
|
||||
**If the PR contains API changes (otherwise, you can ignore this passage)**
|
||||
## Check-list for PRs
|
||||
|
||||
- Checked the Bot API specific sections of the `Stability Policy <https://docs.python-telegram-bot.org/stability_policy.html>`_
|
||||
- Created a PR to remove functionality deprecated in the previous Bot API release (`see here <https://docs.python-telegram-bot.org/en/stable/stability_policy.html#case-2>`_)
|
||||
- [ ] Added `.. versionadded:: NEXT.VERSION`, ``.. versionchanged:: NEXT.VERSION``, ``.. deprecated:: NEXT.VERSION`` or ``.. versionremoved:: 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
|
||||
|
||||
- New classes:
|
||||
**If the PR contains API changes (otherwise, you can ignore this passage)**
|
||||
|
||||
- Added ``self._id_attrs`` and corresponding documentation
|
||||
- ``__init__`` accepts ``api_kwargs`` as kw-only
|
||||
- [ ] Checked the Bot API specific sections of the [Stability Policy](https://docs.python-telegram-bot.org/stability_policy.html)
|
||||
- [ ] Created a PR to remove functionality deprecated in the previous Bot API release ([see here](https://docs.python-telegram-bot.org/en/stable/stability_policy.html#case-2))
|
||||
|
||||
- Added new shortcuts:
|
||||
- New Classes
|
||||
|
||||
- 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``
|
||||
- [ ] Added `self._id_attrs` and corresponding documentation
|
||||
- [ ] `__init__` accepts `api_kwargs` as keyword-only
|
||||
|
||||
- If relevant:
|
||||
- Added New Shortcuts
|
||||
|
||||
- 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
|
||||
- [ ] In [`telegram.Chat`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.chat.html) \& [`telegram.User`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.user.html) for all methods that accept `chat/user_id`
|
||||
- [ ] In [`telegram.Message`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html) for all methods that accept `chat_id` and `message_id`
|
||||
- [ ] For new `telegram.Message` shortcuts: Added `quote` argument if methods accept `reply_to_message_id`
|
||||
- [ ] In [`telegram.CallbackQuery`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.callbackquery.html) for all methods that accept either `chat_id` and `message_id` or `inline_message_id`
|
||||
|
||||
- Add the handlers to the warning loop in the :class:`~telegram.ext.ConversationHandler`
|
||||
- If Relevant
|
||||
|
||||
- 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`` (including the badge) and ``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`
|
||||
- [ ] Added new constants at `telegram.constants` and shortcuts to them as class variables
|
||||
- [ ] Linked new and existing constants in docstrings instead of hard-coded numbers and strings
|
||||
- [ ] Added new message types to `telegram.Message.effective_attachment`
|
||||
- [ ] Added new handlers for new update types
|
||||
- [ ] Added the handlers to the warning loop in the [`telegram.ext.ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.conversationhandler.html)
|
||||
- [ ] 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` (including the badge) and `telegram.constants.BOT_API_VERSION_INFO`
|
||||
- [ ] Added logic for arbitrary callback data in `telegram.ext.ExtBot` for new methods that either accept a `reply_markup` in some form or have a return type that is/contains [`telegram.Message`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html)
|
||||
|
||||
Documenting
|
||||
===========
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<b>We've just released {tag}</b>.
|
||||
Thank you to everyone who contributed to this release.
|
||||
As usual, upgrade using <code>pip install -U python-telegram-bot</code>.
|
||||
|
||||
The release notes can be found <a href="https://docs.python-telegram-bot.org/en/{tag}/changelog.html">here</a>.
|
||||
@@ -0,0 +1,66 @@
|
||||
name: Chango
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
create-chango-fragment:
|
||||
permissions:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||
# added or changed files to the repository.
|
||||
contents: write
|
||||
name: Create chango Fragment
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
IS_RELEASE_PR: ${{ steps.check_title.outputs.IS_RELEASE_PR }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
# needed for commit and push step at the end
|
||||
persist-credentials: true
|
||||
- name: Check PR Title
|
||||
id: check_title
|
||||
run: | # zizmor: ignore[template-injection]
|
||||
if [[ "$(echo "${{ github.event.pull_request.title }}" | tr '[:upper:]' '[:lower:]')" =~ ^bump\ version\ to\ .* ]]; then
|
||||
echo "COMMIT_AND_PUSH=false" >> $GITHUB_OUTPUT
|
||||
echo "IS_RELEASE_PR=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "COMMIT_AND_PUSH=true" >> $GITHUB_OUTPUT
|
||||
echo "IS_RELEASE_PR=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Create the new fragment
|
||||
- uses: Bibo-Joshi/chango@9d6bd9d7612eca5fab2c5161687011be59baaf19 # v0.4.0
|
||||
with:
|
||||
github-token: ${{ secrets.CHANGO_PAT }}
|
||||
query-issue-types: true
|
||||
commit-and-push: ${{ steps.check_title.outputs.COMMIT_AND_PUSH }}
|
||||
|
||||
# Run `chango release` if applicable - needs some additional setup.
|
||||
- name: Set up Python
|
||||
if: steps.check_title.outputs.IS_RELEASE_PR == 'true'
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Do Release
|
||||
if: steps.check_title.outputs.IS_RELEASE_PR == 'true'
|
||||
run: |
|
||||
cd ./target-repo
|
||||
git add changes/unreleased/*
|
||||
pip install . -r docs/requirements-docs.txt
|
||||
VERSION_TAG=$(python -c "from telegram import __version__; print(f'{__version__}')")
|
||||
chango release --uid $VERSION_TAG
|
||||
|
||||
- name: Commit & Push
|
||||
if: steps.check_title.outputs.IS_RELEASE_PR == 'true'
|
||||
uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5.2.0
|
||||
with:
|
||||
commit_message: "Do chango Release"
|
||||
repository: ./target-repo
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Test Admonitions Generation
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- telegram/**
|
||||
- docs/**
|
||||
- .github/workflows/docs-admonitions.yml
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-admonitions:
|
||||
name: Test Admonitions Generation
|
||||
runs-on: ${{matrix.os}}
|
||||
permissions:
|
||||
# for uploading artifacts
|
||||
actions: write
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.12']
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: '**/requirements*.txt'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements-dev-all.txt
|
||||
- name: Test autogeneration of admonitions
|
||||
run: pytest -v --tb=short tests/docs/admonition_inserter.py
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Check Links in Documentation
|
||||
on:
|
||||
schedule:
|
||||
# First day of month at 05:46 in every 2nd month
|
||||
- cron: '46 5 1 */2 *'
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/docs-linkcheck.yml
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-sphinx-build:
|
||||
name: test-sphinx-linkcheck
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.12']
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements-dev-all.txt
|
||||
- name: Check Links
|
||||
run: sphinx-build docs/source docs/build/html --keep-going -j auto -b linkcheck
|
||||
- name: Upload linkcheck output
|
||||
# Run also if the previous steps failed
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: linkcheck-output
|
||||
path: docs/build/html/output.*
|
||||
@@ -21,13 +21,13 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
|
||||
- name: Run zizmor
|
||||
run: uvx zizmor --persona=pedantic --format sarif . > results.sarif
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
pull-requests: write # for srvaroa/labeler to add labels in PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: srvaroa/labeler@fe4b1c73bb8abf2f14a44a6912a8b4fee835d631 # v1.12.0
|
||||
- uses: srvaroa/labeler@0a20eccb8c94a1ee0bed5f16859aece1c45c3e55 # v1.13.0
|
||||
# Config file at .github/labeler.yml
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install pypa/build
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: python3 -m build
|
||||
- name: Store the distribution packages
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
@@ -55,12 +55,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
|
||||
compute-signatures:
|
||||
name: Compute SHA1 Sums and Sign with Sigstore
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
./dist/*.tar.gz
|
||||
./dist/*.whl
|
||||
- name: Store the distribution packages and signatures
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
@@ -110,8 +110,11 @@ jobs:
|
||||
actions: read # for downloading artifacts
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
@@ -119,13 +122,14 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ needs.build.outputs.TAG }}
|
||||
# Create a tag and a GitHub Release. The description can be changed later, as for now
|
||||
# we don't define it through this workflow.
|
||||
# Create a tag and a GitHub Release. The description is filled by the static template, we
|
||||
# just insert the correct tag in the template.
|
||||
run: >-
|
||||
sed "s/{tag}/$TAG/g" .github/workflows/assets/release_template.html |
|
||||
gh release create
|
||||
"$TAG"
|
||||
--repo '${{ github.repository }}'
|
||||
--generate-notes
|
||||
--notes-file -
|
||||
- name: Upload artifact signatures to GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
@@ -137,3 +141,30 @@ jobs:
|
||||
gh release upload
|
||||
"$TAG" dist/**
|
||||
--repo '${{ github.repository }}'
|
||||
|
||||
telegram-channel:
|
||||
name: Publish to Telegram Channel
|
||||
needs:
|
||||
- github-release
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release_pypi
|
||||
permissions: {}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Publish to Telegram Channel
|
||||
env:
|
||||
TAG: ${{ needs.build.outputs.TAG }}
|
||||
# This secret is configured only for the `pypi-release` branch
|
||||
BOT_TOKEN: ${{ secrets.CHANNEL_BOT_TOKEN }}
|
||||
run: >-
|
||||
sed "s/{tag}/$TAG/g" .github/workflows/assets/release_template.html |
|
||||
curl
|
||||
-X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage"
|
||||
-d "chat_id=@pythontelegrambotchannel"
|
||||
-d "parse_mode=HTML"
|
||||
--data-urlencode "text@-"
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install pypa/build
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: python3 -m build
|
||||
- name: Store the distribution packages
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
@@ -55,12 +55,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
- name: Publish to Test PyPI
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
./dist/*.tar.gz
|
||||
./dist/*.whl
|
||||
- name: Store the distribution packages and signatures
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
@@ -112,8 +112,11 @@ jobs:
|
||||
actions: read # for downloading artifacts
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
@@ -121,14 +124,15 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ needs.build.outputs.TAG }}
|
||||
# Create a GitHub Release *draft*. The description can be changed later, as for now
|
||||
# we don't define it through this workflow.
|
||||
# Create a tag and a GitHub Release *draft*. The description is filled by the static
|
||||
# template, we just insert the correct tag in the template.
|
||||
run: >-
|
||||
sed "s/{tag}/$TAG/g" .github/workflows/assets/release_template.html |
|
||||
gh release create
|
||||
"$TAG"
|
||||
--repo '${{ github.repository }}'
|
||||
--generate-notes
|
||||
--draft
|
||||
--notes-file -
|
||||
- name: Upload artifact signatures to GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
name: Bot API Tests
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- telegram/**
|
||||
- tests/**
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
# Run monday and friday morning at 03:07 - odd time to spread load on GitHub Actions
|
||||
- cron: '7 3 * * 1,5'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-conformity:
|
||||
name: check-conformity
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install .[all]
|
||||
python -W ignore -m pip install -r requirements-unit-tests.txt
|
||||
- name: Compare to official api
|
||||
run: |
|
||||
pytest -v tests/test_official/test_official.py --junit-xml=.test_report_official.xml
|
||||
exit $?
|
||||
env:
|
||||
TEST_OFFICIAL: "true"
|
||||
shell: bash --noprofile --norc {0}
|
||||
|
||||
- name: Test Summary
|
||||
id: test_summary
|
||||
uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4
|
||||
if: always() # always run, even if tests fail
|
||||
with:
|
||||
paths: .test_report_official.xml
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Check Type Completeness
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- telegram/**
|
||||
- pyproject.toml
|
||||
- .github/workflows/type_completeness.yml
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-type-completeness:
|
||||
name: test-type-completeness
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Bibo-Joshi/pyright-type-completeness@c85a67ff3c66f51dcbb2d06bfcf4fe83a57d69cc # v1.0.1
|
||||
with:
|
||||
package-name: telegram
|
||||
python-version: 3.12
|
||||
pyright-version: ~=1.1.367
|
||||
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 'v0.8.6'
|
||||
rev: 'v0.11.9'
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: ruff
|
||||
@@ -18,18 +18,18 @@ repos:
|
||||
- cachetools>=5.3.3,<5.5.0
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.10.0
|
||||
rev: 25.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
- --diff
|
||||
- --check
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 7.1.1
|
||||
rev: 7.2.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: v3.3.3
|
||||
rev: v3.3.6
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(?!(tests|docs)).*\.py$
|
||||
@@ -41,7 +41,7 @@ repos:
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.14.1
|
||||
rev: v1.15.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: mypy-ptb
|
||||
@@ -74,7 +74,7 @@ repos:
|
||||
args:
|
||||
- --py39-plus
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
rev: 6.0.1
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
- requirements: docs/requirements-docs.txt
|
||||
- requirements: requirements-dev-all.txt
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
|
||||
+4
-4
@@ -7,10 +7,8 @@ The current development team includes
|
||||
|
||||
- `Hinrich Mahler <https://github.com/Bibo-Joshi>`_ (maintainer)
|
||||
- `Poolitzer <https://github.com/Poolitzer>`_ (community liaison)
|
||||
- `Shivam <https://github.com/Starry69>`_
|
||||
- `Harshil <https://github.com/harshil21>`_
|
||||
- `Dmitry Kolomatskiy <https://github.com/lemontree210>`_
|
||||
- `Aditya <https://github.com/clot27>`_
|
||||
- `Abdelrahman <https://github.com/aelkheir>`_
|
||||
|
||||
Emeritus maintainers include
|
||||
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram),
|
||||
@@ -21,7 +19,7 @@ Contributors
|
||||
|
||||
The following wonderful people contributed directly or indirectly to this project:
|
||||
|
||||
- `Abdelrahman <https://github.com/aelkheir>`_
|
||||
- `Aditya <https://github.com/clot27>`_
|
||||
- `Abshar <https://github.com/abxhr>`_
|
||||
- `Abubakar Alaya <https://github.com/Ecode2>`_
|
||||
- `Alateas <https://github.com/alateas>`_
|
||||
@@ -42,6 +40,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `daimajia <https://github.com/daimajia>`_
|
||||
- `Daniel Reed <https://github.com/nmlorg>`_
|
||||
- `D David Livingston <https://github.com/daviddl9>`_
|
||||
- `Dmitry Kolomatskiy <https://github.com/lemontree210>`_
|
||||
- `DonalDuck004 <https://github.com/DonalDuck004>`_
|
||||
- `Eana Hufwe <https://github.com/blueset>`_
|
||||
- `Ehsan Online <https://github.com/ehsanonline>`_
|
||||
@@ -122,6 +121,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Sam Mosleh <https://github.com/sam-mosleh>`_
|
||||
- `Sascha <https://github.com/saschalalala>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `Shivam <https://github.com/Starry69>`_
|
||||
- `Shivam Saini <https://github.com/shivamsn97>`_
|
||||
- `Siloé Garcez <https://github.com/roast-lord>`_
|
||||
- `Simon Schürrle <https://github.com/SitiSchu>`_
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-8.2-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-9.0-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API version
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
:target: https://pypistats.org/packages/python-telegram-bot
|
||||
:alt: PyPi Package Monthly Download
|
||||
|
||||
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=stable
|
||||
.. image:: https://app.readthedocs.org/projects/python-telegram-bot/badge/?version=stable
|
||||
:target: https://docs.python-telegram-bot.org/en/stable/
|
||||
:alt: Documentation Status
|
||||
|
||||
@@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
|
||||
Telegram API support
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All types and methods of the Telegram Bot API **8.2** are natively supported by this library.
|
||||
All types and methods of the Telegram Bot API **9.0** are natively supported by this library.
|
||||
In addition, Bot API functionality not yet natively included can still be used as described `in our wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_.
|
||||
|
||||
Notable Features
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
breaking = """This release removes all functionality that was deprecated in v20.x. This is in line with our :ref:`stability policy <stability-policy>`.
|
||||
This includes the following changes:
|
||||
|
||||
- Removed ``filters.CHAT`` (all messages have an associated chat) and ``filters.StatusUpdate.USER_SHARED`` (use ``filters.StatusUpdate.USERS_SHARED`` instead).
|
||||
- Removed ``Defaults.disable_web_page_preview`` and ``Defaults.quote``. Use ``Defaults.link_preview_options`` and ``Defaults.do_quote`` instead.
|
||||
- Removed ``ApplicationBuilder.(get_updates_)proxy_url`` and ``HTTPXRequest.proxy_url``. Use ``ApplicationBuilder.(get_updates_)proxy`` and ``HTTPXRequest.proxy`` instead.
|
||||
- Removed the ``*_timeout`` arguments of ``Application.run_polling`` and ``Updater.start_webhook``. Instead, specify the values via ``ApplicationBuilder.get_updates_*_timeout``.
|
||||
- Removed ``constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH``. Use ``constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`` instead.
|
||||
- Removed the argument ``quote`` of ``Message.reply_*``. Use ``do_quote`` instead.
|
||||
- Removed the superfluous ``EncryptedPassportElement.credentials`` without replacement.
|
||||
- Changed attribute value of ``PassportFile.file_date`` from :obj:`int` to :class:`datetime.datetime`. Make sure to adjust your code accordingly.
|
||||
- Changed the attribute value of ``PassportElementErrors.file_hashes`` from :obj:`list` to :obj:`tuple`. Make sure to adjust your code accordingly.
|
||||
- Make ``BaseRequest.read_timeout`` an abstract property. If you subclass ``BaseRequest``, you need to implement this property.
|
||||
- The default value for ``write_timeout`` now defaults to ``DEFAULT_NONE`` also for bot methods that send media. Previously, it was ``20``. If you subclass ``BaseRequest``, make sure to use your desired write timeout if ``RequestData.multipart_data`` is set.
|
||||
"""
|
||||
[[pull_requests]]
|
||||
uid = "4671"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = ["4659"]
|
||||
@@ -0,0 +1,5 @@
|
||||
documentation = "Add `chango <https://chango.readthedocs.io/stable/>`_ As Changelog Management Tool"
|
||||
[[pull_requests]]
|
||||
uid = "4672"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = ["4321"]
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump github/codeql-action from 3.28.8 to 3.28.10"
|
||||
[[pull_requests]]
|
||||
uid = "4697"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump srvaroa/labeler from 1.12.0 to 1.13.0"
|
||||
[[pull_requests]]
|
||||
uid = "4698"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump astral-sh/setup-uv from 5.2.2 to 5.3.1"
|
||||
[[pull_requests]]
|
||||
uid = "4699"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump Bibo-Joshi/chango from 0.3.1 to 0.3.2"
|
||||
[[pull_requests]]
|
||||
uid = "4700"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4"
|
||||
[[pull_requests]]
|
||||
uid = "4701"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump pytest from 8.3.4 to 8.3.5"
|
||||
[[pull_requests]]
|
||||
uid = "4709"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump sphinx from 8.1.3 to 8.2.3"
|
||||
[[pull_requests]]
|
||||
uid = "4710"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump Bibo-Joshi/chango from 0.3.2 to 0.4.0"
|
||||
[[pull_requests]]
|
||||
uid = "4712"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump Version to v22.0"
|
||||
[[pull_requests]]
|
||||
uid = "4719"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
breaking = "Drop backward compatibility for ``user_id`` in ``send_gift`` by updating the order of parameters. Please adapt your code accordingly or use keyword arguments."
|
||||
[[pull_requests]]
|
||||
uid = "4692"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,9 @@
|
||||
documentation = "Documentation Improvements. Among others, add missing ``Returns`` field in ``User.get_profile_photos``"
|
||||
[[pull_requests]]
|
||||
uid = "4730"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4740"
|
||||
author_uid = "aelkheir"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
bugfixes = "Ensure execution of ``Bot.shutdown()`` even if ``Bot.get_me()`` fails in ``Bot.initialize()``"
|
||||
[[pull_requests]]
|
||||
uid = "4733"
|
||||
author_uid = "Poolitzer"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump codecov/test-results-action from 1.0.2 to 1.1.0"
|
||||
[[pull_requests]]
|
||||
uid = "4741"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump actions/setup-python from 5.4.0 to 5.5.0"
|
||||
[[pull_requests]]
|
||||
uid = "4742"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump github/codeql-action from 3.28.10 to 3.28.13"
|
||||
[[pull_requests]]
|
||||
uid = "4743"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump astral-sh/setup-uv from 5.3.1 to 5.4.1"
|
||||
[[pull_requests]]
|
||||
uid = "4744"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump actions/download-artifact from 4.1.8 to 4.2.1"
|
||||
[[pull_requests]]
|
||||
uid = "4745"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Reenable ``test_official`` Blocked by Debug Remnant"
|
||||
[[pull_requests]]
|
||||
uid = "4746"
|
||||
author_uid = "aelkheir"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
documentation = "Update ``AUTHORS.rst``, Adding `@aelkheir <https://github.com/aelkheir>`_ to Active Development Team"
|
||||
[[pull_requests]]
|
||||
uid = "4747"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump `pre-commit` Hooks to Latest Versions"
|
||||
[[pull_requests]]
|
||||
uid = "4748"
|
||||
author_uid = "pre-commit-ci"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,51 @@
|
||||
features = "Full Support for Bot API 9.0"
|
||||
deprecations = """This release comes with several deprecations, in line with our :ref:`stability policy <stability-policy>`.
|
||||
This includes the following:
|
||||
|
||||
- Deprecated ``telegram.constants.StarTransactionsLimit.NANOSTAR_MIN_AMOUNT`` and ``telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT``. These members will be replaced by ``telegram.constants.NanostarLimit.MIN_AMOUNT`` and ``telegram.constants.NanostarLimit.MAX_AMOUNT``.
|
||||
- Deprecated the class ``telegram.constants.StarTransactions``. Its only member ``telegram.constants.StarTransactions.NANOSTAR_VALUE`` will be replaced by ``telegram.constants.Nanostar.VALUE``.
|
||||
- Bot API 9.0 deprecated ``BusinessConnection.can_reply`` in favor of ``BusinessConnection.rights``
|
||||
- Bot API 9.0 deprecated ``ChatFullInfo.can_send_gift`` in favor of ``ChatFullInfo.accepted_gift_types``.
|
||||
- Bot API 9.0 introduced these new required fields to existing classes:
|
||||
- ``TransactionPartnerUser.transaction_type``
|
||||
- ``ChatFullInfo.accepted_gift_types``
|
||||
|
||||
Passing these values as positional arguments is deprecated. We encourage you to use keyword arguments instead, as the the signature will be updated in a future release.
|
||||
|
||||
These deprecations are backward compatible, but we strongly recommend to update your code to use the new members.
|
||||
"""
|
||||
[[pull_requests]]
|
||||
uid = "4756"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = ["4754"]
|
||||
[[pull_requests]]
|
||||
uid = "4757"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4759"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4763"
|
||||
author_uid = "aelkheir"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4766"
|
||||
author_uid = "Bibo-Joshi"
|
||||
[[pull_requests]]
|
||||
uid = "4769"
|
||||
author_uid = "aelkheir"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4773"
|
||||
author_uid = "aelkheir"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4781"
|
||||
author_uid = "aelkheir"
|
||||
closes_threads = []
|
||||
[[pull_requests]]
|
||||
uid = "4782"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Fine-tune ``chango`` and release workflows"
|
||||
[[pull_requests]]
|
||||
uid = "4758"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = ["4720"]
|
||||
@@ -0,0 +1,6 @@
|
||||
bugfixes = "Fix Handling of ``Defaults`` for ``InputPaidMedia``"
|
||||
|
||||
[[pull_requests]]
|
||||
uid = "4761"
|
||||
author_uid = "ngrogolev"
|
||||
closes_threads = ["4753"]
|
||||
@@ -0,0 +1,5 @@
|
||||
documentation = "Clarify Documentation and Type Hints of ``InputMedia`` and ``InputPaidMedia``. Note that the ``media`` parameter accepts only objects of type ``str`` and ``InputFile``. The respective subclasses of ``Input(Paid)Media`` each accept a broader range of input type for the ``media`` parameter."
|
||||
[[pull_requests]]
|
||||
uid = "4762"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump codecov/codecov-action from 5.1.2 to 5.4.2"
|
||||
[[pull_requests]]
|
||||
uid = "4775"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump actions/upload-artifact from 4.5.0 to 4.6.2"
|
||||
[[pull_requests]]
|
||||
uid = "4776"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump stefanzweifel/git-auto-commit-action from 5.1.0 to 5.2.0"
|
||||
[[pull_requests]]
|
||||
uid = "4777"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump github/codeql-action from 3.28.13 to 3.28.16"
|
||||
[[pull_requests]]
|
||||
uid = "4778"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump actions/download-artifact from 4.2.1 to 4.3.0"
|
||||
[[pull_requests]]
|
||||
uid = "4779"
|
||||
author_uid = "dependabot"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
other = "Bump Version to v22.1"
|
||||
[[pull_requests]]
|
||||
uid = "4791"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -1,8 +1,62 @@
|
||||
.. _ptb-changelog:
|
||||
Version 21.11.1
|
||||
===============
|
||||
|
||||
=========
|
||||
Changelog
|
||||
=========
|
||||
*Released 2025-03-01*
|
||||
|
||||
This is the technical changelog for version 21.11. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
Documentation Improvements
|
||||
--------------------------
|
||||
|
||||
- Fix ReadTheDocs Build (:pr:`4695`)
|
||||
|
||||
Version 21.11
|
||||
=============
|
||||
|
||||
*Released 2025-03-01*
|
||||
|
||||
This is the technical changelog for version 21.11. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
Major Changes and New Features
|
||||
------------------------------
|
||||
|
||||
- Full Support for Bot API 8.3 (:pr:`4676` closes :issue:`4677`, :pr:`4682` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4690` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4691` by `aelkheir <https://github.com/aelkheir>`_)
|
||||
- Make ``provider_token`` Argument Optional (:pr:`4689`)
|
||||
- Remove Deprecated ``InlineQueryResultArticle.hide_url`` (:pr:`4640` closes :issue:`4638`)
|
||||
- Accept ``datetime.timedelta`` Input in ``Bot`` Method Parameters (:pr:`4651`)
|
||||
- Extend Customization Support for ``Bot.base_(file_)url`` (:pr:`4632` closes :issue:`3355`)
|
||||
- Support ``allow_paid_broadcast`` in ``AIORateLimiter`` (:pr:`4627` closes :issue:`4578`)
|
||||
- Add ``BaseUpdateProcessor.current_concurrent_updates`` (:pr:`4626` closes :issue:`3984`)
|
||||
|
||||
Minor Changes and Bug Fixes
|
||||
---------------------------
|
||||
|
||||
- Add Bootstrapping Logic to ``Application.run_*`` (:pr:`4673` closes :issue:`4657`)
|
||||
- Fix a Bug in ``edit_user_star_subscription`` (:pr:`4681` by `vavasik800 <https://github.com/vavasik800>`_)
|
||||
- Simplify Handling of Empty Data in ``TelegramObject.de_json`` and Friends (:pr:`4617` closes :issue:`4614`)
|
||||
|
||||
Documentation Improvements
|
||||
--------------------------
|
||||
|
||||
- Documentation Improvements (:pr:`4641`)
|
||||
- Overhaul Admonition Insertion in Documentation (:pr:`4462` closes :issue:`4414`)
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Stabilize Linkcheck Test (:pr:`4693`)
|
||||
- Bump ``pre-commit`` Hooks to Latest Versions (:pr:`4643`)
|
||||
- Refactor Tests for ``TelegramObject`` Classes with Subclasses (:pr:`4654` closes :issue:`4652`)
|
||||
- Use Fine Grained Permissions for GitHub Actions Workflows (:pr:`4668`)
|
||||
|
||||
Dependency Updates
|
||||
------------------
|
||||
|
||||
- Bump ``actions/setup-python`` from 5.3.0 to 5.4.0 (:pr:`4665`)
|
||||
- Bump ``dependabot/fetch-metadata`` from 2.2.0 to 2.3.0 (:pr:`4666`)
|
||||
- Bump ``actions/stale`` from 9.0.0 to 9.1.0 (:pr:`4667`)
|
||||
- Bump ``astral-sh/setup-uv`` from 5.1.0 to 5.2.2 (:pr:`4664`)
|
||||
- Bump ``codecov/test-results-action`` from 1.0.1 to 1.0.2 (:pr:`4663`)
|
||||
|
||||
Version 21.10
|
||||
=============
|
||||
@@ -0,0 +1,105 @@
|
||||
# noqa: INP001
|
||||
# pylint: disable=import-error
|
||||
"""Configuration for the chango changelog tool"""
|
||||
|
||||
import re
|
||||
from collections.abc import Collection
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from chango import Version
|
||||
from chango.concrete import DirectoryChanGo, DirectoryVersionScanner, HeaderVersionHistory
|
||||
from chango.concrete.sections import GitHubSectionChangeNote, Section, SectionVersionNote
|
||||
|
||||
version_scanner = DirectoryVersionScanner(base_directory=".", unreleased_directory="unreleased")
|
||||
|
||||
|
||||
class ChangoSectionChangeNote(
|
||||
GitHubSectionChangeNote.with_sections( # type: ignore[misc]
|
||||
[
|
||||
Section(uid="highlights", title="Highlights", sort_order=0),
|
||||
Section(uid="breaking", title="Breaking Changes", sort_order=1),
|
||||
Section(uid="security", title="Security Changes", sort_order=2),
|
||||
Section(uid="deprecations", title="Deprecations", sort_order=3),
|
||||
Section(uid="features", title="New Features", sort_order=4),
|
||||
Section(uid="bugfixes", title="Bug Fixes", sort_order=5),
|
||||
Section(uid="dependencies", title="Dependencies", sort_order=6),
|
||||
Section(uid="other", title="Other Changes", sort_order=7),
|
||||
Section(uid="documentation", title="Documentation", sort_order=8),
|
||||
Section(uid="internal", title="Internal Changes", sort_order=9),
|
||||
]
|
||||
)
|
||||
):
|
||||
"""Custom change note type for PTB. Mainly overrides get_sections to map labels to sections"""
|
||||
|
||||
OWNER = "python-telegram-bot"
|
||||
REPOSITORY = "python-telegram-bot"
|
||||
|
||||
@classmethod
|
||||
def get_sections(
|
||||
cls,
|
||||
labels: Collection[str],
|
||||
issue_types: Optional[Collection[str]],
|
||||
) -> set[str]:
|
||||
"""Override get_sections to have customized auto-detection of relevant sections based on
|
||||
the pull request and linked issues. Certainly not perfect in all cases, but should be a
|
||||
good start for most PRs.
|
||||
"""
|
||||
combined_labels = set(labels) | (set(issue_types or []))
|
||||
|
||||
mapping = {
|
||||
"🐛 bug": "bugfixes",
|
||||
"💡 feature": "features",
|
||||
"🧹 chore": "internal",
|
||||
"⚙️ bot-api": "features",
|
||||
"⚙️ documentation": "documentation",
|
||||
"⚙️ tests": "internal",
|
||||
"⚙️ ci-cd": "internal",
|
||||
"⚙️ security": "security",
|
||||
"⚙️ examples": "documentation",
|
||||
"⚙️ type-hinting": "other",
|
||||
"🛠 refactor": "internal",
|
||||
"🛠 breaking": "breaking",
|
||||
"⚙️ dependencies": "dependencies",
|
||||
"🔗 github-actions": "internal",
|
||||
"🛠 code-quality": "internal",
|
||||
}
|
||||
|
||||
# we want to return *all* from the mapping that are in the combined_labels
|
||||
# removing superfluous sections from the fragment is a tad easier than adding them
|
||||
found = {section for label, section in mapping.items() if label in combined_labels}
|
||||
|
||||
# if we have not found any sections, we default to "other"
|
||||
return found or {"other"}
|
||||
|
||||
|
||||
class CustomChango(DirectoryChanGo):
|
||||
"""Custom ChanGo class for overriding release"""
|
||||
|
||||
def release(self, version: Version) -> bool:
|
||||
"""replace "14.5" with version.uid except in the contrib guide
|
||||
then call super
|
||||
"""
|
||||
root = Path(__file__).parent.parent / "telegram"
|
||||
python_files = root.rglob("*.py")
|
||||
pattern = re.compile(r"NEXT\.VERSION")
|
||||
excluded_paths = {root / "docs/source/contribute.rst"}
|
||||
for file_path in python_files:
|
||||
if str(file_path) in excluded_paths:
|
||||
continue
|
||||
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
modified = pattern.sub(version.uid, content)
|
||||
|
||||
if content != modified:
|
||||
file_path.write_text(modified, encoding="utf-8")
|
||||
|
||||
return super().release(version)
|
||||
|
||||
|
||||
chango_instance = CustomChango(
|
||||
change_note_type=ChangoSectionChangeNote,
|
||||
version_note_type=SectionVersionNote,
|
||||
version_history_type=HeaderVersionHistory,
|
||||
scanner=version_scanner,
|
||||
)
|
||||
@@ -47,29 +47,29 @@ keyword_args = [
|
||||
"",
|
||||
]
|
||||
|
||||
media_write_timeout_deprecation_methods = [
|
||||
"send_photo",
|
||||
media_write_timeout_change_methods = [
|
||||
"add_sticker_to_set",
|
||||
"create_new_sticker_set",
|
||||
"send_animation",
|
||||
"send_audio",
|
||||
"send_document",
|
||||
"send_media_group",
|
||||
"send_photo",
|
||||
"send_sticker",
|
||||
"send_video",
|
||||
"send_video_note",
|
||||
"send_animation",
|
||||
"send_voice",
|
||||
"send_media_group",
|
||||
"set_chat_photo",
|
||||
"upload_sticker_file",
|
||||
"add_sticker_to_set",
|
||||
"create_new_sticker_set",
|
||||
]
|
||||
media_write_timeout_deprecation = [
|
||||
media_write_timeout_change = [
|
||||
" write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.write_timeout`. By default, ``20`` "
|
||||
" seconds are used as write timeout."
|
||||
"",
|
||||
"",
|
||||
" .. deprecated:: 20.7",
|
||||
" In future versions, the default value will be changed to "
|
||||
" .. versionchanged:: 22.0",
|
||||
" The default value changed to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.",
|
||||
"",
|
||||
"",
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import collections.abc
|
||||
import contextlib
|
||||
import inspect
|
||||
import re
|
||||
import typing
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
@@ -32,11 +32,15 @@ from docs.auxil.kwargs_insertion import (
|
||||
find_insert_pos_for_kwargs,
|
||||
get_updates_read_timeout_addition,
|
||||
keyword_args,
|
||||
media_write_timeout_deprecation,
|
||||
media_write_timeout_deprecation_methods,
|
||||
media_write_timeout_change,
|
||||
media_write_timeout_change_methods,
|
||||
)
|
||||
from docs.auxil.link_code import LINE_NUMBERS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import collections.abc
|
||||
|
||||
|
||||
ADMONITION_INSERTER = AdmonitionInserter()
|
||||
|
||||
# Some base classes are implementation detail
|
||||
@@ -116,9 +120,9 @@ def autodoc_process_docstring(
|
||||
|
||||
if (
|
||||
"post.write_timeout`. Defaults to" in to_insert
|
||||
and method_name in media_write_timeout_deprecation_methods
|
||||
and method_name in media_write_timeout_change_methods
|
||||
):
|
||||
effective_insert: list[str] = media_write_timeout_deprecation
|
||||
effective_insert: list[str] = media_write_timeout_change
|
||||
elif get_updates and to_insert.lstrip().startswith("read_timeout"):
|
||||
effective_insert = [to_insert, *get_updates_read_timeout_addition]
|
||||
else:
|
||||
@@ -128,7 +132,7 @@ def autodoc_process_docstring(
|
||||
insert_idx += len(effective_insert)
|
||||
|
||||
ADMONITION_INSERTER.insert_admonitions(
|
||||
obj=typing.cast(collections.abc.Callable, obj),
|
||||
obj=typing.cast("collections.abc.Callable", obj),
|
||||
docstring_lines=lines,
|
||||
)
|
||||
|
||||
@@ -136,7 +140,7 @@ def autodoc_process_docstring(
|
||||
# (where applicable)
|
||||
if what == "class":
|
||||
ADMONITION_INSERTER.insert_admonitions(
|
||||
obj=typing.cast(type, obj), # since "what" == class, we know it's not just object
|
||||
obj=typing.cast("type", obj), # since "what" == class, we know it's not just object
|
||||
docstring_lines=lines,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
sphinx==8.1.3
|
||||
chango~=0.4.0
|
||||
sphinx==8.2.3
|
||||
furo==2024.8.6
|
||||
furo-sphinx-search @ git+https://github.com/harshil21/furo-sphinx-search@v0.2.0.1
|
||||
sphinx-paramlinks==0.6.0
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
.. include:: ../../CHANGES.rst
|
||||
.. _ptb-changelog:
|
||||
|
||||
=========
|
||||
Changelog
|
||||
=========
|
||||
|
||||
.. chango::
|
||||
|
||||
.. include:: ../../changes/LEGACY.rst
|
||||
@@ -8,6 +8,11 @@ from pathlib import Path
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
# Due to dependency on chango
|
||||
raise RuntimeError("This documentation needs at least Python 3.12")
|
||||
|
||||
|
||||
sys.path.insert(0, str(Path("../..").resolve().absolute()))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
@@ -36,6 +41,7 @@ needs_sphinx = "8.1.3"
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"chango.sphinx_ext",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx.ext.intersphinx",
|
||||
@@ -52,6 +58,9 @@ extensions = [
|
||||
if os.environ.get("READTHEDOCS", "") == "True":
|
||||
extensions.append("sphinx_build_compatibility.extension")
|
||||
|
||||
# Configuration for the chango sphinx directive
|
||||
chango_pyproject_toml_path = Path(__file__).parent.parent.parent
|
||||
|
||||
# For shorter links to Wiki in docstrings
|
||||
extlinks = {
|
||||
"wiki": ("https://github.com/python-telegram-bot/python-telegram-bot/wiki/%s", "%s"),
|
||||
@@ -111,6 +120,13 @@ linkcheck_ignore = [
|
||||
# Anchors are apparently inserted by GitHub dynamically, so let's skip checking them
|
||||
"https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples#",
|
||||
r"https://github\.com/python-telegram-bot/python-telegram-bot/wiki/[\w\-_,]+\#",
|
||||
# The LGPL license link regularly causes network errors for some reason
|
||||
re.escape("https://www.gnu.org/licenses/lgpl-3.0.html"),
|
||||
# The doc-fixes branch may not always exist - doesn't matter, we only link to it from the
|
||||
# contributing guide
|
||||
re.escape("https://docs.python-telegram-bot.org/en/doc-fixes"),
|
||||
# Apparently has some human-verification check and gives 403 in the sphinx build
|
||||
re.escape("https://stackoverflow.com/questions/tagged/python-telegram-bot"),
|
||||
]
|
||||
linkcheck_allowed_redirects = {
|
||||
# Redirects to the default version are okay
|
||||
|
||||
@@ -61,7 +61,7 @@ for this one, too!
|
||||
:any:`examples.nestedconversationbot`
|
||||
-------------------------------------
|
||||
|
||||
A even more complex example of a bot that uses the nested
|
||||
An even more complex example of a bot that uses the nested
|
||||
``ConversationHandler``\ s. While it’s certainly not that complex that
|
||||
you couldn’t built it without nested ``ConversationHanldler``\ s, it
|
||||
gives a good impression on how to work with them. Of course, there is a
|
||||
|
||||
@@ -161,8 +161,6 @@
|
||||
- Used for unpinning a message
|
||||
* - :meth:`~telegram.Bot.unpin_all_chat_messages`
|
||||
- Used for unpinning all pinned chat messages
|
||||
* - :meth:`~telegram.Bot.get_business_connection`
|
||||
- Used for getting information about the business account.
|
||||
* - :meth:`~telegram.Bot.get_user_profile_photos`
|
||||
- Used for obtaining user's profile pictures
|
||||
* - :meth:`~telegram.Bot.get_chat`
|
||||
@@ -396,6 +394,60 @@
|
||||
- Used for obtaining the bot's Telegram Stars transactions
|
||||
* - :meth:`~telegram.Bot.refund_star_payment`
|
||||
- Used for refunding a payment in Telegram Stars
|
||||
* - :meth:`~telegram.Bot.gift_premium_subscription`
|
||||
- Used for gifting Telegram Premium to another user.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
</details>
|
||||
<br>
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<details>
|
||||
<summary>Business Related Methods</summary>
|
||||
|
||||
.. list-table::
|
||||
:align: left
|
||||
:widths: 1 4
|
||||
|
||||
* - :meth:`~telegram.Bot.get_business_connection`
|
||||
- Used for getting information about the business account.
|
||||
* - :meth:`~telegram.Bot.get_business_account_gifts`
|
||||
- Used for getting gifts owned by the business account.
|
||||
* - :meth:`~telegram.Bot.get_business_account_star_balance`
|
||||
- Used for getting the amount of Stars owned by the business account.
|
||||
* - :meth:`~telegram.Bot.read_business_message`
|
||||
- Used for marking a message as read.
|
||||
* - :meth:`~telegram.Bot.delete_story`
|
||||
- Used for deleting business stories posted by the bot.
|
||||
* - :meth:`~telegram.Bot.delete_business_messages`
|
||||
- Used for deleting business messages.
|
||||
* - :meth:`~telegram.Bot.remove_business_account_profile_photo`
|
||||
- Used for removing the business accounts profile photo
|
||||
* - :meth:`~telegram.Bot.set_business_account_name`
|
||||
- Used for setting the business account name.
|
||||
* - :meth:`~telegram.Bot.set_business_account_username`
|
||||
- Used for setting the business account username.
|
||||
* - :meth:`~telegram.Bot.set_business_account_bio`
|
||||
- Used for setting the business account bio.
|
||||
* - :meth:`~telegram.Bot.set_business_account_gift_settings`
|
||||
- Used for setting the business account gift settings.
|
||||
* - :meth:`~telegram.Bot.set_business_account_profile_photo`
|
||||
- Used for setting the business accounts profile photo
|
||||
* - :meth:`~telegram.Bot.post_story`
|
||||
- Used for posting a story on behalf of business account.
|
||||
* - :meth:`~telegram.Bot.edit_story`
|
||||
- Used for editing business stories posted by the bot.
|
||||
* - :meth:`~telegram.Bot.convert_gift_to_stars`
|
||||
- Used for converting owned reqular gifts to stars.
|
||||
* - :meth:`~telegram.Bot.upgrade_gift`
|
||||
- Used for upgrading owned regular gifts to unique ones.
|
||||
* - :meth:`~telegram.Bot.transfer_gift`
|
||||
- Used for transferring owned unique gifts to another user.
|
||||
* - :meth:`~telegram.Bot.transfer_business_account_stars`
|
||||
- Used for transfering Stars from the business account balance to the bot's balance.
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _stability-policy:
|
||||
|
||||
Stability Policy
|
||||
================
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
AcceptedGiftTypes
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.AcceptedGiftTypes
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -4,6 +4,7 @@ Available Types
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
telegram.acceptedgifttypes
|
||||
telegram.animation
|
||||
telegram.audio
|
||||
telegram.birthdate
|
||||
@@ -19,6 +20,7 @@ Available Types
|
||||
telegram.botdescription
|
||||
telegram.botname
|
||||
telegram.botshortdescription
|
||||
telegram.businessbotrights
|
||||
telegram.businessconnection
|
||||
telegram.businessintro
|
||||
telegram.businesslocation
|
||||
@@ -75,6 +77,7 @@ Available Types
|
||||
telegram.forumtopicreopened
|
||||
telegram.generalforumtopichidden
|
||||
telegram.generalforumtopicunhidden
|
||||
telegram.giftinfo
|
||||
telegram.giveaway
|
||||
telegram.giveawaycompleted
|
||||
telegram.giveawaycreated
|
||||
@@ -92,13 +95,20 @@ Available Types
|
||||
telegram.inputpaidmedia
|
||||
telegram.inputpaidmediaphoto
|
||||
telegram.inputpaidmediavideo
|
||||
telegram.inputprofilephoto
|
||||
telegram.inputprofilephotoanimated
|
||||
telegram.inputprofilephotostatic
|
||||
telegram.inputpolloption
|
||||
telegram.inputstorycontent
|
||||
telegram.inputstorycontentphoto
|
||||
telegram.inputstorycontentvideo
|
||||
telegram.keyboardbutton
|
||||
telegram.keyboardbuttonpolltype
|
||||
telegram.keyboardbuttonrequestchat
|
||||
telegram.keyboardbuttonrequestusers
|
||||
telegram.linkpreviewoptions
|
||||
telegram.location
|
||||
telegram.locationaddress
|
||||
telegram.loginurl
|
||||
telegram.maybeinaccessiblemessage
|
||||
telegram.menubutton
|
||||
@@ -116,12 +126,17 @@ Available Types
|
||||
telegram.messageoriginuser
|
||||
telegram.messagereactioncountupdated
|
||||
telegram.messagereactionupdated
|
||||
telegram.ownedgift
|
||||
telegram.ownedgiftregular
|
||||
telegram.ownedgifts
|
||||
telegram.ownedgiftunique
|
||||
telegram.paidmedia
|
||||
telegram.paidmediainfo
|
||||
telegram.paidmediaphoto
|
||||
telegram.paidmediapreview
|
||||
telegram.paidmediapurchased
|
||||
telegram.paidmediavideo
|
||||
telegram.paidmessagepricechanged
|
||||
telegram.photosize
|
||||
telegram.poll
|
||||
telegram.pollanswer
|
||||
@@ -138,9 +153,23 @@ Available Types
|
||||
telegram.sentwebappmessage
|
||||
telegram.shareduser
|
||||
telegram.story
|
||||
telegram.storyarea
|
||||
telegram.storyareaposition
|
||||
telegram.storyareatype
|
||||
telegram.storyareatypelink
|
||||
telegram.storyareatypelocation
|
||||
telegram.storyareatypesuggestedreaction
|
||||
telegram.storyareatypeuniquegift
|
||||
telegram.storyareatypeweather
|
||||
telegram.switchinlinequerychosenchat
|
||||
telegram.telegramobject
|
||||
telegram.textquote
|
||||
telegram.uniquegift
|
||||
telegram.uniquegiftbackdrop
|
||||
telegram.uniquegiftbackdropcolors
|
||||
telegram.uniquegiftinfo
|
||||
telegram.uniquegiftmodel
|
||||
telegram.uniquegiftsymbol
|
||||
telegram.update
|
||||
telegram.user
|
||||
telegram.userchatboosts
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
BusinessBotRights
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.BusinessBotRights
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
GiftInfo
|
||||
========
|
||||
|
||||
.. autoclass:: telegram.GiftInfo
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
InputProfilePhoto
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.InputProfilePhoto
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
InputProfilePhotoAnimated
|
||||
=========================
|
||||
|
||||
.. autoclass:: telegram.InputProfilePhotoAnimated
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
InputProfilePhotoStatic
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.InputProfilePhotoStatic
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
InputStoryContent
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.InputStoryContent
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
InputStoryContentPhoto
|
||||
======================
|
||||
|
||||
.. autoclass:: telegram.InputStoryContentPhoto
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
InputStoryContentVideo
|
||||
======================
|
||||
|
||||
.. autoclass:: telegram.InputStoryContentVideo
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
LocationAddress
|
||||
===============
|
||||
|
||||
.. autoclass:: telegram.LocationAddress
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
OwnedGift
|
||||
=========
|
||||
|
||||
.. autoclass:: telegram.OwnedGift
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
OwnedGiftRegular
|
||||
================
|
||||
|
||||
.. autoclass:: telegram.OwnedGiftRegular
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
OwnedGifts
|
||||
==========
|
||||
|
||||
.. autoclass:: telegram.OwnedGifts
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
OwnedGiftUnique
|
||||
===============
|
||||
|
||||
.. autoclass:: telegram.OwnedGiftUnique
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
PaidMessagePriceChanged
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.PaidMessagePriceChanged
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -22,11 +22,13 @@ Your bot can accept payments from Telegram users. Please see the `introduction t
|
||||
telegram.shippingaddress
|
||||
telegram.shippingoption
|
||||
telegram.shippingquery
|
||||
telegram.staramount
|
||||
telegram.startransaction
|
||||
telegram.startransactions
|
||||
telegram.successfulpayment
|
||||
telegram.transactionpartner
|
||||
telegram.transactionpartneraffiliateprogram
|
||||
telegram.transactionpartnerchat
|
||||
telegram.transactionpartnerfragment
|
||||
telegram.transactionpartnerother
|
||||
telegram.transactionpartnertelegramads
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
StarAmount
|
||||
==========
|
||||
|
||||
.. autoclass:: telegram.StarAmount
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryArea
|
||||
=========
|
||||
|
||||
.. autoclass:: telegram.StoryArea
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaPosition
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.StoryAreaPosition
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaType
|
||||
=============
|
||||
|
||||
.. autoclass:: telegram.StoryAreaType
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaTypeLink
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.StoryAreaTypeLink
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaTypeLocation
|
||||
=====================
|
||||
|
||||
.. autoclass:: telegram.StoryAreaTypeLocation
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaTypeSuggestedReaction
|
||||
==============================
|
||||
|
||||
.. autoclass:: telegram.StoryAreaTypeSuggestedReaction
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaTypeUniqueGift
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.StoryAreaTypeUniqueGift
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
StoryAreaTypeWeather
|
||||
====================
|
||||
|
||||
.. autoclass:: telegram.StoryAreaTypeWeather
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,7 @@
|
||||
TransactionPartnerChat
|
||||
======================
|
||||
|
||||
.. autoclass:: telegram.TransactionPartnerChat
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members: TransactionPartner
|
||||
@@ -0,0 +1,7 @@
|
||||
UniqueGift
|
||||
==========
|
||||
|
||||
.. autoclass:: telegram.UniqueGift
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
UniqueGiftBackdrop
|
||||
==================
|
||||
|
||||
.. autoclass:: telegram.UniqueGiftBackdrop
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
UniqueGiftBackdropColors
|
||||
========================
|
||||
|
||||
.. autoclass:: telegram.UniqueGiftBackdropColors
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
UniqueGiftInfo
|
||||
==============
|
||||
|
||||
.. autoclass:: telegram.UniqueGiftInfo
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
UniqueGiftModel
|
||||
===============
|
||||
|
||||
.. autoclass:: telegram.UniqueGiftModel
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
UniqueGiftSymbol
|
||||
================
|
||||
|
||||
.. autoclass:: telegram.UniqueGiftSymbol
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -98,4 +98,6 @@
|
||||
|
||||
.. |tz-naive-dtms| replace:: For timezone naive :obj:`datetime.datetime` objects, the default timezone of the bot will be used, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is used.
|
||||
|
||||
.. |time-period-input| replace:: :class:`datetime.timedelta` objects are accepted in addition to plain :obj:`int` values.
|
||||
.. |org-verify| replace:: `on behalf of the organization <https://telegram.org/verify#third-party-verification>`__
|
||||
|
||||
.. |time-period-input| replace:: :class:`datetime.timedelta` objects are accepted in addition to plain :obj:`int` values.
|
||||
|
||||
@@ -69,7 +69,7 @@ async def list_button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
|
||||
# Get the data from the callback_data.
|
||||
# If you're using a type checker like MyPy, you'll have to use typing.cast
|
||||
# to make the checker get the expected type of the callback_data
|
||||
number, number_list = cast(tuple[int, list[int]], query.data)
|
||||
number, number_list = cast("tuple[int, list[int]]", query.data)
|
||||
# append the number to the list
|
||||
number_list.append(number)
|
||||
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
"""Simple state machine to handle user support.
|
||||
One admin is supported. The admin can have one active conversation at a time. Other users
|
||||
are put on hold until the admin finishes the current conversation.
|
||||
In each conversation, the admin and the user take turns to send messages.
|
||||
"""
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from telegram import Update
|
||||
from telegram.ext import (
|
||||
Application,
|
||||
CommandHandler,
|
||||
ContextTypes,
|
||||
FiniteStateMachine,
|
||||
MessageHandler,
|
||||
State,
|
||||
StateInfo,
|
||||
filters,
|
||||
)
|
||||
|
||||
# Enable logging
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.DEBUG
|
||||
)
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
||||
logging.getLogger("telegram").setLevel(logging.WARNING)
|
||||
logging.getLogger("telegram.ext.Application").setLevel(logging.DEBUG)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserSupportMachine(FiniteStateMachine[Optional[int]]):
|
||||
|
||||
HOLD = State("HOLD")
|
||||
WELCOMING = State("WELCOMING")
|
||||
WAITING_FOR_REPLY = State("WAITING_FOR_REPLY")
|
||||
WRITING = State("WRITING")
|
||||
|
||||
def __init__(self, admin_id: int):
|
||||
self.admin_id = admin_id
|
||||
super().__init__()
|
||||
|
||||
def _get_admin_state(self) -> tuple[State, int]:
|
||||
return self._states[self.admin_id]
|
||||
|
||||
def get_state_info(self, update: object) -> StateInfo[Optional[int]]:
|
||||
if not isinstance(update, Update) or not (user := update.effective_user):
|
||||
key = None
|
||||
state, version = self.states[key]
|
||||
return StateInfo(key=key, state=state, version=version)
|
||||
|
||||
# Admin is easy - just return the state
|
||||
admin_state, admin_version = self._get_admin_state()
|
||||
if user.id == self.admin_id:
|
||||
logging.debug("Returning admin state: %s", admin_state)
|
||||
return StateInfo(self.admin_id, admin_state, admin_version)
|
||||
|
||||
# If the user state is active in the conversation, we can just return that state
|
||||
user_state, user_version = self._states[user.id]
|
||||
if user_state.matches(self.WELCOMING | self.WRITING | self.WAITING_FOR_REPLY):
|
||||
logging.debug("Returning user state: %s", user_state)
|
||||
return StateInfo(user.id, user_state, user_version)
|
||||
|
||||
# On first interaction, we need to determine what to do with the user
|
||||
# if the admin is not idle, we put the user on hold. Otherwise, they may send the first
|
||||
# message, and we put the admin in waiting for reply to avoid another user occupying the
|
||||
# admin first
|
||||
effective_user_state = self.HOLD if admin_state != State.IDLE else self.WELCOMING
|
||||
self._do_set_state(user.id, effective_user_state, user_version)
|
||||
if effective_user_state == self.WELCOMING:
|
||||
self._do_set_state(self.admin_id, self.WAITING_FOR_REPLY)
|
||||
|
||||
logging.debug("Returning user state: %s", effective_user_state)
|
||||
return StateInfo(user.id, effective_user_state, user_version)
|
||||
|
||||
|
||||
async def welcome_user(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await update.effective_message.forward(context.bot_data["admin_id"])
|
||||
suffix = ""
|
||||
if UserSupportMachine.HOLD in context.fsm.get_state_history(context.fsm_state_info.key)[:-1]:
|
||||
suffix = " Thank you for patiently waiting. We hope you enjoyed the music."
|
||||
|
||||
await update.effective_message.reply_text(
|
||||
"Welcome! Your message has been forwarded to the admin. "
|
||||
f"They will get back to you soon.{suffix}"
|
||||
)
|
||||
await context.set_state(UserSupportMachine.WAITING_FOR_REPLY)
|
||||
await context.fsm.set_state(context.bot_data["admin_id"], UserSupportMachine.WRITING)
|
||||
context.bot_data["active_user"] = update.effective_user.id
|
||||
|
||||
|
||||
async def conversation_timeout(context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
active_user = context.bot_data.get("active_user")
|
||||
admin_id = context.bot_data["admin_id"]
|
||||
|
||||
async def handle(user_id: int) -> None:
|
||||
await context.bot.send_message(
|
||||
user_id, "The conversation has been stopped due to inactivity."
|
||||
)
|
||||
await context.fsm.set_state(user_id, State.IDLE)
|
||||
|
||||
if active_user:
|
||||
await handle(active_user)
|
||||
await handle(admin_id)
|
||||
|
||||
|
||||
async def handle_reply(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if not (active_user := context.bot_data.get("active_user")):
|
||||
logger.warning("No active user found, ignoring message")
|
||||
|
||||
target = (
|
||||
active_user
|
||||
if update.effective_user.id == (admin_id := context.bot_data["admin_id"])
|
||||
else admin_id
|
||||
)
|
||||
await context.bot.send_message(target, update.effective_message.text)
|
||||
logging.debug("Forwarded message to %s", target)
|
||||
await context.set_state(UserSupportMachine.WAITING_FOR_REPLY)
|
||||
logging.debug("Done setting state to WAITING_FOR_REPLY for %s", target)
|
||||
await context.fsm.set_state(target, UserSupportMachine.WRITING)
|
||||
logging.debug("Done setting state to WRITING for %s, context.fsm_key")
|
||||
|
||||
context.fsm.schedule_timeout(
|
||||
when=30,
|
||||
callback=conversation_timeout,
|
||||
cancel_keys=[active_user, admin_id],
|
||||
)
|
||||
|
||||
|
||||
async def stop_conversation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
text = "The conversation has been stopped."
|
||||
admin_id = context.bot_data["admin_id"]
|
||||
active_user = context.bot_data.get("active_user")
|
||||
|
||||
await context.bot.send_message(admin_id, text)
|
||||
await context.fsm.set_state(admin_id, State.IDLE)
|
||||
if active_user:
|
||||
await context.bot.send_message(active_user, text)
|
||||
await context.fsm.set_state(active_user, State.IDLE)
|
||||
|
||||
|
||||
async def hold_melody(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await update.effective_message.reply_text(
|
||||
"You have been put on hold. The admin will get back to you soon. Please hear some music "
|
||||
"while you wait: https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||
)
|
||||
|
||||
|
||||
async def not_your_turn(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await update.effective_message.reply_text(
|
||||
"It's not your turn yet. Please wait for the other party to reply to your message."
|
||||
)
|
||||
|
||||
|
||||
async def unsupported_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await update.effective_message.reply_text("This message is not supported.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
application = Application.builder().token("TOKEN").build()
|
||||
application.fsm = UserSupportMachine(admin_id=123456)
|
||||
application.fsm.set_job_queue(application.job_queue)
|
||||
application.bot_data["admin_id"] = application.fsm.admin_id
|
||||
|
||||
# Users are welcomed only if they are in the corresponding state
|
||||
application.add_handler(
|
||||
MessageHandler(~filters.User(application.fsm.admin_id) & filters.TEXT, welcome_user),
|
||||
state=UserSupportMachine.WELCOMING,
|
||||
)
|
||||
|
||||
# Conversation logic:
|
||||
# * forward messages between user and admin
|
||||
# * stop the conversation at any time (admin or user)
|
||||
# * point out that the other party is currently writing
|
||||
# Important: Order matters!
|
||||
application.add_handler(
|
||||
CommandHandler("stop", stop_conversation),
|
||||
state=UserSupportMachine.WAITING_FOR_REPLY | UserSupportMachine.WRITING,
|
||||
)
|
||||
application.add_handler(
|
||||
MessageHandler(filters.TEXT, handle_reply), state=UserSupportMachine.WRITING
|
||||
)
|
||||
application.add_handler(
|
||||
MessageHandler(filters.TEXT, not_your_turn), state=UserSupportMachine.WAITING_FOR_REPLY
|
||||
)
|
||||
|
||||
# If the admin is busy, put the user on hold
|
||||
application.add_handler(
|
||||
MessageHandler(filters.TEXT, hold_melody), state=UserSupportMachine.HOLD
|
||||
)
|
||||
|
||||
# Fallback
|
||||
application.add_handler(MessageHandler(filters.ALL, unsupported_message), state=State.ANY)
|
||||
|
||||
application.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
"""State machine bot showcasing how concurrency can be handled with FSM.
|
||||
How to use:
|
||||
|
||||
* Use Case 1: Concurrent balance updates
|
||||
- /unsafe_update <balance_update>: Unsafe update of the wallet balance. Send the command
|
||||
multiple times in quick succession (less than 1 second) to see the effect
|
||||
- /safe_update <balance_update>: Safe update of the wallet balance. Send the command
|
||||
multiple times in quick succession (less than 1 second) to see the effect
|
||||
|
||||
* Use Case 2: Declare a winner - who is the fastest?
|
||||
- /unsafe_declare_winner: Unsafe declaration of the user as winner. Send the command
|
||||
multiple times in quick succession (less than 1 second) to see the effect. Needs restart
|
||||
after the winner is declared.
|
||||
- /safe_declare_winner: Safe declaration of the user as winner. Send the command
|
||||
multiple times in quick succession (less than 1 second) to see the effect. Needs restart
|
||||
after the winner is declared.
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from telegram import Update
|
||||
from telegram.constants import ChatAction
|
||||
from telegram.ext import (
|
||||
Application,
|
||||
CommandHandler,
|
||||
ContextTypes,
|
||||
FiniteStateMachine,
|
||||
MessageHandler,
|
||||
State,
|
||||
StateInfo,
|
||||
filters,
|
||||
)
|
||||
|
||||
# Enable logging
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.DEBUG
|
||||
)
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
||||
logging.getLogger("telegram").setLevel(logging.WARNING)
|
||||
logging.getLogger("telegram.ext.Application").setLevel(logging.DEBUG)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConcurrentMachine(FiniteStateMachine[None]):
|
||||
"""This FSM only knows a global state for the whole bot"""
|
||||
|
||||
UPDATING_BALANCE = State("UPDATING_BALANCE")
|
||||
WINNER_DECLARED = State("WINNER_DECLARED")
|
||||
|
||||
def get_state_info(self, update: object) -> StateInfo[None]:
|
||||
state, version = self.states[None]
|
||||
return StateInfo(key=None, state=state, version=version)
|
||||
|
||||
|
||||
########################################
|
||||
# Use case 1: Concurrent balance updates
|
||||
########################################
|
||||
|
||||
|
||||
async def update_balance(context: ContextTypes.DEFAULT_TYPE, update: Update) -> None:
|
||||
initial_balance = context.bot_data.get("balance", 0)
|
||||
balance_update = int(context.args[0])
|
||||
# Simulate heavy computation
|
||||
await update.effective_message.reply_text(
|
||||
f"Initiating balance update: {initial_balance}. Updating ..."
|
||||
)
|
||||
await update.effective_chat.send_action(ChatAction.TYPING)
|
||||
await asyncio.sleep(4.5)
|
||||
new_balance = context.bot_data["balance"] = initial_balance + balance_update
|
||||
await update.effective_message.reply_text(f"Balance updated. New balance: {new_balance}")
|
||||
|
||||
|
||||
async def unsafe_update(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Unsafe update of the wallet balance"""
|
||||
# Simulate heavy computation *before* the update is processed
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await context.fsm.set_state(context.fsm_state_info.key, ConcurrentMachine.UPDATING_BALANCE)
|
||||
|
||||
# At this point, the lock is released such that multiple updates can update
|
||||
# the balance concurrently. This can lead to race conditions.
|
||||
await update_balance(context, update)
|
||||
|
||||
await context.fsm.set_state(context.fsm_state_info.key, State.IDLE)
|
||||
|
||||
|
||||
async def safe_update(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Safe update of the wallet balance"""
|
||||
# Simulate heavy computation *before* the update is processed
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async with context.as_fsm_state(ConcurrentMachine.UPDATING_BALANCE):
|
||||
# At this point, the lock is acquired such that only one update can update
|
||||
# the balance at a time. This prevents race conditions.
|
||||
await update_balance(context, update)
|
||||
|
||||
|
||||
async def busy(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Busy state"""
|
||||
await update.effective_message.reply_text("I'm busy, try again later.")
|
||||
|
||||
|
||||
####################################################
|
||||
# Use case 2: Declare a winner - who is the fastest?
|
||||
####################################################
|
||||
|
||||
|
||||
async def declare_winner_unsafe(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Declare the user as winner"""
|
||||
# Simulate heavy computation *before* the update is processed
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Unsafe state update: No version check, so the state might have already changed
|
||||
await context.fsm.set_state(context.fsm_state_info.key, ConcurrentMachine.WINNER_DECLARED)
|
||||
await update.effective_message.reply_text("You are the winner!")
|
||||
|
||||
|
||||
async def declare_winner_safe(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""Declare the user as winner"""
|
||||
# Simulate heavy computation *before* the update is processed
|
||||
await asyncio.sleep(1)
|
||||
|
||||
try:
|
||||
await context.set_state(ConcurrentMachine.WINNER_DECLARED)
|
||||
await update.effective_message.reply_text("You are the winner!")
|
||||
except ValueError:
|
||||
await update.effective_message.reply_text(
|
||||
"Sorry, you are too late. Someone else was faster."
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
application = Application.builder().token("TOKEN").concurrent_updates(True).build()
|
||||
application.fsm = ConcurrentMachine()
|
||||
|
||||
# Note: OR-combination of states is used here to allow both use cases to be handled
|
||||
# in parallel. Not really necessary for the showcasing, just a nice touch :)
|
||||
|
||||
# Use case 2: Declare a winner - who is the fastest?
|
||||
application.add_handler(
|
||||
CommandHandler("unsafe_declare_winner", declare_winner_unsafe),
|
||||
state=State.IDLE | ConcurrentMachine.UPDATING_BALANCE,
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler("safe_declare_winner", declare_winner_safe),
|
||||
state=State.IDLE | ConcurrentMachine.UPDATING_BALANCE,
|
||||
)
|
||||
|
||||
# Use case 1: Concurrent balance updates
|
||||
application.add_handler(
|
||||
CommandHandler("unsafe_update", unsafe_update, has_args=1),
|
||||
state=State.IDLE | ConcurrentMachine.WINNER_DECLARED,
|
||||
)
|
||||
application.add_handler(
|
||||
CommandHandler("safe_update", safe_update, has_args=1),
|
||||
state=State.IDLE | ConcurrentMachine.WINNER_DECLARED,
|
||||
)
|
||||
# Order matters, so this needs to be added last
|
||||
application.add_handler(
|
||||
MessageHandler(filters.ALL, busy), state=ConcurrentMachine.UPDATING_BALANCE
|
||||
)
|
||||
|
||||
application.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -61,9 +61,9 @@ async def start_with_shipping_callback(update: Update, context: ContextTypes.DEF
|
||||
title,
|
||||
description,
|
||||
payload,
|
||||
PAYMENT_PROVIDER_TOKEN,
|
||||
currency,
|
||||
prices,
|
||||
provider_token=PAYMENT_PROVIDER_TOKEN,
|
||||
need_name=True,
|
||||
need_phone_number=True,
|
||||
need_email=True,
|
||||
@@ -90,7 +90,13 @@ async def start_without_shipping_callback(
|
||||
# optionally pass need_name=True, need_phone_number=True,
|
||||
# need_email=True, need_shipping_address=True, is_flexible=True
|
||||
await context.bot.send_invoice(
|
||||
chat_id, title, description, payload, PAYMENT_PROVIDER_TOKEN, currency, prices
|
||||
chat_id,
|
||||
title,
|
||||
description,
|
||||
payload,
|
||||
currency,
|
||||
prices,
|
||||
provider_token=PAYMENT_PROVIDER_TOKEN,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+6
-2
@@ -105,6 +105,11 @@ search-paths = ["telegram"]
|
||||
[tool.hatch.build]
|
||||
packages = ["telegram"]
|
||||
|
||||
# CHANGO
|
||||
[tool.chango]
|
||||
sys_path = "changes"
|
||||
chango_instance = { name= "chango_instance", module = "config" }
|
||||
|
||||
# BLACK:
|
||||
[tool.black]
|
||||
line-length = 99
|
||||
@@ -120,8 +125,7 @@ line-length = 99
|
||||
show-fixes = true
|
||||
|
||||
[tool.ruff.lint]
|
||||
preview = true
|
||||
explicit-preview-rules = true # TODO: Drop this when RUF022 and RUF023 are out of preview
|
||||
typing-extensions = false
|
||||
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", "SLOT", "PERF", "PYI", "FLY", "AIR", "RUF022",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
build
|
||||
|
||||
# For the test suite
|
||||
pytest==8.3.4
|
||||
pytest==8.3.5
|
||||
|
||||
# needed because pytest doesn't come with native support for coroutines as tests
|
||||
pytest-asyncio==0.21.2
|
||||
|
||||
+66
-1
@@ -20,6 +20,7 @@
|
||||
|
||||
__author__ = "devs@python-telegram-bot.org"
|
||||
__all__ = (
|
||||
"AcceptedGiftTypes",
|
||||
"AffiliateInfo",
|
||||
"Animation",
|
||||
"Audio",
|
||||
@@ -46,6 +47,7 @@ __all__ = (
|
||||
"BotDescription",
|
||||
"BotName",
|
||||
"BotShortDescription",
|
||||
"BusinessBotRights",
|
||||
"BusinessConnection",
|
||||
"BusinessIntro",
|
||||
"BusinessLocation",
|
||||
@@ -103,6 +105,7 @@ __all__ = (
|
||||
"GeneralForumTopicHidden",
|
||||
"GeneralForumTopicUnhidden",
|
||||
"Gift",
|
||||
"GiftInfo",
|
||||
"Gifts",
|
||||
"Giveaway",
|
||||
"GiveawayCompleted",
|
||||
@@ -150,7 +153,13 @@ __all__ = (
|
||||
"InputPaidMediaPhoto",
|
||||
"InputPaidMediaVideo",
|
||||
"InputPollOption",
|
||||
"InputProfilePhoto",
|
||||
"InputProfilePhotoAnimated",
|
||||
"InputProfilePhotoStatic",
|
||||
"InputSticker",
|
||||
"InputStoryContent",
|
||||
"InputStoryContentPhoto",
|
||||
"InputStoryContentVideo",
|
||||
"InputTextMessageContent",
|
||||
"InputVenueMessageContent",
|
||||
"Invoice",
|
||||
@@ -161,6 +170,7 @@ __all__ = (
|
||||
"LabeledPrice",
|
||||
"LinkPreviewOptions",
|
||||
"Location",
|
||||
"LocationAddress",
|
||||
"LoginUrl",
|
||||
"MaskPosition",
|
||||
"MaybeInaccessibleMessage",
|
||||
@@ -180,12 +190,17 @@ __all__ = (
|
||||
"MessageReactionCountUpdated",
|
||||
"MessageReactionUpdated",
|
||||
"OrderInfo",
|
||||
"OwnedGift",
|
||||
"OwnedGiftRegular",
|
||||
"OwnedGiftUnique",
|
||||
"OwnedGifts",
|
||||
"PaidMedia",
|
||||
"PaidMediaInfo",
|
||||
"PaidMediaPhoto",
|
||||
"PaidMediaPreview",
|
||||
"PaidMediaPurchased",
|
||||
"PaidMediaVideo",
|
||||
"PaidMessagePriceChanged",
|
||||
"PassportData",
|
||||
"PassportElementError",
|
||||
"PassportElementErrorDataField",
|
||||
@@ -227,22 +242,38 @@ __all__ = (
|
||||
"ShippingAddress",
|
||||
"ShippingOption",
|
||||
"ShippingQuery",
|
||||
"StarAmount",
|
||||
"StarTransaction",
|
||||
"StarTransactions",
|
||||
"Sticker",
|
||||
"StickerSet",
|
||||
"Story",
|
||||
"StoryArea",
|
||||
"StoryAreaPosition",
|
||||
"StoryAreaType",
|
||||
"StoryAreaTypeLink",
|
||||
"StoryAreaTypeLocation",
|
||||
"StoryAreaTypeSuggestedReaction",
|
||||
"StoryAreaTypeUniqueGift",
|
||||
"StoryAreaTypeWeather",
|
||||
"SuccessfulPayment",
|
||||
"SwitchInlineQueryChosenChat",
|
||||
"TelegramObject",
|
||||
"TextQuote",
|
||||
"TransactionPartner",
|
||||
"TransactionPartnerAffiliateProgram",
|
||||
"TransactionPartnerChat",
|
||||
"TransactionPartnerFragment",
|
||||
"TransactionPartnerOther",
|
||||
"TransactionPartnerTelegramAds",
|
||||
"TransactionPartnerTelegramApi",
|
||||
"TransactionPartnerUser",
|
||||
"UniqueGift",
|
||||
"UniqueGiftBackdrop",
|
||||
"UniqueGiftBackdropColors",
|
||||
"UniqueGiftInfo",
|
||||
"UniqueGiftModel",
|
||||
"UniqueGiftSymbol",
|
||||
"Update",
|
||||
"User",
|
||||
"UserChatBoosts",
|
||||
@@ -271,10 +302,12 @@ __all__ = (
|
||||
"warnings",
|
||||
)
|
||||
|
||||
from telegram._payment.stars.staramount import StarAmount
|
||||
from telegram._payment.stars.startransactions import StarTransaction, StarTransactions
|
||||
from telegram._payment.stars.transactionpartner import (
|
||||
TransactionPartner,
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerChat,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
@@ -299,6 +332,7 @@ from ._botcommandscope import (
|
||||
from ._botdescription import BotDescription, BotShortDescription
|
||||
from ._botname import BotName
|
||||
from ._business import (
|
||||
BusinessBotRights,
|
||||
BusinessConnection,
|
||||
BusinessIntro,
|
||||
BusinessLocation,
|
||||
@@ -350,6 +384,11 @@ from ._chatpermissions import ChatPermissions
|
||||
from ._choseninlineresult import ChosenInlineResult
|
||||
from ._copytextbutton import CopyTextButton
|
||||
from ._dice import Dice
|
||||
from ._files._inputstorycontent import (
|
||||
InputStoryContent,
|
||||
InputStoryContentPhoto,
|
||||
InputStoryContentVideo,
|
||||
)
|
||||
from ._files.animation import Animation
|
||||
from ._files.audio import Audio
|
||||
from ._files.chatphoto import ChatPhoto
|
||||
@@ -368,6 +407,11 @@ from ._files.inputmedia import (
|
||||
InputPaidMediaPhoto,
|
||||
InputPaidMediaVideo,
|
||||
)
|
||||
from ._files.inputprofilephoto import (
|
||||
InputProfilePhoto,
|
||||
InputProfilePhotoAnimated,
|
||||
InputProfilePhotoStatic,
|
||||
)
|
||||
from ._files.inputsticker import InputSticker
|
||||
from ._files.location import Location
|
||||
from ._files.photosize import PhotoSize
|
||||
@@ -389,7 +433,7 @@ from ._forumtopic import (
|
||||
from ._games.callbackgame import CallbackGame
|
||||
from ._games.game import Game
|
||||
from ._games.gamehighscore import GameHighScore
|
||||
from ._gifts import Gift, Gifts
|
||||
from ._gifts import AcceptedGiftTypes, Gift, GiftInfo, Gifts
|
||||
from ._giveaway import Giveaway, GiveawayCompleted, GiveawayCreated, GiveawayWinners
|
||||
from ._inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from ._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
@@ -441,6 +485,7 @@ from ._messageorigin import (
|
||||
MessageOriginUser,
|
||||
)
|
||||
from ._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
|
||||
from ._ownedgift import OwnedGift, OwnedGiftRegular, OwnedGifts, OwnedGiftUnique
|
||||
from ._paidmedia import (
|
||||
PaidMedia,
|
||||
PaidMediaInfo,
|
||||
@@ -449,6 +494,7 @@ from ._paidmedia import (
|
||||
PaidMediaPurchased,
|
||||
PaidMediaVideo,
|
||||
)
|
||||
from ._paidmessagepricechanged import PaidMessagePriceChanged
|
||||
from ._passport.credentials import (
|
||||
Credentials,
|
||||
DataCredentials,
|
||||
@@ -504,8 +550,27 @@ from ._replykeyboardremove import ReplyKeyboardRemove
|
||||
from ._sentwebappmessage import SentWebAppMessage
|
||||
from ._shared import ChatShared, SharedUser, UsersShared
|
||||
from ._story import Story
|
||||
from ._storyarea import (
|
||||
LocationAddress,
|
||||
StoryArea,
|
||||
StoryAreaPosition,
|
||||
StoryAreaType,
|
||||
StoryAreaTypeLink,
|
||||
StoryAreaTypeLocation,
|
||||
StoryAreaTypeSuggestedReaction,
|
||||
StoryAreaTypeUniqueGift,
|
||||
StoryAreaTypeWeather,
|
||||
)
|
||||
from ._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
|
||||
from ._telegramobject import TelegramObject
|
||||
from ._uniquegift import (
|
||||
UniqueGift,
|
||||
UniqueGiftBackdrop,
|
||||
UniqueGiftBackdropColors,
|
||||
UniqueGiftInfo,
|
||||
UniqueGiftModel,
|
||||
UniqueGiftSymbol,
|
||||
)
|
||||
from ._update import Update
|
||||
from ._user import User
|
||||
from ._userprofilephotos import UserProfilePhotos
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user