Compare commits

..

1 Commits

Author SHA1 Message Date
Harshil 0670e42835 initial PoC for code autogeneration 2024-05-31 00:47:53 -04:00
533 changed files with 9660 additions and 20934 deletions
+9 -4
View File
@@ -26,7 +26,7 @@ Setting things up
.. code-block:: bash
$ pip install -r requirements-dev-all.txt
$ pip install -r requirements-all.txt
5. Install pre-commit hooks:
@@ -194,7 +194,7 @@ Feel free to copy (parts of) the checklist to the PR description to remind you o
- 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``
- Updated the Bot API version number in all places: ``README.rst`` and ``README_RAW.rst`` (including the badge), as well as ``telegram.constants.BOT_API_VERSION_INFO``
- Added logic for arbitrary callback data in :class:`telegram.ext.ExtBot` for new methods that either accept a ``reply_markup`` in some form or have a return type that is/contains :class:`~telegram.Message`
Documenting
@@ -210,8 +210,13 @@ doc strings don't have a separate documentation site they generate, instead, the
User facing documentation
-------------------------
We use `sphinx`_ to generate static HTML docs. To build them, first make sure you're running Python 3.10 or above and have the required dependencies installed as explained above.
Then, run the following from the PTB root directory:
We use `sphinx`_ to generate static HTML docs. To build them, first make sure you're running Python 3.9 or above and have the required dependencies:
.. code-block:: bash
$ pip install -r docs/requirements-docs.txt
then run the following from the PTB root directory:
.. code-block:: bash
+2 -2
View File
@@ -1,7 +1,7 @@
name: Bug Report
description: Create a report to help us improve
labels: ["📋 triage"]
type: '🐛 bug'
title: "[BUG]"
labels: ["bug :bug:"]
body:
- type: markdown
+2 -2
View File
@@ -1,7 +1,7 @@
name: Feature Request
description: Suggest an idea for this project
labels: ["📋 triage"]
type: '💡 feature'
title: "[FEATURE]"
labels: ["enhancement"]
body:
- type: textarea
+1 -1
View File
@@ -1,7 +1,7 @@
name: Question
description: Get help with errors or general questions
title: "[QUESTION]"
labels: ["question"]
type: '❔ question'
body:
- type: markdown
-6
View File
@@ -5,9 +5,6 @@ updates:
schedule:
interval: "weekly"
day: "friday"
labels:
- "⚙️ dependencies"
- "🔗 python"
# Updates the dependencies of the GitHub Actions workflows
- package-ecosystem: "github-actions"
@@ -15,6 +12,3 @@ updates:
schedule:
interval: "monthly"
day: "friday"
labels:
- "⚙️ dependencies"
- "🔗 github-actions"
+2 -2
View File
@@ -3,7 +3,7 @@
version: 1
labels:
- label: "⚙️ dependencies"
- label: "dependencies"
authors: ["dependabot[bot]", "pre-commit-ci[bot]"]
- label: "🛠 code-quality"
- label: "code quality"
authors: ["pre-commit-ci[bot]"]
+4 -7
View File
@@ -4,8 +4,6 @@ on:
pull_request:
types: [opened, reopened]
permissions: {}
jobs:
process-dependabot-prs:
permissions:
@@ -18,15 +16,14 @@ jobs:
- name: Fetch Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0
uses: dependabot/fetch-metadata@v2.1.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
persist-credentials: false
- name: Update Version Number in Other Files
uses: jacobtomlinson/gha-find-replace@f1069b438f125e5395d84d1c6fd3b559a7880cb5 # v3
uses: jacobtomlinson/gha-find-replace@v3
with:
find: ${{ steps.dependabot-metadata.outputs.previous-version }}
replace: ${{ steps.dependabot-metadata.outputs.new-version }}
@@ -34,7 +31,7 @@ jobs:
exclude: CHANGES.rst
- name: Commit & Push Changes to PR
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
uses: EndBug/add-and-commit@v9.1.4
with:
message: 'Update version number in other files'
committer_name: GitHub Actions
-41
View File
@@ -1,41 +0,0 @@
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.10']
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@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.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
+4 -18
View File
@@ -3,11 +3,6 @@ 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:
@@ -15,27 +10,18 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: ['3.10']
python-version: [3.9]
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
uses: actions/setup-python@v5
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
python -W ignore -m pip install -r requirements-all.txt
- name: Check Links
run: sphinx-build docs/source docs/build/html -W --keep-going -j auto -b linkcheck
- name: Upload linkcheck output
# Run also if the previous steps failed
if: always()
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: linkcheck-output
path: docs/build/html/output.*
+46
View File
@@ -0,0 +1,46 @@
name: Test Documentation Build
on:
pull_request:
paths:
- telegram/**
- docs/**
push:
branches:
- master
jobs:
test-sphinx-build:
name: test-sphinx-build
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.9]
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
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-all.txt
- name: Test autogeneration of admonitions
run: pytest -v --tb=short tests/docs/admonition_inserter.py
- name: Build docs
run: sphinx-build docs/source docs/build/html -W --keep-going -j auto
- name: Upload docs
uses: actions/upload-artifact@v4
with:
name: HTML Docs
retention-days: 7
path: |
# Exclude the .doctrees folder and .buildinfo file from the artifact
# since they are not needed and add to the size
docs/build/html/*
!docs/build/html/.doctrees
!docs/build/html/.buildinfo
-33
View File
@@ -1,33 +0,0 @@
name: GitHub Actions Security Analysis
on:
push:
branches:
- master
pull_request:
permissions: {}
jobs:
zizmor:
name: Security Analysis with zizmor
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
- 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
with:
sarif_file: results.sarif
category: zizmor
+1 -3
View File
@@ -4,8 +4,6 @@ on:
pull_request:
types: [opened]
permissions: {}
jobs:
pre-commit-ci:
permissions:
@@ -13,7 +11,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@v1.10.1
# Config file at .github/labeler.yml
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+1 -7
View File
@@ -4,17 +4,11 @@ on:
schedule:
- cron: '8 4 * * *'
permissions: {}
jobs:
lock:
runs-on: ubuntu-latest
permissions:
# For locking the threads
issues: write
pull-requests: write
steps:
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
- uses: dessant/lock-threads@v5.0.1
with:
github-token: ${{ github.token }}
issue-inactive-days: '7'
@@ -0,0 +1,19 @@
name: Warning maintainers
on:
pull_request_target:
paths:
- requirements.txt
- requirements-opts.txt
- .pre-commit-config.yaml
permissions:
pull-requests: write
jobs:
job:
runs-on: ubuntu-latest
name: about pre-commit and dependency change
steps:
- name: running the check
uses: Poolitzer/notifier-action@master
with:
notify-message: Hey! Looks like you edited the (optional) requirements or the pre-commit hooks. I'm just a friendly reminder to keep the additional dependencies for the hooks in sync with the requirements :)
repo-token: ${{ secrets.GITHUB_TOKEN }}
+18
View File
@@ -0,0 +1,18 @@
name: Warning maintainers
on:
pull_request_target:
paths:
- README.rst
- README_RAW.rst
permissions:
pull-requests: write
jobs:
job:
runs-on: ubuntu-latest
name: about readme change
steps:
- name: running the check
uses: Poolitzer/notifier-action@master
with:
notify-message: Hey! Looks like you edited README.rst or README_RAW.rst. I'm just a friendly reminder to apply relevant changes to both of those files :)
repo-token: ${{ secrets.GITHUB_TOKEN }}
-139
View File
@@ -1,139 +0,0 @@
name: Publish to PyPI
on:
# manually trigger the workflow
workflow_dispatch:
permissions: {}
jobs:
build:
name: Build Distribution
runs-on: ubuntu-latest
outputs:
TAG: ${{ steps.get_tag.outputs.TAG }}
permissions:
# for uploading artifacts
actions: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: "3.x"
- name: Install pypa/build
run: >-
python3 -m pip install build --user
- 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
with:
name: python-package-distributions
path: dist/
- name: Get Tag Name
id: get_tag
run: |
pip install .
TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
echo "TAG=$TAG" >> $GITHUB_OUTPUT
publish-to-pypi:
name: Publish to PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: release_pypi
url: https://pypi.org/p/python-telegram-bot
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
actions: read # for downloading artifacts
steps:
- name: Download all the dists
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: python-package-distributions
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
compute-signatures:
name: Compute SHA1 Sums and Sign with Sigstore
runs-on: ubuntu-latest
needs:
- publish-to-pypi
permissions:
id-token: write # IMPORTANT: mandatory for sigstore
actions: write # for up/downloading artifacts
steps:
- name: Download all the dists
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: python-package-distributions
path: dist/
- name: Compute SHA1 Sums
run: |
# Compute SHA1 sum of the distribution packages and save it to a file with the same name,
# but with .sha1 extension
for file in dist/*; do
sha1sum $file > $file.sha1
done
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@f514d46b907ebcd5bedc05145c03b69c1edd8b46 # v3.0.0
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Store the distribution packages and signatures
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: python-package-distributions-and-signatures
path: dist/
github-release:
name: Upload to GitHub Release
needs:
- build
- compute-signatures
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
actions: read # for downloading artifacts
steps:
- name: Download all the dists
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: python-package-distributions-and-signatures
path: dist/
- name: Create GitHub Release
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.
run: >-
gh release create
"$TAG"
--repo '${{ github.repository }}'
--generate-notes
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
"$TAG" dist/**
--repo '${{ github.repository }}'
-142
View File
@@ -1,142 +0,0 @@
name: Publish to Test PyPI
on:
# manually trigger the workflow
workflow_dispatch:
permissions: {}
jobs:
build:
name: Build Distribution
runs-on: ubuntu-latest
outputs:
TAG: ${{ steps.get_tag.outputs.TAG }}
permissions:
# for uploading artifacts
actions: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: "3.x"
- name: Install pypa/build
run: >-
python3 -m pip install build --user
- 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
with:
name: python-package-distributions
path: dist/
- name: Get Tag Name
id: get_tag
run: |
pip install .
TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
echo "TAG=$TAG" >> $GITHUB_OUTPUT
publish-to-test-pypi:
name: Publish to Test PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: release_test_pypi
url: https://test.pypi.org/p/python-telegram-bot
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
actions: read # for downloading artifacts
steps:
- name: Download all the dists
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: python-package-distributions
path: dist/
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
with:
repository-url: https://test.pypi.org/legacy/
compute-signatures:
name: Compute SHA1 Sums and Sign with Sigstore
runs-on: ubuntu-latest
needs:
- publish-to-test-pypi
permissions:
id-token: write # IMPORTANT: mandatory for sigstore
actions: write # for up/downloading artifacts
steps:
- name: Download all the dists
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: python-package-distributions
path: dist/
- name: Compute SHA1 Sums
run: |
# Compute SHA1 sum of the distribution packages and save it to a file with the same name,
# but with .sha1 extension
for file in dist/*; do
sha1sum $file > $file.sha1
done
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@f514d46b907ebcd5bedc05145c03b69c1edd8b46 # v3.0.0
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Store the distribution packages and signatures
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: python-package-distributions-and-signatures
path: dist/
github-test-release:
name: Upload to GitHub Release Draft
needs:
- build
- compute-signatures
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
actions: read # for downloading artifacts
steps:
- name: Download all the dists
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: python-package-distributions-and-signatures
path: dist/
- name: Create GitHub Release
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.
run: >-
gh release create
"$TAG"
--repo '${{ github.repository }}'
--generate-notes
--draft
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
"$TAG" dist/**
--repo '${{ github.repository }}'
+2 -7
View File
@@ -3,22 +3,17 @@ on:
schedule:
- cron: '42 2 * * *'
permissions: {}
jobs:
stale:
runs-on: ubuntu-latest
permissions:
# For adding labels and closing
issues: write
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
- uses: actions/stale@v9
with:
# PRs never get stale
days-before-stale: 3
days-before-close: 2
days-before-pr-stale: -1
stale-issue-label: '📋 stale'
stale-issue-label: 'stale'
only-labels: 'question'
stale-issue-message: ''
close-issue-message: 'This issue has been automatically closed due to inactivity. Feel free to comment in order to reopen or ask again in our Telegram support group at https://t.me/pythontelegrambotgroup.'
+6 -9
View File
@@ -11,8 +11,6 @@ on:
# 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
@@ -23,18 +21,17 @@ jobs:
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
uses: actions/setup-python@v5
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
python -W ignore -m pip install -r requirements.txt
python -W ignore -m pip install -r requirements-opts.txt
python -W ignore -m pip install -r requirements-dev.txt
- name: Compare to official api
run: |
pytest -v tests/test_official/test_official.py --junit-xml=.test_report_official.xml
@@ -45,7 +42,7 @@ jobs:
- name: Test Summary
id: test_summary
uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4
uses: test-summary/action@v2.3
if: always() # always run, even if tests fail
with:
paths: .test_report_official.xml
+64 -8
View File
@@ -3,21 +3,77 @@ on:
pull_request:
paths:
- telegram/**
- pyproject.toml
- .github/workflows/type_completeness.yml
- requirements.txt
- requirements-opts.txt
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
- uses: actions/checkout@v4
- run: git fetch --depth=1 # https://github.com/actions/checkout/issues/329#issuecomment-674881489
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
package-name: telegram
python-version: 3.12
pyright-version: ~=1.1.367
python-version: 3.9
cache: 'pip'
cache-dependency-path: '**/requirements*.txt'
- name: Install Pyright
run: |
python -W ignore -m pip install pyright~=1.1.316
- name: Get PR Completeness
# Must run before base completeness, as base completeness will checkout the base branch
# And we can't go back to the PR branch after that in case the PR is coming from a fork
run: |
pip install . -U
pyright --verifytypes telegram --ignoreexternal --outputjson > pr.json || true
pyright --verifytypes telegram --ignoreexternal > pr.readable || true
- name: Get Base Completeness
run: |
git checkout ${{ github.base_ref }}
pip install . -U
pyright --verifytypes telegram --ignoreexternal --outputjson > base.json || true
- name: Compare Completeness
uses: jannekem/run-python-script-action@v1
with:
script: |
import json
import os
from pathlib import Path
base = float(
json.load(open("base.json", "rb"))["typeCompleteness"]["completenessScore"]
)
pr = float(
json.load(open("pr.json", "rb"))["typeCompleteness"]["completenessScore"]
)
base_text = f"This PR changes type completeness from {round(base, 3)} to {round(pr, 3)}."
if base == 0:
text = f"Something is broken in the workflow. Reported type completeness is 0. 💥"
set_summary(text)
print(Path("pr.readable").read_text(encoding="utf-8"))
error(text)
exit(1)
if pr < (base - 0.001):
text = f"{base_text} ❌"
set_summary(text)
print(Path("pr.readable").read_text(encoding="utf-8"))
error(text)
exit(1)
elif pr > (base + 0.001):
text = f"{base_text} ✨"
set_summary(text)
if pr < 1:
print(Path("pr.readable").read_text(encoding="utf-8"))
print(text)
else:
text = f"{base_text} This is less than 0.1 percentage points. ✅"
set_summary(text)
print(Path("pr.readable").read_text(encoding="utf-8"))
print(text)
@@ -1,35 +0,0 @@
name: Check Type Completeness Monthly Run
on:
schedule:
# Run first friday of the month at 03:17 - odd time to spread load on GitHub Actions
- cron: '17 3 1-7 * 5'
permissions: {}
jobs:
test-type-completeness:
name: test-type-completeness
runs-on: ubuntu-latest
steps:
- uses: Bibo-Joshi/pyright-type-completeness@c85a67ff3c66f51dcbb2d06bfcf4fe83a57d69cc # v1.0.1
id: pyright-type-completeness
with:
package-name: telegram
python-version: 3.12
pyright-version: ~=1.1.367
- name: Check Output
uses: jannekem/run-python-script-action@bbfca66c612a28f3eeca0ae40e1f810265e2ea68 # v1.7
env:
TYPE_COMPLETENESS: ${{ steps.pyright-type-completeness.outputs.base-completeness-score }}
with:
script: |
import os
completeness = float(os.getenv("TYPE_COMPLETENESS"))
if completeness >= 1:
exit(0)
text = f"Type Completeness Decreased to {completeness}. ❌"
error(text)
set_summary(text)
exit(1)
+25 -32
View File
@@ -4,9 +4,9 @@ on:
paths:
- telegram/**
- tests/**
- .github/workflows/unit_tests.yml
- pyproject.toml
- requirements-unit-tests.txt
- requirements.txt
- requirements-opts.txt
- requirements-dev.txt
push:
branches:
- master
@@ -14,23 +14,19 @@ on:
# Run monday and friday morning at 03:07 - odd time to spread load on GitHub Actions
- cron: '7 3 * * 1,5'
permissions: {}
jobs:
pytest:
name: pytest
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: False
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
@@ -39,9 +35,9 @@ jobs:
run: |
python -W ignore -m pip install --upgrade pip
python -W ignore -m pip install -U pytest-cov
python -W ignore -m pip install .
python -W ignore -m pip install -r requirements-unit-tests.txt
python -W ignore -m pip install pytest-xdist
python -W ignore -m pip install -r requirements.txt
python -W ignore -m pip install -r requirements-dev.txt
python -W ignore -m pip install pytest-xdist[psutil]
- name: Test with pytest
# We run 4 different suites here
@@ -61,18 +57,21 @@ jobs:
# - without socks support
# - without http2 support
TO_TEST="test_no_passport.py or test_datetime.py or test_defaults.py or test_jobqueue.py or test_applicationbuilder.py or test_ratelimiter.py or test_updater.py or test_callbackdatacache.py or test_request.py"
pytest -v --cov -k "${TO_TEST}" --junit-xml=.test_report_no_optionals_junit.xml
opt_dep_status=$?
pytest -v --cov -k "${TO_TEST}"
# Rerun only failed tests (--lf), and don't run any tests if none failed (--lfnf=none)
pytest -v --cov --cov-append -k "${TO_TEST}" --lf --lfnf=none --junit-xml=.test_report_no_optionals.xml
# No tests were selected, convert returned status code to 0
opt_dep_status=$(( $? == 5 ? 0 : $? ))
# Test the rest
export TEST_WITH_OPT_DEPS='true'
# need to manually install pytz here, because it's no longer in the optional reqs
pip install .[all] pytz
# `-n auto --dist worksteal` uses pytest-xdist to run tests on multiple CPU
# workers. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness.
pytest -v --cov --cov-append -n auto --dist worksteal --junit-xml=.test_report_optionals_junit.xml
main_status=$?
pip install -r requirements-opts.txt
# `-n auto --dist loadfile` uses pytest-xdist to run each test file on a different CPU
# worker. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness, specially on python 3.7 with --dist=loadgroup.
pytest -v --cov --cov-append -n auto --dist loadfile
pytest -v --cov --cov-append -n auto --dist loadfile --lf --lfnf=none --junit-xml=.test_report_optionals.xml
main_status=$(( $? == 5 ? 0 : $? ))
# exit with non-zero status if any of the two pytest runs failed
exit $(( ${opt_dep_status} || ${main_status} ))
env:
@@ -84,23 +83,17 @@ jobs:
- name: Test Summary
id: test_summary
uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4
uses: test-summary/action@v2.3
if: always() # always run, even if tests fail
with:
paths: |
.test_report_no_optionals_junit.xml
.test_report_optionals_junit.xml
.test_report_no_optionals.xml
.test_report_optionals.xml
- name: Submit coverage
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
uses: codecov/codecov-action@v4
with:
env_vars: OS,PYTHON
name: ${{ matrix.os }}-${{ matrix.python-version }}
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload test results to Codecov
uses: codecov/test-results-action@4e79e65778be1cecd5df25e14af1eafb6df80ea9 # v1.0.2
if: ${{ !cancelled() }}
with:
files: .test_report_no_optionals_junit.xml,.test_report_optionals_junit.xml
token: ${{ secrets.CODECOV_TOKEN }}
-4
View File
@@ -67,7 +67,6 @@ docs/_build/
# PyBuilder
target/
.idea/
.run/
# Sublime Text 2
*.sublime*
@@ -93,6 +92,3 @@ telegram.jpg
# virtual env
venv*
# environment manager:
.mise.toml
+15 -15
View File
@@ -1,4 +1,4 @@
# Make sure that the additional_dependencies here match pyproject.toml
# Make sure that the additional_dependencies here match requirements(-opts).txt
ci:
autofix_prs: false
@@ -7,7 +7,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.8.6'
rev: 'v0.4.3'
hooks:
- id: ruff
name: ruff
@@ -15,21 +15,21 @@ repos:
- httpx~=0.27
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1,<1.3
- cachetools~=5.3.3
- aiolimiter~=1.1.0
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.10.0
rev: 24.4.2
hooks:
- id: black
args:
- --diff
- --check
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
rev: 7.0.0
hooks:
- id: flake8
- repo: https://github.com/PyCQA/pylint
rev: v3.3.3
rev: v3.1.0
hooks:
- id: pylint
files: ^(?!(tests|docs)).*\.py$
@@ -37,11 +37,11 @@ repos:
- httpx~=0.27
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1,<1.3
- cachetools~=5.3.3
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1
rev: v1.10.0
hooks:
- id: mypy
name: mypy-ptb
@@ -53,8 +53,8 @@ repos:
- httpx~=0.27
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1,<1.3
- cachetools~=5.3.3
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- id: mypy
name: mypy-examples
@@ -65,14 +65,14 @@ repos:
additional_dependencies:
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- cachetools~=5.3.3
- . # this basically does `pip install -e .`
- repo: https://github.com/asottile/pyupgrade
rev: v3.19.1
rev: v3.15.2
hooks:
- id: pyupgrade
args:
- --py39-plus
- --py38-plus
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
+1 -1
View File
@@ -18,7 +18,7 @@ python:
install:
- method: pip
path: .
- requirements: requirements-dev-all.txt
- requirements: docs/requirements-docs.txt
build:
os: ubuntu-22.04
-12
View File
@@ -23,7 +23,6 @@ The following wonderful people contributed directly or indirectly to this projec
- `Abdelrahman <https://github.com/aelkheir>`_
- `Abshar <https://github.com/abxhr>`_
- `Abubakar Alaya <https://github.com/Ecode2>`_
- `Alateas <https://github.com/alateas>`_
- `Ales Dokshanin <https://github.com/alesdokshanin>`_
- `Alexandre <https://github.com/xTudoS>`_
@@ -31,7 +30,6 @@ The following wonderful people contributed directly or indirectly to this projec
- `Ambro17 <https://github.com/Ambro17>`_
- `Andrej Zhilenkov <https://github.com/Andrej730>`_
- `Anton Tagunov <https://github.com/anton-tagunov>`_
- `Anya Marcano <https://github.com/AnyaMarcanito>`
- `Avanatiker <https://github.com/Avanatiker>`_
- `Balduro <https://github.com/Balduro>`_
- `Bibo-Joshi <https://github.com/Bibo-Joshi>`_
@@ -59,14 +57,11 @@ The following wonderful people contributed directly or indirectly to this projec
- `gamgi <https://github.com/gamgi>`_
- `Gauthamram Ravichandran <https://github.com/GauthamramRavichandran>`_
- `Harshil <https://github.com/harshil21>`_
- `Henry Galue <https://github.com/henryg311>`
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Iulian Onofrei <https://github.com/revolter>`_
- `Jainam Oswal <https://github.com/jainamoswal>`_
- `Jasmin Bom <https://github.com/jsmnbom>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `Jeamhowards Montiel <https://github.com/Jeam-zx>`
- `jeffffc <https://github.com/jeffffc>`_
- `Jelle Besseling <https://github.com/pingiun>`_
- `jh0ker <https://github.com/jh0ker>`_
@@ -75,7 +70,6 @@ The following wonderful people contributed directly or indirectly to this projec
- `Joscha Götzer <https://github.com/Rostgnom>`_
- `jossalgon <https://github.com/jossalgon>`_
- `JRoot3D <https://github.com/JRoot3D>`_
- `Juan Cuevas <https://github.com/cuevasrja>`
- `kenjitagawa <https://github.com/kenjitagawa>`_
- `kennethcheo <https://github.com/kennethcheo>`_
- `Kirill Vasin <https://github.com/vasinkd>`_
@@ -85,16 +79,13 @@ The following wonderful people contributed directly or indirectly to this projec
- `LRezende <https://github.com/lrezende>`_
- `Luca Bellanti <https://github.com/Trifase>`_
- `Lucas Molinari <https://github.com/lucasmolinari>`_
- `Luis Pérez <https://github.com/nemacysts>`_
- `macrojames <https://github.com/macrojames>`_
- `Matheus Lemos <https://github.com/mlemosf>`_
- `Michael Dix <https://github.com/Eisberge>`_
- `Michael Elovskikh <https://github.com/wronglink>`_
- `Miguel C. R. <https://github.com/MiguelX413>`_
- `Miguel Salomon <https://github.com/Migueldsc12>`
- `miles <https://github.com/miles170>`_
- `Mischa Krüger <https://github.com/Makman2>`_
- `Mohd Yusuf <https://github.com/mohdyusuf2312>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `neurrone <https://github.com/neurrone>`_
- `NikitaPirate <https://github.com/NikitaPirate>`_
@@ -105,7 +96,6 @@ The following wonderful people contributed directly or indirectly to this projec
- `Oleg Sushchenko <https://github.com/feuillemorte>`_
- `Or Bin <https://github.com/OrBin>`_
- `overquota <https://github.com/overquota>`_
- `Pablo Martinez <https://github.com/elpekenin>`_
- `Paradox <https://github.com/paradox70>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Paul Larsen <https://github.com/PaulSonOfLars>`_
@@ -117,13 +107,11 @@ The following wonderful people contributed directly or indirectly to this projec
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Riko Naka <https://github.com/rikonaka>`_
- `Rizlas <https://github.com/rizlas>`_
- Snehashish Biswas
- `Sahil Sharma <https://github.com/sahilsharma811>`_
- `Sam Mosleh <https://github.com/sam-mosleh>`_
- `Sascha <https://github.com/saschalalala>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `Shivam Saini <https://github.com/shivamsn97>`_
- `Siloé Garcez <https://github.com/roast-lord>`_
- `Simon Schürrle <https://github.com/SitiSchu>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `syntx <https://github.com/syntx>`_
-363
View File
@@ -4,369 +4,6 @@
Changelog
=========
Version 21.11.1
===============
*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
=============
*Released 2025-01-03*
This is the technical changelog for version 21.10. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 8.2 (:pr:`4633`)
- Bump ``apscheduler`` & Deprecate ``pytz`` Support (:pr:`4582`)
New Features
------------
- Add Parameter ``pattern`` to ``JobQueue.jobs()`` (:pr:`4613` closes :issue:`4544`)
- Allow Input of Type ``Sticker`` for Several Methods (:pr:`4616` closes :issue:`4580`)
Bug Fixes
---------
- Ensure Forward Compatibility of ``Gift`` and ``Gifts`` (:pr:`4634` closes :issue:`4637`)
Documentation Improvements & Internal Changes
---------------------------------------------
- Use Custom Labels for ``dependabot`` PRs (:pr:`4621`)
- Remove Redundant ``pylint`` Suppressions (:pr:`4628`)
- Update Copyright to 2025 (:pr:`4631`)
- Refactor Module Structure and Tests for Star Payments Classes (:pr:`4615` closes :issue:`4593`)
- Unify ``datetime`` Imports (:pr:`4605` by `cuevasrja <https://github.com/cuevasrja>`_ closes :issue:`4577`)
- Add Static Security Analysis of GitHub Actions Workflows (:pr:`4606`)
Dependency Updates
------------------
- Bump ``astral-sh/setup-uv`` from 4.2.0 to 5.1.0 (:pr:`4625`)
- Bump ``codecov/codecov-action`` from 5.1.1 to 5.1.2 (:pr:`4622`)
- Bump ``actions/upload-artifact`` from 4.4.3 to 4.5.0 (:pr:`4623`)
- Bump ``github/codeql-action`` from 3.27.9 to 3.28.0 (:pr:`4624`)
Version 21.9
============
*Released 2024-12-07*
This is the technical changelog for version 21.9. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 8.1 (:pr:`4594` closes :issue:`4592`)
Minor Changes
-------------
- Use ``MessageLimit.DEEP_LINK_LENGTH`` in ``helpers.create_deep_linked_url`` (:pr:`4597` by `nemacysts <https://github.com/nemacysts>`_)
- Allow ``Sequence`` Input for ``allowed_updates`` in ``Application`` and ``Updater`` Methods (:pr:`4589` by `nemacysts <https://github.com/nemacysts>`_)
Dependency Updates
------------------
- Update ``aiolimiter`` requirement from ~=1.1.0 to >=1.1,<1.3 (:pr:`4595`)
- Bump ``pytest`` from 8.3.3 to 8.3.4 (:pr:`4596`)
- Bump ``codecov/codecov-action`` from 4 to 5 (:pr:`4585`)
- Bump ``pylint`` to v3.3.2 to Improve Python 3.13 Support (:pr:`4590` by `nemacysts <https://github.com/nemacysts>`_)
- Bump ``srvaroa/labeler`` from 1.11.1 to 1.12.0 (:pr:`4586`)
Version 21.8
============
*Released 2024-12-01*
This is the technical changelog for version 21.8. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 8.0 (:pr:`4568`, :pr:`4566` closes :issue:`4567`, :pr:`4572`, :pr:`4571`, :pr:`4570`, :pr:`4576`, :pr:`4574`)
Documentation Improvements
--------------------------
- Documentation Improvements (:pr:`4565` by Snehashish06, :pr:`4573`)
Version 21.7
============
*Released 2024-11-04*
This is the technical changelog for version 21.7. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.11 (:pr:`4546` closes :issue:`4543`)
- Add ``Message.reply_paid_media`` (:pr:`4551`)
- Drop Support for Python 3.8 (:pr:`4398` by `elpekenin <https://github.com/elpekenin>`_)
Minor Changes
-------------
- Allow ``Sequence`` in ``Application.add_handlers`` (:pr:`4531` by `roast-lord <https://github.com/roast-lord>`_ closes :issue:`4530`)
- Improve Exception Handling in ``File.download_*`` (:pr:`4542`)
- Use Stable Python 3.13 Release in Test Suite (:pr:`4535`)
Documentation Improvements
--------------------------
- Documentation Improvements (:pr:`4536` by `Ecode2 <https://github.com/Ecode2>`_, :pr:`4556`)
- Fix Linkcheck Workflow (:pr:`4545`)
- Use ``sphinx-build-compatibility`` to Keep Sphinx Compatibility (:pr:`4492`)
Internal Changes
----------------
- Improve Test Instability Caused by ``Message`` Fixtures (:pr:`4507`)
- Stabilize Some Flaky Tests (:pr:`4500`)
- Reduce Creation of HTTP Clients in Tests (:pr:`4493`)
- Update ``pytest-xdist`` Usage (:pr:`4491`)
- Fix Failing Tests by Making Them Independent (:pr:`4494`)
- Introduce Codecov's Test Analysis (:pr:`4487`)
- Maintenance Work on ``Bot`` Tests (:pr:`4489`)
- Introduce ``conftest.py`` for File Related Tests (:pr:`4488`)
- Update Issue Templates to Use Issue Types (:pr:`4553`)
- Update Automation to Label Changes (:pr:`4552`)
Dependency Updates
------------------
- Bump ``srvaroa/labeler`` from 1.11.0 to 1.11.1 (:pr:`4549`)
- Bump ``sphinx`` from 8.0.2 to 8.1.3 (:pr:`4532`)
- Bump ``sphinxcontrib-mermaid`` from 0.9.2 to 1.0.0 (:pr:`4529`)
- Bump ``srvaroa/labeler`` from 1.10.1 to 1.11.0 (:pr:`4509`)
- Bump ``Bibo-Joshi/pyright-type-completeness`` from 1.0.0 to 1.0.1 (:pr:`4510`)
Version 21.6
============
*Released 2024-09-19*
This is the technical changelog for version 21.6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
New Features
------------
- Full Support for Bot API 7.10 (:pr:`4461` closes :issue:`4459`, :pr:`4460`, :pr:`4463` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4464`)
- Add Parameter ``httpx_kwargs`` to ``HTTPXRequest`` (:pr:`4451` closes :issue:`4424`)
Minor Changes
-------------
- Improve Type Completeness (:pr:`4466`)
Internal Changes
----------------
- Update Python 3.13 Test Suite to RC2 (:pr:`4471`)
- Enforce the ``offline_bot`` Fixture in ``Test*WithoutRequest`` (:pr:`4465`)
- Make Tests for ``telegram.ext`` Independent of Networking (:pr:`4454`)
- Rename Testing Base Classes (:pr:`4453`)
Dependency Updates
------------------
- Bump ``pytest`` from 8.3.2 to 8.3.3 (:pr:`4475`)
Version 21.5
============
*Released 2024-09-01*
This is the technical changelog for version 21.5. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.9 (:pr:`4429`)
- Full Support for Bot API 7.8 (:pr:`4408`)
New Features
------------
- Add ``MessageEntity.shift_entities`` and ``MessageEntity.concatenate`` (:pr:`4376` closes :issue:`4372`)
- Add Parameter ``game_pattern`` to ``CallbackQueryHandler`` (:pr:`4353` by `jainamoswal <https://github.com/jainamoswal>`_ closes :issue:`4269`)
- Add Parameter ``read_file_handle`` to ``InputFile`` (:pr:`4388` closes :issue:`4339`)
Documentation Improvements
--------------------------
- Bugfix for "Available In" Admonitions (:pr:`4413`)
- Documentation Improvements (:pr:`4400` closes :issue:`4446`, :pr:`4448` by `Palaptin <https://github.com/Palaptin>`_)
- Document Return Types of ``RequestData`` Members (:pr:`4396`)
- Add Introductory Paragraphs to Telegram Types Subsections (:pr:`4389` by `mohdyusuf2312 <https://github.com/mohdyusuf2312>`_ closes :issue:`4380`)
- Start Adapting to RTD Addons (:pr:`4386`)
Minor and Internal Changes
---------------------------
- Remove Surplus Logging from ``Updater`` Network Loop (:pr:`4432` by `MartinHjelmare <https://github.com/MartinHjelmare>`_)
- Add Internal Constants for Encodings (:pr:`4378` by `elpekenin <https://github.com/elpekenin>`_)
- Improve PyPI Automation (:pr:`4375` closes :issue:`4373`)
- Update Test Suite to New Test Channel Setup (:pr:`4435`)
- Improve Fixture Usage in ``test_message.py`` (:pr:`4431` by `Palaptin <https://github.com/Palaptin>`_)
- Update Python 3.13 Test Suite to RC1 (:pr:`4415`)
- Bump ``ruff`` and Add New Rules (:pr:`4416`)
Dependency Updates
------------------
- Update ``cachetools`` requirement from <5.5.0,>=5.3.3 to >=5.3.3,<5.6.0 (:pr:`4437`)
- Bump ``sphinx`` from 7.4.7 to 8.0.2 and ``furo`` from 2024.7.18 to 2024.8.6 (:pr:`4412`)
- Bump ``test-summary/action`` from 2.3 to 2.4 (:pr:`4410`)
- Bump ``pytest`` from 8.2.2 to 8.3.2 (:pr:`4403`)
- Bump ``dependabot/fetch-metadata`` from 2.1.0 to 2.2.0 (:pr:`4411`)
- Update ``cachetools`` requirement from ~=5.3.3 to >=5.3.3,<5.5.0 (:pr:`4390`)
- Bump ``sphinx`` from 7.3.7 to 7.4.7 (:pr:`4395`)
- Bump ``furo`` from 2024.5.6 to 2024.7.18 (:pr:`4392`)
Version 21.4
============
*Released 2024-07-12*
This is the technical changelog for version 21.4. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.5 (:pr:`4328`, :pr:`4316`, :pr:`4315`, :pr:`4312` closes :issue:`4310`, :pr:`4311`)
- Full Support for Bot API 7.6 (:pr:`4333` closes :issue:`4331`, :pr:`4344`, :pr:`4341`, :pr:`4334`, :pr:`4335`, :pr:`4351`, :pr:`4342`, :pr:`4348`)
- Full Support for Bot API 7.7 (:pr:`4356` closes :issue:`4355`)
- Drop ``python-telegram-bot-raw`` And Switch to ``pyproject.toml`` Based Packaging (:pr:`4288` closes :issue:`4129` and :issue:`4296`)
- Deprecate Inclusion of ``successful_payment`` in ``Message.effective_attachment`` (:pr:`4365` closes :issue:`4350`)
New Features
------------
- Add Support for Python 3.13 Beta (:pr:`4253`)
- Add ``filters.PAID_MEDIA`` (:pr:`4357`)
- Log Received Data on Deserialization Errors (:pr:`4304`)
- Add ``MessageEntity.adjust_message_entities_to_utf_16`` Utility Function (:pr:`4323` by `Antares0982 <https://github.com/Antares0982>`_ closes :issue:`4319`)
- Make Argument ``bot`` of ``TelegramObject.de_json`` Optional (:pr:`4320`)
Documentation Improvements
--------------------------
- Documentation Improvements (:pr:`4303` closes :issue:`4301`)
- Restructure Readme (:pr:`4362`)
- Fix Link-Check Workflow (:pr:`4332`)
Internal Changes
----------------
- Automate PyPI Releases (:pr:`4364` closes :issue:`4318`)
- Add ``mise-en-place`` to ``.gitignore`` (:pr:`4300`)
- Use a Composite Action for Testing Type Completeness (:pr:`4367`)
- Stabilize Some Concurrency Usages in Test Suite (:pr:`4360`)
- Add a Test Case for ``MenuButton`` (:pr:`4363`)
- Extend ``SuccessfulPayment`` Test (:pr:`4349`)
- Small Fixes for ``test_stars.py`` (:pr:`4347`)
- Use Python 3.13 Beta 3 in Test Suite (:pr:`4336`)
Dependency Updates
------------------
- Bump ``ruff`` and Add New Rules (:pr:`4329`)
- Bump ``pre-commit`` Hooks to Latest Versions (:pr:`4337`)
- Add Lower Bound for ``flaky`` Dependency (:pr:`4322` by `Palaptin <https://github.com/Palaptin>`_)
- Bump ``pytest`` from 8.2.1 to 8.2.2 (:pr:`4294`)
Version 21.3
============
*Released 2024-06-07*
This is the technical changelog for version 21.3. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.4 (:pr:`4286`, :pr:`4276` closes :issue:`4275`, :pr:`4285`, :pr:`4283`, :pr:`4280`, :pr:`4278`, :pr:`4279`)
- Deprecate ``python-telegram-bot-raw`` (:pr:`4270`)
- Remove Functionality Deprecated in Bot API 7.3 (:pr:`4266` closes :issue:`4244`)
New Features
------------
- Add Parameter ``chat_id`` to ``ChatMemberHandler`` (:pr:`4290` by `uniquetrij <https://github.com/uniquetrij>`_ closes :issue:`4287`)
Documentation Improvements
--------------------------
- Documentation Improvements (:pr:`4264` closes :issue:`4240`)
Internal Changes
----------------
- Add ``setuptools`` to ``requirements-dev.txt`` (:pr:`4282`)
- Update Settings for pre-commit.ci (:pr:`4265`)
Dependency Updates
------------------
- Bump ``pytest`` from 8.2.0 to 8.2.1 (:pr:`4272`)
Version 21.2
============
+1
View File
@@ -0,0 +1 @@
include LICENSE LICENSE.lesser requirements.txt requirements-opts.txt README_RAW.rst telegram/py.typed
+35 -50
View File
@@ -1,3 +1,6 @@
..
Make sure to apply any changes to this file to README_RAW.rst as well!
.. image:: https://raw.githubusercontent.com/python-telegram-bot/logos/master/logo-text/png/ptb-logo-text_768.png
:align: center
:target: https://python-telegram-bot.org
@@ -11,7 +14,7 @@
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-8.3-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API version
@@ -66,36 +69,30 @@ We have a vibrant community of developers helping each other in our `Telegram gr
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
Introduction
------------
============
This library provides a pure Python, asynchronous interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions **3.9+**.
It's compatible with Python versions **3.8+**.
In addition to the pure API implementation, this library features several convenience methods and shortcuts as well as a number of high-level classes to
In addition to the pure API implementation, this library features a number of high-level classes to
make the development of bots easy and straightforward. These classes are contained in the
``telegram.ext`` submodule.
After installing_ the library, be sure to check out the section on `working with PTB`_.
A pure API implementation *without* ``telegram.ext`` is available as the standalone package ``python-telegram-bot-raw``. `See here for details. <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/README_RAW.rst>`_
Note
----
Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conjunction will result in undesired side-effects, so only install *one* of both.
Telegram API support
~~~~~~~~~~~~~~~~~~~~
====================
All types and methods of the Telegram Bot API **8.3** 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
~~~~~~~~~~~~~~~~
- `Fully asynchronous <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Concurrency>`_
- Convenient shortcut methods, e.g. `Message.reply_text <https://docs.python-telegram-bot.org/en/stable/telegram.message.html#telegram.Message.reply_text>`_
- `Fully annotated with static type hints <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Type-Checking>`_
- `Customizable and extendable interface <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Architecture>`_
- Seamless integration with `webhooks <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Webhooks>`_ and `polling <https://docs.python-telegram-bot.org/en/stable/telegram.ext.application.html#telegram.ext.Application.run_polling>`_
- `Comprehensive documentation and examples <#working-with-ptb>`_
All types and methods of the Telegram Bot API **7.3** are supported.
Installing
----------
==========
You can install or upgrade ``python-telegram-bot`` via
@@ -111,27 +108,22 @@ You can also install ``python-telegram-bot`` from source, though this is usually
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ cd python-telegram-bot
$ pip install build
$ python -m build
$ python setup.py install
Verifying Releases
~~~~~~~~~~~~~~~~~~
------------------
To enable you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team, we have taken the following measures.
Starting with v21.4, all releases are signed via `sigstore <https://www.sigstore.dev>`_.
The corresponding signature files are uploaded to the `GitHub releases page`_.
To verify the signature, please install the `sigstore Python client <https://pypi.org/project/sigstore/>`_ and follow the instructions for `verifying signatures from GitHub Actions <https://github.com/sigstore/sigstore-python?tab=readme-ov-file>`_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``.
Earlier releases are signed with a GPG key.
The signatures are uploaded to both the `GitHub releases page`_ and the `PyPI project <https://pypi.org/project/python-telegram-bot/>`_ and end with a suffix ``.asc``.
We sign all the releases with a GPG key.
The signatures are uploaded to both the `GitHub releases page <https://github.com/python-telegram-bot/python-telegram-bot/releases>`_ and the `PyPI project <https://pypi.org/project/python-telegram-bot/>`_ and end with a suffix ``.asc``.
Please find the public keys `here <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/public_keys>`_.
The keys are named in the format ``<first_version>-<last_version>.gpg``.
The keys are named in the format ``<first_version>-<last_version>.gpg`` or ``<first_version>-current.gpg`` if the key is currently being used for new releases.
In addition, the GitHub release page also contains the sha1 hashes of the release files in the files with the suffix ``.sha1``.
This allows you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team.
Dependencies & Their Versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------------------------
``python-telegram-bot`` tries to use as few 3rd party dependencies as possible.
However, for some features using a 3rd party library is more sane than implementing the functionality again.
@@ -155,10 +147,10 @@ PTB can be installed with optional dependencies:
* ``pip install "python-telegram-bot[passport]"`` installs the `cryptography>=39.0.1 <https://cryptography.io/en/stable>`_ library. Use this, if you want to use Telegram Passport related functionality.
* ``pip install "python-telegram-bot[socks]"`` installs `httpx[socks] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to work behind a Socks5 server.
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1,<1.3 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<5.6.0 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler>=3.10.4,<3.12.0 <https://apscheduler.readthedocs.io/en/3.x/>`_ library. Use this, if you want to use the ``telegram.ext.JobQueue``.
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools~=5.3.3 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.
@@ -167,19 +159,14 @@ Additionally, two shortcuts are provided:
* ``pip install "python-telegram-bot[all]"`` installs all optional dependencies.
* ``pip install "python-telegram-bot[ext]"`` installs all optional dependencies that are related to ``telegram.ext``, i.e. ``[rate-limiter, webhooks, callback-data, job-queue]``.
Working with PTB
----------------
Once you have installed the library, you can begin working with it - so let's get started!
Quick Start
~~~~~~~~~~~
===========
Our Wiki contains an `Introduction to the API <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API>`_ explaining how the pure Bot API can be accessed via ``python-telegram-bot``.
Moreover, the `Tutorial: Your first Bot <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions---Your-first-Bot>`_ gives an introduction on how chatbots can be easily programmed with the help of the ``telegram.ext`` module.
Resources
~~~~~~~~~
=========
- The `package documentation <https://docs.python-telegram-bot.org/>`_ is the technical reference for ``python-telegram-bot``.
It contains descriptions of all available classes, modules, methods and arguments as well as the `changelog <https://docs.python-telegram-bot.org/changelog.html>`_.
@@ -190,7 +177,7 @@ Resources
- The `official Telegram Bot API documentation <https://core.telegram.org/bots/api>`_ is of course always worth a read.
Getting help
~~~~~~~~~~~~
============
If the resources mentioned above don't answer your questions or simply overwhelm you, there are several ways of getting help.
@@ -201,7 +188,7 @@ If the resources mentioned above don't answer your questions or simply overwhelm
3. You can even ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
Concurrency
~~~~~~~~~~~
===========
Since v20.0, ``python-telegram-bot`` is built on top of Pythons ``asyncio`` module.
Because ``asyncio`` is in general single-threaded, ``python-telegram-bot`` does currently not aim to be thread-safe.
@@ -214,22 +201,20 @@ Noteworthy parts of ``python-telegram-bots`` API that are likely to cause issues
* all classes in the ``telegram.ext.filters`` module that allow to add/remove allowed users/chats at runtime
Contributing
------------
============
Contributions of all sizes are welcome.
Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started.
You can also help by `reporting bugs or feature requests <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_.
Donating
--------
========
Occasionally we are asked if we accept donations to support the development.
While we appreciate the thought, maintaining PTB is our hobby, and we have almost no running costs for it. We therefore have nothing set up to accept donations.
If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead.
License
-------
=======
You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 <https://www.gnu.org/licenses/lgpl-3.0.html>`_.
Derivative works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
.. _`GitHub releases page`: https://github.com/python-telegram-bot/python-telegram-bot/releases
Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
+206
View File
@@ -0,0 +1,206 @@
..
Make sure to apply any changes to this file to README.rst as well!
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-raw-logo-text_768.png?raw=true
:align: center
:target: https://python-telegram-bot.org
:alt: python-telegram-bot-raw Logo
.. image:: https://img.shields.io/pypi/v/python-telegram-bot-raw.svg
:target: https://pypi.org/project/python-telegram-bot-raw/
:alt: PyPi Package Version
.. image:: https://img.shields.io/pypi/pyversions/python-telegram-bot-raw.svg
:target: https://pypi.org/project/python-telegram-bot-raw/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API version
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot-raw
:target: https://pypistats.org/packages/python-telegram-bot-raw
:alt: PyPi Package Monthly Download
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=stable
:target: https://docs.python-telegram-bot.org/
:alt: Documentation Status
.. image:: https://img.shields.io/pypi/l/python-telegram-bot-raw.svg
:target: https://www.gnu.org/licenses/lgpl-3.0.html
:alt: LGPLv3 License
.. image:: https://github.com/python-telegram-bot/python-telegram-bot/actions/workflows/unit_tests.yml/badge.svg?branch=master
:target: https://github.com/python-telegram-bot/python-telegram-bot/
:alt: Github Actions workflow
.. image:: https://codecov.io/gh/python-telegram-bot/python-telegram-bot/branch/master/graph/badge.svg
:target: https://app.codecov.io/gh/python-telegram-bot/python-telegram-bot
:alt: Code coverage
.. image:: https://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: https://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Median time to resolve an issue
.. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968
:target: https://app.codacy.com/gh/python-telegram-bot/python-telegram-bot/dashboard
:alt: Code quality: Codacy
.. image:: https://results.pre-commit.ci/badge/github/python-telegram-bot/python-telegram-bot/master.svg
:target: https://results.pre-commit.ci/latest/github/python-telegram-bot/python-telegram-bot/master
:alt: pre-commit.ci status
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code Style: Black
.. image:: https://img.shields.io/badge/Telegram-Channel-blue.svg?logo=telegram
:target: https://t.me/pythontelegrambotchannel
:alt: Telegram Channel
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram
:target: https://telegram.me/pythontelegrambotgroup
:alt: Telegram Group
We have made you a wrapper you can't refuse
We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
Introduction
============
This library provides a pure Python, asynchronous interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions **3.8+**.
``python-telegram-bot-raw`` is part of the `python-telegram-bot <https://python-telegram-bot.org>`_ ecosystem and provides the pure API functionality extracted from PTB. It therefore does not have independent release schedules, changelogs or documentation.
Note
----
Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conjunction will result in undesired side-effects, so only install *one* of both.
Telegram API support
====================
All types and methods of the Telegram Bot API **7.3** are supported.
Installing
==========
You can install or upgrade ``python-telegram-bot`` via
.. code:: shell
$ pip install python-telegram-bot-raw --upgrade
To install a pre-release, use the ``--pre`` `flag <https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-pre>`_ in addition.
You can also install ``python-telegram-bot-raw`` from source, though this is usually not necessary.
.. code:: shell
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ cd python-telegram-bot
$ python setup_raw.py install
Note
----
Installing the ``.tar.gz`` archive available on PyPi directly via ``pip`` will *not* work as expected, as ``pip`` does not recognize that it should use ``setup_raw.py`` instead of ``setup.py``.
Verifying Releases
------------------
We sign all the releases with a GPG key.
The signatures are uploaded to both the `GitHub releases page <https://github.com/python-telegram-bot/python-telegram-bot/releases>`_ and the `PyPI project <https://pypi.org/project/python-telegram-bot/>`_ and end with a suffix ``.asc``.
Please find the public keys `here <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/public_keys>`_.
The keys are named in the format ``<first_version>-<last_version>.gpg`` or ``<first_version>-current.gpg`` if the key is currently being used for new releases.
In addition, the GitHub release page also contains the sha1 hashes of the release files in the files with the suffix ``.sha1``.
This allows you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team.
Dependencies & Their Versions
-----------------------------
``python-telegram-bot`` tries to use as few 3rd party dependencies as possible.
However, for some features using a 3rd party library is more sane than implementing the functionality again.
As these features are *optional*, the corresponding 3rd party dependencies are not installed by default.
Instead, they are listed as optional dependencies.
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
The only required dependency is `httpx ~= 0.27 <https://www.python-httpx.org>`_ for
``telegram.request.HTTPXRequest``, the default networking backend.
``python-telegram-bot`` is most useful when used along with additional libraries.
To minimize dependency conflicts, we try to be liberal in terms of version requirements on the (optional) dependencies.
On the other hand, we have to ensure stability of ``python-telegram-bot``, which is why we do apply version bounds.
If you encounter dependency conflicts due to these bounds, feel free to reach out.
Optional Dependencies
#####################
PTB can be installed with optional dependencies:
* ``pip install "python-telegram-bot-raw[passport]"`` installs the `cryptography>=39.0.1 <https://cryptography.io/en/stable>`_ library. Use this, if you want to use Telegram Passport related functionality.
* ``pip install "python-telegram-bot-raw[socks]"`` installs `httpx[socks] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to work behind a Socks5 server.
* ``pip install "python-telegram-bot-raw[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot-raw[passport,socks]"``.
Additionally, the shortcut ``pip install "python-telegram-bot-raw[all]"`` installs all optional dependencies.
Quick Start
===========
Our Wiki contains an `Introduction to the API <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API>`_ explaining how the pure Bot API can be accessed via ``python-telegram-bot``.
Resources
=========
- The `package documentation <https://docs.python-telegram-bot.org/>`_ is the technical reference for ``python-telegram-bot``.
It contains descriptions of all available classes, modules, methods and arguments as well as the `changelog <https://docs.python-telegram-bot.org/changelog.html>`_.
- The `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ is home to number of more elaborate introductions of the different features of ``python-telegram-bot`` and other useful resources that go beyond the technical documentation.
- Our `examples section <https://docs.python-telegram-bot.org/examples.html>`_ contains several examples that showcase the different features of both the Bot API and ``python-telegram-bot``.
Even if it is not your approach for learning, please take a look at ``echobot.py``. It is the de facto base for most of the bots out there.
The code for these examples is released to the public domain, so you can start by grabbing the code and building on top of it.
- The `official Telegram Bot API documentation <https://core.telegram.org/bots/api>`_ is of course always worth a read.
Getting help
============
If the resources mentioned above don't answer your questions or simply overwhelm you, there are several ways of getting help.
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us! Asking a question here is often the quickest way to get a pointer in the right direction.
2. Ask questions by opening `a discussion <https://github.com/python-telegram-bot/python-telegram-bot/discussions/new>`_.
3. You can even ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
Concurrency
===========
Since v20.0, ``python-telegram-bot`` is built on top of Pythons ``asyncio`` module.
Because ``asyncio`` is in general single-threaded, ``python-telegram-bot`` does currently not aim to be thread-safe.
Contributing
============
Contributions of all sizes are welcome.
Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started.
You can also help by `reporting bugs or feature requests <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_.
Donating
========
Occasionally we are asked if we accept donations to support the development.
While we appreciate the thought, maintaining PTB is our hobby, and we have almost no running costs for it. We therefore have nothing set up to accept donations.
If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead.
License
=======
You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 <https://www.gnu.org/licenses/lgpl-3.0.html>`_.
Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
+11
View File
@@ -0,0 +1,11 @@
## Code autogenerator
This folder is used to run python scripts which can autogenerate code used for adding features of
new Bot API updates.
## Requirements
Requires Python 3.10 and higher, and the package `libcst`.
## Usage
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import sys
from pathlib import Path
import libcst as cst
from libcst.display import dump
sys.path.insert(0, str(Path.cwd().absolute()))
from tests.test_official.scraper import TelegramParameter
class BotVisitor(cst.CSTVisitor):
def visit_FunctionDef(self, node: cst.FunctionDef) -> bool:
if node.name.value == "send_message":
print("returning true for ", node.name.value)
return True
return False
def visit_Arg(self, node: cst.Arg) -> None:
print(node.value.value)
class BotTransformer(cst.CSTTransformer):
def __init__(self, methods: dict[str, TelegramParameter]) -> None:
self.methods = methods
self.stack: list[tuple[str, ...]] = []
def visit_FunctionDef(self, node: cst.FunctionDef) -> bool:
self.stack.append((node.name.value,))
return node.name.value in self.methods
def leave_FunctionDef(
self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef
) -> cst.FunctionDef:
method_name = self.stack.pop()
if original_node.name.value not in self.methods:
return original_node
print(dump(updated_node))
# get which method we are in
method_name = method_name[0]
tg_param = self.methods.pop(method_name)
# Let's add our parameter now at the last position:
# if the arg is required, we will add it to the end anyway (backward compat) and have a
# type hint of Optional[<type>].
annot = cst.Annotation(
annotation=cst.Subscript(
value=cst.Name(value="Optional"),
slice=[
cst.SubscriptElement(
slice=cst.Index(value=cst.Name(value=tg_param.param_type))
)
],
)
)
new_param = cst.Param(
name=cst.Name(tg_param.param_name),
annotation=annot,
default=cst.Name(value="None"),
comma=original_node.params.params[-1].comma,
whitespace_after_param=original_node.params.params[-1].whitespace_after_param,
)
new_params = (*updated_node.params.params, new_param)
return updated_node.with_changes(
params=updated_node.params.with_changes(params=new_params)
)
def add_param_to_bot_method(method_name: str, param: TelegramParameter) -> None:
"""Add a parameter to a method in the Bot class.
Args:
method_name (str): The name of the method.
param (TelegramParameter): The parameter to add.
"""
# All ast editing is done in place
bot_file = Path("telegram/_bot.py")
with bot_file.open() as file:
source = cst.parse_module(file.read())
# s = dump(source)
mod_tree = source.visit(BotTransformer({method_name: param}))
code = mod_tree.code
with bot_file.open("w") as file:
file.write(code)
if __name__ == "__main__":
add_param_to_bot_method("send_message", TelegramParameter("effect_id", "str", False, "desc"))
# failures = parse_failures()
# missing_method_params = failures[0]
# for method_name, param in missing_method_params.items():
# print("Adding parameter", param.param_name, "to method", method_name)
# add_param_to_bot_method(method_name, param)
# break
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This file determines which methods/parameters need to be added to the API. It does so by
running test_official.py.
"""
import os
import re
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path.cwd().absolute()))
os.environ["TEST_OFFICIAL"] = "true"
from functools import cache
from helpers import to_camel_case
from tests.test_official.scraper import TelegramParameter
from tests.test_official.test_official import classes, methods
def run_test_official() -> list:
"""Run test_official.py and gather which errors occured."""
try:
output = subprocess.run(
["pytest", "tests/test_official/test_official.py", "-q", "--no-header", "--tb=line"],
capture_output=True,
check=True,
text=True,
)
except subprocess.CalledProcessError as e: # if test_official.py fails (expected)
output = e.output
else:
output = output.stdout
# truncate part before ===failures===
str_output = output[output.find("====") :]
failures: list[str] = str_output.split("\n")[1:-2]
return failures
def get_telegram_parameter(
param_name: str, method_name: str | None = None, class_name: str | None = None
) -> TelegramParameter:
"""Get a TelegramParameter object from the scraper based on the method and parameter name.
Args:
method_name (str): The name of the method.
param_name (str): The name of the parameter.
Returns:
TelegramParameter: The TelegramParameter object.
"""
if method_name is not None:
for method in methods:
if method.method_name == to_camel_case(method_name):
for param in method.method_parameters:
if param.param_name == param_name:
return param
elif class_name is not None:
for cls in classes:
if cls.class_name == class_name:
for param in cls.class_parameters:
if param.param_name == param_name:
return param
else:
raise ValueError("Either method_name or class_name must be provided.")
raise ValueError(f"Param {param_name} not found in method {method_name} or class {class_name}")
@cache
def parse_failures() -> tuple[dict[str, TelegramParameter], dict[str, TelegramParameter]]:
"""Parse the output of run_test_official() to determine which methods/parameters need to be
added to the API.
Returns:
list[TelegramParameter]: A list of parameters that need to be added to the API.
"""
failures = run_test_official()
# regex patterns
param_missing_str = "AssertionError: Parameter ([a-z_]+) not found in ([a-z_]+)"
attribute_missing_str = "AssertionError: Attribute ([a-z_]+) not found in ([a-zA-Z0-9]+)"
missing_params_in_methods = {} # {method_name: TelegramParameter}
missing_attrs_in_classes = {} # {class_name: TelegramParameter}
for failure in failures:
# We will only count missing parameters/attributes for now:
# missing parameter
if match := re.search(param_missing_str, failure):
param_name = match.group(1)
method_name = match.group(2)
tg_param = get_telegram_parameter(param_name, method_name=method_name)
missing_params_in_methods[method_name] = tg_param
# missing attribute
elif match := re.search(attribute_missing_str, failure):
attr_name = match.group(1)
class_name = match.group(2)
tg_param = get_telegram_parameter(attr_name, class_name=class_name)
missing_attrs_in_classes[class_name] = tg_param
else:
print(f"Unknown failure: {failure}")
return missing_params_in_methods, missing_attrs_in_classes
# run_test_official()
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
def to_camel_case(snake_str: str) -> str:
"""Convert a snake_case string to a CamelCase string.
Args:
snake_str (str): The snake_case string.
Returns:
str: The CamelCase string.
"""
components = snake_str.split("_")
return components[0] + "".join(x.title() for x in components[1:])
+1
View File
@@ -0,0 +1 @@
libcst
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
+227 -204
View File
@@ -1,6 +1,6 @@
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,55 +16,17 @@
# 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 collections import defaultdict
from collections.abc import Iterator
from socket import socket
from types import FunctionType
from typing import Union
from apscheduler.job import Job as APSJob
from typing import Any, Iterator, Union
import telegram
import telegram._utils.defaultvalue
import telegram._utils.types
import telegram.ext
import telegram.ext._utils.types
from tests.auxil.slots import mro_slots
# Define the namespace for type resolution. This helps dealing with the internal imports that
# we do in many places
# The .copy() is important to avoid modifying the original namespace
TG_NAMESPACE = vars(telegram).copy()
TG_NAMESPACE.update(vars(telegram._utils.types))
TG_NAMESPACE.update(vars(telegram._utils.defaultvalue))
TG_NAMESPACE.update(vars(telegram.ext))
TG_NAMESPACE.update(vars(telegram.ext._utils.types))
TG_NAMESPACE.update(vars(telegram.ext._applicationbuilder))
TG_NAMESPACE.update({"socket": socket, "APSJob": APSJob})
class PublicMethod(typing.NamedTuple):
name: str
method: FunctionType
def _is_inherited_method(cls: type, method_name: str) -> bool:
"""Checks if a method is inherited from a parent class.
Inheritance is not considered if the parent class is private.
Recurses through all direcot or indirect parent classes.
"""
# The [1:] slice is used to exclude the class itself from the MRO.
for base in cls.__mro__[1:]:
if method_name in base.__dict__ and not base.__name__.startswith("_"):
return True
return False
def _iter_own_public_methods(cls: type) -> Iterator[PublicMethod]:
def _iter_own_public_methods(cls: type) -> Iterator[tuple[str, type]]:
"""Iterates over methods of a class that are not protected/private,
not camelCase and not inherited from the parent class.
@@ -72,15 +34,13 @@ def _iter_own_public_methods(cls: type) -> Iterator[PublicMethod]:
This function is defined outside the class because it is used to create class constants.
"""
# Use .isfunction() instead of .ismethod() because we want to include static methods.
for m in inspect.getmembers(cls, predicate=inspect.isfunction):
if (
not m[0].startswith("_")
and m[0].islower() # to avoid camelCase methods
and not _is_inherited_method(cls, m[0])
):
yield PublicMethod(m[0], m[1])
return (
m
for m in inspect.getmembers(cls, predicate=inspect.isfunction) # not .ismethod
if not m[0].startswith("_")
and m[0].islower() # to avoid camelCase methods
and m[0] in cls.__dict__ # method is not inherited from parent class
)
class AdmonitionInserter:
@@ -97,12 +57,18 @@ class AdmonitionInserter:
start and end markers.
"""
METHOD_NAMES_FOR_BOT_APP_APPBUILDER: typing.ClassVar[dict[type, str]] = {
cls: tuple(m.name for m in _iter_own_public_methods(cls))
for cls in (telegram.Bot, telegram.ext.ApplicationBuilder, telegram.ext.Application)
FORWARD_REF_SKIP_PATTERN = re.compile(r"^ForwardRef\('DefaultValue\[\w+]'\)$")
"""A pattern that will be used to skip known ForwardRef's that need not be resolved
to a Telegram class, e.g.:
ForwardRef('DefaultValue[None]')
ForwardRef('DefaultValue[DVValueType]')
"""
METHOD_NAMES_FOR_BOT_AND_APPBUILDER: typing.ClassVar[dict[type, str]] = {
cls: tuple(m[0] for m in _iter_own_public_methods(cls)) # m[0] means we take only names
for cls in (telegram.Bot, telegram.ext.ApplicationBuilder)
}
"""A dictionary mapping Bot, Application & ApplicationBuilder classes to their relevant methods
that will
"""A dictionary mapping Bot and ApplicationBuilder classes to their relevant methods that will
be mentioned in 'Returned in' and 'Use in' admonitions in other classes' docstrings.
Methods must be public, not aliases, not inherited from TelegramObject.
"""
@@ -116,20 +82,13 @@ class AdmonitionInserter:
"""Dictionary with admonitions. Contains sub-dictionaries, one per admonition type.
Each sub-dictionary matches bot methods (for "Shortcuts") or telegram classes (for other
admonition types) to texts of admonitions, e.g.:
```
{
"use_in": {
<class 'telegram._chatinvitelink.ChatInviteLink'>:
<"Use in" admonition for ChatInviteLink>,
...
},
"available_in": {
<class 'telegram._chatinvitelink.ChatInviteLink'>:
<"Available in" admonition">,
...
},
"returned_in": {...}
"use_in": {<class 'telegram._chatinvitelink.ChatInviteLink'>:
<"Use in" admonition for ChatInviteLink>, ...},
"available_in": {<class 'telegram._chatinvitelink.ChatInviteLink'>:
<"Available in" admonition">, ...},
"returned_in": {...}
}
```
"""
@@ -168,6 +127,34 @@ class AdmonitionInserter:
# i.e. {telegram._files.sticker.Sticker: {":attr:`telegram.Message.sticker`", ...}}
attrs_for_class = defaultdict(set)
# The following regex is supposed to capture a class name in a line like this:
# media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send.
#
# Note that even if such typing description spans over multiple lines but each line ends
# with a backslash (otherwise Sphinx will throw an error)
# (e.g. EncryptedPassportElement.data), then Sphinx will combine these lines into a single
# line automatically, and it will contain no backslash (only some extra many whitespaces
# from the indentation).
attr_docstr_pattern = re.compile(
r"^\s*(?P<attr_name>[a-z_]+)" # Any number of spaces, named group for attribute
r"\s?\(" # Optional whitespace, opening parenthesis
r".*" # Any number of characters (that could denote a built-in type)
r":class:`.+`" # Marker of a classref, class name in backticks
r".*\):" # Any number of characters, closing parenthesis, colon.
# The ^ colon above along with parenthesis is important because it makes sure that
# the class is mentioned in the attribute description, not in free text.
r".*$", # Any number of characters, end of string (end of line)
re.VERBOSE,
)
# for properties: there is no attr name in docstring. Just check if there's a class name.
prop_docstring_pattern = re.compile(r":class:`.+`.*:")
# pattern for iterating over potentially many class names in docstring for one attribute.
# Tilde is optional (sometimes it is in the docstring, sometimes not).
single_class_name_pattern = re.compile(r":class:`~?(?P<class_name>[\w.]*)`")
classes_to_inspect = inspect.getmembers(telegram, inspect.isclass) + inspect.getmembers(
telegram.ext, inspect.isclass
)
@@ -178,31 +165,40 @@ class AdmonitionInserter:
# docstrings.
name_of_inspected_class_in_docstr = self._generate_class_name_for_link(inspected_class)
# Writing to dictionary: matching the class found in the type hint
# and its subclasses to the attribute of the class being inspected.
# The class in the attribute typehint (or its subclass) is the key,
# ReST link to attribute of the class currently being inspected is the value.
# Parsing part of the docstring with attributes (parsing of properties follows later)
docstring_lines = inspect.getdoc(inspected_class).splitlines()
lines_with_attrs = []
for idx, line in enumerate(docstring_lines):
if line.strip() == "Attributes:":
lines_with_attrs = docstring_lines[idx + 1 :]
break
# best effort - args of __init__ means not all attributes are covered, but there is no
# other way to get type hints of all attributes, other than doing ast parsing maybe.
# (Docstring parsing was discontinued with the closing of #4414)
type_hints = typing.get_type_hints(inspected_class.__init__, localns=TG_NAMESPACE)
class_attrs = [slot for slot in mro_slots(inspected_class) if not slot.startswith("_")]
for target_attr in class_attrs:
try:
self._resolve_arg_and_add_link(
dict_of_methods_for_class=attrs_for_class,
link=f":attr:`{name_of_inspected_class_in_docstr}.{target_attr}`",
type_hints={target_attr: type_hints.get(target_attr)},
resolve_nested_type_vars=False,
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Available in' admonition "
f"(admonition_inserter.py). Class {inspected_class} present in "
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
f" could not be resolved. {e!s}"
) from e
for line in lines_with_attrs:
if not (line_match := attr_docstr_pattern.match(line)):
continue
target_attr = line_match.group("attr_name")
# a typing description of one attribute can contain multiple classes
for match in single_class_name_pattern.finditer(line):
name_of_class_in_attr = match.group("class_name")
# Writing to dictionary: matching the class found in the docstring
# and its subclasses to the attribute of the class being inspected.
# The class in the attribute docstring (or its subclass) is the key,
# ReST link to attribute of the class currently being inspected is the value.
try:
self._resolve_arg_and_add_link(
arg=name_of_class_in_attr,
dict_of_methods_for_class=attrs_for_class,
link=f":attr:`{name_of_inspected_class_in_docstr}.{target_attr}`",
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Available in' admonition "
f"(admonition_inserter.py). Class {name_of_class_in_attr} present in "
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
f" could not be resolved. {e!s}"
) from e
# Properties need to be parsed separately because they act like attributes but not
# listed as attributes.
@@ -213,29 +209,39 @@ class AdmonitionInserter:
if prop_name not in inspected_class.__dict__:
continue
# fget is used to access the actual function under the property wrapper
type_hints = typing.get_type_hints(
getattr(inspected_class, prop_name).fget, localns=TG_NAMESPACE
)
# 1. Can't use typing.get_type_hints because double-quoted type hints
# (like "Application") will throw a NameError
# 2. Can't use inspect.signature because return annotations of properties can be
# hard to parse (like "(self) -> BD").
# 3. fget is used to access the actual function under the property wrapper
docstring = inspect.getdoc(getattr(inspected_class, prop_name).fget)
if docstring is None:
continue
# Writing to dictionary: matching the class found in the docstring and its
# subclasses to the property of the class being inspected.
# The class in the property docstring (or its subclass) is the key,
# ReST link to property of the class currently being inspected is the value.
try:
self._resolve_arg_and_add_link(
dict_of_methods_for_class=attrs_for_class,
link=f":attr:`{name_of_inspected_class_in_docstr}.{prop_name}`",
type_hints={prop_name: type_hints.get("return")},
resolve_nested_type_vars=False,
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Available in' admonition "
f"(admonition_inserter.py). Class {inspected_class} present in "
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
f" could not be resolved. {e!s}"
) from e
first_line = docstring.splitlines()[0]
if not prop_docstring_pattern.match(first_line):
continue
for match in single_class_name_pattern.finditer(first_line):
name_of_class_in_prop = match.group("class_name")
# Writing to dictionary: matching the class found in the docstring and its
# subclasses to the property of the class being inspected.
# The class in the property docstring (or its subclass) is the key,
# ReST link to property of the class currently being inspected is the value.
try:
self._resolve_arg_and_add_link(
arg=name_of_class_in_prop,
dict_of_methods_for_class=attrs_for_class,
link=f":attr:`{name_of_inspected_class_in_docstr}.{prop_name}`",
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Available in' admonition "
f"(admonition_inserter.py). Class {name_of_class_in_prop} present in "
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
f" could not be resolved. {e!s}"
) from e
return self._generate_admonitions(attrs_for_class, admonition_type="available_in")
@@ -243,28 +249,29 @@ class AdmonitionInserter:
"""Creates a dictionary with 'Returned in' admonitions for classes that are returned
in Bot's and ApplicationBuilder's methods.
"""
# Generate a mapping of classes to ReST links to Bot methods which return it,
# i.e. {<class 'telegram._message.Message'>: {:meth:`telegram.Bot.send_message`, ...}}
methods_for_class = defaultdict(set)
for cls, method_names in self.METHOD_NAMES_FOR_BOT_APP_APPBUILDER.items():
for cls, method_names in self.METHOD_NAMES_FOR_BOT_AND_APPBUILDER.items():
for method_name in method_names:
sig = inspect.signature(getattr(cls, method_name))
ret_annot = sig.return_annotation
method_link = self._generate_link_to_method(method_name, cls)
arg = getattr(cls, method_name)
ret_type_hint = typing.get_type_hints(arg, localns=TG_NAMESPACE)
try:
self._resolve_arg_and_add_link(
arg=ret_annot,
dict_of_methods_for_class=methods_for_class,
link=method_link,
type_hints={"return": ret_type_hint.get("return")},
resolve_nested_type_vars=False,
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Returned in' admonition "
f"(admonition_inserter.py). {cls}, method {method_name}. "
f"Couldn't resolve type hint in return annotation {ret_type_hint}. {e!s}"
f"Couldn't resolve type hint in return annotation {ret_annot}. {e!s}"
) from e
return self._generate_admonitions(methods_for_class, admonition_type="returned_in")
@@ -291,13 +298,8 @@ class AdmonitionInserter:
# inspect methods of all telegram classes for return statements that indicate
# that this given method is a shortcut for a Bot method
for _class_name, cls in inspect.getmembers(telegram, predicate=inspect.isclass):
if not cls.__module__.startswith("telegram"):
# For some reason inspect.getmembers() also yields some classes that are
# imported in the namespace but not part of the telegram module.
continue
# no need to inspect Bot's own methods, as Bot can't have shortcuts in Bot
if cls is telegram.Bot:
# no need to inspect Bot's own methods, as Bot can't have shortcuts in Bot
continue
for method_name, method in _iter_own_public_methods(cls):
@@ -307,7 +309,9 @@ class AdmonitionInserter:
continue
bot_method = getattr(telegram.Bot, bot_method_match.group())
link_to_shortcut_method = self._generate_link_to_method(method_name, cls)
shortcuts_for_bot_method[bot_method].add(link_to_shortcut_method)
return self._generate_admonitions(shortcuts_for_bot_method, admonition_type="shortcuts")
@@ -322,24 +326,26 @@ class AdmonitionInserter:
# {:meth:`telegram.Bot.answer_inline_query`, ...}}
methods_for_class = defaultdict(set)
for cls, method_names in self.METHOD_NAMES_FOR_BOT_APP_APPBUILDER.items():
for cls, method_names in self.METHOD_NAMES_FOR_BOT_AND_APPBUILDER.items():
for method_name in method_names:
method_link = self._generate_link_to_method(method_name, cls)
arg = getattr(cls, method_name)
param_type_hints = typing.get_type_hints(arg, localns=TG_NAMESPACE)
param_type_hints.pop("return", None)
try:
self._resolve_arg_and_add_link(
dict_of_methods_for_class=methods_for_class,
link=method_link,
type_hints=param_type_hints,
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Use in' admonition "
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
) from e
sig = inspect.signature(getattr(cls, method_name))
parameters = sig.parameters
for param in parameters.values():
try:
self._resolve_arg_and_add_link(
arg=param.annotation,
dict_of_methods_for_class=methods_for_class,
link=method_link,
)
except NotImplementedError as e:
raise NotImplementedError(
"Error generating Sphinx 'Use in' admonition "
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
f"{param}: Couldn't resolve type hint {param.annotation}. {e!s}"
) from e
return self._generate_admonitions(methods_for_class, admonition_type="use_in")
@@ -355,12 +361,11 @@ class AdmonitionInserter:
for idx, value in list(enumerate(lines)):
if value.startswith(
(
# ".. seealso:",
".. seealso:",
# The docstring contains heading "Examples:", but Sphinx will have it converted
# to ".. admonition: Examples":
".. admonition:: Examples",
".. version",
"Args:",
# The space after ":param" is important because docstring can contain
# ":paramref:" in its plain text in the beginning of a line (e.g. ExtBot):
":param ",
@@ -428,12 +433,12 @@ class AdmonitionInserter:
return admonition_for_class
@staticmethod
def _generate_class_name_for_link(cls_: type) -> str:
def _generate_class_name_for_link(cls: type) -> str:
"""Generates class name that can be used in a ReST link."""
# Check for potential presence of ".ext.", we will need to keep it.
ext = ".ext" if ".ext." in str(cls_) else ""
return f"telegram{ext}.{cls_.__name__}"
ext = ".ext" if ".ext." in str(cls) else ""
return f"telegram{ext}.{cls.__name__}"
def _generate_link_to_method(self, method_name: str, cls: type) -> str:
"""Generates a ReST link to a method of a telegram class."""
@@ -441,22 +446,19 @@ class AdmonitionInserter:
return f":meth:`{self._generate_class_name_for_link(cls)}.{method_name}`"
@staticmethod
def _iter_subclasses(cls_: type) -> Iterator:
if not hasattr(cls_, "__subclasses__") or cls_ is telegram.TelegramObject:
return iter([])
def _iter_subclasses(cls: type) -> Iterator:
return (
# exclude private classes
c
for c in cls_.__subclasses__()
for c in cls.__subclasses__()
if not str(c).split(".")[-1].startswith("_")
)
def _resolve_arg_and_add_link(
self,
arg: Any,
dict_of_methods_for_class: defaultdict,
link: str,
type_hints: dict[str, type],
resolve_nested_type_vars: bool = True,
) -> None:
"""A helper method. Tries to resolve the arg into a valid class. In case of success,
adds the link (to a method, attribute, or property) for that class' and its subclasses'
@@ -464,9 +466,7 @@ class AdmonitionInserter:
**Modifies dictionary in place.**
"""
type_hints.pop("self", None)
for cls in self._resolve_arg(type_hints, resolve_nested_type_vars):
for cls in self._resolve_arg(arg):
# When trying to resolve an argument from args or return annotation,
# the method _resolve_arg returns None if nothing could be resolved.
# Also, if class was resolved correctly, "telegram" will definitely be in its str().
@@ -478,67 +478,88 @@ class AdmonitionInserter:
for subclass in self._iter_subclasses(cls):
dict_of_methods_for_class[subclass].add(link)
def _resolve_arg(
self,
type_hints: dict[str, type],
resolve_nested_type_vars: bool,
) -> list[type]:
def _resolve_arg(self, arg: Any) -> Iterator[Union[type, None]]:
"""Analyzes an argument of a method and recursively yields classes that the argument
or its sub-arguments (in cases like Union[...]) belong to, if they can be resolved to
telegram or telegram.ext classes.
Args:
type_hints: A dictionary of argument names and their types.
resolve_nested_type_vars: If True, nested type variables (like Application[BT, …])
will be resolved to their actual classes. If False, only the outermost type
variable will be resolved. *Only* affects ptb classes, not built-in types.
Useful for checking the return type of methods, where nested type variables
are not really useful.
Raises `NotImplementedError`.
"""
def _is_ptb_class(cls: type) -> bool:
if not hasattr(cls, "__module__"):
return False
return cls.__module__.startswith("telegram")
origin = typing.get_origin(arg)
# will be edited in place
telegram_classes = set()
if (
origin in (collections.abc.Callable, typing.IO)
or arg is None
# no other check available (by type or origin) for these:
or str(type(arg)) in ("<class 'typing._SpecialForm'>", "<class 'ellipsis'>")
):
pass
def recurse_type(type_, is_recursed_from_ptb_class: bool):
next_is_recursed_from_ptb_class = is_recursed_from_ptb_class or _is_ptb_class(type_)
# RECURSIVE CALLS
# for cases like Union[Sequence....
elif origin in (
Union,
collections.abc.Coroutine,
collections.abc.Sequence,
):
for sub_arg in typing.get_args(arg):
yield from self._resolve_arg(sub_arg)
if hasattr(type_, "__origin__"): # For generic types like Union, List, etc.
# Make sure it's not a telegram.ext generic type (e.g. ContextTypes[...])
org = typing.get_origin(type_)
if "telegram.ext" in str(org):
telegram_classes.add(org)
elif isinstance(arg, typing.TypeVar):
# gets access to the "bound=..." parameter
yield from self._resolve_arg(arg.__bound__)
# END RECURSIVE CALLS
args = typing.get_args(type_)
for arg in args:
recurse_type(arg, next_is_recursed_from_ptb_class)
elif isinstance(type_, typing.TypeVar) and (
resolve_nested_type_vars or not is_recursed_from_ptb_class
):
# gets access to the "bound=..." parameter
recurse_type(type_.__bound__, next_is_recursed_from_ptb_class)
elif inspect.isclass(type_) and "telegram" in inspect.getmodule(type_).__name__:
telegram_classes.add(type_)
elif isinstance(type_, typing.ForwardRef):
# Resolving ForwardRef is not easy. https://peps.python.org/pep-0749/ will
# hopefully make it better by introducing typing.resolve_forward_ref() in py3.14
# but that's not there yet
# So for now we fall back to a best effort approach of guessing if the class is
# available in tg or tg.ext
with contextlib.suppress(AttributeError):
telegram_classes.add(self._resolve_class(type_.__forward_arg__))
elif isinstance(arg, typing.ForwardRef):
m = self.FORWARD_REF_PATTERN.match(str(arg))
# We're sure it's a ForwardRef, so, unless it belongs to known exceptions,
# the class must be resolved.
# If it isn't resolved, we'll have the program throw an exception to be sure.
try:
cls = self._resolve_class(m.group("class_name"))
except AttributeError as exc:
# skip known ForwardRef's that need not be resolved to a Telegram class
if self.FORWARD_REF_SKIP_PATTERN.match(str(arg)):
pass
else:
raise NotImplementedError(f"Could not process ForwardRef: {arg}") from exc
else:
yield cls
for type_hint in type_hints.values():
if type_hint is not None:
recurse_type(type_hint, False)
# For custom generics like telegram.ext._application.Application[~BT, ~CCT, ~UD...].
# This must come before the check for isinstance(type) because GenericAlias can also be
# recognized as type if it belongs to <class 'types.GenericAlias'>.
elif str(type(arg)) in (
"<class 'typing._GenericAlias'>",
"<class 'types.GenericAlias'>",
"<class 'typing._LiteralGenericAlias'>",
):
if "telegram" in str(arg):
# get_origin() of telegram.ext._application.Application[~BT, ~CCT, ~UD...]
# will produce <class 'telegram.ext._application.Application'>
yield origin
return list(telegram_classes)
elif isinstance(arg, type):
if "telegram" in str(arg):
yield arg
# For some reason "InlineQueryResult", "InputMedia" & some others are currently not
# recognized as ForwardRefs and are identified as plain strings.
elif isinstance(arg, str):
# args like "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]" can be recognized as strings.
# Remove whatever is in the square brackets because it doesn't need to be parsed.
arg = re.sub(r"\[.+]", "", arg)
cls = self._resolve_class(arg)
# Here we don't want an exception to be thrown since we're not sure it's ForwardRef
if cls is not None:
yield cls
else:
raise NotImplementedError(
f"Cannot process argument {arg} of type {type(arg)} (origin {origin})"
)
@staticmethod
def _resolve_class(name: str) -> Union[type, None]:
@@ -558,14 +579,16 @@ class AdmonitionInserter:
f"telegram.ext.{name}",
f"telegram.ext.filters.{name}",
):
try:
return eval(option)
# NameError will be raised if trying to eval just name and it doesn't work, e.g.
# "Name 'ApplicationBuilder' is not defined".
# AttributeError will be raised if trying to e.g. eval f"telegram.{name}" when the
# class denoted by `name` actually belongs to `telegram.ext`:
# "module 'telegram' has no attribute 'ApplicationBuilder'".
# If neither option works, this is not a PTB class.
with contextlib.suppress(NameError, AttributeError):
return eval(option)
except (NameError, AttributeError):
continue
return None
+3 -2
View File
@@ -1,6 +1,6 @@
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import inspect
from typing import List
keyword_args = [
"Keyword Arguments:",
@@ -84,7 +85,7 @@ get_updates_read_timeout_addition = [
]
def find_insert_pos_for_kwargs(lines: list[str]) -> int:
def find_insert_pos_for_kwargs(lines: List[str]) -> int:
"""Finds the correct position to insert the keyword arguments and returns the index."""
for idx, value in reversed(list(enumerate(lines))): # reversed since :returns: is at the end
if value.startswith("Returns"):
+3 -2
View File
@@ -1,6 +1,6 @@
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,6 +21,7 @@ https://github.com/sphinx-doc/sphinx/issues/1556 is closed
"""
import subprocess
from pathlib import Path
from typing import Dict, Tuple
from sphinx.util import logging
@@ -31,7 +32,7 @@ sphinx_logger = logging.getLogger(__name__)
# must be a module-level variable so that it can be written to by the `autodoc-process-docstring`
# event handler in `sphinx_hooks.py`
LINE_NUMBERS: dict[str, tuple[Path, int, int]] = {}
LINE_NUMBERS: Dict[str, Tuple[Path, int, int]] = {}
def _git_branch() -> str:
+4 -9
View File
@@ -1,6 +1,6 @@
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import collections.abc
import contextlib
import inspect
import re
import typing
@@ -47,7 +46,6 @@ PRIVATE_BASE_CLASSES = {
"_BaseThumbedMedium": "TelegramObject",
"_BaseMedium": "TelegramObject",
"_CredentialsBase": "TelegramObject",
"_ChatBase": "TelegramObject",
}
@@ -154,11 +152,13 @@ def autodoc_process_docstring(
if isinstance(obj, telegram.ext.filters.BaseFilter):
obj = obj.__class__
with contextlib.suppress(Exception):
try:
source_lines, start_line = inspect.getsourcelines(obj)
end_line = start_line + len(source_lines)
file = Path(inspect.getsourcefile(obj)).relative_to(FILE_ROOT)
LINE_NUMBERS[name] = (file, start_line, end_line)
except Exception:
pass
# Since we don't document the `__init__`, we call this manually to have it available for
# attributes -- see the note above
@@ -187,11 +187,6 @@ def autodoc_process_bases(app, name, obj, option, bases: list) -> None:
bases[idx] = ":class:`enum.IntEnum`"
continue
if "FloatEnum" in base:
bases[idx] = ":class:`enum.Enum`"
bases.insert(0, ":class:`float`")
continue
# Drop generics (at least for now)
if base.endswith("]"):
base = base.split("[", maxsplit=1)[0]
+4 -5
View File
@@ -1,6 +1,6 @@
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2025
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -15,7 +15,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import datetime as dtm
import datetime
from enum import Enum
from docutils.nodes import Element
@@ -75,7 +75,7 @@ class TGConstXRefRole(PyXRefRole):
):
return str(value), target
if (
isinstance(value, dtm.datetime)
isinstance(value, datetime.datetime)
and value == telegram.constants.ZERO_DATE
and target in ("telegram.constants.ZERO_DATE",)
):
@@ -88,6 +88,7 @@ class TGConstXRefRole(PyXRefRole):
refnode.rawsource,
CONSTANTS_ROLE,
)
return title, target
except Exception as exc:
sphinx_logger.exception(
"%s:%d: WARNING: Did not convert reference %s due to an exception.",
@@ -97,5 +98,3 @@ class TGConstXRefRole(PyXRefRole):
exc_info=exc,
)
return title, target
else:
return title, target
+3 -5
View File
@@ -1,9 +1,7 @@
sphinx==8.1.3
furo==2024.8.6
sphinx==7.3.7
furo==2024.5.6
furo-sphinx-search @ git+https://github.com/harshil21/furo-sphinx-search@v0.2.0.1
sphinx-paramlinks==0.6.0
sphinxcontrib-mermaid==1.0.0
sphinxcontrib-mermaid==0.9.2
sphinx-copybutton==0.5.2
sphinx-inline-tabs==2023.4.21
# Temporary. See #4387
sphinx-build-compatibility @ git+https://github.com/readthedocs/sphinx-build-compatibility.git@58aabc5f207c6c2421f23d3578adc0b14af57047
+1 -1
View File
@@ -61,5 +61,5 @@
}
.admonition.returned-in > ul, .admonition.available-in > ul, .admonition.use-in > ul, .admonition.shortcuts > ul {
max-height: 200px;
overflow-y: auto;
overflow-y: scroll;
}
+5 -26
View File
@@ -1,4 +1,3 @@
import os
import re
import sys
from pathlib import Path
@@ -13,7 +12,7 @@ sys.path.insert(0, str(Path("../..").resolve().absolute()))
# -- General configuration ------------------------------------------------
# General information about the project.
project = "python-telegram-bot"
copyright = "2015-2025, Leandro Toledo"
copyright = "2015-2024, Leandro Toledo"
author = "Leandro Toledo"
# The version info for the project you're documenting, acts as replacement for
@@ -21,16 +20,12 @@ author = "Leandro Toledo"
# built documents.
#
# The short X.Y version.
# Import needs to be below the sys.path.insert above
import telegram # noqa: E402
version = telegram.__version__
version = "21.2" # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = telegram.__version__
release = "21.2" # telegram.__version__
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "8.1.3"
needs_sphinx = "6.1.3"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -48,10 +43,6 @@ extensions = [
"sphinx_search.extension",
]
# Temporary. See #4387
if os.environ.get("READTHEDOCS", "") == "True":
extensions.append("sphinx_build_compatibility.extension")
# For shorter links to Wiki in docstrings
extlinks = {
"wiki": ("https://github.com/python-telegram-bot/python-telegram-bot/wiki/%s", "%s"),
@@ -111,11 +102,6 @@ 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"),
]
linkcheck_allowed_redirects = {
# Redirects to the default version are okay
@@ -261,14 +247,7 @@ htmlhelp_basename = "python-telegram-bot-doc"
# The base URL which points to the root of the HTML documentation. It is used to indicate the
# location of document using The Canonical Link Relation. Default: ''.
# Set canonical URL from the Read the Docs Domain
html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
# Tell Jinja2 templates the build is running on Read the Docs
html_context = {}
if os.environ.get("READTHEDOCS", "") == "True":
html_context["READTHEDOCS"] = True
html_baseurl = "https://docs.python-telegram-bot.org"
# -- Options for LaTeX output ---------------------------------------------
+1 -55
View File
@@ -25,8 +25,6 @@
- Used for sending documents
* - :meth:`~telegram.Bot.send_game`
- Used for sending a game
* - :meth:`~telegram.Bot.send_gift`
- Used for sending a gift
* - :meth:`~telegram.Bot.send_invoice`
- Used for sending an invoice
* - :meth:`~telegram.Bot.send_location`
@@ -35,8 +33,6 @@
- Used for sending media grouped together
* - :meth:`~telegram.Bot.send_message`
- Used for sending text messages
* - :meth:`~telegram.Bot.send_paid_media`
- Used for sending paid media to channels
* - :meth:`~telegram.Bot.send_photo`
- Used for sending photos
* - :meth:`~telegram.Bot.send_poll`
@@ -153,8 +149,6 @@
- Used for setting a chat title
* - :meth:`~telegram.Bot.set_chat_description`
- Used for setting the description of a chat
* - :meth:`~telegram.Bot.set_user_emoji_status`
- Used for setting the users status emoji
* - :meth:`~telegram.Bot.pin_chat_message`
- Used for pinning a message
* - :meth:`~telegram.Bot.unpin_chat_message`
@@ -183,29 +177,6 @@
</details>
<br>
.. raw:: html
<details>
<summary>Verification on behalf of an organization</summary>
.. list-table::
:align: left
:widths: 1 4
* - :meth:`~telegram.Bot.verify_chat`
- Used for verifying a chat
* - :meth:`~telegram.Bot.verify_user`
- Used for verifying a user
* - :meth:`~telegram.Bot.remove_chat_verification`
- Used for removing the verification from a chat
* - :meth:`~telegram.Bot.remove_user_verification`
- Used for removing the verification from a user
.. raw:: html
</details>
<br>
.. raw:: html
<details>
@@ -382,7 +353,7 @@
.. raw:: html
<details>
<summary>Payments and Stars</summary>
<summary>Miscellaneous</summary>
.. list-table::
:align: left
@@ -390,39 +361,14 @@
* - :meth:`~telegram.Bot.create_invoice_link`
- Used to generate an HTTP link for an invoice
* - :meth:`~telegram.Bot.edit_user_star_subscription`
- Used for editing a user's star subscription
* - :meth:`~telegram.Bot.get_star_transactions`
- Used for obtaining the bot's Telegram Stars transactions
* - :meth:`~telegram.Bot.refund_star_payment`
- Used for refunding a payment in Telegram Stars
.. raw:: html
</details>
<br>
.. raw:: html
<details>
<summary>Miscellaneous</summary>
.. list-table::
:align: left
:widths: 1 4
* - :meth:`~telegram.Bot.close`
- Used for closing server instance when switching to another local server
* - :meth:`~telegram.Bot.log_out`
- Used for logging out from cloud Bot API server
* - :meth:`~telegram.Bot.get_file`
- Used for getting basic info about a file
* - :meth:`~telegram.Bot.get_available_gifts`
- Used for getting information about gifts available for sending
* - :meth:`~telegram.Bot.get_me`
- Used for getting basic information about the bot
* - :meth:`~telegram.Bot.save_prepared_inline_message`
- Used for storing a message to be sent by a user of a Mini App
.. raw:: html
-12
View File
@@ -3,18 +3,6 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
.. raw:: html
<div style="display: none">
Hidden Headline
===============
This is just here to get furo to display the right sidebar.
.. raw:: html
</div>
.. include:: ../../README.rst
.. The toctrees are hidden such that they don't render on the start page but still include the contents into the documentation.
-6
View File
@@ -1,6 +0,0 @@
AffiliateInfo
=============
.. autoclass:: telegram.AffiliateInfo
:members:
:show-inheritance:
+1 -11
View File
@@ -29,7 +29,6 @@ Available Types
telegram.chat
telegram.chatadministratorrights
telegram.chatbackground
telegram.copytextbutton
telegram.backgroundtype
telegram.backgroundtypefill
telegram.backgroundtypewallpaper
@@ -89,10 +88,8 @@ Available Types
telegram.inputmediadocument
telegram.inputmediaphoto
telegram.inputmediavideo
telegram.inputpaidmedia
telegram.inputpaidmediaphoto
telegram.inputpaidmediavideo
telegram.inputpolloption
telegram.inputsticker
telegram.keyboardbutton
telegram.keyboardbuttonpolltype
telegram.keyboardbuttonrequestchat
@@ -116,12 +113,6 @@ Available Types
telegram.messageoriginuser
telegram.messagereactioncountupdated
telegram.messagereactionupdated
telegram.paidmedia
telegram.paidmediainfo
telegram.paidmediaphoto
telegram.paidmediapreview
telegram.paidmediapurchased
telegram.paidmediavideo
telegram.photosize
telegram.poll
telegram.pollanswer
@@ -131,7 +122,6 @@ Available Types
telegram.reactiontype
telegram.reactiontypecustomemoji
telegram.reactiontypeemoji
telegram.reactiontypepaid
telegram.replykeyboardmarkup
telegram.replykeyboardremove
telegram.replyparameters
-2
View File
@@ -1,8 +1,6 @@
Chat
====
.. Also lists methods of _ChatBase, but not the ones of TelegramObject
.. autoclass:: telegram.Chat
:members:
:show-inheritance:
:inherited-members: TelegramObject
+1 -3
View File
@@ -1,8 +1,6 @@
ChatFullInfo
============
.. Also lists methods of _ChatBase, but not the ones of TelegramObject
.. autoclass:: telegram.ChatFullInfo
:members:
:show-inheritance:
:inherited-members: TelegramObject
:show-inheritance:
+1 -1
View File
@@ -5,5 +5,5 @@ telegram.constants Module
:members:
:show-inheritance:
:no-undoc-members:
:inherited-members: Enum, EnumMeta, str, int, float
:inherited-members: Enum, EnumMeta, str, int
:exclude-members: __format__, __new__, __repr__, __str__
-6
View File
@@ -1,6 +0,0 @@
CopyTextButton
==============
.. autoclass:: telegram.CopyTextButton
:members:
:show-inheritance:
@@ -18,7 +18,6 @@ Handlers
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.messagereactionhandler
telegram.ext.paidmediapurchasedhandler
telegram.ext.pollanswerhandler
telegram.ext.pollhandler
telegram.ext.precheckoutqueryhandler
@@ -1,6 +0,0 @@
PaidMediaPurchasedHandler
=========================
.. autoclass:: telegram.ext.PaidMediaPurchasedHandler
:members:
:show-inheritance:
-15
View File
@@ -1,21 +1,6 @@
.. _games-tree:
Games
-----
Your bot can offer users **HTML5 games** to play solo or to compete against each other in groups and one-on-one chats. Create games via `@BotFather <https://telegram.me/BotFather>`_ using the ``/newgame`` command. Please note that this kind of power requires responsibility: you will need to accept the terms for each game that your bots will be offering.
* Games are a new type of content on Telegram, represented by the :class:`telegram.Game` and :class:`telegram.InlineQueryResultGame` objects.
* Once you've created a game via `BotFather <https://t.me/botfather>`_, you can send games to chats as regular messages using the :meth:`~telegram.Bot.sendGame` method, or use :ref:`inline mode <inline-tree>` with :class:`telegram.InlineQueryResultGame`.
* If you send the game message without any buttons, it will automatically have a 'Play ``GameName``' button. When this button is pressed, your bot gets a :class:`telegram.CallbackQuery` with the ``game_short_name`` of the requested game. You provide the correct URL for this particular user and the app opens the game in the in-app browser.
* You can manually add multiple buttons to your game message. Please note that the first button in the first row **must always** launch the game, using the field ``callback_game`` in :class:`telegram.InlineKeyboardButton`. You can add extra buttons according to taste: e.g., for a description of the rules, or to open the game's official community.
* To make your game more attractive, you can upload a GIF animation that demonstrates the game to the users via `BotFather <https://t.me/botfather>`_ (see `Lumberjack <https://t.me/gamebot?game=lumberjack>`_ for example).
* A game message will also display high scores for the current chat. Use :meth:`~telegram.Bot.setGameScore` to post high scores to the chat with the game, add the :paramref:`~telegram.Bot.set_game_score.disable_edit_message` parameter to disable automatic update of the message with the current scoreboard.
* Use :meth:`~telegram.Bot.getGameHighScores` to get data for in-game high score tables.
* You can also add an extra sharing button for users to share their best score to different chats.
* For examples of what can be done using this new stuff, check the `@gamebot <https://t.me/gamebot>`_ and `@gamee <https://t.me/gamee>`_ bots.
.. toctree::
:titlesonly:
-6
View File
@@ -1,6 +0,0 @@
Gift
====
.. autoclass:: telegram.Gift
:members:
:show-inheritance:
-6
View File
@@ -1,6 +0,0 @@
Gifts
=====
.. autoclass:: telegram.Gifts
:members:
:show-inheritance:
-9
View File
@@ -1,14 +1,6 @@
.. _inline-tree:
Inline Mode
-----------
The following methods and objects allow your bot to work in `inline mode <https://core.telegram.org/bots/inline>`_.
Please see Telegrams `Introduction to Inline bots <https://core.telegram.org/bots/inline>`_ for more details.
To enable this option, send the ``/setinline`` command to `@BotFather <https://t.me/botfather>`_ and provide the placeholder text that the user will see in the input field after typing your bot's name.
.. toctree::
:titlesonly:
@@ -42,4 +34,3 @@ To enable this option, send the ``/setinline`` command to `@BotFather <https://t
telegram.inputvenuemessagecontent
telegram.inputcontactmessagecontent
telegram.inputinvoicemessagecontent
telegram.preparedinlinemessage
-6
View File
@@ -1,6 +0,0 @@
InputPaidMedia
==============
.. autoclass:: telegram.InputPaidMedia
:members:
:show-inheritance:
@@ -1,6 +0,0 @@
InputPaidMediaPhoto
===================
.. autoclass:: telegram.InputPaidMediaPhoto
:members:
:show-inheritance:
@@ -1,6 +0,0 @@
InputPaidMediaVideo
===================
.. autoclass:: telegram.InputPaidMediaVideo
:members:
:show-inheritance:
-6
View File
@@ -1,6 +0,0 @@
PaidMedia
=========
.. autoclass:: telegram.PaidMedia
:members:
:show-inheritance:
-6
View File
@@ -1,6 +0,0 @@
PaidMediaInfo
=============
.. autoclass:: telegram.PaidMediaInfo
:members:
:show-inheritance:
-6
View File
@@ -1,6 +0,0 @@
PaidMediaPhoto
==============
.. autoclass:: telegram.PaidMediaPhoto
:members:
:show-inheritance:
@@ -1,6 +0,0 @@
PaidMediaPreview
================
.. autoclass:: telegram.PaidMediaPreview
:members:
:show-inheritance:
@@ -1,6 +0,0 @@
PaidMediaPurchased
==================
.. autoclass:: telegram.PaidMediaPurchased
:members:
:show-inheritance:
-6
View File
@@ -1,6 +0,0 @@
PaidMediaVideo
==============
.. autoclass:: telegram.PaidMediaVideo
:members:
:show-inheritance:
-3
View File
@@ -1,9 +1,6 @@
Passport
--------
Passport is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Please see the `manual <https://core.telegram.org/passport>`_ for details.
.. toctree::
:titlesonly:
-21
View File
@@ -1,35 +1,14 @@
.. _payments-tree:
Payments
--------
Your bot can accept payments from Telegram users. Please see the `introduction to payments <https://core.telegram.org/bots/payments>`_ for more details on the process and how to set up payments for your bot.
.. toctree::
:titlesonly:
telegram.affiliateinfo
telegram.invoice
telegram.labeledprice
telegram.orderinfo
telegram.precheckoutquery
telegram.refundedpayment
telegram.revenuewithdrawalstate
telegram.revenuewithdrawalstatefailed
telegram.revenuewithdrawalstatepending
telegram.revenuewithdrawalstatesucceeded
telegram.shippingaddress
telegram.shippingoption
telegram.shippingquery
telegram.startransaction
telegram.startransactions
telegram.successfulpayment
telegram.transactionpartner
telegram.transactionpartneraffiliateprogram
telegram.transactionpartnerchat
telegram.transactionpartnerfragment
telegram.transactionpartnerother
telegram.transactionpartnertelegramads
telegram.transactionpartnertelegramapi
telegram.transactionpartneruser
+1 -1
View File
@@ -1,6 +1,6 @@
PhotoSize
=========
.. Also lists methods of _BaseMedium, but not the ones of TelegramObject
.. Also lists methods of _BaseThumbedMedium, but not the ones of TelegramObject
.. autoclass:: telegram.PhotoSize
:members:
@@ -1,6 +0,0 @@
PreparedInlineMessage
=====================
.. autoclass:: telegram.PreparedInlineMessage
:members:
:show-inheritance:
@@ -1,6 +0,0 @@
ReactionTypePaid
================
.. autoclass:: telegram.ReactionTypePaid
:members:
:show-inheritance:
-6
View File
@@ -1,6 +0,0 @@
RefundedPayment
===============
.. autoclass:: telegram.RefundedPayment
:members:
:show-inheritance:
@@ -1,7 +0,0 @@
RevenueWithdrawalState
======================
.. autoclass:: telegram.RevenueWithdrawalState
:members:
:show-inheritance:
:inherited-members: TelegramObject
@@ -1,7 +0,0 @@
RevenueWithdrawalStateFailed
=============================
.. autoclass:: telegram.RevenueWithdrawalStateFailed
:members:
:show-inheritance:
:inherited-members: TelegramObject
@@ -1,7 +0,0 @@
RevenueWithdrawalStatePending
=============================
.. autoclass:: telegram.RevenueWithdrawalStatePending
:members:
:show-inheritance:
:inherited-members: TelegramObject
@@ -1,7 +0,0 @@
RevenueWithdrawalStateSucceeded
===============================
.. autoclass:: telegram.RevenueWithdrawalStateSucceeded
:members:
:show-inheritance:
:inherited-members: TelegramObject
-7
View File
@@ -1,7 +0,0 @@
StarTransaction
===============
.. autoclass:: telegram.StarTransaction
:members:
:show-inheritance:
:inherited-members: TelegramObject
@@ -1,8 +0,0 @@
StarTransactions
================
.. autoclass:: telegram.StarTransactions
:members:
:show-inheritance:
:inherited-members: TelegramObject
-5
View File
@@ -1,14 +1,9 @@
Stickers
--------
The following methods and objects allow your bot to handle stickers and sticker sets.
.. toctree::
:titlesonly:
telegram.gift
telegram.gifts
telegram.inputsticker
telegram.maskposition
telegram.sticker
telegram.stickerset
@@ -1,7 +0,0 @@
TransactionPartner
==================
.. autoclass:: telegram.TransactionPartner
:members:
:show-inheritance:
:inherited-members: TelegramObject
@@ -1,6 +0,0 @@
TransactionPartnerAffiliateProgram
===================================
.. autoclass:: telegram.TransactionPartnerAffiliateProgram
:members:
:show-inheritance:
@@ -1,7 +0,0 @@
TransactionPartnerChat
======================
.. autoclass:: telegram.TransactionPartnerChat
:members:
:show-inheritance:
:inherited-members: TransactionPartner
@@ -1,7 +0,0 @@
TransactionPartnerFragment
==========================
.. autoclass:: telegram.TransactionPartnerFragment
:members:
:show-inheritance:
:inherited-members: TransactionPartner
@@ -1,7 +0,0 @@
TransactionPartnerOther
=======================
.. autoclass:: telegram.TransactionPartnerOther
:members:
:show-inheritance:
:inherited-members: TransactionPartner
@@ -1,7 +0,0 @@
TransactionPartnerTelegramAds
=============================
.. autoclass:: telegram.TransactionPartnerTelegramAds
:members:
:show-inheritance:
:inherited-members: TransactionPartner
@@ -1,7 +0,0 @@
TransactionPartnerTelegramApi
=============================
.. autoclass:: telegram.TransactionPartnerTelegramApi
:members:
:show-inheritance:
:inherited-members: TransactionPartner
@@ -1,7 +0,0 @@
TransactionPartnerUser
======================
.. autoclass:: telegram.TransactionPartnerUser
:members:
:show-inheritance:
:inherited-members: TransactionPartner
+1 -21
View File
@@ -16,8 +16,6 @@
.. |editreplymarkup| replace:: It is currently only possible to edit messages without :attr:`telegram.Message.reply_markup` or with inline keyboards.
.. |bcid_edit_time| replace:: Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within *48 hours* from the time they were sent.
.. |toapikwargsbase| replace:: These arguments are also considered by :meth:`~telegram.TelegramObject.to_dict` and :meth:`~telegram.TelegramObject.to_json`, i.e. when passing objects to Telegram. Passing them to Telegram is however not guaranteed to work for all kinds of objects, e.g. this will fail for objects that can not directly be JSON serialized.
.. |toapikwargsarg| replace:: Arbitrary keyword arguments. Can be used to store data for which there are no dedicated attributes. |toapikwargsbase|
@@ -60,12 +58,10 @@
.. |removed_thumb_note| replace:: Removed the deprecated argument and attribute ``thumb``.
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url`` which made thumbnail_url mandatory.
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``.
.. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``.
.. |thumbnail_url_mandatory| replace:: Removal of the deprecated argument ``thumb_url`` made ``thumbnail_url`` mandatory.
.. |async_context_manager| replace:: Asynchronous context manager which
.. |reply_parameters| replace:: Description of the message to reply to.
@@ -85,19 +81,3 @@
.. |non_optional_story_argument| replace:: As of this version, this argument is now required. In accordance with our `stability policy <https://docs.python-telegram-bot.org/en/stable/stability_policy.html>`__, the signature will be kept as optional for now, though they are mandatory and an error will be raised if you don't pass it.
.. |business_id_str| replace:: Unique identifier of the business connection on behalf of which the message will be sent.
.. |business_id_str_edit| replace:: Unique identifier of the business connection on behalf of which the message to be edited was sent
.. |message_effect_id| replace:: Unique identifier of the message effect to be added to the message; for private chats only.
.. |show_cap_above_med| replace:: :obj:`True`, if the caption must be shown above the message media.
.. |tg_stars| replace:: `Telegram Stars <https://t.me/BotNews/90>`__
.. |allow_paid_broadcast| replace:: Pass True to allow up to :tg-const:`telegram.constants.FloodLimit.PAID_MESSAGES_PER_SECOND` messages per second, ignoring `broadcasting limits <https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once>`__ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
.. |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.
.. |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.
+4 -4
View File
@@ -12,7 +12,7 @@ To use arbitrary callback data, you must install PTB via
`pip install "python-telegram-bot[callback-data]"`
"""
import logging
from typing import cast
from typing import List, Tuple, cast
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends a message with 5 inline buttons attached."""
number_list: list[int] = []
number_list: List[int] = []
await update.message.reply_text("Please choose:", reply_markup=build_keyboard(number_list))
@@ -55,7 +55,7 @@ async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.effective_message.reply_text("All clear!")
def build_keyboard(current_list: list[int]) -> InlineKeyboardMarkup:
def build_keyboard(current_list: List[int]) -> InlineKeyboardMarkup:
"""Helper function to build the next inline keyboard."""
return InlineKeyboardMarkup.from_column(
[InlineKeyboardButton(str(i), callback_data=(i, current_list)) for i in range(1, 6)]
@@ -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)
+2 -2
View File
@@ -12,7 +12,7 @@ bot.
"""
import logging
from typing import Optional
from typing import Optional, Tuple
from telegram import Chat, ChatMember, ChatMemberUpdated, Update
from telegram.constants import ParseMode
@@ -37,7 +37,7 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
def extract_status_change(chat_member_update: ChatMemberUpdated) -> Optional[tuple[bool, bool]]:
def extract_status_change(chat_member_update: ChatMemberUpdated) -> Optional[Tuple[bool, bool]]:
"""Takes a ChatMemberUpdated instance and extracts whether the 'old_chat_member' was a member
of the chat and whether the 'new_chat_member' is a member of the chat. Returns None, if
the status didn't change.
+3 -3
View File
@@ -12,7 +12,7 @@ bot.
import logging
from collections import defaultdict
from typing import Optional
from typing import DefaultDict, Optional, Set
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.constants import ParseMode
@@ -40,7 +40,7 @@ class ChatData:
"""Custom class for chat_data. Here we store data per message."""
def __init__(self) -> None:
self.clicks_per_message: defaultdict[int, int] = defaultdict(int)
self.clicks_per_message: DefaultDict[int, int] = defaultdict(int)
# The [ExtBot, dict, ChatData, dict] is for type checkers like mypy
@@ -57,7 +57,7 @@ class CustomContext(CallbackContext[ExtBot, dict, ChatData, dict]):
self._message_id: Optional[int] = None
@property
def bot_user_ids(self) -> set[int]:
def bot_user_ids(self) -> Set[int]:
"""Custom shortcut to access a value stored in the bot_data dict"""
return self.bot_data.setdefault("user_ids", set())
+2 -1
View File
@@ -15,6 +15,7 @@ bot.
"""
import logging
from typing import Dict
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
@@ -45,7 +46,7 @@ reply_keyboard = [
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data: dict[str, str]) -> str:
def facts_to_str(user_data: Dict[str, str]) -> str:
"""Helper function for formatting the gathered user info."""
facts = [f"{key} - {value}" for key, value in user_data.items()]
return "\n".join(facts).join(["\n", "\n"])
+5 -5
View File
@@ -15,7 +15,7 @@ bot.
"""
import logging
from typing import Any
from typing import Any, Dict, Tuple
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
@@ -66,7 +66,7 @@ END = ConversationHandler.END
# Helper
def _name_switcher(level: str) -> tuple[str, str]:
def _name_switcher(level: str) -> Tuple[str, str]:
if level == PARENTS:
return "Father", "Mother"
return "Brother", "Sister"
@@ -122,7 +122,7 @@ async def adding_self(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str
async def show_data(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str:
"""Pretty print gathered data."""
def pretty_print(data: dict[str, Any], level: str) -> str:
def pretty_print(data: Dict[str, Any], level: str) -> str:
people = data.get(level)
if not people:
return "\nNo information yet."
@@ -371,8 +371,8 @@ def main() -> None:
entry_points=[CommandHandler("start", start)],
states={
SHOWING: [CallbackQueryHandler(start, pattern="^" + str(END) + "$")],
SELECTING_ACTION: selection_handlers, # type: ignore[dict-item]
SELECTING_LEVEL: selection_handlers, # type: ignore[dict-item]
SELECTING_ACTION: selection_handlers,
SELECTING_LEVEL: selection_handlers,
DESCRIBING_SELF: [description_conv],
STOPPING: [CommandHandler("start", start)],
},
+10 -10
View File
@@ -47,9 +47,9 @@ async def msg(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
# Files will be downloaded to current directory
for data in passport_data.decrypted_data: # This is where the data gets decrypted
if data.type == "phone_number":
logger.info("Phone: %s", data.phone_number)
print("Phone: ", data.phone_number)
elif data.type == "email":
logger.info("Email: %s", data.email)
print("Email: ", data.email)
if data.type in (
"personal_details",
"passport",
@@ -58,7 +58,7 @@ async def msg(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"internal_passport",
"address",
):
logger.info(data.type, data.data)
print(data.type, data.data)
if data.type in (
"utility_bill",
"bank_statement",
@@ -66,28 +66,28 @@ async def msg(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"passport_registration",
"temporary_registration",
):
logger.info(data.type, len(data.files), "files")
print(data.type, len(data.files), "files")
for file in data.files:
actual_file = await file.get_file()
logger.info(actual_file)
print(actual_file)
await actual_file.download_to_drive()
if (
data.type in ("passport", "driver_license", "identity_card", "internal_passport")
and data.front_side
):
front_file = await data.front_side.get_file()
logger.info(data.type, front_file)
print(data.type, front_file)
await front_file.download_to_drive()
if data.type in ("driver_license" and "identity_card") and data.reverse_side:
reverse_file = await data.reverse_side.get_file()
logger.info(data.type, reverse_file)
print(data.type, reverse_file)
await reverse_file.download_to_drive()
if (
data.type in ("passport", "driver_license", "identity_card", "internal_passport")
and data.selfie
):
selfie_file = await data.selfie.get_file()
logger.info(data.type, selfie_file)
print(data.type, selfie_file)
await selfie_file.download_to_drive()
if data.translation and data.type in (
"passport",
@@ -100,10 +100,10 @@ async def msg(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"passport_registration",
"temporary_registration",
):
logger.info(data.type, len(data.translation), "translation")
print(data.type, len(data.translation), "translation")
for file in data.translation:
actual_file = await file.get_file()
logger.info(actual_file)
print(actual_file)
await actual_file.download_to_drive()
+42 -47
View File
@@ -2,7 +2,7 @@
# pylint: disable=unused-argument
# This program is dedicated to the public domain under the CC0 license.
"""Basic example for a bot that can receive payments from users."""
"""Basic example for a bot that can receive payment from user."""
import logging
@@ -26,44 +26,44 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# Insert the token from your payment provider.
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
PAYMENT_PROVIDER_TOKEN = "PAYMENT_PROVIDER_TOKEN"
async def start_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Provides instructions on how to use the bot."""
"""Displays info on how to use the bot."""
msg = (
"Use /shipping to receive an invoice with shipping included, or /noshipping for an "
"Use /shipping to get an invoice for shipping-payment, or /noshipping for an "
"invoice without shipping."
)
await update.message.reply_text(msg)
async def start_with_shipping_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends an invoice which triggers a shipping query."""
"""Sends an invoice with shipping-payment."""
chat_id = update.message.chat_id
title = "Payment Example"
description = "Example of a payment process using the python-telegram-bot library."
# Unique payload to identify this payment request as being from your bot
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
payload = "Custom-Payload"
# Set up the currency.
# List of supported currencies: https://core.telegram.org/bots/payments#supported-currencies
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
currency = "USD"
# Price in dollars
# price in dollars
price = 1
# Convert price to cents from dollars.
# price * 100 so as to include 2 decimal points
# check https://core.telegram.org/bots/payments#supported-currencies for more details
prices = [LabeledPrice("Test", price * 100)]
# Optional parameters like need_shipping_address and is_flexible trigger extra user prompts
# https://docs.python-telegram-bot.org/en/stable/telegram.bot.html#telegram.Bot.send_invoice
# 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,
provider_token=PAYMENT_PROVIDER_TOKEN,
need_name=True,
need_phone_number=True,
need_email=True,
@@ -75,91 +75,86 @@ async def start_with_shipping_callback(update: Update, context: ContextTypes.DEF
async def start_without_shipping_callback(
update: Update, context: ContextTypes.DEFAULT_TYPE
) -> None:
"""Sends an invoice without requiring shipping details."""
"""Sends an invoice without shipping-payment."""
chat_id = update.message.chat_id
title = "Payment Example"
description = "Example of a payment process using the python-telegram-bot library."
# Unique payload to identify this payment request as being from your bot
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
payload = "Custom-Payload"
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
currency = "USD"
# Price in dollars
# price in dollars
price = 1
# Convert price to cents from dollars.
# price * 100 so as to include 2 decimal points
prices = [LabeledPrice("Test", price * 100)]
# 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,
currency,
prices,
provider_token=PAYMENT_PROVIDER_TOKEN,
chat_id, title, description, payload, PAYMENT_PROVIDER_TOKEN, currency, prices
)
async def shipping_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handles the ShippingQuery with available shipping options."""
"""Answers the ShippingQuery with ShippingOptions"""
query = update.shipping_query
# Verify if the payload matches, ensure it's from your bot
# check the payload, is this from your bot?
if query.invoice_payload != "Custom-Payload":
# If not, respond with an error
# answer False pre_checkout_query
await query.answer(ok=False, error_message="Something went wrong...")
return
# Define available shipping options
# First option with a single price entry
# First option has a single LabeledPrice
options = [ShippingOption("1", "Shipping Option A", [LabeledPrice("A", 100)])]
# Second option with multiple price entries
# second option has an array of LabeledPrice objects
price_list = [LabeledPrice("B1", 150), LabeledPrice("B2", 200)]
options.append(ShippingOption("2", "Shipping Option B", price_list))
await query.answer(ok=True, shipping_options=options)
# After (optional) shipping, process the pre-checkout step
# after (optional) shipping, it's the pre-checkout
async def precheckout_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Responds to the PreCheckoutQuery as the final confirmation for checkout."""
"""Answers the PreQecheckoutQuery"""
query = update.pre_checkout_query
# Verify if the payload matches, ensure it's from your bot
# check the payload, is this from your bot?
if query.invoice_payload != "Custom-Payload":
# If not, respond with an error
# answer False pre_checkout_query
await query.answer(ok=False, error_message="Something went wrong...")
else:
await query.answer(ok=True)
# Final callback after successful payment
# finally, after contacting the payment provider...
async def successful_payment_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Acknowledges successful payment and thanks the user."""
await update.message.reply_text("Thank you for your payment.")
"""Confirms the successful payment."""
# do something after successfully receiving payment?
await update.message.reply_text("Thank you for your payment!")
def main() -> None:
"""Starts the bot and sets up handlers."""
"""Run the bot."""
# Create the Application and pass it your bot's token.
application = Application.builder().token("TOKEN").build()
# Start command to display usage instructions
# simple start function
application.add_handler(CommandHandler("start", start_callback))
# Command handlers for starting the payment process
# Add command handler to start the payment invoice
application.add_handler(CommandHandler("shipping", start_with_shipping_callback))
application.add_handler(CommandHandler("noshipping", start_without_shipping_callback))
# Handler for shipping query (if product requires shipping)
# Optional handler if your product requires shipping
application.add_handler(ShippingQueryHandler(shipping_callback))
# Pre-checkout handler for verifying payment details.
# Pre-checkout handler to final check
application.add_handler(PreCheckoutQueryHandler(precheckout_callback))
# Handler for successful payment. Notify the user that the payment was successful.
# Success! Notify your user!
application.add_handler(
MessageHandler(filters.SUCCESSFUL_PAYMENT, successful_payment_callback)
)
# Start polling for updates until interrupted (CTRL+C)
# Run the bot until the user presses Ctrl-C
application.run_polling(allowed_updates=Update.ALL_TYPES)
+2 -1
View File
@@ -15,6 +15,7 @@ bot.
"""
import logging
from typing import Dict
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
@@ -46,7 +47,7 @@ reply_keyboard = [
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data: dict[str, str]) -> str:
def facts_to_str(user_data: Dict[str, str]) -> str:
"""Helper function for formatting the gathered user info."""
facts = [f"{key} - {value}" for key, value in user_data.items()]
return "\n".join(facts).join(["\n", "\n"])

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